1
0
mirror of https://github.com/esphome/esphome.git synced 2025-11-05 09:31:54 +00:00

Compare commits

..

181 Commits

Author SHA1 Message Date
Jesse Hills
a7ad4482f0 Merge pull request #5664 from esphome/bump-2023.10.6
2023.10.6
2023-11-03 08:49:48 +13:00
Jesse Hills
aa17661002 Bump version to 2023.10.6 2023-11-03 08:11:58 +13:00
Jesse Hills
4e65aac7ae Revert "Ensure that all uses of strncpy in wifi component are safe." (#5662) 2023-11-03 08:11:58 +13:00
Jesse Hills
229ba18e6c Merge pull request #5645 from esphome/bump-2023.10.5
2023.10.5
2023-11-01 14:07:42 +13:00
Jesse Hills
b99be250a0 Bump version to 2023.10.5 2023-11-01 12:19:16 +13:00
Jimmy Hedman
b9d4e2e501 Remove some explicit IPAddress casts (#5639) 2023-11-01 12:19:16 +13:00
Kevin P. Fleming
ef2531edf3 Ensure that all uses of strncpy in wifi component are safe. (#5636) 2023-11-01 12:19:16 +13:00
Jesse Hills
eae3089201 Add on_client_connected and disconnected to voice assistant (#5629) 2023-11-01 12:19:16 +13:00
Jesse Hills
0ea4de5f4c Add connection triggers to api (#5628) 2023-11-01 12:19:16 +13:00
Jesse Hills
1e0daefa16 Merge pull request #5627 from esphome/bump-2023.10.4
2023.10.4
2023-10-30 16:33:36 +13:00
Jesse Hills
6d991a1fc8 Bump version to 2023.10.4 2023-10-30 13:59:58 +13:00
Jesse Hills
a1845e1e72 Handle enum type in tuya text_sensor (#5626) 2023-10-30 13:59:58 +13:00
Dewet Diener
f96a839bcf Fix bug when requesting italic gfonts (#5623) 2023-10-30 13:59:58 +13:00
Jimmy Hedman
1282a15b14 Fixes ip include on arduino 2.7.4 (#5620) 2023-10-30 13:59:58 +13:00
Roger Busser
35039b45e4 Update current_based_cover bugfix (#5587) 2023-10-30 13:59:58 +13:00
Jesse Hills
390766eb67 Merge pull request #5596 from esphome/bump-2023.10.3
2023.10.3
2023-10-24 13:40:13 +13:00
Jesse Hills
899d280ac7 Bump version to 2023.10.3 2023-10-24 12:56:25 +13:00
Keith Burzinski
96dc7f0259 Set IP address type only when IPv4 and IPv6 are both enabled (#5595) 2023-10-24 12:56:25 +13:00
Jesse Hills
0104bf3fc8 Merge pull request #5590 from esphome/bump-2023.10.2
2023.10.2
2023-10-24 10:15:07 +13:00
Jesse Hills
9b1e1bf56c Bump version to 2023.10.2 2023-10-24 08:32:26 +13:00
dentra
33e0f16b3b Allow set climate preset to NONE (#5588) 2023-10-24 08:32:26 +13:00
Samuel Sieb
0807d60c6a fix canbus send config (#5585)
Co-authored-by: Samuel Sieb <samuel@sieb.net>
2023-10-24 08:32:03 +13:00
Jimmy Hedman
f018fde369 Set addr type when copy from ip4_addr_t (#5583) 2023-10-24 08:30:47 +13:00
Jimmy Hedman
c47f8fc02c Remove explicit cast for IPAddress (#5574)
* Remove explicit cast for IPAddress

* Make linter happy
2023-10-24 08:30:47 +13:00
Trent Houliston
76ab923780 Publish the pulse_meter total when setting the total (#5475) 2023-10-24 08:30:47 +13:00
Keith Burzinski
11dba3147d Improv Serial support via USB CDC and JTAG (#5559) 2023-10-24 08:30:47 +13:00
Jesse Hills
8c2d9101d5 Fix XOR condition (#5567) 2023-10-24 08:30:47 +13:00
Jesse Hills
61b8004536 Merge pull request #5566 from esphome/bump-2023.10.1
2023.10.1
2023-10-19 14:27:10 +13:00
Jesse Hills
db02c4ea21 Bump version to 2023.10.1 2023-10-19 13:30:13 +13:00
Mike La Spina
f077a5962d Incorrect ESP32 Strapping PIN Defined (#5563)
Co-authored-by: descipher <120155735+GelidusResearch@users.noreply.github.com>
2023-10-19 13:30:12 +13:00
Jesse Hills
fa4ba43eb9 Create IPv4 sockets if ipv6 is not enabled (#5565) 2023-10-19 13:30:12 +13:00
Jesse Hills
9579423b24 esp32_improv add timeout (#5556) 2023-10-19 13:30:12 +13:00
Jesse Hills
02449f24c9 Fix voice_assistant without a speaker (#5558) 2023-10-19 13:30:12 +13:00
Jesse Hills
b973238323 Merge pull request #5555 from esphome/bump-2023.10.0
2023.10.0
2023-10-18 17:38:50 +13:00
Jesse Hills
582b8383d2 Bump version to 2023.10.0 2023-10-18 16:47:03 +13:00
Jesse Hills
e1c9418aee Merge pull request #5554 from esphome/bump-2023.10.0b4
2023.10.0b4
2023-10-18 15:44:37 +13:00
Jesse Hills
2aa787f5f0 Bump version to 2023.10.0b4 2023-10-18 14:28:03 +13:00
Jesse Hills
2189a40a39 esp32_improv advertise capabilities and state in ble service data (#5553) 2023-10-18 14:28:03 +13:00
Fabian Bläse
51688d4078 SML: fix incomplete sign extension for abbreviated transmissions (#5544) 2023-10-18 14:28:03 +13:00
Jesse Hills
cc4c0e3e0b Fix default libretiny manufacturer reported to HA (#5549) 2023-10-18 14:28:02 +13:00
Jesse Hills
1a44c6487e Merge pull request #5548 from esphome/bump-2023.10.0b3
2023.10.0b3
2023-10-17 20:49:16 +13:00
Jesse Hills
5e7ce610a0 Bump version to 2023.10.0b3 2023-10-17 20:15:14 +13:00
Jesse Hills
1f02096edb More voice assistant fixes (#5547) 2023-10-17 20:15:14 +13:00
Jesse Hills
fd7d3c4332 Fix esp32_improv authorizer with no binary sensors in config (#5546) 2023-10-17 20:15:14 +13:00
Jesse Hills
61cf566560 Add stream start and end events (#5545) 2023-10-17 20:15:14 +13:00
Christian
97d624114d Add change i2c address and allow multi conf for TB6612FNG (#5492) 2023-10-17 20:15:14 +13:00
raineth
52e8a2e9e4 Make IPAddress's operator!= compare values, not memory addresses. (#5537)
Co-authored-by: Ben Winslow <rain@bluecherry.net>
2023-10-17 20:15:14 +13:00
Jesse Hills
261c271d60 Prometheus fix for esp-idf and fix newlines (#5536) 2023-10-17 20:15:14 +13:00
Jesse Hills
cb6e314336 Merge pull request #5528 from esphome/bump-2023.10.0b2
2023.10.0b2
2023-10-13 15:56:01 +13:00
Jesse Hills
90315b3c40 Bump version to 2023.10.0b2 2023-10-13 14:16:22 +13:00
Cossid
5d7c3d1622 BP1658CJ - Clear all channels before sleeping. (#5525) 2023-10-13 14:16:22 +13:00
Cossid
8c1ad1e9a6 SM10BIT_BASE - Add delays and ACKs, clear all channels before sleeping. (#5526) 2023-10-13 14:16:22 +13:00
Jesse Hills
969f6dbe13 Update Improv BLE component (#5518) 2023-10-13 14:16:22 +13:00
Cossid
6cce6d4c36 BD5758D - Add delays and ACKs (#5524) 2023-10-13 14:16:22 +13:00
Nippey
d27e5e9c97 Update htu21d.cpp, fix publishing of heater level (#5520) 2023-10-13 14:16:22 +13:00
Jesse Hills
af3b22f8b7 Merge pull request #5516 from esphome/bump-2023.10.0b1
2023.10.0b1
2023-10-12 16:54:08 +13:00
Jesse Hills
cbc1b29f3e Merge branch 'dev' into bump-2023.10.0b1 2023-10-12 15:54:56 +13:00
Jesse Hills
f4ce8b8b6c Bump curl to 7.74.0-1.3+deb11u10 (#5517) 2023-10-12 15:52:57 +13:00
Jesse Hills
54363f1246 Bump version to 2023.10.0b1 2023-10-12 15:14:43 +13:00
Jesse Hills
11727391ad Bump version to 2023.11.0-dev 2023-10-12 15:14:43 +13:00
Jesse Hills
d500531c04 Merge branch 'dev' into bump-2023.10.0b1 2023-10-12 15:14:42 +13:00
Daniel Baulig
689dcd1e24 Add detail param to allow listing of select options in WebServer REST API (#5503) 2023-10-12 14:55:01 +13:00
Michael Hansen
2fc4e88271 Small fixes for voice assistant (#5513)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2023-10-12 14:51:29 +13:00
Mark Veinot
853d81c6dd As3935 calibration (#5366) 2023-10-12 14:27:36 +13:00
NP v/d Spek
e35de626a4 Allow manual set "Invert_display" (#5494) 2023-10-12 14:26:07 +13:00
justdaniel-gh
41f29c46d0 Fix e131 and voice_assistant sockets (#5502)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2023-10-10 23:42:38 +00:00
Oleg Tarasov
9d95f5c1da Added Nextion display error handling during setup (#5493) 2023-10-11 11:56:53 +13:00
Guillermo Ruffino
5d5cc96017 fix build lang schema for spi and i2c (#5509) 2023-10-11 11:52:40 +13:00
Jimmy Hedman
c9a8911029 Prepare protobuf for ESP-IDF >= 5 (#5510) 2023-10-11 11:51:53 +13:00
Robert Resch
c77a9ad363 Don't allow entity category "CONFIG" for sensors (#5505)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2023-10-10 07:00:05 +00:00
Jesse Hills
511af5845e Remote wake word support for voice assistant (#5229) 2023-10-10 19:52:42 +13:00
Oleg Tarasov
6b96089f02 Fixed precision for Nextion sensor with float values (#5497) 2023-10-09 19:16:12 -05:00
Jesse Hills
46be886ca6 Use platform consts (#5508) 2023-10-09 16:54:15 -05:00
Jesse Hills
be7e167c63 Deep sleep is only available on esp32 and esp8266 (#5507) 2023-10-10 09:41:28 +13:00
Ilia Sotnikov
c65d78f568 [Sprinkler] Initialize timers early to avoid crash (#5499) 2023-10-08 22:37:48 -05:00
Ilia Sotnikov
412a866de8 Move to Pillow 10.x (#5489)
Co-authored-by: Franck Nijhof <frenck@frenck.nl>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2023-10-09 13:47:52 +13:00
Jesse Hills
e09c217fde Bump docker dependencies (#5501) 2023-10-09 11:13:12 +13:00
Adam Goode
af62c2d9cf Implement sensor component for MMC5983 (#5361) 2023-10-09 10:26:58 +13:00
Angel Nunez Mencias
aba3cd557a add USE_SPI define (#5500) 2023-10-08 21:01:26 +00:00
Pavlo Dudnytskyi
7e7c83b3ca Support for Haier IR protocol added (#5403) 2023-10-09 09:49:55 +13:00
Luke Ansell
ee4ccf2762 Increased debug message precision (#5496) 2023-10-09 09:20:43 +13:00
Austin
fa0dcac2c7 Initial ESP32-H2 Support (#5498) 2023-10-09 07:34:12 +13:00
Keith Burzinski
44e5b0c745 Move CONF_IRQ_PIN into const.py (#5488) 2023-10-06 00:28:51 -05:00
Clyde Stubbs
b6d5cb4142 St7789v and SPI data rate (#5472) 2023-10-04 15:18:33 -05:00
Clyde Stubbs
4e8cba49f1 MAX7219 - Update intensity (#5477) 2023-10-03 20:15:44 -05:00
Clyde Stubbs
506c2ba6c7 ST7789v - Allow predefined backlight pin to be disabled. (#5476)
* Allow predefined backlight pin to be disabled.

* Add test

* Update esphome/components/st7789v/display.py
2023-10-03 20:10:26 -05:00
kahrendt
050fa0d4c1 Fix units for SPS30 number concentration sensors (#5452) 2023-10-03 23:01:57 +13:00
kahrendt
88bb051f37 Add xor automation condition (#5453) 2023-10-03 22:58:11 +13:00
Jesse Hills
0d800958aa Merge pull request #5474 from esphome/bump-2023.9.3
2023.9.3
2023-10-03 21:25:58 +13:00
Jesse Hills
471533d041 Bump version to 2023.9.3 2023-10-03 13:35:19 +13:00
Faidon Liambotis
7dfc4c74da Tuya Number: split "multiply" to a separate option (#5458) 2023-10-03 13:35:19 +13:00
dwildstr
f709350b04 Sleep mode fix for BP5758D driver (#5461)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2023-10-03 13:35:19 +13:00
dependabot[bot]
85c5928baa Bump zeroconf from 0.115.0 to 0.115.1 (#5470)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-03 13:35:19 +13:00
Clyde Stubbs
f5dfbaff4b Support RP2040 hardware SPI (#5466) 2023-10-03 13:35:18 +13:00
Maxime Gauduin
689c2f11a3 add pin config for denky_d4 (#5471) 2023-10-03 13:35:18 +13:00
dependabot[bot]
f73fd97525 Bump zeroconf from 0.112.0 to 0.115.0 (#5432)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-03 13:35:18 +13:00
Faidon Liambotis
f38849828d Tuya Number: split "multiply" to a separate option (#5458) 2023-10-03 13:23:18 +13:00
dwildstr
e95ba57a61 Sleep mode fix for BP5758D driver (#5461)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2023-10-02 23:06:54 +00:00
dependabot[bot]
4913256597 Bump zeroconf from 0.115.0 to 0.115.1 (#5470)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-03 10:51:29 +13:00
dependabot[bot]
401a386219 Bump actions/setup-python from 4.7.0 to 4.7.1 (#5467)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-03 10:25:15 +13:00
Clyde Stubbs
205f41509b Support RP2040 hardware SPI (#5466) 2023-10-03 10:24:20 +13:00
Maxime Gauduin
e87c8d550b add pin config for denky_d4 (#5471) 2023-10-02 21:15:29 +00:00
De Cock Xavier
a33b8abce8 Feat/component poller suspend (#5423) 2023-10-03 09:25:13 +13:00
Jesse Hills
40523e6823 Merge pull request #5465 from esphome/bump-2023.9.2
2023.9.2
2023-10-02 21:24:01 +13:00
Jesse Hills
5e1472185c Bump version to 2023.9.2 2023-10-02 17:01:22 +13:00
Jesse Hills
af005a6554 Ensure esphome directory exists on addon startup (#5464) 2023-10-02 17:01:22 +13:00
Angel Nunez Mencias
efd31be21c Fix SPI support for second bus on 2023.9.1 (#5456) 2023-10-02 17:01:22 +13:00
Avri Chen-Roth
e9bda2810f Fix an Issue with IR Remote Climate and Whirlpool protocol toggle (#5447)
Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2023-10-02 17:01:22 +13:00
Clyde Stubbs
ec4777b8d0 SPI fixes for buggy components (#5446) 2023-10-02 17:01:22 +13:00
Jesse Hills
589b9e10b2 Ensure esphome directory exists on addon startup (#5464) 2023-10-02 13:45:24 +13:00
Angel Nunez Mencias
2513ede3ec Add testcases for multiple SPI buses on ESP32 Arduino (#5457) 2023-09-29 20:48:51 -05:00
Angel Nunez Mencias
0b5a57ead4 Fix SPI support for second bus on 2023.9.1 (#5456) 2023-09-29 23:34:56 +00:00
John K. Luebs
b3dc2d43a5 Do not enable SHT3x heater by default. Fixes #4886. (#5445) 2023-09-29 18:27:40 -05:00
De Cock Xavier
d3913be7e5 [ssd1351] fix: wait for the component to be at least in setup phase b… (#5454) 2023-09-29 17:08:56 -05:00
leoshusar
2c94c3d96f [BP1658CJ] Missing clock line delays and ack bit (#5448)
* fix: missing clock line delays and ack bit

* chore: remove esphome namespace from delay methods

* style: removed trailing whitespace
2023-09-29 16:40:56 +13:00
Avri Chen-Roth
4d81153150 Fix an Issue with IR Remote Climate and Whirlpool protocol toggle (#5447)
Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2023-09-29 14:17:32 +13:00
Clyde Stubbs
507dc5f496 SPI fixes for buggy components (#5446) 2023-09-29 00:36:31 +00:00
dependabot[bot]
4335543575 Bump zeroconf from 0.112.0 to 0.115.0 (#5432)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-29 10:58:51 +13:00
Jesse Hills
9b75121337 Merge pull request #5442 from esphome/bump-2023.9.1
2023.9.1
2023-09-28 13:05:04 +13:00
Jesse Hills
d262548d2e Bump version to 2023.9.1 2023-09-28 11:52:35 +13:00
Jesse Hills
b5b654e054 Migrate dashboard json files to /data folder instead of wiping out (#5441) 2023-09-28 11:52:35 +13:00
Marc J
dae8ab563c Tuya Number Scaling by step value (#5108) 2023-09-28 11:52:35 +13:00
Jesse Hills
12365976c4 Migrate dashboard json files to /data folder instead of wiping out (#5441) 2023-09-28 11:40:36 +13:00
Jesse Hills
4ac4492241 Fix .esphome path when not using envvar (#5440) 2023-09-28 11:20:44 +13:00
Jimmy Hedman
57b7dd0fa2 Refactor ip address representation (#5252) 2023-09-27 21:38:43 +13:00
Marc J
9d4f471855 Tuya Number Scaling by step value (#5108) 2023-09-27 07:45:21 +00:00
Jesse Hills
5751e9ec59 Merge pull request #5435 from esphome/bump-2023.9.0
2023.9.0
2023-09-27 17:19:58 +13:00
Jesse Hills
cc1b7a7a56 Bump version to 2023.9.0 2023-09-27 16:21:35 +13:00
Jesse Hills
29249cdc1b Merge pull request #5434 from esphome/bump-2023.9.0b4
2023.9.0b4
2023-09-27 13:34:33 +13:00
Jesse Hills
e5bae8187f Bump version to 2023.9.0b4 2023-09-27 12:28:12 +13:00
Clyde Stubbs
69adebfefa Fix #4896 and #4903 (#5433) 2023-09-27 12:28:12 +13:00
Guillermo Ruffino
7dabbb65d0 Wireguard keepalive remove uint16 type (#5430) 2023-09-27 12:28:11 +13:00
Kuba Szczodrzyński
b30bab8c1b LibreTiny: enable MQTT, bump to v1.4.1 (#5419) 2023-09-27 12:28:11 +13:00
Clyde Stubbs
5360e14a9c Fix #4896 and #4903 (#5433) 2023-09-27 12:25:14 +13:00
Guillermo Ruffino
86db559f6e Wireguard keepalive remove uint16 type (#5430) 2023-09-27 12:25:00 +13:00
Kuba Szczodrzyński
2f7a378c7b LibreTiny: enable MQTT, bump to v1.4.1 (#5419) 2023-09-27 10:23:21 +13:00
dependabot[bot]
607d0b4264 Bump pylint from 2.17.5 to 2.17.6 (#5429)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-26 09:28:25 +13:00
Jesse Hills
0a1ed58454 Merge pull request #5426 from esphome/bump-2023.9.0b3
2023.9.0b3
2023-09-25 16:15:14 +13:00
Jesse Hills
5f5ee9c920 Bump version to 2023.9.0b3 2023-09-25 12:10:35 +13:00
dependabot[bot]
0aeebdd289 Bump zeroconf from 0.108.0 to 0.112.0 (#5392)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-25 12:10:35 +13:00
Odd Stråbø
33e2aa341e dallas: limit addresses to 64 bits (#5413) 2023-09-25 12:10:35 +13:00
Ilia Sotnikov
a42788812e [RP2040W] Fix WiFi bootloop upon LibreTiny support (#5414) 2023-09-25 12:10:35 +13:00
Clyde Stubbs
b07a038bc8 Fix SPI inverted clock on ESP8266 (#5416) 2023-09-25 12:10:34 +13:00
Kapil Yedidi
17e1d4c245 Fix typo in documentation (#5425) 2023-09-25 12:05:56 +13:00
dependabot[bot]
a031cc3b84 Bump zeroconf from 0.108.0 to 0.112.0 (#5392)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-25 10:25:51 +13:00
dependabot[bot]
c34d5111fc Bump actions/checkout from 4.0.0 to 4.1.0 (#5420)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-25 10:23:40 +13:00
Odd Stråbø
727056a28c dallas: limit addresses to 64 bits (#5413) 2023-09-25 10:15:28 +13:00
Ilia Sotnikov
0ca8dcd08e [RP2040W] Fix WiFi bootloop upon LibreTiny support (#5414) 2023-09-24 22:44:55 +13:00
Andrew Garrett
3c7c4e1dba Make ESPHome data dir configurable (#5417) 2023-09-24 22:34:37 +13:00
Clyde Stubbs
518ecb4cc4 Fix SPI inverted clock on ESP8266 (#5416) 2023-09-21 21:15:50 -07:00
Jesse Hills
55e36ab982 Merge pull request #5412 from esphome/bump-2023.9.0b2
2023.9.0b2
2023-09-21 12:52:21 +12:00
Jesse Hills
90835ab917 Bump version to 2023.9.0b2 2023-09-21 10:35:38 +12:00
Samuel Sieb
5b46088ae4 support keypads with pulldowns (#5404)
Co-authored-by: Samuel Sieb <samuel@sieb.net>
2023-09-21 10:35:38 +12:00
Kuba Szczodrzyński
d7e267eca5 Wizard: fix colored text in input prompts (#5313) 2023-09-21 10:35:38 +12:00
Trent Houliston
807c47a076 Make the pulse meter timeout on startup when no pulses are received (#5388) 2023-09-21 10:35:38 +12:00
Kevin P. Fleming
7ebe6a5894 http_request: Cleanups and safety improvements (#5360) 2023-09-21 10:35:37 +12:00
Anthony
41c829fa32 Remove Wi-Fi dependency from Midea component (#5394) 2023-09-21 10:35:37 +12:00
Joris S
8f1ce8c7f7 Climate preset fix (#5407) 2023-09-21 10:35:37 +12:00
Samuel Sieb
e55636ed52 fix handling of web server version (#5405)
Co-authored-by: Samuel Sieb <samuel@sieb.net>
2023-09-21 10:35:37 +12:00
Samuel Sieb
e886262055 fix disabled wifi power on 8266 (#5409)
Co-authored-by: Samuel Sieb <samuel@sieb.net>
2023-09-21 10:35:37 +12:00
Philipp Helo Rehs
2fa7f8c511 Add E-Trailer Gaslevel support to Mopeka Std Check (#5397)
* Add E-Trailer Gaslevel support to Mopeka Std Check

Signed-off-by: Philipp Helo Rehs <Philipp.Rehs@hhu.de>

* fix format

---------

Signed-off-by: Philipp Helo Rehs <Philipp.Rehs@hhu.de>
Co-authored-by: Philipp Helo Rehs <Philipp.Rehs@hhu.de>
2023-09-21 10:35:37 +12:00
Trevor North
4622ef770d Add shelly-dimmer-stm32 51.7 to known versions (#5400)
This version removes support for no-neutral setups in favor of fixing flickering some users have experienced.
2023-09-21 10:35:37 +12:00
rmmacias
d76f18b4f2 Update radon_eye_listener.cpp (#5401)
New devices identifiers do not star by the hardcoded string. FR:RE222 is the 8-char length string of my devices bought in 2023. This proposal aims at solve the topic by making the detection track devices starting only by FR:R
2023-09-21 10:35:37 +12:00
phoenixswiss
ec20778d83 Fix Waveshare 7.5v2 epaper screens are always powered on (#5283) 2023-09-21 10:35:37 +12:00
Michael Hansen
b3ca71c6fb Add patch to apt install (#5389) 2023-09-21 10:35:37 +12:00
Samuel Sieb
1100f67b66 support keypads with pulldowns (#5404)
Co-authored-by: Samuel Sieb <samuel@sieb.net>
2023-09-21 10:26:36 +12:00
Kuba Szczodrzyński
056a28906b Wizard: fix colored text in input prompts (#5313) 2023-09-21 10:09:23 +12:00
Trent Houliston
2c2821cd96 Make the pulse meter timeout on startup when no pulses are received (#5388) 2023-09-21 10:04:03 +12:00
Kevin P. Fleming
157a3e53dd http_request: Cleanups and safety improvements (#5360) 2023-09-21 10:02:29 +12:00
Anthony
61edf8c196 Remove Wi-Fi dependency from Midea component (#5394) 2023-09-21 09:30:22 +12:00
Joris S
397f57ce74 Climate preset fix (#5407) 2023-09-21 09:28:03 +12:00
Samuel Sieb
bf253c21fa fix handling of web server version (#5405)
Co-authored-by: Samuel Sieb <samuel@sieb.net>
2023-09-21 09:25:16 +12:00
Samuel Sieb
e3eef1cc6d fix disabled wifi power on 8266 (#5409)
Co-authored-by: Samuel Sieb <samuel@sieb.net>
2023-09-21 09:20:54 +12:00
Philipp Helo Rehs
11f6e555f9 Add E-Trailer Gaslevel support to Mopeka Std Check (#5397)
* Add E-Trailer Gaslevel support to Mopeka Std Check

Signed-off-by: Philipp Helo Rehs <Philipp.Rehs@hhu.de>

* fix format

---------

Signed-off-by: Philipp Helo Rehs <Philipp.Rehs@hhu.de>
Co-authored-by: Philipp Helo Rehs <Philipp.Rehs@hhu.de>
2023-09-17 17:30:52 +12:00
Fabian
164631fcec Ci find YAML tests dynamically (#5399)
* Find all YAML test files dynamically.

* Merge error

* GitHub set-ouput syntax upgrade.

---------

Co-authored-by: Your Name <you@example.com>
2023-09-17 17:24:31 +12:00
Trevor North
a61e3fadf6 Add shelly-dimmer-stm32 51.7 to known versions (#5400)
This version removes support for no-neutral setups in favor of fixing flickering some users have experienced.
2023-09-17 17:20:31 +12:00
rmmacias
b5f2d69ca5 Update radon_eye_listener.cpp (#5401)
New devices identifiers do not star by the hardcoded string. FR:RE222 is the 8-char length string of my devices bought in 2023. This proposal aims at solve the topic by making the detection track devices starting only by FR:R
2023-09-17 17:18:51 +12:00
phoenixswiss
55b5c0fc32 Fix Waveshare 7.5v2 epaper screens are always powered on (#5283) 2023-09-14 06:20:21 +00:00
Michael Hansen
280b090dfc Add patch to apt install (#5389) 2023-09-13 18:13:55 -05:00
Jesse Hills
9d97807587 Bump version to 2023.10.0-dev 2023-09-13 14:22:39 +12:00
vr6racer
11433c8c17 SX1509 component (#5385) 2023-09-13 14:14:54 +12:00
179 changed files with 3160 additions and 872 deletions

View File

@@ -40,9 +40,9 @@ jobs:
arch: [amd64, armv7, aarch64] arch: [amd64, armv7, aarch64]
build_type: ["ha-addon", "docker", "lint"] build_type: ["ha-addon", "docker", "lint"]
steps: steps:
- uses: actions/checkout@v4.0.0 - uses: actions/checkout@v4.1.0
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v4.7.0 uses: actions/setup-python@v4.7.1
with: with:
python-version: "3.9" python-version: "3.9"
- name: Set up Docker Buildx - name: Set up Docker Buildx

View File

@@ -34,13 +34,13 @@ jobs:
cache-key: ${{ steps.cache-key.outputs.key }} cache-key: ${{ steps.cache-key.outputs.key }}
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v4.0.0 uses: actions/checkout@v4.1.0
- name: Generate cache-key - name: Generate cache-key
id: cache-key id: cache-key
run: echo key="${{ hashFiles('requirements.txt', 'requirements_optional.txt', 'requirements_test.txt') }}" >> $GITHUB_OUTPUT run: echo key="${{ hashFiles('requirements.txt', 'requirements_optional.txt', 'requirements_test.txt') }}" >> $GITHUB_OUTPUT
- name: Set up Python ${{ env.DEFAULT_PYTHON }} - name: Set up Python ${{ env.DEFAULT_PYTHON }}
id: python id: python
uses: actions/setup-python@v4.7.0 uses: actions/setup-python@v4.7.1
with: with:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore Python virtual environment - name: Restore Python virtual environment
@@ -66,7 +66,7 @@ jobs:
- common - common
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v4.0.0 uses: actions/checkout@v4.1.0
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:
@@ -87,7 +87,7 @@ jobs:
- common - common
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v4.0.0 uses: actions/checkout@v4.1.0
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:
@@ -108,7 +108,7 @@ jobs:
- common - common
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v4.0.0 uses: actions/checkout@v4.1.0
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:
@@ -129,7 +129,7 @@ jobs:
- common - common
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v4.0.0 uses: actions/checkout@v4.1.0
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:
@@ -150,7 +150,7 @@ jobs:
- common - common
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v4.0.0 uses: actions/checkout@v4.1.0
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:
@@ -171,7 +171,7 @@ jobs:
- common - common
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v4.0.0 uses: actions/checkout@v4.1.0
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:
@@ -191,7 +191,7 @@ jobs:
- common - common
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v4.0.0 uses: actions/checkout@v4.1.0
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:
@@ -210,6 +210,17 @@ jobs:
run: script/ci-suggest-changes run: script/ci-suggest-changes
if: always() if: always()
compile-tests-list:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.1.0
- name: Find all YAML test files
id: set-matrix
run: echo "matrix=$(ls tests/test*.yaml | jq -R -s -c 'split("\n")[:-1]')" >> $GITHUB_OUTPUT
compile-tests: compile-tests:
name: Run YAML test ${{ matrix.file }} name: Run YAML test ${{ matrix.file }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -222,23 +233,24 @@ jobs:
- pylint - pylint
- pytest - pytest
- pyupgrade - pyupgrade
- compile-tests-list
strategy: strategy:
fail-fast: false fail-fast: false
max-parallel: 2 max-parallel: 2
matrix: matrix:
file: [1, 2, 3, 3.1, 4, 5, 6, 7, 8, 10, 11.5] file: ${{ fromJson(needs.compile-tests-list.outputs.matrix) }}
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v4.0.0 uses: actions/checkout@v4.1.0
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }} cache-key: ${{ needs.common.outputs.cache-key }}
- name: Run esphome compile tests/test${{ matrix.file }}.yaml - name: Run esphome compile ${{ matrix.file }}
run: | run: |
. venv/bin/activate . venv/bin/activate
esphome compile tests/test${{ matrix.file }}.yaml esphome compile ${{ matrix.file }}
clang-tidy: clang-tidy:
name: ${{ matrix.name }} name: ${{ matrix.name }}
@@ -284,7 +296,7 @@ jobs:
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v4.0.0 uses: actions/checkout@v4.1.0
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:

View File

@@ -19,7 +19,7 @@ jobs:
outputs: outputs:
tag: ${{ steps.tag.outputs.tag }} tag: ${{ steps.tag.outputs.tag }}
steps: steps:
- uses: actions/checkout@v4.0.0 - uses: actions/checkout@v4.1.0
- name: Get tag - name: Get tag
id: tag id: tag
# yamllint disable rule:line-length # yamllint disable rule:line-length
@@ -43,9 +43,9 @@ jobs:
if: github.repository == 'esphome/esphome' && github.event_name == 'release' if: github.repository == 'esphome/esphome' && github.event_name == 'release'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4.0.0 - uses: actions/checkout@v4.1.0
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v4.7.0 uses: actions/setup-python@v4.7.1
with: with:
python-version: "3.x" python-version: "3.x"
- name: Set up python environment - name: Set up python environment
@@ -88,9 +88,9 @@ jobs:
target: "lint" target: "lint"
baseimg: "docker" baseimg: "docker"
steps: steps:
- uses: actions/checkout@v4.0.0 - uses: actions/checkout@v4.1.0
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v4.7.0 uses: actions/setup-python@v4.7.1
with: with:
python-version: "3.9" python-version: "3.9"

View File

@@ -13,16 +13,16 @@ jobs:
if: github.repository == 'esphome/esphome' if: github.repository == 'esphome/esphome'
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4.0.0 uses: actions/checkout@v4.1.0
- name: Checkout Home Assistant - name: Checkout Home Assistant
uses: actions/checkout@v4.0.0 uses: actions/checkout@v4.1.0
with: with:
repository: home-assistant/core repository: home-assistant/core
path: lib/home-assistant path: lib/home-assistant
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v4.7.0 uses: actions/setup-python@v4.7.1
with: with:
python-version: 3.11 python-version: 3.11

View File

@@ -17,6 +17,6 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v4.0.0 uses: actions/checkout@v4.1.0
- name: Run yamllint - name: Run yamllint
uses: frenck/action-yamllint@v1.4.1 uses: frenck/action-yamllint@v1.4.1

View File

@@ -131,7 +131,7 @@ esphome/components/i2s_audio/* @jesserockz
esphome/components/i2s_audio/media_player/* @jesserockz esphome/components/i2s_audio/media_player/* @jesserockz
esphome/components/i2s_audio/microphone/* @jesserockz esphome/components/i2s_audio/microphone/* @jesserockz
esphome/components/i2s_audio/speaker/* @jesserockz esphome/components/i2s_audio/speaker/* @jesserockz
esphome/components/ili9xxx/* @nielsnl68 esphome/components/ili9xxx/* @clydebarrow @nielsnl68
esphome/components/improv_base/* @esphome/core esphome/components/improv_base/* @esphome/core
esphome/components/improv_serial/* @esphome/core esphome/components/improv_serial/* @esphome/core
esphome/components/ina260/* @mreditor97 esphome/components/ina260/* @mreditor97
@@ -186,6 +186,7 @@ esphome/components/mitsubishi/* @RubyBailey
esphome/components/mlx90393/* @functionpointer esphome/components/mlx90393/* @functionpointer
esphome/components/mlx90614/* @jesserockz esphome/components/mlx90614/* @jesserockz
esphome/components/mmc5603/* @benhoff esphome/components/mmc5603/* @benhoff
esphome/components/mmc5983/* @agoode
esphome/components/modbus_controller/* @martgras esphome/components/modbus_controller/* @martgras
esphome/components/modbus_controller/binary_sensor/* @martgras esphome/components/modbus_controller/binary_sensor/* @martgras
esphome/components/modbus_controller/number/* @martgras esphome/components/modbus_controller/number/* @martgras
@@ -224,7 +225,7 @@ esphome/components/pn532_spi/* @OttoWinter @jesserockz
esphome/components/power_supply/* @esphome/core esphome/components/power_supply/* @esphome/core
esphome/components/preferences/* @esphome/core esphome/components/preferences/* @esphome/core
esphome/components/psram/* @esphome/core esphome/components/psram/* @esphome/core
esphome/components/pulse_meter/* @cstaahl @stevebaxter esphome/components/pulse_meter/* @TrentHouliston @cstaahl @stevebaxter
esphome/components/pvvx_mithermometer/* @pasiz esphome/components/pvvx_mithermometer/* @pasiz
esphome/components/qmp6988/* @andrewpc esphome/components/qmp6988/* @andrewpc
esphome/components/qr_code/* @wjtje esphome/components/qr_code/* @wjtje
@@ -270,7 +271,7 @@ esphome/components/sn74hc165/* @jesserockz
esphome/components/socket/* @esphome/core esphome/components/socket/* @esphome/core
esphome/components/sonoff_d1/* @anatoly-savchenkov esphome/components/sonoff_d1/* @anatoly-savchenkov
esphome/components/speaker/* @jesserockz esphome/components/speaker/* @jesserockz
esphome/components/spi/* @esphome/core esphome/components/spi/* @clydebarrow @esphome/core
esphome/components/spi_device/* @clydebarrow esphome/components/spi_device/* @clydebarrow
esphome/components/spi_led_strip/* @clydebarrow esphome/components/spi_led_strip/* @clydebarrow
esphome/components/sprinkler/* @kbx81 esphome/components/sprinkler/* @kbx81

View File

@@ -26,10 +26,11 @@ RUN \
python3-venv=3.9.2-3 \ python3-venv=3.9.2-3 \
iputils-ping=3:20210202-1 \ iputils-ping=3:20210202-1 \
git=1:2.30.2-1+deb11u2 \ git=1:2.30.2-1+deb11u2 \
curl=7.74.0-1.3+deb11u7 \ curl=7.74.0-1.3+deb11u10 \
openssh-client=1:8.4p1-5+deb11u1 \ openssh-client=1:8.4p1-5+deb11u2 \
python3-cffi=1.14.5-1 \ python3-cffi=1.14.5-1 \
libcairo2=1.16.0-5; \ libcairo2=1.16.0-5 \
patch=2.7.6-7; \
if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \ if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \
apt-get install -y --no-install-recommends \ apt-get install -y --no-install-recommends \
build-essential=12.9 \ build-essential=12.9 \

View File

@@ -41,8 +41,15 @@ fi
mkdir -p "${pio_cache_base}" mkdir -p "${pio_cache_base}"
mkdir -p /config/esphome
if bashio::fs.directory_exists '/config/esphome/.esphome'; then if bashio::fs.directory_exists '/config/esphome/.esphome'; then
bashio::log.info "Removing old .esphome directory..." bashio::log.info "Migrating old .esphome directory..."
if bashio::fs.file_exists '/config/esphome/.esphome/esphome.json'; then
mv /config/esphome/.esphome/esphome.json /data/esphome.json
fi
mkdir -p "/data/storage"
mv /config/esphome/.esphome/*.json /data/storage/ || true
rm -rf /config/esphome/.esphome rm -rf /config/esphome/.esphome
fi fi

View File

@@ -11,6 +11,7 @@ from esphome.const import (
CONF_TRIGGER_ID, CONF_TRIGGER_ID,
CONF_TYPE_ID, CONF_TYPE_ID,
CONF_TIME, CONF_TIME,
CONF_UPDATE_INTERVAL,
) )
from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor
from esphome.util import Registry from esphome.util import Registry
@@ -69,6 +70,8 @@ WhileAction = cg.esphome_ns.class_("WhileAction", Action)
RepeatAction = cg.esphome_ns.class_("RepeatAction", Action) RepeatAction = cg.esphome_ns.class_("RepeatAction", Action)
WaitUntilAction = cg.esphome_ns.class_("WaitUntilAction", Action, cg.Component) WaitUntilAction = cg.esphome_ns.class_("WaitUntilAction", Action, cg.Component)
UpdateComponentAction = cg.esphome_ns.class_("UpdateComponentAction", Action) UpdateComponentAction = cg.esphome_ns.class_("UpdateComponentAction", Action)
SuspendComponentAction = cg.esphome_ns.class_("SuspendComponentAction", Action)
ResumeComponentAction = cg.esphome_ns.class_("ResumeComponentAction", Action)
Automation = cg.esphome_ns.class_("Automation") Automation = cg.esphome_ns.class_("Automation")
LambdaCondition = cg.esphome_ns.class_("LambdaCondition", Condition) LambdaCondition = cg.esphome_ns.class_("LambdaCondition", Condition)
@@ -138,6 +141,7 @@ AUTOMATION_SCHEMA = cv.Schema(
AndCondition = cg.esphome_ns.class_("AndCondition", Condition) AndCondition = cg.esphome_ns.class_("AndCondition", Condition)
OrCondition = cg.esphome_ns.class_("OrCondition", Condition) OrCondition = cg.esphome_ns.class_("OrCondition", Condition)
NotCondition = cg.esphome_ns.class_("NotCondition", Condition) NotCondition = cg.esphome_ns.class_("NotCondition", Condition)
XorCondition = cg.esphome_ns.class_("XorCondition", Condition)
@register_condition("and", AndCondition, validate_condition_list) @register_condition("and", AndCondition, validate_condition_list)
@@ -158,6 +162,12 @@ async def not_condition_to_code(config, condition_id, template_arg, args):
return cg.new_Pvariable(condition_id, template_arg, condition) return cg.new_Pvariable(condition_id, template_arg, condition)
@register_condition("xor", XorCondition, validate_condition_list)
async def xor_condition_to_code(config, condition_id, template_arg, args):
conditions = await build_condition_list(config, template_arg, args)
return cg.new_Pvariable(condition_id, template_arg, conditions)
@register_condition("lambda", LambdaCondition, cv.returning_lambda) @register_condition("lambda", LambdaCondition, cv.returning_lambda)
async def lambda_condition_to_code(config, condition_id, template_arg, args): async def lambda_condition_to_code(config, condition_id, template_arg, args):
lambda_ = await cg.process_lambda(config, args, return_type=bool) lambda_ = await cg.process_lambda(config, args, return_type=bool)
@@ -303,6 +313,41 @@ async def component_update_action_to_code(config, action_id, template_arg, args)
return cg.new_Pvariable(action_id, template_arg, comp) return cg.new_Pvariable(action_id, template_arg, comp)
@register_action(
"component.suspend",
SuspendComponentAction,
maybe_simple_id(
{
cv.Required(CONF_ID): cv.use_id(cg.PollingComponent),
}
),
)
async def component_suspend_action_to_code(config, action_id, template_arg, args):
comp = await cg.get_variable(config[CONF_ID])
return cg.new_Pvariable(action_id, template_arg, comp)
@register_action(
"component.resume",
ResumeComponentAction,
maybe_simple_id(
{
cv.Required(CONF_ID): cv.use_id(cg.PollingComponent),
cv.Optional(CONF_UPDATE_INTERVAL): cv.templatable(
cv.positive_time_period_milliseconds
),
}
),
)
async def component_resume_action_to_code(config, action_id, template_arg, args):
comp = await cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, comp)
if CONF_UPDATE_INTERVAL in config:
template_ = await cg.templatable(config[CONF_UPDATE_INTERVAL], args, int)
cg.add(var.set_update_interval(template_))
return var
async def build_action(full_config, template_arg, args): async def build_action(full_config, template_arg, args):
registry_entry, config = cg.extract_registry_entry_config( registry_entry, config = cg.extract_registry_entry_config(
ACTION_REGISTRY, full_config ACTION_REGISTRY, full_config

View File

@@ -4,13 +4,14 @@ from esphome.components import sensor, i2c
from esphome import pins from esphome import pins
from esphome.const import ( from esphome.const import (
CONF_ID, CONF_ID,
CONF_IRQ_PIN,
CONF_VOLTAGE, CONF_VOLTAGE,
DEVICE_CLASS_CURRENT, DEVICE_CLASS_CURRENT,
DEVICE_CLASS_POWER, DEVICE_CLASS_POWER,
DEVICE_CLASS_VOLTAGE, DEVICE_CLASS_VOLTAGE,
STATE_CLASS_MEASUREMENT, STATE_CLASS_MEASUREMENT,
UNIT_VOLT,
UNIT_AMPERE, UNIT_AMPERE,
UNIT_VOLT,
UNIT_WATT, UNIT_WATT,
) )
@@ -19,7 +20,6 @@ DEPENDENCIES = ["i2c"]
ade7953_ns = cg.esphome_ns.namespace("ade7953") ade7953_ns = cg.esphome_ns.namespace("ade7953")
ADE7953 = ade7953_ns.class_("ADE7953", cg.PollingComponent, i2c.I2CDevice) ADE7953 = ade7953_ns.class_("ADE7953", cg.PollingComponent, i2c.I2CDevice)
CONF_IRQ_PIN = "irq_pin"
CONF_CURRENT_A = "current_a" CONF_CURRENT_A = "current_a"
CONF_CURRENT_B = "current_b" CONF_CURRENT_B = "current_b"
CONF_ACTIVE_POWER_A = "active_power_a" CONF_ACTIVE_POWER_A = "active_power_a"

View File

@@ -151,7 +151,7 @@ async def to_code(config):
pos = 0 pos = 0
for frameIndex in range(frames): for frameIndex in range(frames):
image.seek(frameIndex) image.seek(frameIndex)
frame = image.convert("LA", dither=Image.NONE) frame = image.convert("LA", dither=Image.Dither.NONE)
if CONF_RESIZE in config: if CONF_RESIZE in config:
frame = frame.resize([width, height]) frame = frame.resize([width, height])
pixels = list(frame.getdata()) pixels = list(frame.getdata())
@@ -259,7 +259,7 @@ async def to_code(config):
if transparent: if transparent:
alpha = image.split()[-1] alpha = image.split()[-1]
has_alpha = alpha.getextrema()[0] < 0xFF has_alpha = alpha.getextrema()[0] < 0xFF
frame = image.convert("1", dither=Image.NONE) frame = image.convert("1", dither=Image.Dither.NONE)
if CONF_RESIZE in config: if CONF_RESIZE in config:
frame = frame.resize([width, height]) frame = frame.resize([width, height])
if transparent: if transparent:

View File

@@ -18,6 +18,8 @@ from esphome.const import (
CONF_TRIGGER_ID, CONF_TRIGGER_ID,
CONF_EVENT, CONF_EVENT,
CONF_TAG, CONF_TAG,
CONF_ON_CLIENT_CONNECTED,
CONF_ON_CLIENT_DISCONNECTED,
) )
from esphome.core import coroutine_with_priority from esphome.core import coroutine_with_priority
@@ -87,6 +89,12 @@ CONFIG_SCHEMA = cv.Schema(
cv.Required(CONF_KEY): validate_encryption_key, cv.Required(CONF_KEY): validate_encryption_key,
} }
), ),
cv.Optional(CONF_ON_CLIENT_CONNECTED): automation.validate_automation(
single=True
),
cv.Optional(CONF_ON_CLIENT_DISCONNECTED): automation.validate_automation(
single=True
),
} }
).extend(cv.COMPONENT_SCHEMA) ).extend(cv.COMPONENT_SCHEMA)
@@ -116,6 +124,20 @@ async def to_code(config):
cg.add(var.register_user_service(trigger)) cg.add(var.register_user_service(trigger))
await automation.build_automation(trigger, func_args, conf) await automation.build_automation(trigger, func_args, conf)
if CONF_ON_CLIENT_CONNECTED in config:
await automation.build_automation(
var.get_client_connected_trigger(),
[(cg.std_string, "client_info"), (cg.std_string, "client_address")],
config[CONF_ON_CLIENT_CONNECTED],
)
if CONF_ON_CLIENT_DISCONNECTED in config:
await automation.build_automation(
var.get_client_disconnected_trigger(),
[(cg.std_string, "client_info"), (cg.std_string, "client_address")],
config[CONF_ON_CLIENT_DISCONNECTED],
)
if encryption_config := config.get(CONF_ENCRYPTION): if encryption_config := config.get(CONF_ENCRYPTION):
decoded = base64.b64decode(encryption_config[CONF_KEY]) decoded = base64.b64decode(encryption_config[CONF_KEY])
cg.add(var.set_noise_psk(list(decoded))) cg.add(var.set_noise_psk(list(decoded)))

View File

@@ -1413,6 +1413,18 @@ message SubscribeVoiceAssistantRequest {
bool subscribe = 1; bool subscribe = 1;
} }
enum VoiceAssistantRequestFlag {
VOICE_ASSISTANT_REQUEST_NONE = 0;
VOICE_ASSISTANT_REQUEST_USE_VAD = 1;
VOICE_ASSISTANT_REQUEST_USE_WAKE_WORD = 2;
}
message VoiceAssistantAudioSettings {
uint32 noise_suppression_level = 1;
uint32 auto_gain = 2;
float volume_multiplier = 3;
}
message VoiceAssistantRequest { message VoiceAssistantRequest {
option (id) = 90; option (id) = 90;
option (source) = SOURCE_SERVER; option (source) = SOURCE_SERVER;
@@ -1420,7 +1432,8 @@ message VoiceAssistantRequest {
bool start = 1; bool start = 1;
string conversation_id = 2; string conversation_id = 2;
bool use_vad = 3; uint32 flags = 3;
VoiceAssistantAudioSettings audio_settings = 4;
} }
message VoiceAssistantResponse { message VoiceAssistantResponse {
@@ -1442,6 +1455,12 @@ enum VoiceAssistantEvent {
VOICE_ASSISTANT_INTENT_END = 6; VOICE_ASSISTANT_INTENT_END = 6;
VOICE_ASSISTANT_TTS_START = 7; VOICE_ASSISTANT_TTS_START = 7;
VOICE_ASSISTANT_TTS_END = 8; VOICE_ASSISTANT_TTS_END = 8;
VOICE_ASSISTANT_WAKE_WORD_START = 9;
VOICE_ASSISTANT_WAKE_WORD_END = 10;
VOICE_ASSISTANT_STT_VAD_START = 11;
VOICE_ASSISTANT_STT_VAD_END = 12;
VOICE_ASSISTANT_TTS_STREAM_START = 98;
VOICE_ASSISTANT_TTS_STREAM_END = 99;
} }
message VoiceAssistantEventData { message VoiceAssistantEventData {

View File

@@ -31,9 +31,9 @@ APIConnection::APIConnection(std::unique_ptr<socket::Socket> sock, APIServer *pa
this->proto_write_buffer_.reserve(64); this->proto_write_buffer_.reserve(64);
#if defined(USE_API_PLAINTEXT) #if defined(USE_API_PLAINTEXT)
helper_ = std::unique_ptr<APIFrameHelper>{new APIPlaintextFrameHelper(std::move(sock))}; this->helper_ = std::unique_ptr<APIFrameHelper>{new APIPlaintextFrameHelper(std::move(sock))};
#elif defined(USE_API_NOISE) #elif defined(USE_API_NOISE)
helper_ = std::unique_ptr<APIFrameHelper>{new APINoiseFrameHelper(std::move(sock), parent->get_noise_ctx())}; this->helper_ = std::unique_ptr<APIFrameHelper>{new APINoiseFrameHelper(std::move(sock), parent->get_noise_ctx())};
#else #else
#error "No frame helper defined" #error "No frame helper defined"
#endif #endif
@@ -41,14 +41,16 @@ APIConnection::APIConnection(std::unique_ptr<socket::Socket> sock, APIServer *pa
void APIConnection::start() { void APIConnection::start() {
this->last_traffic_ = millis(); this->last_traffic_ = millis();
APIError err = helper_->init(); APIError err = this->helper_->init();
if (err != APIError::OK) { if (err != APIError::OK) {
on_fatal_error(); on_fatal_error();
ESP_LOGW(TAG, "%s: Helper init failed: %s errno=%d", client_info_.c_str(), api_error_to_str(err), errno); ESP_LOGW(TAG, "%s: Helper init failed: %s errno=%d", this->client_combined_info_.c_str(), api_error_to_str(err),
errno);
return; return;
} }
client_info_ = helper_->getpeername(); this->client_info_ = helper_->getpeername();
helper_->set_log_info(client_info_); this->client_peername_ = this->client_info_;
this->helper_->set_log_info(this->client_info_);
} }
APIConnection::~APIConnection() { APIConnection::~APIConnection() {
@@ -57,6 +59,11 @@ APIConnection::~APIConnection() {
bluetooth_proxy::global_bluetooth_proxy->unsubscribe_api_connection(this); bluetooth_proxy::global_bluetooth_proxy->unsubscribe_api_connection(this);
} }
#endif #endif
#ifdef USE_VOICE_ASSISTANT
if (voice_assistant::global_voice_assistant->get_api_connection() == this) {
voice_assistant::global_voice_assistant->client_subscription(this, false);
}
#endif
} }
void APIConnection::loop() { void APIConnection::loop() {
@@ -67,7 +74,7 @@ void APIConnection::loop() {
// when network is disconnected force disconnect immediately // when network is disconnected force disconnect immediately
// don't wait for timeout // don't wait for timeout
this->on_fatal_error(); this->on_fatal_error();
ESP_LOGW(TAG, "%s: Network unavailable, disconnecting", client_info_.c_str()); ESP_LOGW(TAG, "%s: Network unavailable, disconnecting", this->client_combined_info_.c_str());
return; return;
} }
if (this->next_close_) { if (this->next_close_) {
@@ -77,24 +84,26 @@ void APIConnection::loop() {
return; return;
} }
APIError err = helper_->loop(); APIError err = this->helper_->loop();
if (err != APIError::OK) { if (err != APIError::OK) {
on_fatal_error(); on_fatal_error();
ESP_LOGW(TAG, "%s: Socket operation failed: %s errno=%d", client_info_.c_str(), api_error_to_str(err), errno); ESP_LOGW(TAG, "%s: Socket operation failed: %s errno=%d", this->client_combined_info_.c_str(),
api_error_to_str(err), errno);
return; return;
} }
ReadPacketBuffer buffer; ReadPacketBuffer buffer;
err = helper_->read_packet(&buffer); err = this->helper_->read_packet(&buffer);
if (err == APIError::WOULD_BLOCK) { if (err == APIError::WOULD_BLOCK) {
// pass // pass
} else if (err != APIError::OK) { } else if (err != APIError::OK) {
on_fatal_error(); on_fatal_error();
if (err == APIError::SOCKET_READ_FAILED && errno == ECONNRESET) { if (err == APIError::SOCKET_READ_FAILED && errno == ECONNRESET) {
ESP_LOGW(TAG, "%s: Connection reset", client_info_.c_str()); ESP_LOGW(TAG, "%s: Connection reset", this->client_combined_info_.c_str());
} else if (err == APIError::CONNECTION_CLOSED) { } else if (err == APIError::CONNECTION_CLOSED) {
ESP_LOGW(TAG, "%s: Connection closed", client_info_.c_str()); ESP_LOGW(TAG, "%s: Connection closed", this->client_combined_info_.c_str());
} else { } else {
ESP_LOGW(TAG, "%s: Reading failed: %s errno=%d", client_info_.c_str(), api_error_to_str(err), errno); ESP_LOGW(TAG, "%s: Reading failed: %s errno=%d", this->client_combined_info_.c_str(), api_error_to_str(err),
errno);
} }
return; return;
} else { } else {
@@ -114,7 +123,7 @@ void APIConnection::loop() {
// Disconnect if not responded within 2.5*keepalive // Disconnect if not responded within 2.5*keepalive
if (now - this->last_traffic_ > (keepalive * 5) / 2) { if (now - this->last_traffic_ > (keepalive * 5) / 2) {
on_fatal_error(); on_fatal_error();
ESP_LOGW(TAG, "%s didn't respond to ping request in time. Disconnecting...", this->client_info_.c_str()); ESP_LOGW(TAG, "%s didn't respond to ping request in time. Disconnecting...", this->client_combined_info_.c_str());
} }
} else if (now - this->last_traffic_ > keepalive) { } else if (now - this->last_traffic_ > keepalive) {
ESP_LOGVV(TAG, "Sending keepalive PING..."); ESP_LOGVV(TAG, "Sending keepalive PING...");
@@ -168,7 +177,7 @@ DisconnectResponse APIConnection::disconnect(const DisconnectRequest &msg) {
// remote initiated disconnect_client // remote initiated disconnect_client
// don't close yet, we still need to send the disconnect response // don't close yet, we still need to send the disconnect response
// close will happen on next loop // close will happen on next loop
ESP_LOGD(TAG, "%s requested disconnected", client_info_.c_str()); ESP_LOGD(TAG, "%s requested disconnected", this->client_combined_info_.c_str());
this->next_close_ = true; this->next_close_ = true;
DisconnectResponse resp; DisconnectResponse resp;
return resp; return resp;
@@ -907,25 +916,33 @@ BluetoothConnectionsFreeResponse APIConnection::subscribe_bluetooth_connections_
#endif #endif
#ifdef USE_VOICE_ASSISTANT #ifdef USE_VOICE_ASSISTANT
bool APIConnection::request_voice_assistant(bool start, const std::string &conversation_id, bool use_vad) { void APIConnection::subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) {
if (!this->voice_assistant_subscription_) if (voice_assistant::global_voice_assistant != nullptr) {
return false; voice_assistant::global_voice_assistant->client_subscription(this, msg.subscribe);
VoiceAssistantRequest msg; }
msg.start = start;
msg.conversation_id = conversation_id;
msg.use_vad = use_vad;
return this->send_voice_assistant_request(msg);
} }
void APIConnection::on_voice_assistant_response(const VoiceAssistantResponse &msg) { void APIConnection::on_voice_assistant_response(const VoiceAssistantResponse &msg) {
if (voice_assistant::global_voice_assistant != nullptr) { if (voice_assistant::global_voice_assistant != nullptr) {
if (voice_assistant::global_voice_assistant->get_api_connection() != this) {
return;
}
if (msg.error) {
voice_assistant::global_voice_assistant->failed_to_start();
return;
}
struct sockaddr_storage storage; struct sockaddr_storage storage;
socklen_t len = sizeof(storage); socklen_t len = sizeof(storage);
this->helper_->getpeername((struct sockaddr *) &storage, &len); this->helper_->getpeername((struct sockaddr *) &storage, &len);
voice_assistant::global_voice_assistant->start(&storage, msg.port); voice_assistant::global_voice_assistant->start_streaming(&storage, msg.port);
} }
}; };
void APIConnection::on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) { void APIConnection::on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) {
if (voice_assistant::global_voice_assistant != nullptr) { if (voice_assistant::global_voice_assistant != nullptr) {
if (voice_assistant::global_voice_assistant->get_api_connection() != this) {
return;
}
voice_assistant::global_voice_assistant->on_event(msg); voice_assistant::global_voice_assistant->on_event(msg);
} }
} }
@@ -1005,12 +1022,14 @@ bool APIConnection::send_log_message(int level, const char *tag, const char *lin
} }
HelloResponse APIConnection::hello(const HelloRequest &msg) { HelloResponse APIConnection::hello(const HelloRequest &msg) {
this->client_info_ = msg.client_info + " (" + this->helper_->getpeername() + ")"; this->client_info_ = msg.client_info;
this->helper_->set_log_info(client_info_); this->client_peername_ = this->helper_->getpeername();
this->client_combined_info_ = this->client_info_ + " (" + this->client_peername_ + ")";
this->helper_->set_log_info(this->client_combined_info_);
this->client_api_version_major_ = msg.api_version_major; this->client_api_version_major_ = msg.api_version_major;
this->client_api_version_minor_ = msg.api_version_minor; this->client_api_version_minor_ = msg.api_version_minor;
ESP_LOGV(TAG, "Hello from client: '%s' | API Version %" PRIu32 ".%" PRIu32, this->client_info_.c_str(), ESP_LOGV(TAG, "Hello from client: '%s' | %s | API Version %" PRIu32 ".%" PRIu32, this->client_info_.c_str(),
this->client_api_version_major_, this->client_api_version_minor_); this->client_peername_.c_str(), this->client_api_version_major_, this->client_api_version_minor_);
HelloResponse resp; HelloResponse resp;
resp.api_version_major = 1; resp.api_version_major = 1;
@@ -1028,9 +1047,9 @@ ConnectResponse APIConnection::connect(const ConnectRequest &msg) {
// bool invalid_password = 1; // bool invalid_password = 1;
resp.invalid_password = !correct; resp.invalid_password = !correct;
if (correct) { if (correct) {
ESP_LOGD(TAG, "%s: Connected successfully", this->client_info_.c_str()); ESP_LOGD(TAG, "%s: Connected successfully", this->client_combined_info_.c_str());
this->connection_state_ = ConnectionState::AUTHENTICATED; this->connection_state_ = ConnectionState::AUTHENTICATED;
this->parent_->get_client_connected_trigger()->trigger(this->client_info_, this->client_peername_);
#ifdef USE_HOMEASSISTANT_TIME #ifdef USE_HOMEASSISTANT_TIME
if (homeassistant::global_homeassistant_time != nullptr) { if (homeassistant::global_homeassistant_time != nullptr) {
this->send_time_request(); this->send_time_request();
@@ -1104,10 +1123,11 @@ bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint32_t message_type)
return false; return false;
if (!this->helper_->can_write_without_blocking()) { if (!this->helper_->can_write_without_blocking()) {
delay(0); delay(0);
APIError err = helper_->loop(); APIError err = this->helper_->loop();
if (err != APIError::OK) { if (err != APIError::OK) {
on_fatal_error(); on_fatal_error();
ESP_LOGW(TAG, "%s: Socket operation failed: %s errno=%d", client_info_.c_str(), api_error_to_str(err), errno); ESP_LOGW(TAG, "%s: Socket operation failed: %s errno=%d", this->client_combined_info_.c_str(),
api_error_to_str(err), errno);
return false; return false;
} }
if (!this->helper_->can_write_without_blocking()) { if (!this->helper_->can_write_without_blocking()) {
@@ -1126,9 +1146,10 @@ bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint32_t message_type)
if (err != APIError::OK) { if (err != APIError::OK) {
on_fatal_error(); on_fatal_error();
if (err == APIError::SOCKET_WRITE_FAILED && errno == ECONNRESET) { if (err == APIError::SOCKET_WRITE_FAILED && errno == ECONNRESET) {
ESP_LOGW(TAG, "%s: Connection reset", client_info_.c_str()); ESP_LOGW(TAG, "%s: Connection reset", this->client_combined_info_.c_str());
} else { } else {
ESP_LOGW(TAG, "%s: Packet write failed %s errno=%d", client_info_.c_str(), api_error_to_str(err), errno); ESP_LOGW(TAG, "%s: Packet write failed %s errno=%d", this->client_combined_info_.c_str(), api_error_to_str(err),
errno);
} }
return false; return false;
} }
@@ -1137,11 +1158,11 @@ bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint32_t message_type)
} }
void APIConnection::on_unauthenticated_access() { void APIConnection::on_unauthenticated_access() {
this->on_fatal_error(); this->on_fatal_error();
ESP_LOGD(TAG, "%s: tried to access without authentication.", this->client_info_.c_str()); ESP_LOGD(TAG, "%s: tried to access without authentication.", this->client_combined_info_.c_str());
} }
void APIConnection::on_no_setup_connection() { void APIConnection::on_no_setup_connection() {
this->on_fatal_error(); this->on_fatal_error();
ESP_LOGD(TAG, "%s: tried to access without full connection.", this->client_info_.c_str()); ESP_LOGD(TAG, "%s: tried to access without full connection.", this->client_combined_info_.c_str());
} }
void APIConnection::on_fatal_error() { void APIConnection::on_fatal_error() {
this->helper_->close(); this->helper_->close();

View File

@@ -121,10 +121,7 @@ class APIConnection : public APIServerConnection {
#endif #endif
#ifdef USE_VOICE_ASSISTANT #ifdef USE_VOICE_ASSISTANT
void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) override { void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) override;
this->voice_assistant_subscription_ = msg.subscribe;
}
bool request_voice_assistant(bool start, const std::string &conversation_id, bool use_vad);
void on_voice_assistant_response(const VoiceAssistantResponse &msg) override; void on_voice_assistant_response(const VoiceAssistantResponse &msg) override;
void on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) override; void on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) override;
#endif #endif
@@ -183,6 +180,8 @@ class APIConnection : public APIServerConnection {
} }
bool send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) override; bool send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) override;
std::string get_client_combined_info() const { return this->client_combined_info_; }
protected: protected:
friend APIServer; friend APIServer;
@@ -202,6 +201,8 @@ class APIConnection : public APIServerConnection {
std::unique_ptr<APIFrameHelper> helper_; std::unique_ptr<APIFrameHelper> helper_;
std::string client_info_; std::string client_info_;
std::string client_peername_;
std::string client_combined_info_;
uint32_t client_api_version_major_{0}; uint32_t client_api_version_major_{0};
uint32_t client_api_version_minor_{0}; uint32_t client_api_version_minor_{0};
#ifdef USE_ESP32_CAMERA #ifdef USE_ESP32_CAMERA
@@ -213,9 +214,6 @@ class APIConnection : public APIServerConnection {
uint32_t last_traffic_; uint32_t last_traffic_;
bool sent_ping_{false}; bool sent_ping_{false};
bool service_call_subscription_{false}; bool service_call_subscription_{false};
#ifdef USE_VOICE_ASSISTANT
bool voice_assistant_subscription_{false};
#endif
bool next_close_ = false; bool next_close_ = false;
APIServer *parent_; APIServer *parent_;
InitialStateIterator initial_state_iterator_; InitialStateIterator initial_state_iterator_;

View File

@@ -410,6 +410,20 @@ const char *proto_enum_to_string<enums::BluetoothDeviceRequestType>(enums::Bluet
} }
#endif #endif
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
template<> const char *proto_enum_to_string<enums::VoiceAssistantRequestFlag>(enums::VoiceAssistantRequestFlag value) {
switch (value) {
case enums::VOICE_ASSISTANT_REQUEST_NONE:
return "VOICE_ASSISTANT_REQUEST_NONE";
case enums::VOICE_ASSISTANT_REQUEST_USE_VAD:
return "VOICE_ASSISTANT_REQUEST_USE_VAD";
case enums::VOICE_ASSISTANT_REQUEST_USE_WAKE_WORD:
return "VOICE_ASSISTANT_REQUEST_USE_WAKE_WORD";
default:
return "UNKNOWN";
}
}
#endif
#ifdef HAS_PROTO_MESSAGE_DUMP
template<> const char *proto_enum_to_string<enums::VoiceAssistantEvent>(enums::VoiceAssistantEvent value) { template<> const char *proto_enum_to_string<enums::VoiceAssistantEvent>(enums::VoiceAssistantEvent value) {
switch (value) { switch (value) {
case enums::VOICE_ASSISTANT_ERROR: case enums::VOICE_ASSISTANT_ERROR:
@@ -430,6 +444,18 @@ template<> const char *proto_enum_to_string<enums::VoiceAssistantEvent>(enums::V
return "VOICE_ASSISTANT_TTS_START"; return "VOICE_ASSISTANT_TTS_START";
case enums::VOICE_ASSISTANT_TTS_END: case enums::VOICE_ASSISTANT_TTS_END:
return "VOICE_ASSISTANT_TTS_END"; return "VOICE_ASSISTANT_TTS_END";
case enums::VOICE_ASSISTANT_WAKE_WORD_START:
return "VOICE_ASSISTANT_WAKE_WORD_START";
case enums::VOICE_ASSISTANT_WAKE_WORD_END:
return "VOICE_ASSISTANT_WAKE_WORD_END";
case enums::VOICE_ASSISTANT_STT_VAD_START:
return "VOICE_ASSISTANT_STT_VAD_START";
case enums::VOICE_ASSISTANT_STT_VAD_END:
return "VOICE_ASSISTANT_STT_VAD_END";
case enums::VOICE_ASSISTANT_TTS_STREAM_START:
return "VOICE_ASSISTANT_TTS_STREAM_START";
case enums::VOICE_ASSISTANT_TTS_STREAM_END:
return "VOICE_ASSISTANT_TTS_STREAM_END";
default: default:
return "UNKNOWN"; return "UNKNOWN";
} }
@@ -6344,6 +6370,56 @@ void SubscribeVoiceAssistantRequest::dump_to(std::string &out) const {
out.append("}"); out.append("}");
} }
#endif #endif
bool VoiceAssistantAudioSettings::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 1: {
this->noise_suppression_level = value.as_uint32();
return true;
}
case 2: {
this->auto_gain = value.as_uint32();
return true;
}
default:
return false;
}
}
bool VoiceAssistantAudioSettings::decode_32bit(uint32_t field_id, Proto32Bit value) {
switch (field_id) {
case 3: {
this->volume_multiplier = value.as_float();
return true;
}
default:
return false;
}
}
void VoiceAssistantAudioSettings::encode(ProtoWriteBuffer buffer) const {
buffer.encode_uint32(1, this->noise_suppression_level);
buffer.encode_uint32(2, this->auto_gain);
buffer.encode_float(3, this->volume_multiplier);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void VoiceAssistantAudioSettings::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64];
out.append("VoiceAssistantAudioSettings {\n");
out.append(" noise_suppression_level: ");
sprintf(buffer, "%" PRIu32, this->noise_suppression_level);
out.append(buffer);
out.append("\n");
out.append(" auto_gain: ");
sprintf(buffer, "%" PRIu32, this->auto_gain);
out.append(buffer);
out.append("\n");
out.append(" volume_multiplier: ");
sprintf(buffer, "%g", this->volume_multiplier);
out.append(buffer);
out.append("\n");
out.append("}");
}
#endif
bool VoiceAssistantRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { bool VoiceAssistantRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) { switch (field_id) {
case 1: { case 1: {
@@ -6351,7 +6427,7 @@ bool VoiceAssistantRequest::decode_varint(uint32_t field_id, ProtoVarInt value)
return true; return true;
} }
case 3: { case 3: {
this->use_vad = value.as_bool(); this->flags = value.as_uint32();
return true; return true;
} }
default: default:
@@ -6364,6 +6440,10 @@ bool VoiceAssistantRequest::decode_length(uint32_t field_id, ProtoLengthDelimite
this->conversation_id = value.as_string(); this->conversation_id = value.as_string();
return true; return true;
} }
case 4: {
this->audio_settings = value.as_message<VoiceAssistantAudioSettings>();
return true;
}
default: default:
return false; return false;
} }
@@ -6371,7 +6451,8 @@ bool VoiceAssistantRequest::decode_length(uint32_t field_id, ProtoLengthDelimite
void VoiceAssistantRequest::encode(ProtoWriteBuffer buffer) const { void VoiceAssistantRequest::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(1, this->start); buffer.encode_bool(1, this->start);
buffer.encode_string(2, this->conversation_id); buffer.encode_string(2, this->conversation_id);
buffer.encode_bool(3, this->use_vad); buffer.encode_uint32(3, this->flags);
buffer.encode_message<VoiceAssistantAudioSettings>(4, this->audio_settings);
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void VoiceAssistantRequest::dump_to(std::string &out) const { void VoiceAssistantRequest::dump_to(std::string &out) const {
@@ -6385,8 +6466,13 @@ void VoiceAssistantRequest::dump_to(std::string &out) const {
out.append("'").append(this->conversation_id).append("'"); out.append("'").append(this->conversation_id).append("'");
out.append("\n"); out.append("\n");
out.append(" use_vad: "); out.append(" flags: ");
out.append(YESNO(this->use_vad)); sprintf(buffer, "%" PRIu32, this->flags);
out.append(buffer);
out.append("\n");
out.append(" audio_settings: ");
this->audio_settings.dump_to(out);
out.append("\n"); out.append("\n");
out.append("}"); out.append("}");
} }

View File

@@ -165,6 +165,11 @@ enum BluetoothDeviceRequestType : uint32_t {
BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE = 5, BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE = 5,
BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE = 6, BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE = 6,
}; };
enum VoiceAssistantRequestFlag : uint32_t {
VOICE_ASSISTANT_REQUEST_NONE = 0,
VOICE_ASSISTANT_REQUEST_USE_VAD = 1,
VOICE_ASSISTANT_REQUEST_USE_WAKE_WORD = 2,
};
enum VoiceAssistantEvent : uint32_t { enum VoiceAssistantEvent : uint32_t {
VOICE_ASSISTANT_ERROR = 0, VOICE_ASSISTANT_ERROR = 0,
VOICE_ASSISTANT_RUN_START = 1, VOICE_ASSISTANT_RUN_START = 1,
@@ -175,6 +180,12 @@ enum VoiceAssistantEvent : uint32_t {
VOICE_ASSISTANT_INTENT_END = 6, VOICE_ASSISTANT_INTENT_END = 6,
VOICE_ASSISTANT_TTS_START = 7, VOICE_ASSISTANT_TTS_START = 7,
VOICE_ASSISTANT_TTS_END = 8, VOICE_ASSISTANT_TTS_END = 8,
VOICE_ASSISTANT_WAKE_WORD_START = 9,
VOICE_ASSISTANT_WAKE_WORD_END = 10,
VOICE_ASSISTANT_STT_VAD_START = 11,
VOICE_ASSISTANT_STT_VAD_END = 12,
VOICE_ASSISTANT_TTS_STREAM_START = 98,
VOICE_ASSISTANT_TTS_STREAM_END = 99,
}; };
enum AlarmControlPanelState : uint32_t { enum AlarmControlPanelState : uint32_t {
ALARM_STATE_DISARMED = 0, ALARM_STATE_DISARMED = 0,
@@ -1651,11 +1662,26 @@ class SubscribeVoiceAssistantRequest : public ProtoMessage {
protected: protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
}; };
class VoiceAssistantAudioSettings : public ProtoMessage {
public:
uint32_t noise_suppression_level{0};
uint32_t auto_gain{0};
float volume_multiplier{0.0f};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class VoiceAssistantRequest : public ProtoMessage { class VoiceAssistantRequest : public ProtoMessage {
public: public:
bool start{false}; bool start{false};
std::string conversation_id{}; std::string conversation_id{};
bool use_vad{false}; uint32_t flags{0};
VoiceAssistantAudioSettings audio_settings{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;

View File

@@ -1,13 +1,13 @@
#include "api_server.h" #include "api_server.h"
#include <cerrno>
#include "api_connection.h" #include "api_connection.h"
#include "esphome/components/network/util.h"
#include "esphome/core/application.h" #include "esphome/core/application.h"
#include "esphome/core/defines.h" #include "esphome/core/defines.h"
#include "esphome/core/hal.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
#include "esphome/core/util.h" #include "esphome/core/util.h"
#include "esphome/core/version.h" #include "esphome/core/version.h"
#include "esphome/core/hal.h"
#include "esphome/components/network/util.h"
#include <cerrno>
#ifdef USE_LOGGER #ifdef USE_LOGGER
#include "esphome/components/logger/logger.h" #include "esphome/components/logger/logger.h"
@@ -111,6 +111,7 @@ void APIServer::loop() {
[](const std::unique_ptr<APIConnection> &conn) { return !conn->remove_; }); [](const std::unique_ptr<APIConnection> &conn) { return !conn->remove_; });
// print disconnection messages // print disconnection messages
for (auto it = new_end; it != this->clients_.end(); ++it) { for (auto it = new_end; it != this->clients_.end(); ++it) {
this->client_disconnected_trigger_->trigger((*it)->client_info_, (*it)->client_peername_);
ESP_LOGV(TAG, "Removing connection to %s", (*it)->client_info_.c_str()); ESP_LOGV(TAG, "Removing connection to %s", (*it)->client_info_.c_str());
} }
// resize vector // resize vector
@@ -322,22 +323,6 @@ void APIServer::on_shutdown() {
delay(10); delay(10);
} }
#ifdef USE_VOICE_ASSISTANT
bool APIServer::start_voice_assistant(const std::string &conversation_id, bool use_vad) {
for (auto &c : this->clients_) {
if (c->request_voice_assistant(true, conversation_id, use_vad))
return true;
}
return false;
}
void APIServer::stop_voice_assistant() {
for (auto &c : this->clients_) {
if (c->request_voice_assistant(false, "", false))
return;
}
}
#endif
#ifdef USE_ALARM_CONTROL_PANEL #ifdef USE_ALARM_CONTROL_PANEL
void APIServer::on_alarm_control_panel_update(alarm_control_panel::AlarmControlPanel *obj) { void APIServer::on_alarm_control_panel_update(alarm_control_panel::AlarmControlPanel *obj) {
if (obj->is_internal()) if (obj->is_internal())

View File

@@ -1,16 +1,17 @@
#pragma once #pragma once
#include "api_noise_context.h"
#include "api_pb2.h"
#include "api_pb2_service.h"
#include "esphome/components/socket/socket.h"
#include "esphome/core/automation.h"
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/core/controller.h" #include "esphome/core/controller.h"
#include "esphome/core/defines.h" #include "esphome/core/defines.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
#include "esphome/components/socket/socket.h"
#include "api_pb2.h"
#include "api_pb2_service.h"
#include "list_entities.h" #include "list_entities.h"
#include "subscribe_state.h" #include "subscribe_state.h"
#include "user_services.h" #include "user_services.h"
#include "api_noise_context.h"
#include <vector> #include <vector>
@@ -80,11 +81,6 @@ class APIServer : public Component, public Controller {
void request_time(); void request_time();
#endif #endif
#ifdef USE_VOICE_ASSISTANT
bool start_voice_assistant(const std::string &conversation_id, bool use_vad);
void stop_voice_assistant();
#endif
#ifdef USE_ALARM_CONTROL_PANEL #ifdef USE_ALARM_CONTROL_PANEL
void on_alarm_control_panel_update(alarm_control_panel::AlarmControlPanel *obj) override; void on_alarm_control_panel_update(alarm_control_panel::AlarmControlPanel *obj) override;
#endif #endif
@@ -102,6 +98,11 @@ class APIServer : public Component, public Controller {
const std::vector<HomeAssistantStateSubscription> &get_state_subs() const; const std::vector<HomeAssistantStateSubscription> &get_state_subs() const;
const std::vector<UserServiceDescriptor *> &get_user_services() const { return this->user_services_; } const std::vector<UserServiceDescriptor *> &get_user_services() const { return this->user_services_; }
Trigger<std::string, std::string> *get_client_connected_trigger() const { return this->client_connected_trigger_; }
Trigger<std::string, std::string> *get_client_disconnected_trigger() const {
return this->client_disconnected_trigger_;
}
protected: protected:
std::unique_ptr<socket::Socket> socket_ = nullptr; std::unique_ptr<socket::Socket> socket_ = nullptr;
uint16_t port_{6053}; uint16_t port_{6053};
@@ -111,6 +112,8 @@ class APIServer : public Component, public Controller {
std::string password_; std::string password_;
std::vector<HomeAssistantStateSubscription> state_subs_; std::vector<HomeAssistantStateSubscription> state_subs_;
std::vector<UserServiceDescriptor *> user_services_; std::vector<UserServiceDescriptor *> user_services_;
Trigger<std::string, std::string> *client_connected_trigger_ = new Trigger<std::string, std::string>();
Trigger<std::string, std::string> *client_disconnected_trigger_ = new Trigger<std::string, std::string>();
#ifdef USE_API_NOISE #ifdef USE_API_NOISE
std::shared_ptr<APINoiseContext> noise_ctx_ = std::make_shared<APINoiseContext>(); std::shared_ptr<APINoiseContext> noise_ctx_ = std::make_shared<APINoiseContext>();

View File

@@ -2,14 +2,17 @@ import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome import pins from esphome import pins
from esphome.const import ( from esphome.const import (
CONF_CAPACITANCE,
CONF_DIV_RATIO,
CONF_INDOOR, CONF_INDOOR,
CONF_WATCHDOG_THRESHOLD, CONF_IRQ_PIN,
CONF_NOISE_LEVEL,
CONF_SPIKE_REJECTION,
CONF_LIGHTNING_THRESHOLD, CONF_LIGHTNING_THRESHOLD,
CONF_MASK_DISTURBER, CONF_MASK_DISTURBER,
CONF_DIV_RATIO, CONF_CALIBRATION,
CONF_CAPACITANCE, CONF_TUNE_ANTENNA,
CONF_NOISE_LEVEL,
CONF_SPIKE_REJECTION,
CONF_WATCHDOG_THRESHOLD,
) )
MULTI_CONF = True MULTI_CONF = True
@@ -19,7 +22,6 @@ CONF_AS3935_ID = "as3935_id"
as3935_ns = cg.esphome_ns.namespace("as3935") as3935_ns = cg.esphome_ns.namespace("as3935")
AS3935 = as3935_ns.class_("AS3935Component", cg.Component) AS3935 = as3935_ns.class_("AS3935Component", cg.Component)
CONF_IRQ_PIN = "irq_pin"
AS3935_SCHEMA = cv.Schema( AS3935_SCHEMA = cv.Schema(
{ {
cv.GenerateID(): cv.declare_id(AS3935), cv.GenerateID(): cv.declare_id(AS3935),
@@ -34,6 +36,8 @@ AS3935_SCHEMA = cv.Schema(
cv.Optional(CONF_MASK_DISTURBER, default=False): cv.boolean, cv.Optional(CONF_MASK_DISTURBER, default=False): cv.boolean,
cv.Optional(CONF_DIV_RATIO, default=0): cv.one_of(0, 16, 32, 64, 128, int=True), cv.Optional(CONF_DIV_RATIO, default=0): cv.one_of(0, 16, 32, 64, 128, int=True),
cv.Optional(CONF_CAPACITANCE, default=0): cv.int_range(min=0, max=15), cv.Optional(CONF_CAPACITANCE, default=0): cv.int_range(min=0, max=15),
cv.Optional(CONF_TUNE_ANTENNA, default=False): cv.boolean,
cv.Optional(CONF_CALIBRATION, default=True): cv.boolean,
} }
) )
@@ -51,3 +55,5 @@ async def setup_as3935(var, config):
cg.add(var.set_mask_disturber(config[CONF_MASK_DISTURBER])) cg.add(var.set_mask_disturber(config[CONF_MASK_DISTURBER]))
cg.add(var.set_div_ratio(config[CONF_DIV_RATIO])) cg.add(var.set_div_ratio(config[CONF_DIV_RATIO]))
cg.add(var.set_capacitance(config[CONF_CAPACITANCE])) cg.add(var.set_capacitance(config[CONF_CAPACITANCE]))
cg.add(var.set_tune_antenna(config[CONF_TUNE_ANTENNA]))
cg.add(var.set_calibration(config[CONF_CALIBRATION]))

View File

@@ -21,6 +21,14 @@ void AS3935Component::setup() {
this->write_mask_disturber(this->mask_disturber_); this->write_mask_disturber(this->mask_disturber_);
this->write_div_ratio(this->div_ratio_); this->write_div_ratio(this->div_ratio_);
this->write_capacitance(this->capacitance_); this->write_capacitance(this->capacitance_);
// Handle setting up tuning or auto-calibration
if (this->tune_antenna_) {
ESP_LOGCONFIG(TAG, " Antenna tuning: ENABLED - lightning detection will not function in this mode");
this->tune_antenna();
} else if (this->calibration_) {
this->calibrate_oscillator();
}
} }
void AS3935Component::dump_config() { void AS3935Component::dump_config() {
@@ -227,6 +235,87 @@ uint32_t AS3935Component::get_lightning_energy_() {
return pure_light; return pure_light;
} }
// REG0x03, bit [7:6], manufacturer default: 0 (16 division ratio).
// This function returns the current division ratio of the resonance frequency.
// The antenna resonance frequency should be within 3.5 percent of 500kHz, and
// so when modifying the resonance frequency with the internal capacitors
// (tuneCap()) it's important to keep in mind that the displayed frequency on
// the IRQ pin is divided by this number.
uint8_t AS3935Component::read_div_ratio() {
ESP_LOGV(TAG, "Calling read_div_ratio");
uint8_t reg_val = this->read_register_(INT_MASK_ANT, DIV_MASK);
reg_val >>= 6; // Front of the line.
if (reg_val == 0) {
return 16;
} else if (reg_val == 1) {
return 32;
} else if (reg_val == 2) {
return 64;
} else if (reg_val == 3) {
return 128;
}
ESP_LOGW(TAG, "Unknown response received for div_ratio");
return 0;
}
uint8_t AS3935Component::read_capacitance() {
ESP_LOGV(TAG, "Calling read_capacitance");
uint8_t reg_val = this->read_register_(FREQ_DISP_IRQ, CAP_MASK) * 8;
return (reg_val);
}
// REG0x08, bits [5,6,7], manufacturer default: 0.
// This will send the frequency of the oscillators to the IRQ pin.
// _osc 1, bit[5] = TRCO - System RCO at 32.768kHz
// _osc 2, bit[6] = SRCO - Timer RCO Oscillators 1.1MHz
// _osc 3, bit[7] = LCO - Frequency of the Antenna
void AS3935Component::display_oscillator(bool state, uint8_t osc) {
if ((osc < 1) || (osc > 3))
return;
this->write_register(FREQ_DISP_IRQ, OSC_MASK, state, 4 + osc);
}
// REG0x3D, bits[7:0]
// This function calibrates both internal oscillators The oscillators are tuned
// based on the resonance frequency of the antenna and so it should be trimmed
// before the calibration is done.
bool AS3935Component::calibrate_oscillator() {
ESP_LOGI(TAG, "Starting oscillators calibration...");
this->write_register(CALIB_RCO, WIPE_ALL, DIRECT_COMMAND, 0); // Send command to calibrate the oscillators
this->display_oscillator(true, 2);
delay(2); // Give time for the internal oscillators to start up.
this->display_oscillator(false, 2);
// Check it they were calibrated successfully.
uint8_t reg_val_srco = this->read_register_(CALIB_SRCO, CALIB_MASK_NOK);
uint8_t reg_val_trco = this->read_register_(CALIB_TRCO, CALIB_MASK_NOK);
// reg_val_srco &= CALIB_MASK;
// reg_val_srco >>= 6;
// reg_val_trco &= CALIB_MASK;
// reg_val_trco >>= 6;
if (!reg_val_srco && !reg_val_trco) { // Zero upon success
ESP_LOGI(TAG, "Calibration was succesful");
return true;
} else {
ESP_LOGW(TAG, "Calibration was NOT succesful");
return false;
}
}
void AS3935Component::tune_antenna() {
ESP_LOGI(TAG, "Starting antenna tuning...");
uint8_t div_ratio = this->read_div_ratio();
uint8_t tune_val = this->read_capacitance();
ESP_LOGI(TAG, "Division Ratio is set to: %d", div_ratio);
ESP_LOGI(TAG, "Internal Capacitor is set to: %d", tune_val);
ESP_LOGI(TAG, "Displaying oscillator on INT pin. Measure its frequency - multiply value by Division Ratio");
this->display_oscillator(true, ANTFREQ);
}
uint8_t AS3935Component::read_register_(uint8_t reg, uint8_t mask) { uint8_t AS3935Component::read_register_(uint8_t reg, uint8_t mask) {
uint8_t value = this->read_register(reg); uint8_t value = this->read_register(reg);
value &= (~mask); value &= (~mask);

View File

@@ -13,6 +13,9 @@
namespace esphome { namespace esphome {
namespace as3935 { namespace as3935 {
static const uint8_t DIRECT_COMMAND = 0x96;
static const uint8_t ANTFREQ = 3;
enum AS3935RegisterNames { enum AS3935RegisterNames {
AFE_GAIN = 0x00, AFE_GAIN = 0x00,
THRESHOLD, THRESHOLD,
@@ -30,6 +33,7 @@ enum AS3935RegisterNames {
}; };
enum AS3935RegisterMasks { enum AS3935RegisterMasks {
WIPE_ALL = 0x0,
GAIN_MASK = 0x3E, GAIN_MASK = 0x3E,
SPIKE_MASK = 0xF, SPIKE_MASK = 0xF,
IO_MASK = 0xC1, IO_MASK = 0xC1,
@@ -44,6 +48,7 @@ enum AS3935RegisterMasks {
NOISE_FLOOR_MASK = 0x70, NOISE_FLOOR_MASK = 0x70,
OSC_MASK = 0xE0, OSC_MASK = 0xE0,
CALIB_MASK = 0x7F, CALIB_MASK = 0x7F,
CALIB_MASK_NOK = 0xBF,
DIV_MASK = 0x3F DIV_MASK = 0x3F
}; };
@@ -90,6 +95,13 @@ class AS3935Component : public Component {
void write_div_ratio(uint8_t div_ratio); void write_div_ratio(uint8_t div_ratio);
void set_capacitance(uint8_t capacitance) { capacitance_ = capacitance; } void set_capacitance(uint8_t capacitance) { capacitance_ = capacitance; }
void write_capacitance(uint8_t capacitance); void write_capacitance(uint8_t capacitance);
uint8_t read_div_ratio();
uint8_t read_capacitance();
bool calibrate_oscillator();
void display_oscillator(bool state, uint8_t osc);
void tune_antenna();
void set_tune_antenna(bool tune_antenna) { tune_antenna_ = tune_antenna; }
void set_calibration(bool calibration) { calibration_ = calibration; }
protected: protected:
uint8_t read_interrupt_register_(); uint8_t read_interrupt_register_();
@@ -112,6 +124,8 @@ class AS3935Component : public Component {
bool mask_disturber_; bool mask_disturber_;
uint8_t div_ratio_; uint8_t div_ratio_;
uint8_t capacitance_; uint8_t capacitance_;
bool tune_antenna_;
bool calibration_;
}; };
} // namespace as3935 } // namespace as3935

View File

@@ -2,13 +2,19 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.core import CORE, coroutine_with_priority from esphome.core import CORE, coroutine_with_priority
from esphome.const import (
PLATFORM_ESP32,
PLATFORM_ESP8266,
PLATFORM_BK72XX,
PLATFORM_RTL87XX,
)
CODEOWNERS = ["@OttoWinter"] CODEOWNERS = ["@OttoWinter"]
CONFIG_SCHEMA = cv.All( CONFIG_SCHEMA = cv.All(
cv.Schema({}), cv.Schema({}),
cv.only_with_arduino, cv.only_with_arduino,
cv.only_on(["esp32", "esp8266", "bk72xx", "rtl87xx"]), cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_BK72XX, PLATFORM_RTL87XX]),
) )

View File

@@ -12,6 +12,8 @@ static const uint8_t BP1658CJ_ADDR_START_3CH = 0x10;
static const uint8_t BP1658CJ_ADDR_START_2CH = 0x20; static const uint8_t BP1658CJ_ADDR_START_2CH = 0x20;
static const uint8_t BP1658CJ_ADDR_START_5CH = 0x30; static const uint8_t BP1658CJ_ADDR_START_5CH = 0x30;
static const uint8_t BP1658CJ_DELAY = 2;
void BP1658CJ::setup() { void BP1658CJ::setup() {
ESP_LOGCONFIG(TAG, "Setting up BP1658CJ Output Component..."); ESP_LOGCONFIG(TAG, "Setting up BP1658CJ Output Component...");
this->data_pin_->setup(); this->data_pin_->setup();
@@ -35,10 +37,14 @@ void BP1658CJ::loop() {
uint8_t data[12]; uint8_t data[12];
if (this->pwm_amounts_[0] == 0 && this->pwm_amounts_[1] == 0 && this->pwm_amounts_[2] == 0 && if (this->pwm_amounts_[0] == 0 && this->pwm_amounts_[1] == 0 && this->pwm_amounts_[2] == 0 &&
this->pwm_amounts_[3] == 0 && this->pwm_amounts_[4] == 0) { this->pwm_amounts_[3] == 0 && this->pwm_amounts_[4] == 0) {
// Off / Sleep
data[0] = BP1658CJ_MODEL_ID + BP1658CJ_ADDR_STANDBY;
for (int i = 1; i < 12; i++) for (int i = 1; i < 12; i++)
data[i] = 0; data[i] = 0;
// First turn all channels off
data[0] = BP1658CJ_MODEL_ID + BP1658CJ_ADDR_START_5CH;
this->write_buffer_(data, 12);
// Then sleep
data[0] = BP1658CJ_MODEL_ID + BP1658CJ_ADDR_STANDBY;
this->write_buffer_(data, 12); this->write_buffer_(data, 12);
} else if (this->pwm_amounts_[0] == 0 && this->pwm_amounts_[1] == 0 && this->pwm_amounts_[2] == 0 && } else if (this->pwm_amounts_[0] == 0 && this->pwm_amounts_[1] == 0 && this->pwm_amounts_[2] == 0 &&
(this->pwm_amounts_[3] > 0 || this->pwm_amounts_[4] > 0)) { (this->pwm_amounts_[3] > 0 || this->pwm_amounts_[4] > 0)) {
@@ -81,27 +87,41 @@ void BP1658CJ::set_channel_value_(uint8_t channel, uint16_t value) {
} }
this->pwm_amounts_[channel] = value; this->pwm_amounts_[channel] = value;
} }
void BP1658CJ::write_bit_(bool value) { void BP1658CJ::write_bit_(bool value) {
this->clock_pin_->digital_write(false);
this->data_pin_->digital_write(value); this->data_pin_->digital_write(value);
this->clock_pin_->digital_write(true); this->clock_pin_->digital_write(true);
delayMicroseconds(BP1658CJ_DELAY);
this->clock_pin_->digital_write(false);
} }
void BP1658CJ::write_byte_(uint8_t data) { void BP1658CJ::write_byte_(uint8_t data) {
for (uint8_t mask = 0x80; mask; mask >>= 1) { for (uint8_t mask = 0x80; mask; mask >>= 1) {
this->write_bit_(data & mask); this->write_bit_(data & mask);
delayMicroseconds(BP1658CJ_DELAY);
} }
this->clock_pin_->digital_write(false);
this->data_pin_->digital_write(true); // ack bit
this->data_pin_->pin_mode(gpio::FLAG_INPUT);
this->clock_pin_->digital_write(true); this->clock_pin_->digital_write(true);
delayMicroseconds(BP1658CJ_DELAY);
this->clock_pin_->digital_write(false);
this->data_pin_->pin_mode(gpio::FLAG_OUTPUT);
} }
void BP1658CJ::write_buffer_(uint8_t *buffer, uint8_t size) { void BP1658CJ::write_buffer_(uint8_t *buffer, uint8_t size) {
this->data_pin_->digital_write(false); this->data_pin_->digital_write(false);
this->clock_pin_->digital_write(false);
for (uint32_t i = 0; i < size; i++) { for (uint32_t i = 0; i < size; i++) {
this->write_byte_(buffer[i]); this->write_byte_(buffer[i]);
delayMicroseconds(BP1658CJ_DELAY);
} }
this->clock_pin_->digital_write(false);
this->clock_pin_->digital_write(true); this->clock_pin_->digital_write(true);
this->data_pin_->digital_write(true); this->data_pin_->digital_write(true);
} }

View File

@@ -17,12 +17,16 @@ static const uint8_t BP5758D_ADDR_START_2CH = 0b00100000;
static const uint8_t BP5758D_ADDR_START_5CH = 0b00110000; static const uint8_t BP5758D_ADDR_START_5CH = 0b00110000;
static const uint8_t BP5758D_ALL_DATA_CHANNEL_ENABLEMENT = 0b00011111; static const uint8_t BP5758D_ALL_DATA_CHANNEL_ENABLEMENT = 0b00011111;
static const uint8_t BP5758D_DELAY = 2;
void BP5758D::setup() { void BP5758D::setup() {
ESP_LOGCONFIG(TAG, "Setting up BP5758D Output Component..."); ESP_LOGCONFIG(TAG, "Setting up BP5758D Output Component...");
this->data_pin_->setup(); this->data_pin_->setup();
this->data_pin_->digital_write(false); this->data_pin_->digital_write(false);
delayMicroseconds(BP5758D_DELAY);
this->clock_pin_->setup(); this->clock_pin_->setup();
this->clock_pin_->digital_write(false); this->clock_pin_->digital_write(false);
delayMicroseconds(BP5758D_DELAY);
this->channel_current_.resize(5, 0); this->channel_current_.resize(5, 0);
this->pwm_amounts_.resize(5, 0); this->pwm_amounts_.resize(5, 0);
} }
@@ -39,10 +43,14 @@ void BP5758D::loop() {
uint8_t data[17]; uint8_t data[17];
if (this->pwm_amounts_[0] == 0 && this->pwm_amounts_[1] == 0 && this->pwm_amounts_[2] == 0 && if (this->pwm_amounts_[0] == 0 && this->pwm_amounts_[1] == 0 && this->pwm_amounts_[2] == 0 &&
this->pwm_amounts_[3] == 0 && this->pwm_amounts_[4] == 0) { this->pwm_amounts_[3] == 0 && this->pwm_amounts_[4] == 0) {
// Off / Sleep for (int i = 1; i < 17; i++)
data[0] = BP5758D_MODEL_ID + BP5758D_ADDR_STANDBY;
for (int i = 1; i < 16; i++)
data[i] = 0; data[i] = 0;
// First turn all channels off
data[0] = BP5758D_MODEL_ID + BP5758D_ADDR_START_5CH;
this->write_buffer_(data, 17);
// Then sleep
data[0] = BP5758D_MODEL_ID + BP5758D_ADDR_STANDBY;
this->write_buffer_(data, 17); this->write_buffer_(data, 17);
} else if (this->pwm_amounts_[0] == 0 && this->pwm_amounts_[1] == 0 && this->pwm_amounts_[2] == 0 && } else if (this->pwm_amounts_[0] == 0 && this->pwm_amounts_[1] == 0 && this->pwm_amounts_[2] == 0 &&
(this->pwm_amounts_[3] > 0 || this->pwm_amounts_[4] > 0)) { (this->pwm_amounts_[3] > 0 || this->pwm_amounts_[4] > 0)) {
@@ -119,28 +127,42 @@ void BP5758D::set_channel_value_(uint8_t channel, uint16_t value) {
void BP5758D::set_channel_current_(uint8_t channel, uint8_t current) { this->channel_current_[channel] = current; } void BP5758D::set_channel_current_(uint8_t channel, uint8_t current) { this->channel_current_[channel] = current; }
void BP5758D::write_bit_(bool value) { void BP5758D::write_bit_(bool value) {
this->clock_pin_->digital_write(false);
this->data_pin_->digital_write(value); this->data_pin_->digital_write(value);
delayMicroseconds(BP5758D_DELAY);
this->clock_pin_->digital_write(true); this->clock_pin_->digital_write(true);
delayMicroseconds(BP5758D_DELAY);
this->clock_pin_->digital_write(false);
delayMicroseconds(BP5758D_DELAY);
} }
void BP5758D::write_byte_(uint8_t data) { void BP5758D::write_byte_(uint8_t data) {
for (uint8_t mask = 0x80; mask; mask >>= 1) { for (uint8_t mask = 0x80; mask; mask >>= 1) {
this->write_bit_(data & mask); this->write_bit_(data & mask);
} }
this->clock_pin_->digital_write(false);
this->data_pin_->digital_write(true); // ack bit
this->data_pin_->pin_mode(gpio::FLAG_INPUT);
this->clock_pin_->digital_write(true); this->clock_pin_->digital_write(true);
delayMicroseconds(BP5758D_DELAY);
this->clock_pin_->digital_write(false);
delayMicroseconds(BP5758D_DELAY);
this->data_pin_->pin_mode(gpio::FLAG_OUTPUT);
} }
void BP5758D::write_buffer_(uint8_t *buffer, uint8_t size) { void BP5758D::write_buffer_(uint8_t *buffer, uint8_t size) {
this->data_pin_->digital_write(false); this->data_pin_->digital_write(false);
delayMicroseconds(BP5758D_DELAY);
this->clock_pin_->digital_write(false);
delayMicroseconds(BP5758D_DELAY);
for (uint32_t i = 0; i < size; i++) { for (uint32_t i = 0; i < size; i++) {
this->write_byte_(buffer[i]); this->write_byte_(buffer[i]);
} }
this->clock_pin_->digital_write(false);
this->clock_pin_->digital_write(true); this->clock_pin_->digital_write(true);
delayMicroseconds(BP5758D_DELAY);
this->data_pin_->digital_write(true); this->data_pin_->digital_write(true);
delayMicroseconds(BP5758D_DELAY);
} }
} // namespace bp5758d } // namespace bp5758d

View File

@@ -17,6 +17,7 @@ CONF_ON_FRAME = "on_frame"
def validate_id(config): def validate_id(config):
if CONF_CAN_ID in config:
can_id = config[CONF_CAN_ID] can_id = config[CONF_CAN_ID]
id_ext = config[CONF_USE_EXTENDED_ID] id_ext = config[CONF_USE_EXTENDED_ID]
if not id_ext: if not id_ext:
@@ -151,22 +152,18 @@ async def canbus_action_to_code(config, action_id, template_arg, args):
if can_id := config.get(CONF_CAN_ID): if can_id := config.get(CONF_CAN_ID):
can_id = await cg.templatable(can_id, args, cg.uint32) can_id = await cg.templatable(can_id, args, cg.uint32)
cg.add(var.set_can_id(can_id)) cg.add(var.set_can_id(can_id))
use_extended_id = await cg.templatable( cg.add(var.set_use_extended_id(config[CONF_USE_EXTENDED_ID]))
config[CONF_USE_EXTENDED_ID], args, cg.uint32
)
cg.add(var.set_use_extended_id(use_extended_id))
remote_transmission_request = await cg.templatable( cg.add(
config[CONF_REMOTE_TRANSMISSION_REQUEST], args, bool var.set_remote_transmission_request(config[CONF_REMOTE_TRANSMISSION_REQUEST])
) )
cg.add(var.set_remote_transmission_request(remote_transmission_request))
data = config[CONF_DATA] data = config[CONF_DATA]
if isinstance(data, bytes):
data = [int(x) for x in data]
if cg.is_template(data): if cg.is_template(data):
templ = await cg.templatable(data, args, cg.std_vector.template(cg.uint8)) templ = await cg.templatable(data, args, cg.std_vector.template(cg.uint8))
cg.add(var.set_data_template(templ)) cg.add(var.set_data_template(templ))
else: else:
if isinstance(data, bytes):
data = [int(x) for x in data]
cg.add(var.set_data_static(data)) cg.add(var.set_data_static(data))
return var return var

View File

@@ -2,7 +2,13 @@ import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import web_server_base from esphome.components import web_server_base
from esphome.components.web_server_base import CONF_WEB_SERVER_BASE_ID from esphome.components.web_server_base import CONF_WEB_SERVER_BASE_ID
from esphome.const import CONF_ID from esphome.const import (
CONF_ID,
PLATFORM_ESP32,
PLATFORM_ESP8266,
PLATFORM_BK72XX,
PLATFORM_RTL87XX,
)
from esphome.core import coroutine_with_priority, CORE from esphome.core import coroutine_with_priority, CORE
AUTO_LOAD = ["web_server_base"] AUTO_LOAD = ["web_server_base"]
@@ -21,7 +27,7 @@ CONFIG_SCHEMA = cv.All(
), ),
} }
).extend(cv.COMPONENT_SCHEMA), ).extend(cv.COMPONENT_SCHEMA),
cv.only_on(["esp32", "esp8266", "bk72xx", "rtl87xx"]), cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_BK72XX, PLATFORM_RTL87XX]),
) )

View File

@@ -48,7 +48,7 @@ void CaptivePortal::start() {
this->dns_server_ = make_unique<DNSServer>(); this->dns_server_ = make_unique<DNSServer>();
this->dns_server_->setErrorReplyCode(DNSReplyCode::NoError); this->dns_server_->setErrorReplyCode(DNSReplyCode::NoError);
network::IPAddress ip = wifi::global_wifi_component->wifi_soft_ap_ip(); network::IPAddress ip = wifi::global_wifi_component->wifi_soft_ap_ip();
this->dns_server_->start(53, "*", (uint32_t) ip); this->dns_server_->start(53, "*", ip);
#endif #endif
this->base_->get_server()->onNotFound([this](AsyncWebServerRequest *req) { this->base_->get_server()->onNotFound([this](AsyncWebServerRequest *req) {

View File

@@ -213,6 +213,8 @@ ClimateCall &ClimateCall::set_preset(const std::string &preset) {
this->set_preset(CLIMATE_PRESET_SLEEP); this->set_preset(CLIMATE_PRESET_SLEEP);
} else if (str_equals_case_insensitive(preset, "ACTIVITY")) { } else if (str_equals_case_insensitive(preset, "ACTIVITY")) {
this->set_preset(CLIMATE_PRESET_ACTIVITY); this->set_preset(CLIMATE_PRESET_ACTIVITY);
} else if (str_equals_case_insensitive(preset, "NONE")) {
this->set_preset(CLIMATE_PRESET_NONE);
} else { } else {
if (this->parent_->get_traits().supports_custom_preset(preset)) { if (this->parent_->get_traits().supports_custom_preset(preset)) {
this->custom_preset_ = preset; this->custom_preset_ = preset;

View File

@@ -104,7 +104,8 @@ void CurrentBasedCover::loop() {
ESP_LOGD(TAG, "'%s' - Close position reached. Took %.1fs.", this->name_.c_str(), dur); ESP_LOGD(TAG, "'%s' - Close position reached. Took %.1fs.", this->name_.c_str(), dur);
this->direction_idle_(COVER_CLOSED); this->direction_idle_(COVER_CLOSED);
} }
} else if (now - this->start_dir_time_ > this->max_duration_) { }
if (now - this->start_dir_time_ > this->max_duration_) {
ESP_LOGD(TAG, "'%s' - Max duration reached. Stopping cover.", this->name_.c_str()); ESP_LOGD(TAG, "'%s' - Max duration reached. Stopping cover.", this->name_.c_str());
this->direction_idle_(); this->direction_idle_();
} }

View File

@@ -24,7 +24,7 @@ CONFIG_SCHEMA = cv.All(
).extend( ).extend(
{ {
cv.GenerateID(CONF_DALLAS_ID): cv.use_id(DallasComponent), cv.GenerateID(CONF_DALLAS_ID): cv.use_id(DallasComponent),
cv.Optional(CONF_ADDRESS): cv.hex_int, cv.Optional(CONF_ADDRESS): cv.hex_uint64_t,
cv.Optional(CONF_INDEX): cv.positive_int, cv.Optional(CONF_INDEX): cv.positive_int,
cv.Optional(CONF_RESOLUTION, default=12): cv.int_range(min=9, max=12), cv.Optional(CONF_RESOLUTION, default=12): cv.int_range(min=9, max=12),
} }

View File

@@ -14,6 +14,8 @@ from esphome.const import (
CONF_SLEEP_DURATION, CONF_SLEEP_DURATION,
CONF_TIME_ID, CONF_TIME_ID,
CONF_WAKEUP_PIN, CONF_WAKEUP_PIN,
PLATFORM_ESP32,
PLATFORM_ESP8266,
) )
from esphome.components.esp32 import get_esp32_variant from esphome.components.esp32 import get_esp32_variant
@@ -24,6 +26,7 @@ from esphome.components.esp32.const import (
VARIANT_ESP32S3, VARIANT_ESP32S3,
VARIANT_ESP32C2, VARIANT_ESP32C2,
VARIANT_ESP32C6, VARIANT_ESP32C6,
VARIANT_ESP32H2,
) )
WAKEUP_PINS = { WAKEUP_PINS = {
@@ -98,6 +101,7 @@ WAKEUP_PINS = {
], ],
VARIANT_ESP32C2: [0, 1, 2, 3, 4, 5], VARIANT_ESP32C2: [0, 1, 2, 3, 4, 5],
VARIANT_ESP32C6: [0, 1, 2, 3, 4, 5, 6, 7], VARIANT_ESP32C6: [0, 1, 2, 3, 4, 5, 6, 7],
VARIANT_ESP32H2: [7, 8, 9, 10, 11, 12, 13, 14],
} }
@@ -163,7 +167,8 @@ WAKEUP_CAUSES_SCHEMA = cv.Schema(
} }
) )
CONFIG_SCHEMA = cv.Schema( CONFIG_SCHEMA = cv.All(
cv.Schema(
{ {
cv.GenerateID(): cv.declare_id(DeepSleepComponent), cv.GenerateID(): cv.declare_id(DeepSleepComponent),
cv.Optional(CONF_RUN_DURATION): cv.Any( cv.Optional(CONF_RUN_DURATION): cv.Any(
@@ -172,7 +177,9 @@ CONFIG_SCHEMA = cv.Schema(
), ),
cv.Optional(CONF_SLEEP_DURATION): cv.positive_time_period_milliseconds, cv.Optional(CONF_SLEEP_DURATION): cv.positive_time_period_milliseconds,
cv.Optional(CONF_WAKEUP_PIN): cv.All( cv.Optional(CONF_WAKEUP_PIN): cv.All(
cv.only_on_esp32, pins.internal_gpio_input_pin_schema, validate_pin_number cv.only_on_esp32,
pins.internal_gpio_input_pin_schema,
validate_pin_number,
), ),
cv.Optional(CONF_WAKEUP_PIN_MODE): cv.All( cv.Optional(CONF_WAKEUP_PIN_MODE): cv.All(
cv.only_on_esp32, cv.enum(WAKEUP_PIN_MODES), upper=True cv.only_on_esp32, cv.enum(WAKEUP_PIN_MODES), upper=True
@@ -190,7 +197,9 @@ CONFIG_SCHEMA = cv.Schema(
), ),
cv.Optional(CONF_TOUCH_WAKEUP): cv.All(cv.only_on_esp32, cv.boolean), cv.Optional(CONF_TOUCH_WAKEUP): cv.All(cv.only_on_esp32, cv.boolean),
} }
).extend(cv.COMPONENT_SCHEMA) ).extend(cv.COMPONENT_SCHEMA),
cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266]),
)
async def to_code(config): async def to_code(config):

View File

@@ -40,7 +40,6 @@ void E131Component::setup() {
this->mark_failed(); this->mark_failed();
return; return;
} }
server.ss_family = AF_INET;
err = this->socket_->bind((struct sockaddr *) &server, sizeof(server)); err = this->socket_->bind((struct sockaddr *) &server, sizeof(server));
if (err != 0) { if (err != 0) {

View File

@@ -67,8 +67,8 @@ bool E131Component::join_igmp_groups_() {
if (!universe.second) if (!universe.second)
continue; continue;
ip4_addr_t multicast_addr = {static_cast<uint32_t>( ip4_addr_t multicast_addr =
network::IPAddress(239, 255, ((universe.first >> 8) & 0xff), ((universe.first >> 0) & 0xff)))}; network::IPAddress(239, 255, ((universe.first >> 8) & 0xff), ((universe.first >> 0) & 0xff));
auto err = igmp_joingroup(IP4_ADDR_ANY4, &multicast_addr); auto err = igmp_joingroup(IP4_ADDR_ANY4, &multicast_addr);
@@ -101,8 +101,7 @@ void E131Component::leave_(int universe) {
} }
if (listen_method_ == E131_MULTICAST) { if (listen_method_ == E131_MULTICAST) {
ip4_addr_t multicast_addr = { ip4_addr_t multicast_addr = network::IPAddress(239, 255, ((universe >> 8) & 0xff), ((universe >> 0) & 0xff));
static_cast<uint32_t>(network::IPAddress(239, 255, ((universe >> 8) & 0xff), ((universe >> 0) & 0xff)))};
igmp_leavegroup(IP4_ADDR_ANY4, &multicast_addr); igmp_leavegroup(IP4_ADDR_ANY4, &multicast_addr);
} }

View File

@@ -25,6 +25,7 @@ from esphome.const import (
KEY_NAME, KEY_NAME,
KEY_TARGET_FRAMEWORK, KEY_TARGET_FRAMEWORK,
KEY_TARGET_PLATFORM, KEY_TARGET_PLATFORM,
PLATFORM_ESP32,
TYPE_GIT, TYPE_GIT,
TYPE_LOCAL, TYPE_LOCAL,
__version__, __version__,
@@ -62,7 +63,7 @@ AUTO_LOAD = ["preferences"]
def set_core_data(config): def set_core_data(config):
CORE.data[KEY_ESP32] = {} CORE.data[KEY_ESP32] = {}
CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = "esp32" CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = PLATFORM_ESP32
conf = config[CONF_FRAMEWORK] conf = config[CONF_FRAMEWORK]
if conf[CONF_TYPE] == FRAMEWORK_ESP_IDF: if conf[CONF_TYPE] == FRAMEWORK_ESP_IDF:
CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "esp-idf" CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "esp-idf"

View File

@@ -235,6 +235,7 @@ ESP32_BOARD_PINS = {
"SDA": 5, "SDA": 5,
"SS": 15, "SS": 15,
}, },
"denky_d4": {"RX": 8, "LED": 14},
"esp-wrover-kit": {}, "esp-wrover-kit": {},
"esp32-devkitlipo": {}, "esp32-devkitlipo": {},
"esp32-evb": { "esp32-evb": {

View File

@@ -18,7 +18,7 @@ _ESP_SDIO_PINS = {
11: "Flash Command", 11: "Flash Command",
} }
_ESP32_STRAPPING_PINS = {0, 2, 4, 12, 15} _ESP32_STRAPPING_PINS = {0, 2, 5, 12, 15}
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)

View File

@@ -1,11 +1,53 @@
import logging
from esphome.const import CONF_INPUT, CONF_MODE, CONF_NUMBER
import esphome.config_validation as cv import esphome.config_validation as cv
_ESP32H2_SPI_FLASH_PINS = {6, 7, 15, 16, 17, 18, 19, 20, 21}
_ESP32H2_USB_JTAG_PINS = {26, 27}
_ESP32H2_STRAPPING_PINS = {2, 3, 8, 9, 25}
_LOGGER = logging.getLogger(__name__)
def esp32_h2_validate_gpio_pin(value): def esp32_h2_validate_gpio_pin(value):
# ESP32-H2 not yet supported if value < 0 or value > 27:
raise cv.Invalid("ESP32-H2 isn't supported yet") raise cv.Invalid(f"Invalid pin number: {value} (must be 0-27)")
if value in _ESP32H2_STRAPPING_PINS:
_LOGGER.warning(
"GPIO%d is a Strapping PIN and should be avoided.\n"
"Attaching external pullup/down resistors to strapping pins can cause unexpected failures.\n"
"See https://esphome.io/guides/faq.html#why-am-i-getting-a-warning-about-strapping-pins",
value,
)
if value in _ESP32H2_SPI_FLASH_PINS:
_LOGGER.warning(
"GPIO%d is reserved for SPI Flash communication on some ESP32-H2 chip variants.\n"
"Utilizing SPI-reserved pins could cause unexpected failures.\n"
"See https://docs.espressif.com/projects/esp-idf/en/latest/esp32h2/api-reference/peripherals/gpio.html",
value,
)
if value in _ESP32H2_USB_JTAG_PINS:
_LOGGER.warning(
"GPIO%d is reserved for the USB-Serial-JTAG interface.\n"
"To use this pin as GPIO, USB-Serial-JTAG will be disabled.",
value,
)
return value
def esp32_h2_validate_supports(value): def esp32_h2_validate_supports(value):
# ESP32-H2 not yet supported num = value[CONF_NUMBER]
raise cv.Invalid("ESP32-H2 isn't supported yet") mode = value[CONF_MODE]
is_input = mode[CONF_INPUT]
if num < 0 or num > 27:
raise cv.Invalid(f"Invalid pin number: {value} (must be 0-27)")
if is_input:
# All ESP32 pins support input mode
pass
return value

View File

@@ -2,9 +2,9 @@
#ifdef USE_ESP32 #ifdef USE_ESP32
#include "ble_uuid.h"
#include <cstring>
#include <cstdio> #include <cstdio>
#include <cstring>
#include "ble_uuid.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
namespace esphome { namespace esphome {
@@ -16,8 +16,8 @@ BLEAdvertising::BLEAdvertising() {
this->advertising_data_.set_scan_rsp = false; this->advertising_data_.set_scan_rsp = false;
this->advertising_data_.include_name = true; this->advertising_data_.include_name = true;
this->advertising_data_.include_txpower = true; this->advertising_data_.include_txpower = true;
this->advertising_data_.min_interval = 0x20; this->advertising_data_.min_interval = 0;
this->advertising_data_.max_interval = 0x40; this->advertising_data_.max_interval = 0;
this->advertising_data_.appearance = 0x00; this->advertising_data_.appearance = 0x00;
this->advertising_data_.manufacturer_len = 0; this->advertising_data_.manufacturer_len = 0;
this->advertising_data_.p_manufacturer_data = nullptr; this->advertising_data_.p_manufacturer_data = nullptr;
@@ -42,6 +42,17 @@ void BLEAdvertising::remove_service_uuid(ESPBTUUID uuid) {
this->advertising_uuids_.end()); this->advertising_uuids_.end());
} }
void BLEAdvertising::set_service_data(const std::vector<uint8_t> &data) {
delete[] this->advertising_data_.p_service_data;
this->advertising_data_.p_service_data = nullptr;
this->advertising_data_.service_data_len = data.size();
if (!data.empty()) {
// NOLINTNEXTLINE(cppcoreguidelines-owning-memory)
this->advertising_data_.p_service_data = new uint8_t[data.size()];
memcpy(this->advertising_data_.p_service_data, data.data(), data.size());
}
}
void BLEAdvertising::set_manufacturer_data(const std::vector<uint8_t> &data) { void BLEAdvertising::set_manufacturer_data(const std::vector<uint8_t> &data) {
delete[] this->advertising_data_.p_manufacturer_data; delete[] this->advertising_data_.p_manufacturer_data;
this->advertising_data_.p_manufacturer_data = nullptr; this->advertising_data_.p_manufacturer_data = nullptr;
@@ -85,8 +96,6 @@ void BLEAdvertising::start() {
this->scan_response_data_.set_scan_rsp = true; this->scan_response_data_.set_scan_rsp = true;
this->scan_response_data_.include_name = true; this->scan_response_data_.include_name = true;
this->scan_response_data_.include_txpower = true; this->scan_response_data_.include_txpower = true;
this->scan_response_data_.min_interval = 0;
this->scan_response_data_.max_interval = 0;
this->scan_response_data_.manufacturer_len = 0; this->scan_response_data_.manufacturer_len = 0;
this->scan_response_data_.appearance = 0; this->scan_response_data_.appearance = 0;
this->scan_response_data_.flag = 0; this->scan_response_data_.flag = 0;

View File

@@ -21,6 +21,7 @@ class BLEAdvertising {
void set_scan_response(bool scan_response) { this->scan_response_ = scan_response; } void set_scan_response(bool scan_response) { this->scan_response_ = scan_response; }
void set_min_preferred_interval(uint16_t interval) { this->advertising_data_.min_interval = interval; } void set_min_preferred_interval(uint16_t interval) { this->advertising_data_.min_interval = interval; }
void set_manufacturer_data(const std::vector<uint8_t> &data); void set_manufacturer_data(const std::vector<uint8_t> &data);
void set_service_data(const std::vector<uint8_t> &data);
void start(); void start();
void stop(); void stop();

View File

@@ -90,6 +90,8 @@ void BLEService::stop() {
ESP_LOGE(TAG, "esp_ble_gatts_stop_service failed: %d", err); ESP_LOGE(TAG, "esp_ble_gatts_stop_service failed: %d", err);
return; return;
} }
esp32_ble::global_ble->get_advertising()->remove_service_uuid(this->uuid_);
esp32_ble::global_ble->get_advertising()->start();
this->running_state_ = STOPPING; this->running_state_ = STOPPING;
} }

View File

@@ -7,11 +7,11 @@
#ifdef USE_ESP32 #ifdef USE_ESP32
#include <esp_bt_defs.h>
#include <esp_gap_ble_api.h> #include <esp_gap_ble_api.h>
#include <esp_gatt_defs.h> #include <esp_gatt_defs.h>
#include <esp_gattc_api.h> #include <esp_gattc_api.h>
#include <esp_gatts_api.h> #include <esp_gatts_api.h>
#include <esp_bt_defs.h>
namespace esphome { namespace esphome {
namespace esp32_ble_server { namespace esp32_ble_server {

View File

@@ -4,7 +4,7 @@ from esphome.components import binary_sensor, output, esp32_ble_server
from esphome.const import CONF_ID from esphome.const import CONF_ID
AUTO_LOAD = ["binary_sensor", "output", "esp32_ble_server"] AUTO_LOAD = ["esp32_ble_server"]
CODEOWNERS = ["@jesserockz"] CODEOWNERS = ["@jesserockz"]
CONFLICTS_WITH = ["esp32_ble_beacon"] CONFLICTS_WITH = ["esp32_ble_beacon"]
DEPENDENCIES = ["wifi", "esp32"] DEPENDENCIES = ["wifi", "esp32"]
@@ -36,6 +36,9 @@ CONFIG_SCHEMA = cv.Schema(
cv.Optional( cv.Optional(
CONF_AUTHORIZED_DURATION, default="1min" CONF_AUTHORIZED_DURATION, default="1min"
): cv.positive_time_period_milliseconds, ): cv.positive_time_period_milliseconds,
cv.Optional(
CONF_WIFI_TIMEOUT, default="1min"
): cv.positive_time_period_milliseconds,
} }
).extend(cv.COMPONENT_SCHEMA) ).extend(cv.COMPONENT_SCHEMA)
@@ -53,6 +56,8 @@ async def to_code(config):
cg.add(var.set_identify_duration(config[CONF_IDENTIFY_DURATION])) cg.add(var.set_identify_duration(config[CONF_IDENTIFY_DURATION]))
cg.add(var.set_authorized_duration(config[CONF_AUTHORIZED_DURATION])) cg.add(var.set_authorized_duration(config[CONF_AUTHORIZED_DURATION]))
cg.add(var.set_wifi_timeout(config[CONF_WIFI_TIMEOUT]))
if CONF_AUTHORIZER in config and config[CONF_AUTHORIZER] is not None: if CONF_AUTHORIZER in config and config[CONF_AUTHORIZER] is not None:
activator = await cg.get_variable(config[CONF_AUTHORIZER]) activator = await cg.get_variable(config[CONF_AUTHORIZER])
cg.add(var.set_authorizer(activator)) cg.add(var.set_authorizer(activator))

View File

@@ -18,6 +18,17 @@ ESP32ImprovComponent::ESP32ImprovComponent() { global_improv_component = this; }
void ESP32ImprovComponent::setup() { void ESP32ImprovComponent::setup() {
this->service_ = global_ble_server->create_service(improv::SERVICE_UUID, true); this->service_ = global_ble_server->create_service(improv::SERVICE_UUID, true);
this->setup_characteristics(); this->setup_characteristics();
#ifdef USE_BINARY_SENSOR
if (this->authorizer_ != nullptr) {
this->authorizer_->add_on_state_callback([this](bool state) {
if (state) {
this->authorized_start_ = millis();
this->identify_start_ = 0;
}
});
}
#endif
} }
void ESP32ImprovComponent::setup_characteristics() { void ESP32ImprovComponent::setup_characteristics() {
@@ -50,8 +61,10 @@ void ESP32ImprovComponent::setup_characteristics() {
BLEDescriptor *capabilities_descriptor = new BLE2902(); BLEDescriptor *capabilities_descriptor = new BLE2902();
this->capabilities_->add_descriptor(capabilities_descriptor); this->capabilities_->add_descriptor(capabilities_descriptor);
uint8_t capabilities = 0x00; uint8_t capabilities = 0x00;
#ifdef USE_OUTPUT
if (this->status_indicator_ != nullptr) if (this->status_indicator_ != nullptr)
capabilities |= improv::CAPABILITY_IDENTIFY; capabilities |= improv::CAPABILITY_IDENTIFY;
#endif
this->capabilities_->set_value(capabilities); this->capabilities_->set_value(capabilities);
this->setup_complete_ = true; this->setup_complete_ = true;
} }
@@ -63,8 +76,7 @@ void ESP32ImprovComponent::loop() {
switch (this->state_) { switch (this->state_) {
case improv::STATE_STOPPED: case improv::STATE_STOPPED:
if (this->status_indicator_ != nullptr) this->set_status_indicator_state_(false);
this->status_indicator_->turn_off();
if (this->service_->is_created() && this->should_start_ && this->setup_complete_) { if (this->service_->is_created() && this->should_start_ && this->setup_complete_) {
if (this->service_->is_running()) { if (this->service_->is_running()) {
@@ -80,18 +92,22 @@ void ESP32ImprovComponent::loop() {
} }
break; break;
case improv::STATE_AWAITING_AUTHORIZATION: { case improv::STATE_AWAITING_AUTHORIZATION: {
if (this->authorizer_ == nullptr || this->authorizer_->state) { #ifdef USE_BINARY_SENSOR
if (this->authorizer_ == nullptr ||
(this->authorized_start_ != 0 && ((now - this->authorized_start_) < this->authorized_duration_))) {
this->set_state_(improv::STATE_AUTHORIZED); this->set_state_(improv::STATE_AUTHORIZED);
this->authorized_start_ = now; } else
} else { #else
if (this->status_indicator_ != nullptr) { this->set_state_(improv::STATE_AUTHORIZED);
#endif
{
if (!this->check_identify_()) if (!this->check_identify_())
this->status_indicator_->turn_on(); this->set_status_indicator_state_(true);
}
} }
break; break;
} }
case improv::STATE_AUTHORIZED: { case improv::STATE_AUTHORIZED: {
#ifdef USE_BINARY_SENSOR
if (this->authorizer_ != nullptr) { if (this->authorizer_ != nullptr) {
if (now - this->authorized_start_ > this->authorized_duration_) { if (now - this->authorized_start_ > this->authorized_duration_) {
ESP_LOGD(TAG, "Authorization timeout"); ESP_LOGD(TAG, "Authorization timeout");
@@ -99,25 +115,14 @@ void ESP32ImprovComponent::loop() {
return; return;
} }
} }
if (this->status_indicator_ != nullptr) { #endif
if (!this->check_identify_()) { if (!this->check_identify_()) {
if ((now % 1000) < 500) { this->set_status_indicator_state_((now % 1000) < 500);
this->status_indicator_->turn_on();
} else {
this->status_indicator_->turn_off();
}
}
} }
break; break;
} }
case improv::STATE_PROVISIONING: { case improv::STATE_PROVISIONING: {
if (this->status_indicator_ != nullptr) { this->set_status_indicator_state_((now % 200) < 100);
if ((now % 200) < 100) {
this->status_indicator_->turn_on();
} else {
this->status_indicator_->turn_off();
}
}
if (wifi::global_wifi_component->is_connected()) { if (wifi::global_wifi_component->is_connected()) {
wifi::global_wifi_component->save_wifi_sta(this->connecting_sta_.get_ssid(), wifi::global_wifi_component->save_wifi_sta(this->connecting_sta_.get_ssid(),
this->connecting_sta_.get_password()); this->connecting_sta_.get_password());
@@ -142,13 +147,27 @@ void ESP32ImprovComponent::loop() {
} }
case improv::STATE_PROVISIONED: { case improv::STATE_PROVISIONED: {
this->incoming_data_.clear(); this->incoming_data_.clear();
if (this->status_indicator_ != nullptr) this->set_status_indicator_state_(false);
this->status_indicator_->turn_off();
break; break;
} }
} }
} }
void ESP32ImprovComponent::set_status_indicator_state_(bool state) {
#ifdef USE_OUTPUT
if (this->status_indicator_ == nullptr)
return;
if (this->status_indicator_state_ == state)
return;
this->status_indicator_state_ = state;
if (state) {
this->status_indicator_->turn_on();
} else {
this->status_indicator_->turn_off();
}
#endif
}
bool ESP32ImprovComponent::check_identify_() { bool ESP32ImprovComponent::check_identify_() {
uint32_t now = millis(); uint32_t now = millis();
@@ -156,11 +175,7 @@ bool ESP32ImprovComponent::check_identify_() {
if (identify) { if (identify) {
uint32_t time = now % 1000; uint32_t time = now % 1000;
if (time < 600 && time % 200 < 100) { this->set_status_indicator_state_(time < 600 && time % 200 < 100);
this->status_indicator_->turn_on();
} else {
this->status_indicator_->turn_off();
}
} }
return identify; return identify;
} }
@@ -174,6 +189,25 @@ void ESP32ImprovComponent::set_state_(improv::State state) {
if (state != improv::STATE_STOPPED) if (state != improv::STATE_STOPPED)
this->status_->notify(); this->status_->notify();
} }
std::vector<uint8_t> service_data(8, 0);
service_data[0] = 0x77; // PR
service_data[1] = 0x46; // IM
service_data[2] = static_cast<uint8_t>(state);
uint8_t capabilities = 0x00;
#ifdef USE_OUTPUT
if (this->status_indicator_ != nullptr)
capabilities |= improv::CAPABILITY_IDENTIFY;
#endif
service_data[3] = capabilities;
service_data[4] = 0x00; // Reserved
service_data[5] = 0x00; // Reserved
service_data[6] = 0x00; // Reserved
service_data[7] = 0x00; // Reserved
esp32_ble::global_ble->get_advertising()->set_service_data(service_data);
esp32_ble::global_ble->get_advertising()->start();
} }
void ESP32ImprovComponent::set_error_(improv::Error error) { void ESP32ImprovComponent::set_error_(improv::Error error) {
@@ -213,8 +247,12 @@ float ESP32ImprovComponent::get_setup_priority() const { return setup_priority::
void ESP32ImprovComponent::dump_config() { void ESP32ImprovComponent::dump_config() {
ESP_LOGCONFIG(TAG, "ESP32 Improv:"); ESP_LOGCONFIG(TAG, "ESP32 Improv:");
#ifdef USE_BINARY_SENSOR
LOG_BINARY_SENSOR(" ", "Authorizer", this->authorizer_); LOG_BINARY_SENSOR(" ", "Authorizer", this->authorizer_);
#endif
#ifdef USE_OUTPUT
ESP_LOGCONFIG(TAG, " Status Indicator: '%s'", YESNO(this->status_indicator_ != nullptr)); ESP_LOGCONFIG(TAG, " Status Indicator: '%s'", YESNO(this->status_indicator_ != nullptr));
#endif
} }
void ESP32ImprovComponent::process_incoming_data_() { void ESP32ImprovComponent::process_incoming_data_() {
@@ -273,8 +311,10 @@ void ESP32ImprovComponent::process_incoming_data_() {
void ESP32ImprovComponent::on_wifi_connect_timeout_() { void ESP32ImprovComponent::on_wifi_connect_timeout_() {
this->set_error_(improv::ERROR_UNABLE_TO_CONNECT); this->set_error_(improv::ERROR_UNABLE_TO_CONNECT);
this->set_state_(improv::STATE_AUTHORIZED); this->set_state_(improv::STATE_AUTHORIZED);
#ifdef USE_BINARY_SENSOR
if (this->authorizer_ != nullptr) if (this->authorizer_ != nullptr)
this->authorized_start_ = millis(); this->authorized_start_ = millis();
#endif
ESP_LOGW(TAG, "Timed out trying to connect to given WiFi network"); ESP_LOGW(TAG, "Timed out trying to connect to given WiFi network");
wifi::global_wifi_component->clear_sta(); wifi::global_wifi_component->clear_sta();
} }

View File

@@ -1,14 +1,22 @@
#pragma once #pragma once
#include "esphome/components/binary_sensor/binary_sensor.h"
#include "esphome/components/esp32_ble_server/ble_characteristic.h"
#include "esphome/components/esp32_ble_server/ble_server.h"
#include "esphome/components/output/binary_output.h"
#include "esphome/components/wifi/wifi_component.h"
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/core/defines.h"
#include "esphome/core/helpers.h" #include "esphome/core/helpers.h"
#include "esphome/core/preferences.h" #include "esphome/core/preferences.h"
#include "esphome/components/esp32_ble_server/ble_characteristic.h"
#include "esphome/components/esp32_ble_server/ble_server.h"
#include "esphome/components/wifi/wifi_component.h"
#ifdef USE_BINARY_SENSOR
#include "esphome/components/binary_sensor/binary_sensor.h"
#endif
#ifdef USE_OUTPUT
#include "esphome/components/output/binary_output.h"
#endif
#include <vector> #include <vector>
#ifdef USE_ESP32 #ifdef USE_ESP32
@@ -34,11 +42,18 @@ class ESP32ImprovComponent : public Component, public BLEServiceComponent {
void stop() override; void stop() override;
bool is_active() const { return this->state_ != improv::STATE_STOPPED; } bool is_active() const { return this->state_ != improv::STATE_STOPPED; }
#ifdef USE_BINARY_SENSOR
void set_authorizer(binary_sensor::BinarySensor *authorizer) { this->authorizer_ = authorizer; } void set_authorizer(binary_sensor::BinarySensor *authorizer) { this->authorizer_ = authorizer; }
#endif
#ifdef USE_OUTPUT
void set_status_indicator(output::BinaryOutput *status_indicator) { this->status_indicator_ = status_indicator; } void set_status_indicator(output::BinaryOutput *status_indicator) { this->status_indicator_ = status_indicator; }
#endif
void set_identify_duration(uint32_t identify_duration) { this->identify_duration_ = identify_duration; } void set_identify_duration(uint32_t identify_duration) { this->identify_duration_ = identify_duration; }
void set_authorized_duration(uint32_t authorized_duration) { this->authorized_duration_ = authorized_duration; } void set_authorized_duration(uint32_t authorized_duration) { this->authorized_duration_ = authorized_duration; }
void set_wifi_timeout(uint32_t wifi_timeout) { this->wifi_timeout_ = wifi_timeout; }
uint32_t get_wifi_timeout() const { return this->wifi_timeout_; }
protected: protected:
bool should_start_{false}; bool should_start_{false};
bool setup_complete_{false}; bool setup_complete_{false};
@@ -48,6 +63,8 @@ class ESP32ImprovComponent : public Component, public BLEServiceComponent {
uint32_t authorized_start_{0}; uint32_t authorized_start_{0};
uint32_t authorized_duration_; uint32_t authorized_duration_;
uint32_t wifi_timeout_{};
std::vector<uint8_t> incoming_data_; std::vector<uint8_t> incoming_data_;
wifi::WiFiAP connecting_sta_; wifi::WiFiAP connecting_sta_;
@@ -58,12 +75,19 @@ class ESP32ImprovComponent : public Component, public BLEServiceComponent {
BLECharacteristic *rpc_response_; BLECharacteristic *rpc_response_;
BLECharacteristic *capabilities_; BLECharacteristic *capabilities_;
#ifdef USE_BINARY_SENSOR
binary_sensor::BinarySensor *authorizer_{nullptr}; binary_sensor::BinarySensor *authorizer_{nullptr};
#endif
#ifdef USE_OUTPUT
output::BinaryOutput *status_indicator_{nullptr}; output::BinaryOutput *status_indicator_{nullptr};
#endif
improv::State state_{improv::STATE_STOPPED}; improv::State state_{improv::STATE_STOPPED};
improv::Error error_state_{improv::ERROR_NONE}; improv::Error error_state_{improv::ERROR_NONE};
bool status_indicator_state_{false};
void set_status_indicator_state_(bool state);
void set_state_(improv::State state); void set_state_(improv::State state);
void set_error_(improv::Error error); void set_error_(improv::Error error);
void send_response_(std::vector<uint8_t> &response); void send_response_(std::vector<uint8_t> &response);

View File

@@ -64,6 +64,7 @@ RMT_CHANNELS = {
esp32.const.VARIANT_ESP32S3: [0, 1, 2, 3], esp32.const.VARIANT_ESP32S3: [0, 1, 2, 3],
esp32.const.VARIANT_ESP32C3: [0, 1], esp32.const.VARIANT_ESP32C3: [0, 1],
esp32.const.VARIANT_ESP32C6: [0, 1], esp32.const.VARIANT_ESP32C6: [0, 1],
esp32.const.VARIANT_ESP32H2: [0, 1],
} }

View File

@@ -11,6 +11,7 @@ from esphome.const import (
KEY_FRAMEWORK_VERSION, KEY_FRAMEWORK_VERSION,
KEY_TARGET_FRAMEWORK, KEY_TARGET_FRAMEWORK,
KEY_TARGET_PLATFORM, KEY_TARGET_PLATFORM,
PLATFORM_ESP8266,
) )
from esphome.core import CORE, coroutine_with_priority from esphome.core import CORE, coroutine_with_priority
import esphome.config_validation as cv import esphome.config_validation as cv
@@ -38,7 +39,7 @@ AUTO_LOAD = ["preferences"]
def set_core_data(config): def set_core_data(config):
CORE.data[KEY_ESP8266] = {} CORE.data[KEY_ESP8266] = {}
CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = "esp8266" CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = PLATFORM_ESP8266
CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "arduino" CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "arduino"
CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] = cv.Version.parse( CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] = cv.Version.parse(
config[CONF_FRAMEWORK][CONF_VERSION] config[CONF_FRAMEWORK][CONF_VERSION]

View File

@@ -236,7 +236,7 @@ bool EthernetComponent::can_proceed() { return this->is_connected(); }
network::IPAddress EthernetComponent::get_ip_address() { network::IPAddress EthernetComponent::get_ip_address() {
esp_netif_ip_info_t ip; esp_netif_ip_info_t ip;
esp_netif_get_ip_info(this->eth_netif_, &ip); esp_netif_get_ip_info(this->eth_netif_, &ip);
return {ip.ip.addr}; return network::IPAddress(&ip.ip);
} }
void EthernetComponent::eth_event_handler(void *arg, esp_event_base_t event_base, int32_t event, void *event_data) { void EthernetComponent::eth_event_handler(void *arg, esp_event_base_t event_base, int32_t event, void *event_data) {
@@ -293,9 +293,9 @@ void EthernetComponent::start_connect_() {
esp_netif_ip_info_t info; esp_netif_ip_info_t info;
if (this->manual_ip_.has_value()) { if (this->manual_ip_.has_value()) {
info.ip.addr = static_cast<uint32_t>(this->manual_ip_->static_ip); info.ip = this->manual_ip_->static_ip;
info.gw.addr = static_cast<uint32_t>(this->manual_ip_->gateway); info.gw = this->manual_ip_->gateway;
info.netmask.addr = static_cast<uint32_t>(this->manual_ip_->subnet); info.netmask = this->manual_ip_->subnet;
} else { } else {
info.ip.addr = 0; info.ip.addr = 0;
info.gw.addr = 0; info.gw.addr = 0;
@@ -318,24 +318,14 @@ void EthernetComponent::start_connect_() {
ESPHL_ERROR_CHECK(err, "DHCPC set IP info error"); ESPHL_ERROR_CHECK(err, "DHCPC set IP info error");
if (this->manual_ip_.has_value()) { if (this->manual_ip_.has_value()) {
if (uint32_t(this->manual_ip_->dns1) != 0) { if (this->manual_ip_->dns1.is_set()) {
ip_addr_t d; ip_addr_t d;
#if LWIP_IPV6 d = this->manual_ip_->dns1;
d.type = IPADDR_TYPE_V4;
d.u_addr.ip4.addr = static_cast<uint32_t>(this->manual_ip_->dns1);
#else
d.addr = static_cast<uint32_t>(this->manual_ip_->dns1);
#endif
dns_setserver(0, &d); dns_setserver(0, &d);
} }
if (uint32_t(this->manual_ip_->dns2) != 0) { if (this->manual_ip_->dns2.is_set()) {
ip_addr_t d; ip_addr_t d;
#if LWIP_IPV6 d = this->manual_ip_->dns2;
d.type = IPADDR_TYPE_V4;
d.u_addr.ip4.addr = static_cast<uint32_t>(this->manual_ip_->dns2);
#else
d.addr = static_cast<uint32_t>(this->manual_ip_->dns2);
#endif
dns_setserver(1, &d); dns_setserver(1, &d);
} }
} else { } else {
@@ -360,21 +350,16 @@ bool EthernetComponent::is_connected() { return this->state_ == EthernetComponen
void EthernetComponent::dump_connect_params_() { void EthernetComponent::dump_connect_params_() {
esp_netif_ip_info_t ip; esp_netif_ip_info_t ip;
esp_netif_get_ip_info(this->eth_netif_, &ip); esp_netif_get_ip_info(this->eth_netif_, &ip);
ESP_LOGCONFIG(TAG, " IP Address: %s", network::IPAddress(ip.ip.addr).str().c_str()); ESP_LOGCONFIG(TAG, " IP Address: %s", network::IPAddress(&ip.ip).str().c_str());
ESP_LOGCONFIG(TAG, " Hostname: '%s'", App.get_name().c_str()); ESP_LOGCONFIG(TAG, " Hostname: '%s'", App.get_name().c_str());
ESP_LOGCONFIG(TAG, " Subnet: %s", network::IPAddress(ip.netmask.addr).str().c_str()); ESP_LOGCONFIG(TAG, " Subnet: %s", network::IPAddress(&ip.netmask).str().c_str());
ESP_LOGCONFIG(TAG, " Gateway: %s", network::IPAddress(ip.gw.addr).str().c_str()); ESP_LOGCONFIG(TAG, " Gateway: %s", network::IPAddress(&ip.gw).str().c_str());
const ip_addr_t *dns_ip1 = dns_getserver(0); const ip_addr_t *dns_ip1 = dns_getserver(0);
const ip_addr_t *dns_ip2 = dns_getserver(1); const ip_addr_t *dns_ip2 = dns_getserver(1);
#if LWIP_IPV6 ESP_LOGCONFIG(TAG, " DNS1: %s", network::IPAddress(dns_ip1).str().c_str());
ESP_LOGCONFIG(TAG, " DNS1: %s", network::IPAddress(dns_ip1->u_addr.ip4.addr).str().c_str()); ESP_LOGCONFIG(TAG, " DNS2: %s", network::IPAddress(dns_ip2).str().c_str());
ESP_LOGCONFIG(TAG, " DNS2: %s", network::IPAddress(dns_ip2->u_addr.ip4.addr).str().c_str());
#else
ESP_LOGCONFIG(TAG, " DNS1: %s", network::IPAddress(dns_ip1->addr).str().c_str());
ESP_LOGCONFIG(TAG, " DNS2: %s", network::IPAddress(dns_ip2->addr).str().c_str());
#endif
#if ENABLE_IPV6 #if ENABLE_IPV6
if (this->ipv6_count_ > 0) { if (this->ipv6_count_ > 0) {

View File

@@ -67,18 +67,13 @@ def validate_pillow_installed(value):
except ImportError as err: except ImportError as err:
raise cv.Invalid( raise cv.Invalid(
"Please install the pillow python package to use this feature. " "Please install the pillow python package to use this feature. "
'(pip install pillow">4.0.0,<10.0.0")' '(pip install "pillow==10.0.1")'
) from err ) from err
if version.parse(PIL.__version__) < version.parse("4.0.0"): if version.parse(PIL.__version__) != version.parse("10.0.1"):
raise cv.Invalid( raise cv.Invalid(
"Please update your pillow installation to at least 4.0.x. " "Please update your pillow installation to 10.0.1. "
'(pip install pillow">4.0.0,<10.0.0")' '(pip install "pillow==10.0.1")'
)
if version.parse(PIL.__version__) >= version.parse("10.0.0"):
raise cv.Invalid(
"Please downgrade your pillow installation to below 10.0.0. "
'(pip install pillow">4.0.0,<10.0.0")'
) )
return value return value
@@ -142,11 +137,10 @@ def validate_weight_name(value):
def download_gfonts(value): def download_gfonts(value):
wght = value[CONF_WEIGHT] name = (
if value[CONF_ITALIC]: f"{value[CONF_FAMILY]}:ital,wght@{int(value[CONF_ITALIC])},{value[CONF_WEIGHT]}"
wght = f"1,{wght}" )
name = f"{value[CONF_FAMILY]}@{value[CONF_WEIGHT]}" url = f"https://fonts.googleapis.com/css2?family={name}"
url = f"https://fonts.googleapis.com/css2?family={value[CONF_FAMILY]}:wght@{wght}"
path = _compute_gfonts_local_path(value) path = _compute_gfonts_local_path(value)
if path.is_file(): if path.is_file():

View File

@@ -8,12 +8,15 @@ from esphome.const import (
CONF_CHANNEL, CONF_CHANNEL,
CONF_SPEED, CONF_SPEED,
CONF_DIRECTION, CONF_DIRECTION,
CONF_ADDRESS,
) )
DEPENDENCIES = ["i2c"] DEPENDENCIES = ["i2c"]
CODEOWNERS = ["@max246"] CODEOWNERS = ["@max246"]
MULTI_CONF = True
grove_tb6612fng_ns = cg.esphome_ns.namespace("grove_tb6612fng") grove_tb6612fng_ns = cg.esphome_ns.namespace("grove_tb6612fng")
GROVE_TB6612FNG = grove_tb6612fng_ns.class_( GROVE_TB6612FNG = grove_tb6612fng_ns.class_(
"GroveMotorDriveTB6612FNG", cg.Component, i2c.I2CDevice "GroveMotorDriveTB6612FNG", cg.Component, i2c.I2CDevice
@@ -33,6 +36,9 @@ GROVETB6612FNGMotorStandbyAction = grove_tb6612fng_ns.class_(
GROVETB6612FNGMotorNoStandbyAction = grove_tb6612fng_ns.class_( GROVETB6612FNGMotorNoStandbyAction = grove_tb6612fng_ns.class_(
"GROVETB6612FNGMotorNoStandbyAction", automation.Action "GROVETB6612FNGMotorNoStandbyAction", automation.Action
) )
GROVETB6612FNGMotorChangeAddressAction = grove_tb6612fng_ns.class_(
"GROVETB6612FNGMotorChangeAddressAction", automation.Action
)
DIRECTION_TYPE = { DIRECTION_TYPE = {
"FORWARD": 1, "FORWARD": 1,
@@ -150,3 +156,22 @@ async def grove_tb6612fng_no_standby_to_code(config, action_id, template_arg, ar
await cg.register_parented(var, config[CONF_ID]) await cg.register_parented(var, config[CONF_ID])
return var return var
@automation.register_action(
"grove_tb6612fng.change_address",
GROVETB6612FNGMotorChangeAddressAction,
cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(GROVE_TB6612FNG),
cv.Required(CONF_ADDRESS): cv.i2c_address,
}
),
)
async def grove_tb6612fng_change_address_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID])
template_channel = await cg.templatable(config[CONF_ADDRESS], args, int)
cg.add(var.set_address(template_channel))
return var

View File

@@ -84,8 +84,7 @@ class GroveMotorDriveTB6612FNG : public Component, public i2c::I2CDevice {
*************************************************************/ *************************************************************/
void set_i2c_addr(uint8_t addr); void set_i2c_addr(uint8_t addr);
/************************************************************* /***********************************change_address
Description
Drive a motor. Drive a motor.
Parameter Parameter
chl: MOTOR_CHA or MOTOR_CHB chl: MOTOR_CHA or MOTOR_CHB
@@ -204,5 +203,13 @@ class GROVETB6612FNGMotorNoStandbyAction : public Action<Ts...>, public Parented
void play(Ts... x) override { this->parent_->not_standby(); } void play(Ts... x) override { this->parent_->not_standby(); }
}; };
template<typename... Ts>
class GROVETB6612FNGMotorChangeAddressAction : public Action<Ts...>, public Parented<GroveMotorDriveTB6612FNG> {
public:
TEMPLATABLE_VALUE(uint8_t, address)
void play(Ts... x) override { this->parent_->set_i2c_addr(this->address_.value(x...)); }
};
} // namespace grove_tb6612fng } // namespace grove_tb6612fng
} // namespace esphome } // namespace esphome

View File

@@ -3,6 +3,9 @@ import esphome.config_validation as cv
from esphome.components import i2c, sensor from esphome.components import i2c, sensor
from esphome.const import ( from esphome.const import (
CONF_ADDRESS, CONF_ADDRESS,
CONF_FIELD_STRENGTH_X,
CONF_FIELD_STRENGTH_Y,
CONF_FIELD_STRENGTH_Z,
CONF_ID, CONF_ID,
CONF_OVERSAMPLING, CONF_OVERSAMPLING,
CONF_RANGE, CONF_RANGE,
@@ -18,9 +21,6 @@ DEPENDENCIES = ["i2c"]
hmc5883l_ns = cg.esphome_ns.namespace("hmc5883l") hmc5883l_ns = cg.esphome_ns.namespace("hmc5883l")
CONF_FIELD_STRENGTH_X = "field_strength_x"
CONF_FIELD_STRENGTH_Y = "field_strength_y"
CONF_FIELD_STRENGTH_Z = "field_strength_z"
CONF_HEADING = "heading" CONF_HEADING = "heading"
HMC5883LComponent = hmc5883l_ns.class_( HMC5883LComponent = hmc5883l_ns.class_(

View File

@@ -3,6 +3,7 @@ from esphome.const import (
KEY_FRAMEWORK_VERSION, KEY_FRAMEWORK_VERSION,
KEY_TARGET_FRAMEWORK, KEY_TARGET_FRAMEWORK,
KEY_TARGET_PLATFORM, KEY_TARGET_PLATFORM,
PLATFORM_HOST,
) )
from esphome.core import CORE from esphome.core import CORE
import esphome.config_validation as cv import esphome.config_validation as cv
@@ -20,7 +21,7 @@ AUTO_LOAD = ["network"]
def set_core_data(config): def set_core_data(config):
CORE.data[KEY_HOST] = {} CORE.data[KEY_HOST] = {}
CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = "host" CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = PLATFORM_HOST
CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "host" CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "host"
CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] = cv.Version(1, 0, 0) CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] = cv.Version(1, 0, 0)
return config return config

View File

@@ -80,8 +80,6 @@ template<typename... Ts> class HttpRequestSendAction : public Action<Ts...> {
TEMPLATABLE_VALUE(std::string, url) TEMPLATABLE_VALUE(std::string, url)
TEMPLATABLE_VALUE(const char *, method) TEMPLATABLE_VALUE(const char *, method)
TEMPLATABLE_VALUE(std::string, body) TEMPLATABLE_VALUE(std::string, body)
TEMPLATABLE_VALUE(const char *, useragent)
TEMPLATABLE_VALUE(uint16_t, timeout)
void add_header(const char *key, TemplatableValue<const char *, Ts...> value) { this->headers_.insert({key, value}); } void add_header(const char *key, TemplatableValue<const char *, Ts...> value) { this->headers_.insert({key, value}); }
@@ -105,13 +103,6 @@ template<typename... Ts> class HttpRequestSendAction : public Action<Ts...> {
auto f = std::bind(&HttpRequestSendAction<Ts...>::encode_json_func_, this, x..., std::placeholders::_1); auto f = std::bind(&HttpRequestSendAction<Ts...>::encode_json_func_, this, x..., std::placeholders::_1);
this->parent_->set_body(json::build_json(f)); this->parent_->set_body(json::build_json(f));
} }
if (this->useragent_.has_value()) {
this->parent_->set_useragent(this->useragent_.value(x...));
}
if (this->timeout_.has_value()) {
this->parent_->set_timeout(this->timeout_.value(x...));
}
if (!this->headers_.empty()) {
std::list<Header> headers; std::list<Header> headers;
for (const auto &item : this->headers_) { for (const auto &item : this->headers_) {
auto val = item.second; auto val = item.second;
@@ -121,9 +112,9 @@ template<typename... Ts> class HttpRequestSendAction : public Action<Ts...> {
headers.push_back(header); headers.push_back(header);
} }
this->parent_->set_headers(headers); this->parent_->set_headers(headers);
}
this->parent_->send(this->response_triggers_); this->parent_->send(this->response_triggers_);
this->parent_->close(); this->parent_->close();
this->parent_->set_body("");
} }
protected: protected:

View File

@@ -76,7 +76,7 @@ void HTU21DComponent::update() {
if (this->humidity_ != nullptr) if (this->humidity_ != nullptr)
this->humidity_->publish_state(humidity); this->humidity_->publish_state(humidity);
if (this->heater_ != nullptr) if (this->heater_ != nullptr)
this->heater_->publish_state(humidity); this->heater_->publish_state(heater_level);
this->status_clear_warning(); this->status_clear_warning();
} }

View File

@@ -12,6 +12,9 @@ from esphome.const import (
CONF_SDA, CONF_SDA,
CONF_ADDRESS, CONF_ADDRESS,
CONF_I2C_ID, CONF_I2C_ID,
PLATFORM_ESP32,
PLATFORM_ESP8266,
PLATFORM_RP2040,
) )
from esphome.core import coroutine_with_priority, CORE from esphome.core import coroutine_with_priority, CORE
@@ -60,7 +63,7 @@ CONFIG_SCHEMA = cv.All(
cv.Optional(CONF_SCAN, default=True): cv.boolean, cv.Optional(CONF_SCAN, default=True): cv.boolean,
} }
).extend(cv.COMPONENT_SCHEMA), ).extend(cv.COMPONENT_SCHEMA),
cv.only_on(["esp32", "esp8266", "rp2040"]), cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_RP2040]),
) )

View File

@@ -37,6 +37,8 @@ void I2SAudioMicrophone::setup() {
void I2SAudioMicrophone::start() { void I2SAudioMicrophone::start() {
if (this->is_failed()) if (this->is_failed())
return; return;
if (this->state_ == microphone::STATE_RUNNING)
return; // Already running
this->state_ = microphone::STATE_STARTING; this->state_ = microphone::STATE_STARTING;
} }
void I2SAudioMicrophone::start_() { void I2SAudioMicrophone::start_() {
@@ -66,8 +68,9 @@ void I2SAudioMicrophone::start_() {
i2s_set_adc_mode(ADC_UNIT_1, this->adc_channel_); i2s_set_adc_mode(ADC_UNIT_1, this->adc_channel_);
i2s_adc_enable(this->parent_->get_port()); i2s_adc_enable(this->parent_->get_port());
} else { } else
#endif #endif
{
if (this->pdm_) if (this->pdm_)
config.mode = (i2s_mode_t) (config.mode | I2S_MODE_PDM); config.mode = (i2s_mode_t) (config.mode | I2S_MODE_PDM);
@@ -77,9 +80,7 @@ void I2SAudioMicrophone::start_() {
pin_config.data_in_num = this->din_pin_; pin_config.data_in_num = this->din_pin_;
i2s_set_pin(this->parent_->get_port(), &pin_config); i2s_set_pin(this->parent_->get_port(), &pin_config);
#if SOC_I2S_SUPPORTS_ADC
} }
#endif
this->state_ = microphone::STATE_RUNNING; this->state_ = microphone::STATE_RUNNING;
this->high_freq_.start(); this->high_freq_.start();
} }
@@ -110,6 +111,10 @@ size_t I2SAudioMicrophone::read(int16_t *buf, size_t len) {
this->status_set_warning(); this->status_set_warning();
return 0; return 0;
} }
if (bytes_read == 0) {
this->status_set_warning();
return 0;
}
this->status_clear_warning(); this->status_clear_warning();
if (this->bits_per_sample_ == I2S_BITS_PER_SAMPLE_16BIT) { if (this->bits_per_sample_ == I2S_BITS_PER_SAMPLE_16BIT) {
return bytes_read; return bytes_read;

View File

@@ -11,7 +11,7 @@
namespace esphome { namespace esphome {
namespace i2s_audio { namespace i2s_audio {
static const size_t BUFFER_COUNT = 10; static const size_t BUFFER_COUNT = 20;
static const char *const TAG = "i2s_audio.speaker"; static const char *const TAG = "i2s_audio.speaker";
@@ -19,7 +19,7 @@ void I2SAudioSpeaker::setup() {
ESP_LOGCONFIG(TAG, "Setting up I2S Audio Speaker..."); ESP_LOGCONFIG(TAG, "Setting up I2S Audio Speaker...");
this->buffer_queue_ = xQueueCreate(BUFFER_COUNT, sizeof(DataEvent)); this->buffer_queue_ = xQueueCreate(BUFFER_COUNT, sizeof(DataEvent));
this->event_queue_ = xQueueCreate(20, sizeof(TaskEvent)); this->event_queue_ = xQueueCreate(BUFFER_COUNT, sizeof(TaskEvent));
} }
void I2SAudioSpeaker::start() { this->state_ = speaker::STATE_STARTING; } void I2SAudioSpeaker::start() { this->state_ = speaker::STATE_STARTING; }
@@ -47,7 +47,7 @@ void I2SAudioSpeaker::player_task(void *params) {
.communication_format = I2S_COMM_FORMAT_STAND_I2S, .communication_format = I2S_COMM_FORMAT_STAND_I2S,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
.dma_buf_count = 8, .dma_buf_count = 8,
.dma_buf_len = 1024, .dma_buf_len = 128,
.use_apll = false, .use_apll = false,
.tx_desc_auto_clear = true, .tx_desc_auto_clear = true,
.fixed_mclk = I2S_PIN_NO_CHANGE, .fixed_mclk = I2S_PIN_NO_CHANGE,
@@ -60,7 +60,17 @@ void I2SAudioSpeaker::player_task(void *params) {
} }
#endif #endif
i2s_driver_install(this_speaker->parent_->get_port(), &config, 0, nullptr); esp_err_t err = i2s_driver_install(this_speaker->parent_->get_port(), &config, 0, nullptr);
if (err != ESP_OK) {
event.type = TaskEventType::WARNING;
event.err = err;
xQueueSend(this_speaker->event_queue_, &event, 0);
event.type = TaskEventType::STOPPED;
xQueueSend(this_speaker->event_queue_, &event, 0);
while (true) {
delay(10);
}
}
#if SOC_I2S_SUPPORTS_DAC #if SOC_I2S_SUPPORTS_DAC
if (this_speaker->internal_dac_mode_ == I2S_DAC_CHANNEL_DISABLE) { if (this_speaker->internal_dac_mode_ == I2S_DAC_CHANNEL_DISABLE) {
@@ -88,9 +98,7 @@ void I2SAudioSpeaker::player_task(void *params) {
} }
if (data_event.stop) { if (data_event.stop) {
// Stop signal from main thread // Stop signal from main thread
while (xQueueReceive(this_speaker->buffer_queue_, &data_event, 0) == pdTRUE) { xQueueReset(this_speaker->buffer_queue_); // Flush queue
// Flush queue
}
break; break;
} }
size_t bytes_written; size_t bytes_written;
@@ -103,7 +111,7 @@ void I2SAudioSpeaker::player_task(void *params) {
uint32_t sample = (buffer[current] << 16) | (buffer[current] & 0xFFFF); uint32_t sample = (buffer[current] << 16) | (buffer[current] & 0xFFFF);
esp_err_t err = i2s_write(this_speaker->parent_->get_port(), &sample, sizeof(sample), &bytes_written, esp_err_t err = i2s_write(this_speaker->parent_->get_port(), &sample, sizeof(sample), &bytes_written,
(100 / portTICK_PERIOD_MS)); (10 / portTICK_PERIOD_MS));
if (err != ESP_OK) { if (err != ESP_OK) {
event = {.type = TaskEventType::WARNING, .err = err}; event = {.type = TaskEventType::WARNING, .err = err};
xQueueSend(this_speaker->event_queue_, &event, portMAX_DELAY); xQueueSend(this_speaker->event_queue_, &event, portMAX_DELAY);
@@ -122,7 +130,6 @@ void I2SAudioSpeaker::player_task(void *params) {
event.type = TaskEventType::STOPPING; event.type = TaskEventType::STOPPING;
xQueueSend(this_speaker->event_queue_, &event, portMAX_DELAY); xQueueSend(this_speaker->event_queue_, &event, portMAX_DELAY);
i2s_stop(this_speaker->parent_->get_port());
i2s_driver_uninstall(this_speaker->parent_->get_port()); i2s_driver_uninstall(this_speaker->parent_->get_port());
event.type = TaskEventType::STOPPED; event.type = TaskEventType::STOPPED;
@@ -151,17 +158,24 @@ void I2SAudioSpeaker::watch_() {
if (xQueueReceive(this->event_queue_, &event, 0) == pdTRUE) { if (xQueueReceive(this->event_queue_, &event, 0) == pdTRUE) {
switch (event.type) { switch (event.type) {
case TaskEventType::STARTING: case TaskEventType::STARTING:
ESP_LOGD(TAG, "Starting I2S Audio Speaker");
break;
case TaskEventType::STARTED: case TaskEventType::STARTED:
ESP_LOGD(TAG, "Started I2S Audio Speaker");
break;
case TaskEventType::STOPPING: case TaskEventType::STOPPING:
ESP_LOGD(TAG, "Stopping I2S Audio Speaker");
break; break;
case TaskEventType::PLAYING: case TaskEventType::PLAYING:
this->status_clear_warning(); this->status_clear_warning();
break; break;
case TaskEventType::STOPPED: case TaskEventType::STOPPED:
this->parent_->unlock();
this->state_ = speaker::STATE_STOPPED; this->state_ = speaker::STATE_STOPPED;
vTaskDelete(this->player_task_handle_); vTaskDelete(this->player_task_handle_);
this->player_task_handle_ = nullptr; this->player_task_handle_ = nullptr;
this->parent_->unlock();
xQueueReset(this->buffer_queue_);
ESP_LOGD(TAG, "Stopped I2S Audio Speaker");
break; break;
case TaskEventType::WARNING: case TaskEventType::WARNING:
ESP_LOGW(TAG, "Error writing to I2S: %s", esp_err_to_name(event.err)); ESP_LOGW(TAG, "Error writing to I2S: %s", esp_err_to_name(event.err));
@@ -177,9 +191,9 @@ void I2SAudioSpeaker::loop() {
this->start_(); this->start_();
break; break;
case speaker::STATE_RUNNING: case speaker::STATE_RUNNING:
case speaker::STATE_STOPPING:
this->watch_(); this->watch_();
break; break;
case speaker::STATE_STOPPING:
case speaker::STATE_STOPPED: case speaker::STATE_STOPPED:
break; break;
} }

View File

@@ -1,7 +1,7 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome import core, pins from esphome import core, pins
from esphome.components import display, spi from esphome.components import display, spi, font
from esphome.core import CORE, HexInt from esphome.core import CORE, HexInt
from esphome.const import ( from esphome.const import (
CONF_COLOR_PALETTE, CONF_COLOR_PALETTE,
@@ -13,7 +13,6 @@ from esphome.const import (
CONF_PAGES, CONF_PAGES,
CONF_RESET_PIN, CONF_RESET_PIN,
CONF_DIMENSIONS, CONF_DIMENSIONS,
CONF_DATA_RATE,
) )
DEPENDENCIES = ["spi"] DEPENDENCIES = ["spi"]
@@ -25,7 +24,7 @@ def AUTO_LOAD():
return [] return []
CODEOWNERS = ["@nielsnl68"] CODEOWNERS = ["@nielsnl68", "@clydebarrow"]
ili9XXX_ns = cg.esphome_ns.namespace("ili9xxx") ili9XXX_ns = cg.esphome_ns.namespace("ili9xxx")
ili9XXXSPI = ili9XXX_ns.class_( ili9XXXSPI = ili9XXX_ns.class_(
@@ -42,6 +41,7 @@ MODELS = {
"ILI9341": ili9XXX_ns.class_("ILI9XXXILI9341", ili9XXXSPI), "ILI9341": ili9XXX_ns.class_("ILI9XXXILI9341", ili9XXXSPI),
"ILI9342": ili9XXX_ns.class_("ILI9XXXILI9342", ili9XXXSPI), "ILI9342": ili9XXX_ns.class_("ILI9XXXILI9342", ili9XXXSPI),
"ILI9481": ili9XXX_ns.class_("ILI9XXXILI9481", ili9XXXSPI), "ILI9481": ili9XXX_ns.class_("ILI9XXXILI9481", ili9XXXSPI),
"ILI9481-18": ili9XXX_ns.class_("ILI9XXXILI948118", ili9XXXSPI),
"ILI9486": ili9XXX_ns.class_("ILI9XXXILI9486", ili9XXXSPI), "ILI9486": ili9XXX_ns.class_("ILI9XXXILI9486", ili9XXXSPI),
"ILI9488": ili9XXX_ns.class_("ILI9XXXILI9488", ili9XXXSPI), "ILI9488": ili9XXX_ns.class_("ILI9XXXILI9488", ili9XXXSPI),
"ILI9488_A": ili9XXX_ns.class_("ILI9XXXILI9488A", ili9XXXSPI), "ILI9488_A": ili9XXX_ns.class_("ILI9XXXILI9488A", ili9XXXSPI),
@@ -54,6 +54,7 @@ COLOR_PALETTE = cv.one_of("NONE", "GRAYSCALE", "IMAGE_ADAPTIVE")
CONF_LED_PIN = "led_pin" CONF_LED_PIN = "led_pin"
CONF_COLOR_PALETTE_IMAGES = "color_palette_images" CONF_COLOR_PALETTE_IMAGES = "color_palette_images"
CONF_INVERT_DISPLAY = "invert_display"
def _validate(config): def _validate(config):
@@ -84,6 +85,7 @@ def _validate(config):
CONFIG_SCHEMA = cv.All( CONFIG_SCHEMA = cv.All(
font.validate_pillow_installed,
display.FULL_DISPLAY_SCHEMA.extend( display.FULL_DISPLAY_SCHEMA.extend(
{ {
cv.GenerateID(): cv.declare_id(ili9XXXSPI), cv.GenerateID(): cv.declare_id(ili9XXXSPI),
@@ -99,11 +101,11 @@ CONFIG_SCHEMA = cv.All(
cv.Optional(CONF_COLOR_PALETTE_IMAGES, default=[]): cv.ensure_list( cv.Optional(CONF_COLOR_PALETTE_IMAGES, default=[]): cv.ensure_list(
cv.file_ cv.file_
), ),
cv.Optional(CONF_DATA_RATE, default="40MHz"): spi.SPI_DATA_RATE_SCHEMA, cv.Optional(CONF_INVERT_DISPLAY): cv.boolean,
} }
) )
.extend(cv.polling_component_schema("1s")) .extend(cv.polling_component_schema("1s"))
.extend(spi.spi_device_schema(False)), .extend(spi.spi_device_schema(False, "40MHz")),
cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA), cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA),
_validate, _validate,
) )
@@ -140,8 +142,6 @@ async def to_code(config):
rhs = [] rhs = []
for x in range(256): for x in range(256):
rhs.extend([HexInt(x), HexInt(x), HexInt(x)]) rhs.extend([HexInt(x), HexInt(x), HexInt(x)])
prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs)
cg.add(var.set_palette(prog_arr))
elif config[CONF_COLOR_PALETTE] == "IMAGE_ADAPTIVE": elif config[CONF_COLOR_PALETTE] == "IMAGE_ADAPTIVE":
cg.add(var.set_buffer_color_mode(ILI9XXXColorMode.BITS_8_INDEXED)) cg.add(var.set_buffer_color_mode(ILI9XXXColorMode.BITS_8_INDEXED))
from PIL import Image from PIL import Image
@@ -165,7 +165,7 @@ async def to_code(config):
x = x + i.width x = x + i.width
# reduce the colors on combined image to 256. # reduce the colors on combined image to 256.
converted = ref_image.convert("P", palette=Image.ADAPTIVE, colors=256) converted = ref_image.convert("P", palette=Image.Palette.ADAPTIVE, colors=256)
# if you want to verify how the images look use # if you want to verify how the images look use
# ref_image.save("ref_in.png") # ref_image.save("ref_in.png")
# converted.save("ref_out.png") # converted.save("ref_out.png")
@@ -179,5 +179,5 @@ async def to_code(config):
prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs) prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs)
cg.add(var.set_palette(prog_arr)) cg.add(var.set_palette(prog_arr))
spi_data_rate = str(spi.SPI_DATA_RATE_OPTIONS[config[CONF_DATA_RATE]]) if CONF_INVERT_DISPLAY in config:
cg.add_define("ILI9XXXDisplay_DATA_RATE", cg.RawExpression(spi_data_rate)) cg.add(var.invert_display(config[CONF_INVERT_DISPLAY]))

View File

@@ -12,11 +12,13 @@ static const char *const TAG = "ili9xxx";
void ILI9XXXDisplay::setup() { void ILI9XXXDisplay::setup() {
this->setup_pins_(); this->setup_pins_();
this->initialize(); this->initialize();
this->command(this->pre_invertdisplay_ ? ILI9XXX_INVON : ILI9XXX_INVOFF);
this->x_low_ = this->width_; this->x_low_ = this->width_;
this->y_low_ = this->height_; this->y_low_ = this->height_;
this->x_high_ = 0; this->x_high_ = 0;
this->y_high_ = 0; this->y_high_ = 0;
if (this->buffer_color_mode_ == BITS_16) { if (this->buffer_color_mode_ == BITS_16) {
this->init_internal_(this->get_buffer_length_() * 2); this->init_internal_(this->get_buffer_length_() * 2);
if (this->buffer_ != nullptr) { if (this->buffer_ != nullptr) {
@@ -59,6 +61,7 @@ void ILI9XXXDisplay::dump_config() {
if (this->is_18bitdisplay_) { if (this->is_18bitdisplay_) {
ESP_LOGCONFIG(TAG, " 18-Bit Mode: YES"); ESP_LOGCONFIG(TAG, " 18-Bit Mode: YES");
} }
ESP_LOGCONFIG(TAG, " Data rate: %dMHz", (unsigned) (this->data_rate_ / 1000000));
LOG_PIN(" Reset Pin: ", this->reset_pin_); LOG_PIN(" Reset Pin: ", this->reset_pin_);
LOG_PIN(" DC Pin: ", this->dc_pin_); LOG_PIN(" DC Pin: ", this->dc_pin_);
@@ -332,7 +335,12 @@ void ILI9XXXDisplay::set_addr_window_(uint16_t x1, uint16_t y1, uint16_t w, uint
this->command(ILI9XXX_RAMWR); // Write to RAM this->command(ILI9XXX_RAMWR); // Write to RAM
} }
void ILI9XXXDisplay::invert_display_(bool invert) { this->command(invert ? ILI9XXX_INVON : ILI9XXX_INVOFF); } void ILI9XXXDisplay::invert_display(bool invert) {
this->pre_invertdisplay_ = invert;
if (is_ready()) {
this->command(invert ? ILI9XXX_INVON : ILI9XXX_INVOFF);
}
}
int ILI9XXXDisplay::get_width_internal() { return this->width_; } int ILI9XXXDisplay::get_width_internal() { return this->width_; }
int ILI9XXXDisplay::get_height_internal() { return this->height_; } int ILI9XXXDisplay::get_height_internal() { return this->height_; }
@@ -344,7 +352,7 @@ void ILI9XXXM5Stack::initialize() {
this->width_ = 320; this->width_ = 320;
if (this->height_ == 0) if (this->height_ == 0)
this->height_ = 240; this->height_ = 240;
this->invert_display_(true); this->pre_invertdisplay_ = true;
} }
// M5CORE display // Based on the configuration settings of M5stact's M5GFX code. // M5CORE display // Based on the configuration settings of M5stact's M5GFX code.
@@ -354,7 +362,7 @@ void ILI9XXXM5CORE::initialize() {
this->width_ = 320; this->width_ = 320;
if (this->height_ == 0) if (this->height_ == 0)
this->height_ = 240; this->height_ = 240;
this->invert_display_(true); this->pre_invertdisplay_ = true;
} }
// 24_TFT display // 24_TFT display
@@ -387,6 +395,17 @@ void ILI9XXXILI9481::initialize() {
} }
} }
void ILI9XXXILI948118::initialize() {
this->init_lcd_(INITCMD_ILI9481_18);
if (this->width_ == 0) {
this->width_ = 320;
}
if (this->height_ == 0) {
this->height_ = 480;
}
this->is_18bitdisplay_ = true;
}
// 35_TFT display // 35_TFT display
void ILI9XXXILI9486::initialize() { void ILI9XXXILI9486::initialize() {
this->init_lcd_(INITCMD_ILI9486); this->init_lcd_(INITCMD_ILI9486);
@@ -450,7 +469,7 @@ void ILI9XXXS3BoxLite::initialize() {
if (this->height_ == 0) { if (this->height_ == 0) {
this->height_ = 240; this->height_ = 240;
} }
this->invert_display_(true); this->pre_invertdisplay_ = true;
} }
} // namespace ili9xxx } // namespace ili9xxx

View File

@@ -33,6 +33,7 @@ class ILI9XXXDisplay : public PollingComponent,
this->height_ = height; this->height_ = height;
this->width_ = width; this->width_ = width;
} }
void invert_display(bool invert);
void command(uint8_t value); void command(uint8_t value);
void data(uint8_t value); void data(uint8_t value);
void send_command(uint8_t command_byte, const uint8_t *data_bytes, uint8_t num_data_bytes); void send_command(uint8_t command_byte, const uint8_t *data_bytes, uint8_t num_data_bytes);
@@ -55,7 +56,7 @@ class ILI9XXXDisplay : public PollingComponent,
void display_(); void display_();
void init_lcd_(const uint8_t *init_cmd); void init_lcd_(const uint8_t *init_cmd);
void set_addr_window_(uint16_t x, uint16_t y, uint16_t w, uint16_t h); void set_addr_window_(uint16_t x, uint16_t y, uint16_t w, uint16_t h);
void invert_display_(bool invert);
void reset_(); void reset_();
int16_t width_{0}; ///< Display width as modified by current rotation int16_t width_{0}; ///< Display width as modified by current rotation
@@ -88,6 +89,7 @@ class ILI9XXXDisplay : public PollingComponent,
bool prossing_update_ = false; bool prossing_update_ = false;
bool need_update_ = false; bool need_update_ = false;
bool is_18bitdisplay_ = false; bool is_18bitdisplay_ = false;
bool pre_invertdisplay_ = false;
}; };
//----------- M5Stack display -------------- //----------- M5Stack display --------------
@@ -120,6 +122,12 @@ class ILI9XXXILI9481 : public ILI9XXXDisplay {
void initialize() override; void initialize() override;
}; };
//----------- ILI9481 in 18 bit mode --------------
class ILI9XXXILI948118 : public ILI9XXXDisplay {
protected:
void initialize() override;
};
//----------- ILI9XXX_35_TFT rotated display -------------- //----------- ILI9XXX_35_TFT rotated display --------------
class ILI9XXXILI9486 : public ILI9XXXDisplay { class ILI9XXXILI9486 : public ILI9XXXDisplay {
protected: protected:

View File

@@ -94,8 +94,32 @@ static const uint8_t PROGMEM INITCMD_ILI9481[] = {
ILI9XXX_IFCTR , 1, 0x83, ILI9XXX_IFCTR , 1, 0x83,
ILI9XXX_GMCTR ,12, 0x00, 0x26, 0x21, 0x00, 0x00, 0x1F, 0x65, 0x23, 0x77, 0x00, 0x0F, 0x00, ILI9XXX_GMCTR ,12, 0x00, 0x26, 0x21, 0x00, 0x00, 0x1F, 0x65, 0x23, 0x77, 0x00, 0x0F, 0x00,
ILI9XXX_IFMODE , 1, 0x00, // CommandAccessProtect ILI9XXX_IFMODE , 1, 0x00, // CommandAccessProtect
ILI9XXX_PTLAR , 4, 0, 0, 1, 0xDF,
0xE4 , 1, 0xA0, 0xE4 , 1, 0xA0,
ILI9XXX_MADCTL , 1, MADCTL_MV | MADCTL_BGR, // Memory Access Control
ILI9XXX_CSCON , 1, 0x01, ILI9XXX_CSCON , 1, 0x01,
ILI9XXX_PIXFMT, 1, 0x55, // 16 bit mode
ILI9XXX_INVON, 0,
ILI9XXX_DISPON, 0x80, // Set display on
0x00 // end
};
static const uint8_t PROGMEM INITCMD_ILI9481_18[] = {
ILI9XXX_SLPOUT , 0x80, // Exit sleep mode
ILI9XXX_PWSET , 3, 0x07, 0x41, 0x1D,
ILI9XXX_VMCTR , 3, 0x00, 0x1C, 0x1F,
ILI9XXX_PWSETN , 2, 0x01, 0x11,
ILI9XXX_PWCTR1 , 5, 0x10, 0x3B, 0x00, 0x02, 0x11,
ILI9XXX_VMCTR1 , 1, 0x03,
ILI9XXX_IFCTR , 1, 0x83,
ILI9XXX_GMCTR ,12, 0x00, 0x26, 0x21, 0x00, 0x00, 0x1F, 0x65, 0x23, 0x77, 0x00, 0x0F, 0x00,
ILI9XXX_IFMODE , 1, 0x00, // CommandAccessProtect
ILI9XXX_PTLAR , 4, 0, 0, 1, 0xDF,
0xE4 , 1, 0xA0,
ILI9XXX_MADCTL , 1, MADCTL_MX| MADCTL_BGR, // Memory Access Control
ILI9XXX_CSCON , 1, 0x01,
ILI9XXX_PIXFMT, 1, 0x66, // 18 bit mode
ILI9XXX_INVON, 0,
ILI9XXX_DISPON, 0x80, // Set display on ILI9XXX_DISPON, 0x80, // Set display on
0x00 // end 0x00 // end
}; };

View File

@@ -255,7 +255,11 @@ async def to_code(config):
transparent = config[CONF_USE_TRANSPARENCY] transparent = config[CONF_USE_TRANSPARENCY]
dither = Image.NONE if config[CONF_DITHER] == "NONE" else Image.FLOYDSTEINBERG dither = (
Image.Dither.NONE
if config[CONF_DITHER] == "NONE"
else Image.Dither.FLOYDSTEINBERG
)
if config[CONF_TYPE] == "GRAYSCALE": if config[CONF_TYPE] == "GRAYSCALE":
image = image.convert("LA", dither=dither) image = image.convert("LA", dither=dither)
pixels = list(image.getdata()) pixels = list(image.getdata())

View File

@@ -1,10 +1,14 @@
from esphome.components.logger import USB_CDC, USB_SERIAL_JTAG from esphome.components import improv_base
from esphome.components.esp32 import get_esp32_variant
from esphome.components.esp32.const import (
VARIANT_ESP32S3,
)
from esphome.components.logger import USB_CDC
from esphome.const import CONF_BAUD_RATE, CONF_HARDWARE_UART, CONF_ID, CONF_LOGGER from esphome.const import CONF_BAUD_RATE, CONF_HARDWARE_UART, CONF_ID, CONF_LOGGER
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.core import CORE from esphome.core import CORE
import esphome.final_validate as fv import esphome.final_validate as fv
from esphome.components import improv_base
AUTO_LOAD = ["improv_base"] AUTO_LOAD = ["improv_base"]
CODEOWNERS = ["@esphome/core"] CODEOWNERS = ["@esphome/core"]
@@ -30,7 +34,10 @@ def validate_logger(config):
if logger_conf[CONF_BAUD_RATE] == 0: if logger_conf[CONF_BAUD_RATE] == 0:
raise cv.Invalid("improv_serial requires the logger baud_rate to be not 0") raise cv.Invalid("improv_serial requires the logger baud_rate to be not 0")
if CORE.using_esp_idf: if CORE.using_esp_idf:
if logger_conf[CONF_HARDWARE_UART] in [USB_SERIAL_JTAG, USB_CDC]: if (
logger_conf[CONF_HARDWARE_UART] == USB_CDC
and get_esp32_variant() == VARIANT_ESP32S3
):
raise cv.Invalid( raise cv.Invalid(
"improv_serial does not support the selected logger hardware_uart" "improv_serial does not support the selected logger hardware_uart"
) )

View File

@@ -31,26 +31,57 @@ void ImprovSerialComponent::setup() {
void ImprovSerialComponent::dump_config() { ESP_LOGCONFIG(TAG, "Improv Serial:"); } void ImprovSerialComponent::dump_config() { ESP_LOGCONFIG(TAG, "Improv Serial:"); }
int ImprovSerialComponent::available_() { optional<uint8_t> ImprovSerialComponent::read_byte_() {
optional<uint8_t> byte;
uint8_t data = 0;
#ifdef USE_ARDUINO #ifdef USE_ARDUINO
return this->hw_serial_->available(); if (this->hw_serial_->available()) {
this->hw_serial_->readBytes(&data, 1);
byte = data;
}
#endif #endif
#ifdef USE_ESP_IDF #ifdef USE_ESP_IDF
switch (logger::global_logger->get_uart()) {
case logger::UART_SELECTION_UART0:
case logger::UART_SELECTION_UART1:
#if !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32C6) && \
!defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32S3)
case logger::UART_SELECTION_UART2:
#endif // !USE_ESP32_VARIANT_ESP32C3 && !USE_ESP32_VARIANT_ESP32S2 && !USE_ESP32_VARIANT_ESP32S3
if (this->uart_num_ >= 0) {
size_t available; size_t available;
uart_get_buffered_data_len(this->uart_num_, &available); uart_get_buffered_data_len(this->uart_num_, &available);
return available; if (available) {
#endif
}
uint8_t ImprovSerialComponent::read_byte_() {
uint8_t data;
#ifdef USE_ARDUINO
this->hw_serial_->readBytes(&data, 1);
#endif
#ifdef USE_ESP_IDF
uart_read_bytes(this->uart_num_, &data, 1, 20 / portTICK_PERIOD_MS); uart_read_bytes(this->uart_num_, &data, 1, 20 / portTICK_PERIOD_MS);
byte = data;
}
}
break;
#if defined(CONFIG_ESP_CONSOLE_USB_CDC) && (defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3))
case logger::UART_SELECTION_USB_CDC:
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
if (esp_usb_console_available_for_read()) {
#else
if (esp_usb_console_read_available()) {
#endif #endif
return data; esp_usb_console_read_buf((char *) &data, 1);
byte = data;
}
break;
#endif // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32S3)
case logger::UART_SELECTION_USB_SERIAL_JTAG: {
if (usb_serial_jtag_read_bytes((char *) &data, 1, 20 / portTICK_PERIOD_MS)) {
byte = data;
}
break;
}
#endif // USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32S3
default:
break;
}
#endif
return byte;
} }
void ImprovSerialComponent::write_data_(std::vector<uint8_t> &data) { void ImprovSerialComponent::write_data_(std::vector<uint8_t> &data) {
@@ -59,24 +90,49 @@ void ImprovSerialComponent::write_data_(std::vector<uint8_t> &data) {
this->hw_serial_->write(data.data(), data.size()); this->hw_serial_->write(data.data(), data.size());
#endif #endif
#ifdef USE_ESP_IDF #ifdef USE_ESP_IDF
switch (logger::global_logger->get_uart()) {
case logger::UART_SELECTION_UART0:
case logger::UART_SELECTION_UART1:
#if !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32C6) && \
!defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32S3)
case logger::UART_SELECTION_UART2:
#endif // !USE_ESP32_VARIANT_ESP32C3 && !USE_ESP32_VARIANT_ESP32S2 && !USE_ESP32_VARIANT_ESP32S3
uart_write_bytes(this->uart_num_, data.data(), data.size()); uart_write_bytes(this->uart_num_, data.data(), data.size());
break;
#if defined(CONFIG_ESP_CONSOLE_USB_CDC) && (defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3))
case logger::UART_SELECTION_USB_CDC: {
const char *msg = (char *) data.data();
esp_usb_console_write_buf(msg, data.size());
break;
}
#endif // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32S3)
case logger::UART_SELECTION_USB_SERIAL_JTAG:
usb_serial_jtag_write_bytes((char *) data.data(), data.size(), 20 / portTICK_PERIOD_MS);
break;
#endif // USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32S3
default:
break;
}
#endif #endif
} }
void ImprovSerialComponent::loop() { void ImprovSerialComponent::loop() {
const uint32_t now = millis(); if (this->last_read_byte_ && (millis() - this->last_read_byte_ > IMPROV_SERIAL_TIMEOUT)) {
if (now - this->last_read_byte_ > 50) { this->last_read_byte_ = 0;
this->rx_buffer_.clear(); this->rx_buffer_.clear();
this->last_read_byte_ = now; ESP_LOGV(TAG, "Improv Serial timeout");
} }
while (this->available_()) { auto byte = this->read_byte_();
uint8_t byte = this->read_byte_(); while (byte.has_value()) {
if (this->parse_improv_serial_byte_(byte)) { if (this->parse_improv_serial_byte_(byte.value())) {
this->last_read_byte_ = now; this->last_read_byte_ = millis();
} else { } else {
this->last_read_byte_ = 0;
this->rx_buffer_.clear(); this->rx_buffer_.clear();
} }
byte = this->read_byte_();
} }
if (this->state_ == improv::STATE_PROVISIONING) { if (this->state_ == improv::STATE_PROVISIONING) {

View File

@@ -14,6 +14,13 @@
#endif #endif
#ifdef USE_ESP_IDF #ifdef USE_ESP_IDF
#include <driver/uart.h> #include <driver/uart.h>
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32S3) || \
defined(USE_ESP32_VARIANT_ESP32H2)
#include <driver/usb_serial_jtag.h>
#endif
#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
#include <esp_private/usb_console.h>
#endif
#endif #endif
namespace esphome { namespace esphome {
@@ -26,6 +33,7 @@ enum ImprovSerialType : uint8_t {
TYPE_RPC_RESPONSE = 0x04 TYPE_RPC_RESPONSE = 0x04
}; };
static const uint16_t IMPROV_SERIAL_TIMEOUT = 100;
static const uint8_t IMPROV_SERIAL_VERSION = 1; static const uint8_t IMPROV_SERIAL_VERSION = 1;
class ImprovSerialComponent : public Component, public improv_base::ImprovBase { class ImprovSerialComponent : public Component, public improv_base::ImprovBase {
@@ -48,8 +56,7 @@ class ImprovSerialComponent : public Component, public improv_base::ImprovBase {
std::vector<uint8_t> build_rpc_settings_response_(improv::Command command); std::vector<uint8_t> build_rpc_settings_response_(improv::Command command);
std::vector<uint8_t> build_version_info_(); std::vector<uint8_t> build_version_info_();
int available_(); optional<uint8_t> read_byte_();
uint8_t read_byte_();
void write_data_(std::vector<uint8_t> &data); void write_data_(std::vector<uint8_t> &data);
#ifdef USE_ARDUINO #ifdef USE_ARDUINO

View File

@@ -12,6 +12,8 @@ from esphome.const import (
ENTITY_CATEGORY_DIAGNOSTIC, ENTITY_CATEGORY_DIAGNOSTIC,
KEY_CORE, KEY_CORE,
KEY_FRAMEWORK_VERSION, KEY_FRAMEWORK_VERSION,
PLATFORM_ESP32,
PLATFORM_RP2040,
) )
from esphome.core import CORE from esphome.core import CORE
@@ -49,7 +51,7 @@ CONFIG_SCHEMA = cv.All(
state_class=STATE_CLASS_MEASUREMENT, state_class=STATE_CLASS_MEASUREMENT,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC, entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
).extend(cv.polling_component_schema("60s")), ).extend(cv.polling_component_schema("60s")),
cv.only_on(["esp32", "rp2040"]), cv.only_on([PLATFORM_ESP32, PLATFORM_RP2040]),
validate_config, validate_config,
) )

View File

@@ -170,7 +170,7 @@ def _notify_old_style(config):
ARDUINO_VERSIONS = { ARDUINO_VERSIONS = {
"dev": (cv.Version(0, 0, 0), "https://github.com/libretiny-eu/libretiny.git"), "dev": (cv.Version(0, 0, 0), "https://github.com/libretiny-eu/libretiny.git"),
"latest": (cv.Version(0, 0, 0), None), "latest": (cv.Version(0, 0, 0), None),
"recommended": (cv.Version(1, 4, 0), None), "recommended": (cv.Version(1, 4, 1), None),
} }
@@ -251,7 +251,7 @@ async def component_to_code(config):
# setup board config # setup board config
cg.add_platformio_option("board", config[CONF_BOARD]) cg.add_platformio_option("board", config[CONF_BOARD])
cg.add_build_flag("-DUSE_LIBRETINY") cg.add_build_flag("-DUSE_LIBRETINY")
cg.add_build_flag(f"-DUSE_{config[CONF_COMPONENT_ID]}") cg.add_build_flag(f"-DUSE_{config[CONF_COMPONENT_ID].upper()}")
cg.add_build_flag(f"-DUSE_LIBRETINY_VARIANT_{config[CONF_FAMILY]}") cg.add_build_flag(f"-DUSE_LIBRETINY_VARIANT_{config[CONF_FAMILY]}")
cg.add_define("ESPHOME_BOARD", config[CONF_BOARD]) cg.add_define("ESPHOME_BOARD", config[CONF_BOARD])
cg.add_define("ESPHOME_VARIANT", FAMILY_FRIENDLY[config[CONF_FAMILY]]) cg.add_define("ESPHOME_VARIANT", FAMILY_FRIENDLY[config[CONF_FAMILY]])

View File

@@ -32,6 +32,7 @@ from esphome.components.esp32.const import (
VARIANT_ESP32S3, VARIANT_ESP32S3,
VARIANT_ESP32C2, VARIANT_ESP32C2,
VARIANT_ESP32C6, VARIANT_ESP32C6,
VARIANT_ESP32H2,
) )
from esphome.components.libretiny import get_libretiny_component, get_libretiny_family from esphome.components.libretiny import get_libretiny_component, get_libretiny_family
from esphome.components.libretiny.const import ( from esphome.components.libretiny.const import (
@@ -86,6 +87,7 @@ UART_SELECTION_ESP32 = {
VARIANT_ESP32C3: [UART0, UART1, USB_SERIAL_JTAG], VARIANT_ESP32C3: [UART0, UART1, USB_SERIAL_JTAG],
VARIANT_ESP32C2: [UART0, UART1], VARIANT_ESP32C2: [UART0, UART1],
VARIANT_ESP32C6: [UART0, UART1, USB_CDC, USB_SERIAL_JTAG], VARIANT_ESP32C6: [UART0, UART1, USB_CDC, USB_SERIAL_JTAG],
VARIANT_ESP32H2: [UART0, UART1, USB_CDC, USB_SERIAL_JTAG],
} }
UART_SELECTION_ESP8266 = [UART0, UART0_SWAP, UART1] UART_SELECTION_ESP8266 = [UART0, UART0_SWAP, UART1]

View File

@@ -3,8 +3,21 @@
#ifdef USE_ESP_IDF #ifdef USE_ESP_IDF
#include <driver/uart.h> #include <driver/uart.h>
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32S3) || \
defined(USE_ESP32_VARIANT_ESP32H2)
#include <driver/usb_serial_jtag.h>
#include <esp_vfs_dev.h>
#include <esp_vfs_usb_serial_jtag.h>
#endif
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "esp_idf_version.h" #include "esp_idf_version.h"
#include <cstdint>
#include <cstdio>
#include <fcntl.h>
#endif // USE_ESP_IDF #endif // USE_ESP_IDF
#if defined(USE_ESP32_FRAMEWORK_ARDUINO) || defined(USE_ESP_IDF) #if defined(USE_ESP32_FRAMEWORK_ARDUINO) || defined(USE_ESP_IDF)
@@ -93,6 +106,58 @@ void Logger::log_vprintf_(int level, const char *tag, int line, const __FlashStr
} }
#endif #endif
#ifdef USE_ESP_IDF
void Logger::init_uart_() {
uart_config_t uart_config{};
uart_config.baud_rate = (int) baud_rate_;
uart_config.data_bits = UART_DATA_8_BITS;
uart_config.parity = UART_PARITY_DISABLE;
uart_config.stop_bits = UART_STOP_BITS_1;
uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
uart_config.source_clk = UART_SCLK_DEFAULT;
#endif
uart_param_config(this->uart_num_, &uart_config);
const int uart_buffer_size = tx_buffer_size_;
// Install UART driver using an event queue here
uart_driver_install(this->uart_num_, uart_buffer_size, uart_buffer_size, 10, nullptr, 0);
}
#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
void Logger::init_usb_cdc_() {}
#endif
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32S3) || \
defined(USE_ESP32_VARIANT_ESP32H2)
void Logger::init_usb_serial_jtag_() {
setvbuf(stdin, NULL, _IONBF, 0); // Disable buffering on stdin
// Minicom, screen, idf_monitor send CR when ENTER key is pressed
esp_vfs_dev_usb_serial_jtag_set_rx_line_endings(ESP_LINE_ENDINGS_CR);
// Move the caret to the beginning of the next line on '\n'
esp_vfs_dev_usb_serial_jtag_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF);
// Enable non-blocking mode on stdin and stdout
fcntl(fileno(stdout), F_SETFL, 0);
fcntl(fileno(stdin), F_SETFL, 0);
usb_serial_jtag_driver_config_t usb_serial_jtag_config{};
usb_serial_jtag_config.rx_buffer_size = 512;
usb_serial_jtag_config.tx_buffer_size = 512;
esp_err_t ret = ESP_OK;
// Install USB-SERIAL-JTAG driver for interrupt-driven reads and writes
ret = usb_serial_jtag_driver_install(&usb_serial_jtag_config);
if (ret != ESP_OK) {
return;
}
// Tell vfs to use usb-serial-jtag driver
esp_vfs_usb_serial_jtag_use_driver();
}
#endif
#endif
int HOT Logger::level_for(const char *tag) { int HOT Logger::level_for(const char *tag) {
// Uses std::vector<> for low memory footprint, though the vector // Uses std::vector<> for low memory footprint, though the vector
// could be sorted to minimize lookup times. This feature isn't used that // could be sorted to minimize lookup times. This feature isn't used that
@@ -120,19 +185,19 @@ void HOT Logger::log_message_(int level, const char *tag, int offset) {
#ifdef USE_ESP_IDF #ifdef USE_ESP_IDF
if ( if (
#if defined(USE_ESP32_VARIANT_ESP32S2) #if defined(USE_ESP32_VARIANT_ESP32S2)
uart_ == UART_SELECTION_USB_CDC this->uart_ == UART_SELECTION_USB_CDC
#elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) #elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32H2)
uart_ == UART_SELECTION_USB_SERIAL_JTAG this->uart_ == UART_SELECTION_USB_SERIAL_JTAG
#elif defined(USE_ESP32_VARIANT_ESP32S3) #elif defined(USE_ESP32_VARIANT_ESP32S3)
uart_ == UART_SELECTION_USB_CDC || uart_ == UART_SELECTION_USB_SERIAL_JTAG this->uart_ == UART_SELECTION_USB_CDC || this->uart_ == UART_SELECTION_USB_SERIAL_JTAG
#else #else
/* DISABLES CODE */ (false) // NOLINT /* DISABLES CODE */ (false) // NOLINT
#endif #endif
) { ) {
puts(msg); puts(msg);
} else { } else {
uart_write_bytes(uart_num_, msg, strlen(msg)); uart_write_bytes(this->uart_num_, msg, strlen(msg));
uart_write_bytes(uart_num_, "\n", 1); uart_write_bytes(this->uart_num_, "\n", 1);
} }
#endif #endif
} }
@@ -209,45 +274,38 @@ void Logger::pre_setup() {
} }
#endif // USE_ARDUINO #endif // USE_ARDUINO
#ifdef USE_ESP_IDF #ifdef USE_ESP_IDF
uart_num_ = UART_NUM_0; this->uart_num_ = UART_NUM_0;
switch (uart_) { switch (this->uart_) {
case UART_SELECTION_UART0: case UART_SELECTION_UART0:
uart_num_ = UART_NUM_0; this->uart_num_ = UART_NUM_0;
break; break;
case UART_SELECTION_UART1: case UART_SELECTION_UART1:
uart_num_ = UART_NUM_1; this->uart_num_ = UART_NUM_1;
break; break;
#if !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32C6) && \ #if !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32C6) && \
!defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32S3) !defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32S3) && !defined(USE_ESP32_VARIANT_ESP32H2)
case UART_SELECTION_UART2: case UART_SELECTION_UART2:
uart_num_ = UART_NUM_2; this->uart_num_ = UART_NUM_2;
break; break;
#endif // !USE_ESP32_VARIANT_ESP32C3 && !USE_ESP32_VARIANT_ESP32S2 && !USE_ESP32_VARIANT_ESP32S3 #endif // !USE_ESP32_VARIANT_ESP32C3 && !USE_ESP32_VARIANT_ESP32S2 && !USE_ESP32_VARIANT_ESP32S3 &&
// !USE_ESP32_VARIANT_ESP32H2
#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) #if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
case UART_SELECTION_USB_CDC: case UART_SELECTION_USB_CDC:
uart_num_ = -1; this->uart_num_ = -1;
this->init_usb_cdc_();
break; break;
#endif // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3 #endif // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32S3) #if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32S3) || \
defined(USE_ESP32_VARIANT_ESP32H2)
case UART_SELECTION_USB_SERIAL_JTAG: case UART_SELECTION_USB_SERIAL_JTAG:
uart_num_ = -1; this->uart_num_ = -1;
this->init_usb_serial_jtag_();
break; break;
#endif // USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32S3 #endif // USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32S3 ||
// USE_ESP32_VARIANT_ESP32H2
} }
if (uart_num_ >= 0) { if (this->uart_num_ >= 0) {
uart_config_t uart_config{}; this->init_uart_();
uart_config.baud_rate = (int) baud_rate_;
uart_config.data_bits = UART_DATA_8_BITS;
uart_config.parity = UART_PARITY_DISABLE;
uart_config.stop_bits = UART_STOP_BITS_1;
uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
uart_config.source_clk = UART_SCLK_DEFAULT;
#endif
uart_param_config(uart_num_, &uart_config);
const int uart_buffer_size = tx_buffer_size_;
// Install UART driver using an event queue here
uart_driver_install(uart_num_, uart_buffer_size, uart_buffer_size, 10, nullptr, 0);
} }
#endif // USE_ESP_IDF #endif // USE_ESP_IDF
} }
@@ -331,9 +389,10 @@ const char *const LOG_LEVELS[] = {"NONE", "ERROR", "WARN", "INFO", "CONFIG", "DE
const char *const UART_SELECTIONS[] = { const char *const UART_SELECTIONS[] = {
"UART0", "UART1", "UART0", "UART1",
#if !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32C6) && \ #if !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32C6) && \
!defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32S3) !defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32S3) && !defined(USE_ESP32_VARIANT_ESP32H2)
"UART2", "UART2",
#endif // !USE_ESP32_VARIANT_ESP32C3 && !USE_ESP32_VARIANT_ESP32S2 && !USE_ESP32_VARIANT_ESP32S3 #endif // !USE_ESP32_VARIANT_ESP32C3 && !USE_ESP32_VARINT_ESP32C6 && !USE_ESP32_VARIANT_ESP32S2 &&
// !USE_ESP32_VARIANT_ESP32S3 && !USE_ESP32_VARIANT_ESP32H2
#if defined(USE_ESP_IDF) #if defined(USE_ESP_IDF)
#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) #if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
"USB_CDC", "USB_CDC",

View File

@@ -41,16 +41,19 @@ enum UARTSelection {
UART_SELECTION_UART1, UART_SELECTION_UART1,
#if defined(USE_ESP32) #if defined(USE_ESP32)
#if !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32C6) && \ #if !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32C6) && \
!defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32S3) !defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32S3) && !defined(USE_ESP32_VARIANT_ESP32H2)
UART_SELECTION_UART2, UART_SELECTION_UART2,
#endif // !USE_ESP32_VARIANT_ESP32C3 && !USE_ESP32_VARIANT_ESP32S2 && !USE_ESP32_VARIANT_ESP32S3 #endif // !USE_ESP32_VARIANT_ESP32C3 && !USE_ESP32_VARIANT_ESP32C6 && !USE_ESP32_VARIANT_ESP32S2 &&
// !USE_ESP32_VARIANT_ESP32S3 && !USE_ESP32_VARIANT_ESP32H2
#ifdef USE_ESP_IDF #ifdef USE_ESP_IDF
#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) #if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
UART_SELECTION_USB_CDC, UART_SELECTION_USB_CDC,
#endif // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3 #endif // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32S3) #if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32S3) || \
defined(USE_ESP32_VARIANT_ESP32H2)
UART_SELECTION_USB_SERIAL_JTAG, UART_SELECTION_USB_SERIAL_JTAG,
#endif // USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32S3 #endif // USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32S3 ||
// USE_ESP32_VARIANT_ESP32H2
#endif // USE_ESP_IDF #endif // USE_ESP_IDF
#endif // USE_ESP32 #endif // USE_ESP32
#ifdef USE_ESP8266 #ifdef USE_ESP8266
@@ -104,6 +107,16 @@ class Logger : public Component {
#endif #endif
protected: protected:
#ifdef USE_ESP_IDF
void init_uart_();
#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
void init_usb_cdc_();
#endif
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32S3) || \
defined(USE_ESP32_VARIANT_ESP32H2)
void init_usb_serial_jtag_();
#endif
#endif
void write_header_(int level, const char *tag, int line); void write_header_(int level, const char *tag, int line);
void write_footer_(); void write_footer_();
void log_message_(int level, const char *tag, int offset = 0); void log_message_(int level, const char *tag, int offset = 0);

View File

@@ -21,6 +21,7 @@ CONF_COLUMNS = "columns"
CONF_KEYS = "keys" CONF_KEYS = "keys"
CONF_DEBOUNCE_TIME = "debounce_time" CONF_DEBOUNCE_TIME = "debounce_time"
CONF_HAS_DIODES = "has_diodes" CONF_HAS_DIODES = "has_diodes"
CONF_HAS_PULLDOWNS = "has_pulldowns"
def check_keys(obj): def check_keys(obj):
@@ -45,6 +46,7 @@ CONFIG_SCHEMA = cv.All(
cv.Optional(CONF_KEYS): cv.string, cv.Optional(CONF_KEYS): cv.string,
cv.Optional(CONF_DEBOUNCE_TIME, default=1): cv.int_range(min=1, max=100), cv.Optional(CONF_DEBOUNCE_TIME, default=1): cv.int_range(min=1, max=100),
cv.Optional(CONF_HAS_DIODES): cv.boolean, cv.Optional(CONF_HAS_DIODES): cv.boolean,
cv.Optional(CONF_HAS_PULLDOWNS): cv.boolean,
} }
), ),
check_keys, check_keys,
@@ -69,3 +71,5 @@ async def to_code(config):
cg.add(var.set_debounce_time(config[CONF_DEBOUNCE_TIME])) cg.add(var.set_debounce_time(config[CONF_DEBOUNCE_TIME]))
if CONF_HAS_DIODES in config: if CONF_HAS_DIODES in config:
cg.add(var.set_has_diodes(config[CONF_HAS_DIODES])) cg.add(var.set_has_diodes(config[CONF_HAS_DIODES]))
if CONF_HAS_PULLDOWNS in config:
cg.add(var.set_has_pulldowns(config[CONF_HAS_PULLDOWNS]))

View File

@@ -11,11 +11,16 @@ void MatrixKeypad::setup() {
if (!has_diodes_) { if (!has_diodes_) {
pin->pin_mode(gpio::FLAG_INPUT); pin->pin_mode(gpio::FLAG_INPUT);
} else { } else {
pin->digital_write(true); pin->digital_write(!has_pulldowns_);
} }
} }
for (auto *pin : this->columns_) for (auto *pin : this->columns_) {
if (has_pulldowns_) {
pin->pin_mode(gpio::FLAG_INPUT);
} else {
pin->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP); pin->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP);
}
}
} }
void MatrixKeypad::loop() { void MatrixKeypad::loop() {
@@ -28,9 +33,9 @@ void MatrixKeypad::loop() {
for (auto *row : this->rows_) { for (auto *row : this->rows_) {
if (!has_diodes_) if (!has_diodes_)
row->pin_mode(gpio::FLAG_OUTPUT); row->pin_mode(gpio::FLAG_OUTPUT);
row->digital_write(false); row->digital_write(has_pulldowns_);
for (auto *col : this->columns_) { for (auto *col : this->columns_) {
if (!col->digital_read()) { if (col->digital_read() == has_pulldowns_) {
if (key != -1) { if (key != -1) {
error = true; error = true;
} else { } else {
@@ -39,7 +44,7 @@ void MatrixKeypad::loop() {
} }
pos++; pos++;
} }
row->digital_write(true); row->digital_write(!has_pulldowns_);
if (!has_diodes_) if (!has_diodes_)
row->pin_mode(gpio::FLAG_INPUT); row->pin_mode(gpio::FLAG_INPUT);
} }

View File

@@ -28,6 +28,7 @@ class MatrixKeypad : public key_provider::KeyProvider, public Component {
void set_keys(std::string keys) { keys_ = std::move(keys); }; void set_keys(std::string keys) { keys_ = std::move(keys); };
void set_debounce_time(int debounce_time) { debounce_time_ = debounce_time; }; void set_debounce_time(int debounce_time) { debounce_time_ = debounce_time; };
void set_has_diodes(int has_diodes) { has_diodes_ = has_diodes; }; void set_has_diodes(int has_diodes) { has_diodes_ = has_diodes; };
void set_has_pulldowns(int has_pulldowns) { has_pulldowns_ = has_pulldowns; };
void register_listener(MatrixKeypadListener *listener); void register_listener(MatrixKeypadListener *listener);
@@ -37,6 +38,7 @@ class MatrixKeypad : public key_provider::KeyProvider, public Component {
std::string keys_; std::string keys_;
int debounce_time_ = 0; int debounce_time_ = 0;
bool has_diodes_{false}; bool has_diodes_{false};
bool has_pulldowns_{false};
int pressed_key_ = -1; int pressed_key_ = -1;
std::vector<MatrixKeypadListener *> listeners_{}; std::vector<MatrixKeypadListener *> listeners_{};

View File

@@ -10,7 +10,7 @@ namespace max6675 {
class MAX6675Sensor : public sensor::Sensor, class MAX6675Sensor : public sensor::Sensor,
public PollingComponent, public PollingComponent,
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_LEADING, public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_LEADING,
spi::DATA_RATE_1KHZ> { spi::DATA_RATE_1MHZ> {
public: public:
void setup() override; void setup() override;
void dump_config() override; void dump_config() override;

View File

@@ -164,6 +164,10 @@ void MAX7219Component::send_to_all_(uint8_t a_register, uint8_t data) {
this->disable(); this->disable();
} }
void MAX7219Component::update() { void MAX7219Component::update() {
if (this->intensity_changed_) {
this->send_to_all_(MAX7219_REGISTER_INTENSITY, this->intensity_);
this->intensity_changed_ = false;
}
for (uint8_t i = 0; i < this->num_chips_ * 8; i++) for (uint8_t i = 0; i < this->num_chips_ * 8; i++)
this->buffer_[i] = 0; this->buffer_[i] = 0;
if (this->writer_.has_value()) if (this->writer_.has_value())
@@ -218,8 +222,11 @@ uint8_t MAX7219Component::printf(const char *format, ...) {
} }
void MAX7219Component::set_writer(max7219_writer_t &&writer) { this->writer_ = writer; } void MAX7219Component::set_writer(max7219_writer_t &&writer) { this->writer_ = writer; }
void MAX7219Component::set_intensity(uint8_t intensity) { void MAX7219Component::set_intensity(uint8_t intensity) {
intensity &= 0xF;
if (intensity != this->intensity_) {
this->intensity_changed_ = true;
this->intensity_ = intensity; this->intensity_ = intensity;
this->send_to_all_(MAX7219_REGISTER_INTENSITY, this->intensity_); }
} }
void MAX7219Component::set_num_chips(uint8_t num_chips) { this->num_chips_ = num_chips; } void MAX7219Component::set_num_chips(uint8_t num_chips) { this->num_chips_ = num_chips; }

View File

@@ -52,7 +52,8 @@ class MAX7219Component : public PollingComponent,
void send_byte_(uint8_t a_register, uint8_t data); void send_byte_(uint8_t a_register, uint8_t data);
void send_to_all_(uint8_t a_register, uint8_t data); void send_to_all_(uint8_t a_register, uint8_t data);
uint8_t intensity_{15}; /// Intensity of the display from 0 to 15 (most) uint8_t intensity_{15}; // Intensity of the display from 0 to 15 (most)
bool intensity_changed_{}; // True if we need to re-send the intensity
uint8_t num_chips_{1}; uint8_t num_chips_{1};
uint8_t *buffer_; uint8_t *buffer_;
bool reverse_{false}; bool reverse_{false};

View File

@@ -13,8 +13,7 @@ namespace mdns {
void MDNSComponent::setup() { void MDNSComponent::setup() {
this->compile_records_(); this->compile_records_();
network::IPAddress addr = network::get_ip_address(); MDNS.begin(this->hostname_.c_str());
MDNS.begin(this->hostname_.c_str(), (uint32_t) addr);
for (const auto &service : this->services_) { for (const auto &service : this->services_) {
// Strip the leading underscore from the proto and service_type. While it is // Strip the leading underscore from the proto and service_type. While it is

View File

@@ -35,7 +35,7 @@ from esphome.components.climate import (
) )
CODEOWNERS = ["@dudanov"] CODEOWNERS = ["@dudanov"]
DEPENDENCIES = ["climate", "uart", "wifi"] DEPENDENCIES = ["climate", "uart"]
AUTO_LOAD = ["sensor"] AUTO_LOAD = ["sensor"]
CONF_OUTDOOR_TEMPERATURE = "outdoor_temperature" CONF_OUTDOOR_TEMPERATURE = "outdoor_temperature"
CONF_POWER_USAGE = "power_usage" CONF_POWER_USAGE = "power_usage"

View File

@@ -3,6 +3,9 @@ import esphome.config_validation as cv
from esphome.components import i2c, sensor from esphome.components import i2c, sensor
from esphome.const import ( from esphome.const import (
CONF_ADDRESS, CONF_ADDRESS,
CONF_FIELD_STRENGTH_X,
CONF_FIELD_STRENGTH_Y,
CONF_FIELD_STRENGTH_Z,
CONF_ID, CONF_ID,
ICON_MAGNET, ICON_MAGNET,
STATE_CLASS_MEASUREMENT, STATE_CLASS_MEASUREMENT,
@@ -16,9 +19,6 @@ DEPENDENCIES = ["i2c"]
mmc5603_ns = cg.esphome_ns.namespace("mmc5603") mmc5603_ns = cg.esphome_ns.namespace("mmc5603")
CONF_FIELD_STRENGTH_X = "field_strength_x"
CONF_FIELD_STRENGTH_Y = "field_strength_y"
CONF_FIELD_STRENGTH_Z = "field_strength_z"
CONF_HEADING = "heading" CONF_HEADING = "heading"
MMC5603Component = mmc5603_ns.class_( MMC5603Component = mmc5603_ns.class_(

View File

@@ -0,0 +1 @@
CODEOWNERS = ["@agoode"]

View File

@@ -0,0 +1,141 @@
// See https://github.com/sparkfun/SparkFun_MMC5983MA_Magnetometer_Arduino_Library/tree/main
// for datasheets and an Arduino implementation.
#include "mmc5983.h"
#include "esphome/core/log.h"
namespace esphome {
namespace mmc5983 {
static const char *const TAG = "mmc5983";
namespace {
constexpr uint8_t IC0_ADDR = 0x09;
constexpr uint8_t IC1_ADDR = 0x0a;
constexpr uint8_t IC2_ADDR = 0x0b;
constexpr uint8_t IC3_ADDR = 0x0c;
constexpr uint8_t PRODUCT_ID_ADDR = 0x2f;
float convert_data_to_millitesla(uint8_t data_17_10, uint8_t data_9_2, uint8_t data_1_0) {
int32_t counts = (data_17_10 << 10) | (data_9_2 << 2) | data_1_0;
counts -= 131072; // "Null Field Output" from datasheet.
// Sensitivity is 16384 counts/gauss, which is 163840 counts/mT.
return counts / 163840.0f;
}
} // namespace
void MMC5983Component::update() {
// Schedule a SET/RESET. This will recalibrate the sensor.
// We are supposed to be able to set this once, and have it automatically continue every reading, but
// this does not appear to work in continuous mode, even with En_prd_set turned on in Internal Control 2.
// Bit 5 = Auto_SR_en (automatic SET/RESET enable).
const uint8_t ic0_value = 0b10000;
i2c::ErrorCode err = this->write_register(IC0_ADDR, &ic0_value, 1);
if (err != i2c::ErrorCode::ERROR_OK) {
ESP_LOGW(TAG, "Writing Internal Control 0 failed with i2c error %d", err);
this->status_set_warning();
}
// Read out the data, 7 bytes starting from 0x00.
uint8_t data[7];
err = this->read_register(0x00, data, sizeof(data));
if (err != i2c::ErrorCode::ERROR_OK) {
ESP_LOGW(TAG, "Reading data failed with i2c error %d", err);
this->status_set_warning();
return;
}
// Unpack the data and publish to sensors.
// Data is in this format:
// data[0]: Xout[17:10]
// data[1]: Xout[9:2]
// data[2]: Yout[17:10]
// data[3]: Yout[9:2]
// data[4]: Zout[17:10]
// data[5]: Zout[9:2]
// data[6]: { Xout[1], Xout[0], Yout[1], Yout[0], Zout[1], Zout[0], 0, 0 }
if (this->x_sensor_) {
this->x_sensor_->publish_state(convert_data_to_millitesla(data[0], data[1], (data[6] & 0b11000000) >> 6));
}
if (this->y_sensor_) {
this->y_sensor_->publish_state(convert_data_to_millitesla(data[2], data[3], (data[6] & 0b00110000) >> 4));
}
if (this->z_sensor_) {
this->z_sensor_->publish_state(convert_data_to_millitesla(data[4], data[5], (data[6] & 0b00001100) >> 2));
}
}
void MMC5983Component::setup() {
ESP_LOGCONFIG(TAG, "Setting up MMC5983...");
// Verify product id.
const uint8_t mmc5983_product_id = 0x30;
uint8_t id;
i2c::ErrorCode err = this->read_register(PRODUCT_ID_ADDR, &id, 1);
if (err != i2c::ErrorCode::ERROR_OK) {
ESP_LOGE(TAG, "Reading product id failed with i2c error %d", err);
this->mark_failed();
return;
}
if (id != mmc5983_product_id) {
ESP_LOGE(TAG, "Product id 0x%02x does not match expected value 0x%02x", id, mmc5983_product_id);
this->mark_failed();
return;
}
// Initialize Internal Control registers to 0.
// Internal Control 0.
const uint8_t zero = 0;
err = this->write_register(IC0_ADDR, &zero, 1);
if (err != i2c::ErrorCode::ERROR_OK) {
ESP_LOGE(TAG, "Initializing Internal Control 0 failed with i2c error %d", err);
this->mark_failed();
return;
}
// Internal Control 1.
err = this->write_register(IC1_ADDR, &zero, 1);
if (err != i2c::ErrorCode::ERROR_OK) {
ESP_LOGE(TAG, "Initializing Internal Control 1 failed with i2c error %d", err);
this->mark_failed();
return;
}
// Internal Control 2.
err = this->write_register(IC2_ADDR, &zero, 1);
if (err != i2c::ErrorCode::ERROR_OK) {
ESP_LOGE(TAG, "Initializing Internal Control 2 failed with i2c error %d", err);
this->mark_failed();
return;
}
// Internal Control 3.
err = this->write_register(IC3_ADDR, &zero, 1);
if (err != i2c::ErrorCode::ERROR_OK) {
ESP_LOGE(TAG, "Initializing Internal Control 3 failed with i2c error %d", err);
this->mark_failed();
return;
}
// Enable continuous mode at 100 Hz, using Internal Control 2.
// Bit 3 = Cmm_en (continuous mode enable).
// Bit [2:0] = Cm_freq. 0b101 = 100 Hz, the fastest reading speed at Bandwidth=100 Hz.
const uint8_t ic2_value = 0b00001101;
err = this->write_register(IC2_ADDR, &ic2_value, 1);
if (err != i2c::ErrorCode::ERROR_OK) {
ESP_LOGE(TAG, "Writing Internal Control 2 failed with i2c error %d", err);
this->mark_failed();
return;
}
}
void MMC5983Component::dump_config() {
ESP_LOGD(TAG, "MMC5983:");
LOG_I2C_DEVICE(this);
LOG_SENSOR(" ", "X", this->x_sensor_);
LOG_SENSOR(" ", "Y", this->y_sensor_);
LOG_SENSOR(" ", "Z", this->z_sensor_);
}
float MMC5983Component::get_setup_priority() const { return setup_priority::DATA; }
} // namespace mmc5983
} // namespace esphome

View File

@@ -0,0 +1,28 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/i2c/i2c.h"
namespace esphome {
namespace mmc5983 {
class MMC5983Component : public PollingComponent, public i2c::I2CDevice {
public:
void update() override;
void setup() override;
void dump_config() override;
float get_setup_priority() const override;
void set_x_sensor(sensor::Sensor *x_sensor) { x_sensor_ = x_sensor; }
void set_y_sensor(sensor::Sensor *y_sensor) { y_sensor_ = y_sensor; }
void set_z_sensor(sensor::Sensor *z_sensor) { z_sensor_ = z_sensor; }
protected:
sensor::Sensor *x_sensor_{nullptr};
sensor::Sensor *y_sensor_{nullptr};
sensor::Sensor *z_sensor_{nullptr};
};
} // namespace mmc5983
} // namespace esphome

View File

@@ -0,0 +1,55 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c, sensor
from esphome.const import (
CONF_FIELD_STRENGTH_X,
CONF_FIELD_STRENGTH_Y,
CONF_FIELD_STRENGTH_Z,
CONF_ID,
ICON_MAGNET,
STATE_CLASS_MEASUREMENT,
UNIT_MICROTESLA,
)
DEPENDENCIES = ["i2c"]
mmc5983_ns = cg.esphome_ns.namespace("mmc5983")
MMC5983Component = mmc5983_ns.class_(
"MMC5983Component", cg.PollingComponent, i2c.I2CDevice
)
field_strength_schema = sensor.sensor_schema(
unit_of_measurement=UNIT_MICROTESLA,
icon=ICON_MAGNET,
accuracy_decimals=4,
state_class=STATE_CLASS_MEASUREMENT,
)
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(MMC5983Component),
cv.Optional(CONF_FIELD_STRENGTH_X): field_strength_schema,
cv.Optional(CONF_FIELD_STRENGTH_Y): field_strength_schema,
cv.Optional(CONF_FIELD_STRENGTH_Z): field_strength_schema,
}
)
.extend(cv.polling_component_schema("60s"))
.extend(i2c.i2c_device_schema(0x30))
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
if x_config := config.get(CONF_FIELD_STRENGTH_X):
sens = await sensor.new_sensor(x_config)
cg.add(var.set_x_sensor(sens))
if y_config := config.get(CONF_FIELD_STRENGTH_Y):
sens = await sensor.new_sensor(y_config)
cg.add(var.set_y_sensor(sens))
if z_config := config.get(CONF_FIELD_STRENGTH_Z):
sens = await sensor.new_sensor(z_config)
cg.add(var.set_z_sensor(sens))

View File

@@ -71,7 +71,8 @@ bool MopekaStdCheck::parse_device(const esp32_ble_tracker::ESPBTDevice &device)
const auto *mopeka_data = (const mopeka_std_package *) manu_data.data.data(); const auto *mopeka_data = (const mopeka_std_package *) manu_data.data.data();
const u_int8_t hardware_id = mopeka_data->data_1 & 0xCF; const u_int8_t hardware_id = mopeka_data->data_1 & 0xCF;
if (static_cast<SensorType>(hardware_id) != STANDARD && static_cast<SensorType>(hardware_id) != XL) { if (static_cast<SensorType>(hardware_id) != STANDARD && static_cast<SensorType>(hardware_id) != XL &&
static_cast<SensorType>(hardware_id) != ETRAILER) {
ESP_LOGE(TAG, "[%s] Unsupported Sensor Type (0x%X)", device.address_str().c_str(), hardware_id); ESP_LOGE(TAG, "[%s] Unsupported Sensor Type (0x%X)", device.address_str().c_str(), hardware_id);
return false; return false;
} }

View File

@@ -14,6 +14,7 @@ namespace mopeka_std_check {
enum SensorType { enum SensorType {
STANDARD = 0x02, STANDARD = 0x02,
XL = 0x03, XL = 0x03,
ETRAILER = 0x46,
}; };
// 4 values in one struct so it aligns to 8 byte. One `mopeka_std_values` is 40 bit long. // 4 values in one struct so it aligns to 8 byte. One `mopeka_std_values` is 40 bit long.

View File

@@ -43,6 +43,9 @@ from esphome.const import (
CONF_USE_ABBREVIATIONS, CONF_USE_ABBREVIATIONS,
CONF_USERNAME, CONF_USERNAME,
CONF_WILL_MESSAGE, CONF_WILL_MESSAGE,
PLATFORM_ESP32,
PLATFORM_ESP8266,
PLATFORM_BK72XX,
) )
from esphome.core import coroutine_with_priority, CORE from esphome.core import coroutine_with_priority, CORE
from esphome.components.esp32 import add_idf_sdkconfig_option from esphome.components.esp32 import add_idf_sdkconfig_option
@@ -250,7 +253,7 @@ CONFIG_SCHEMA = cv.All(
} }
), ),
validate_config, validate_config,
cv.only_on(["esp32", "esp8266"]), cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_BK72XX]),
) )
@@ -271,10 +274,10 @@ def exp_mqtt_message(config):
async def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config) await cg.register_component(var, config)
# Add required libraries for ESP8266 # Add required libraries for ESP8266 and LibreTiny
if CORE.is_esp8266: if CORE.is_esp8266 or CORE.is_libretiny:
# https://github.com/heman/async-mqtt-client/blob/master/library.json # https://github.com/heman/async-mqtt-client/blob/master/library.json
cg.add_library("heman/AsyncMqttClient-esphome", "1.0.0") cg.add_library("heman/AsyncMqttClient-esphome", "2.0.0")
cg.add_define("USE_MQTT") cg.add_define("USE_MQTT")
cg.add_global(mqtt_ns.using) cg.add_global(mqtt_ns.using)

View File

@@ -19,9 +19,7 @@ class MQTTBackendESP8266 final : public MQTTBackend {
void set_will(const char *topic, uint8_t qos, bool retain, const char *payload) final { void set_will(const char *topic, uint8_t qos, bool retain, const char *payload) final {
mqtt_client_.setWill(topic, qos, retain, payload); mqtt_client_.setWill(topic, qos, retain, payload);
} }
void set_server(network::IPAddress ip, uint16_t port) final { void set_server(network::IPAddress ip, uint16_t port) final { mqtt_client_.setServer(ip, port); }
mqtt_client_.setServer(IPAddress(static_cast<uint32_t>(ip)), port);
}
void set_server(const char *host, uint16_t port) final { mqtt_client_.setServer(host, port); } void set_server(const char *host, uint16_t port) final { mqtt_client_.setServer(host, port); }
#if ASYNC_TCP_SSL_ENABLED #if ASYNC_TCP_SSL_ENABLED
void set_secure(bool secure) { mqtt_client.setSecure(secure); } void set_secure(bool secure) { mqtt_client.setSecure(secure); }

View File

@@ -0,0 +1,72 @@
#pragma once
#ifdef USE_LIBRETINY
#include "mqtt_backend.h"
#include <AsyncMqttClient.h>
namespace esphome {
namespace mqtt {
class MQTTBackendLibreTiny final : public MQTTBackend {
public:
void set_keep_alive(uint16_t keep_alive) final { mqtt_client_.setKeepAlive(keep_alive); }
void set_client_id(const char *client_id) final { mqtt_client_.setClientId(client_id); }
void set_clean_session(bool clean_session) final { mqtt_client_.setCleanSession(clean_session); }
void set_credentials(const char *username, const char *password) final {
mqtt_client_.setCredentials(username, password);
}
void set_will(const char *topic, uint8_t qos, bool retain, const char *payload) final {
mqtt_client_.setWill(topic, qos, retain, payload);
}
void set_server(network::IPAddress ip, uint16_t port) final { mqtt_client_.setServer(IPAddress(ip), port); }
void set_server(const char *host, uint16_t port) final { mqtt_client_.setServer(host, port); }
#if ASYNC_TCP_SSL_ENABLED
void set_secure(bool secure) { mqtt_client.setSecure(secure); }
void add_server_fingerprint(const uint8_t *fingerprint) { mqtt_client.addServerFingerprint(fingerprint); }
#endif
void set_on_connect(std::function<on_connect_callback_t> &&callback) final {
this->mqtt_client_.onConnect(std::move(callback));
}
void set_on_disconnect(std::function<on_disconnect_callback_t> &&callback) final {
auto async_callback = [callback](AsyncMqttClientDisconnectReason reason) {
// int based enum so casting isn't a problem
callback(static_cast<MQTTClientDisconnectReason>(reason));
};
this->mqtt_client_.onDisconnect(std::move(async_callback));
}
void set_on_subscribe(std::function<on_subscribe_callback_t> &&callback) final {
this->mqtt_client_.onSubscribe(std::move(callback));
}
void set_on_unsubscribe(std::function<on_unsubscribe_callback_t> &&callback) final {
this->mqtt_client_.onUnsubscribe(std::move(callback));
}
void set_on_message(std::function<on_message_callback_t> &&callback) final {
auto async_callback = [callback](const char *topic, const char *payload,
AsyncMqttClientMessageProperties async_properties, size_t len, size_t index,
size_t total) { callback(topic, payload, len, index, total); };
mqtt_client_.onMessage(std::move(async_callback));
}
void set_on_publish(std::function<on_publish_user_callback_t> &&callback) final {
this->mqtt_client_.onPublish(std::move(callback));
}
bool connected() const final { return mqtt_client_.connected(); }
void connect() final { mqtt_client_.connect(); }
void disconnect() final { mqtt_client_.disconnect(true); }
bool subscribe(const char *topic, uint8_t qos) final { return mqtt_client_.subscribe(topic, qos) != 0; }
bool unsubscribe(const char *topic) final { return mqtt_client_.unsubscribe(topic) != 0; }
bool publish(const char *topic, const char *payload, size_t length, uint8_t qos, bool retain) final {
return mqtt_client_.publish(topic, qos, retain, payload, length, false, 0) != 0;
}
using MQTTBackend::publish;
protected:
AsyncMqttClient mqtt_client_;
};
} // namespace mqtt
} // namespace esphome
#endif // defined(USE_LIBRETINY)

View File

@@ -106,6 +106,9 @@ void MQTTClientComponent::send_device_info_() {
#ifdef USE_ESP32 #ifdef USE_ESP32
root["platform"] = "ESP32"; root["platform"] = "ESP32";
#endif #endif
#ifdef USE_LIBRETINY
root["platform"] = lt_cpu_get_model_name();
#endif
root["board"] = ESPHOME_BOARD; root["board"] = ESPHOME_BOARD;
#if defined(USE_WIFI) #if defined(USE_WIFI)
@@ -156,7 +159,7 @@ void MQTTClientComponent::start_dnslookup_() {
this->dns_resolve_error_ = false; this->dns_resolve_error_ = false;
this->dns_resolved_ = false; this->dns_resolved_ = false;
ip_addr_t addr; ip_addr_t addr;
#ifdef USE_ESP32 #if defined(USE_ESP32) || defined(USE_LIBRETINY)
err_t err = dns_gethostbyname_addrtype(this->credentials_.address.c_str(), &addr, err_t err = dns_gethostbyname_addrtype(this->credentials_.address.c_str(), &addr,
MQTTClientComponent::dns_found_callback, this, LWIP_DNS_ADDRTYPE_IPV4); MQTTClientComponent::dns_found_callback, this, LWIP_DNS_ADDRTYPE_IPV4);
#endif #endif
@@ -168,11 +171,7 @@ void MQTTClientComponent::start_dnslookup_() {
case ERR_OK: { case ERR_OK: {
// Got IP immediately // Got IP immediately
this->dns_resolved_ = true; this->dns_resolved_ = true;
#if LWIP_IPV6 this->ip_ = network::IPAddress(&addr);
this->ip_ = addr.u_addr.ip4.addr;
#else
this->ip_ = addr.addr;
#endif
this->start_connect_(); this->start_connect_();
return; return;
} }
@@ -223,11 +222,7 @@ void MQTTClientComponent::dns_found_callback(const char *name, const ip_addr_t *
if (ipaddr == nullptr) { if (ipaddr == nullptr) {
a_this->dns_resolve_error_ = true; a_this->dns_resolve_error_ = true;
} else { } else {
#if LWIP_IPV6 a_this->ip_ = network::IPAddress(ipaddr);
a_this->ip_ = ipaddr->u_addr.ip4.addr;
#else
a_this->ip_ = ipaddr->addr;
#endif
a_this->dns_resolved_ = true; a_this->dns_resolved_ = true;
} }
} }

View File

@@ -13,6 +13,8 @@
#include "mqtt_backend_esp32.h" #include "mqtt_backend_esp32.h"
#elif defined(USE_ESP8266) #elif defined(USE_ESP8266)
#include "mqtt_backend_esp8266.h" #include "mqtt_backend_esp8266.h"
#elif defined(USE_LIBRETINY)
#include "mqtt_backend_libretiny.h"
#endif #endif
#include "lwip/ip_addr.h" #include "lwip/ip_addr.h"
@@ -300,6 +302,8 @@ class MQTTClientComponent : public Component {
MQTTBackendESP32 mqtt_backend_; MQTTBackendESP32 mqtt_backend_;
#elif defined(USE_ESP8266) #elif defined(USE_ESP8266)
MQTTBackendESP8266 mqtt_backend_; MQTTBackendESP8266 mqtt_backend_;
#elif defined(USE_LIBRETINY)
MQTTBackendLibreTiny mqtt_backend_;
#endif #endif
MQTTClientState state_{MQTT_CLIENT_DISCONNECTED}; MQTTClientState state_{MQTT_CLIENT_DISCONNECTED};

View File

@@ -3,42 +3,113 @@
#include <string> #include <string>
#include <cstdio> #include <cstdio>
#include <array> #include <array>
#include "esphome/core/macros.h"
#if defined(USE_ESP_IDF) || defined(USE_LIBRETINY) || USE_ARDUINO_VERSION_CODE > VERSION_CODE(3, 0, 0)
#include <lwip/ip_addr.h>
#endif
#if USE_ARDUINO
#include <Arduino.h>
#include <IPAddress.h>
#endif /* USE_ADRDUINO */
#if USE_ESP32_FRAMEWORK_ARDUINO
#define arduino_ns Arduino_h
#elif USE_LIBRETINY
#define arduino_ns arduino
#elif USE_ARDUINO
#define arduino_ns
#endif
#ifdef USE_ESP32
#include <cstring>
#include <esp_netif.h>
#endif
namespace esphome { namespace esphome {
namespace network { namespace network {
struct IPAddress { struct IPAddress {
public: public:
IPAddress() : addr_({0, 0, 0, 0}) {} IPAddress() { ip_addr_set_zero(&ip_addr_); }
IPAddress(uint8_t first, uint8_t second, uint8_t third, uint8_t fourth) : addr_({first, second, third, fourth}) {} IPAddress(uint8_t first, uint8_t second, uint8_t third, uint8_t fourth) {
IPAddress(uint32_t raw) { IP_ADDR4(&ip_addr_, first, second, third, fourth);
addr_[0] = (uint8_t) (raw >> 0);
addr_[1] = (uint8_t) (raw >> 8);
addr_[2] = (uint8_t) (raw >> 16);
addr_[3] = (uint8_t) (raw >> 24);
} }
operator uint32_t() const { IPAddress(const ip_addr_t *other_ip) { ip_addr_copy(ip_addr_, *other_ip); }
uint32_t res = 0; IPAddress(const std::string &in_address) { ipaddr_aton(in_address.c_str(), &ip_addr_); }
res |= ((uint32_t) addr_[0]) << 0; IPAddress(ip4_addr_t *other_ip) {
res |= ((uint32_t) addr_[1]) << 8; memcpy((void *) &ip_addr_, (void *) other_ip, sizeof(ip4_addr_t));
res |= ((uint32_t) addr_[2]) << 16; #if USE_ESP32 && LWIP_IPV6
res |= ((uint32_t) addr_[3]) << 24; ip_addr_.type = IPADDR_TYPE_V4;
return res; #endif
} }
std::string str() const { #if USE_ARDUINO
char buffer[24]; IPAddress(const arduino_ns::IPAddress &other_ip) { ip_addr_set_ip4_u32(&ip_addr_, other_ip); }
snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d", addr_[0], addr_[1], addr_[2], addr_[3]); #endif
return buffer; #if LWIP_IPV6
IPAddress(ip6_addr_t *other_ip) {
memcpy((void *) &ip_addr_, (void *) other_ip, sizeof(ip6_addr_t));
ip_addr_.type = IPADDR_TYPE_V6;
} }
bool operator==(const IPAddress &other) const { #endif /* LWIP_IPV6 */
return addr_[0] == other.addr_[0] && addr_[1] == other.addr_[1] && addr_[2] == other.addr_[2] &&
addr_[3] == other.addr_[3]; #ifdef USE_ESP32
#if LWIP_IPV6
IPAddress(esp_ip6_addr_t *other_ip) {
memcpy((void *) &ip_addr_.u_addr.ip6, (void *) other_ip, sizeof(esp_ip6_addr_t));
ip_addr_.type = IPADDR_TYPE_V6;
}
#endif /* LWIP_IPV6 */
IPAddress(esp_ip4_addr_t *other_ip) { memcpy((void *) &ip_addr_, (void *) other_ip, sizeof(esp_ip4_addr_t)); }
operator esp_ip_addr_t() const {
esp_ip_addr_t tmp;
#if LWIP_IPV6
memcpy((void *) &tmp, (void *) &ip_addr_, sizeof(ip_addr_));
#else
memcpy((void *) &tmp.u_addr.ip4, (void *) &ip_addr_, sizeof(ip_addr_));
#endif /* LWIP_IPV6 */
return tmp;
}
operator esp_ip4_addr_t() const {
esp_ip4_addr_t tmp;
#if LWIP_IPV6
memcpy((void *) &tmp, (void *) &ip_addr_.u_addr.ip4, sizeof(esp_ip4_addr_t));
#else
memcpy((void *) &tmp, (void *) &ip_addr_, sizeof(ip_addr_));
#endif /* LWIP_IPV6 */
return tmp;
}
#endif /* USE_ESP32 */
operator ip_addr_t() const { return ip_addr_; }
#if LWIP_IPV6
operator ip4_addr_t() const { return *ip_2_ip4(&ip_addr_); }
#endif /* LWIP_IPV6 */
#if USE_ARDUINO
operator arduino_ns::IPAddress() const { return ip_addr_get_ip4_u32(&ip_addr_); }
#endif
bool is_set() { return !ip_addr_isany(&ip_addr_); }
bool is_ip4() { return IP_IS_V4(&ip_addr_); }
bool is_ip6() { return IP_IS_V6(&ip_addr_); }
std::string str() const { return ipaddr_ntoa(&ip_addr_); }
bool operator==(const IPAddress &other) const { return ip_addr_cmp(&ip_addr_, &other.ip_addr_); }
bool operator!=(const IPAddress &other) const { return !ip_addr_cmp(&ip_addr_, &other.ip_addr_); }
IPAddress &operator+=(uint8_t increase) {
if (IP_IS_V4(&ip_addr_)) {
#if LWIP_IPV6
(((u8_t *) (&ip_addr_.u_addr.ip4))[3]) += increase;
#else
(((u8_t *) (&ip_addr_.addr))[3]) += increase;
#endif /* LWIP_IPV6 */
}
return *this;
} }
uint8_t operator[](int index) const { return addr_[index]; }
uint8_t &operator[](int index) { return addr_[index]; }
protected: protected:
std::array<uint8_t, 4> addr_; ip_addr_t ip_addr_;
}; };
} // namespace network } // namespace network

View File

@@ -61,6 +61,11 @@ bool Nextion::check_connect_() {
std::string response; std::string response;
this->recv_ret_string_(response, 0, false); this->recv_ret_string_(response, 0, false);
if (!response.empty() && response[0] == 0x1A) {
// Swallow invalid variable name responses that may be caused by the above commands
ESP_LOGD(TAG, "0x1A error ignored during setup");
return false;
}
if (response.empty() || response.find("comok") == std::string::npos) { if (response.empty() || response.find("comok") == std::string::npos) {
#ifdef NEXTION_PROTOCOL_LOG #ifdef NEXTION_PROTOCOL_LOG
ESP_LOGN(TAG, "Bad connect request %s", response.c_str()); ESP_LOGN(TAG, "Bad connect request %s", response.c_str());

View File

@@ -76,9 +76,15 @@ void NextionSensor::set_state(float state, bool publish, bool send_to_nextion) {
} }
} }
float published_state = state;
if (this->wave_chan_id_ == UINT8_MAX) { if (this->wave_chan_id_ == UINT8_MAX) {
if (publish) { if (publish) {
this->publish_state(state); if (this->precision_ > 0) {
double to_multiply = pow(10, -this->precision_);
published_state = (float) (state * to_multiply);
}
this->publish_state(published_state);
} else { } else {
this->raw_state = state; this->raw_state = state;
this->state = state; this->state = state;
@@ -87,7 +93,7 @@ void NextionSensor::set_state(float state, bool publish, bool send_to_nextion) {
} }
this->update_component_settings(); this->update_component_settings();
ESP_LOGN(TAG, "Wrote state for sensor \"%s\" state %lf", this->variable_name_.c_str(), state); ESP_LOGN(TAG, "Wrote state for sensor \"%s\" state %lf", this->variable_name_.c_str(), published_state);
} }
void NextionSensor::wave_update_() { void NextionSensor::wave_update_() {

View File

@@ -106,4 +106,5 @@ async def output_set_level_to_code(config, action_id, template_arg, args):
async def to_code(config): async def to_code(config):
cg.add_define("USE_OUTPUT")
cg.add_global(output_ns.using) cg.add_global(output_ns.using)

View File

@@ -17,12 +17,12 @@ struct PM25AQIData {
uint16_t pm10_env, ///< Environmental PM1.0 uint16_t pm10_env, ///< Environmental PM1.0
pm25_env, ///< Environmental PM2.5 pm25_env, ///< Environmental PM2.5
pm100_env; ///< Environmental PM10.0 pm100_env; ///< Environmental PM10.0
uint16_t particles_03um, ///< 0.3um Particle Count uint16_t particles_03um, ///> 0.3um Particle Count
particles_05um, ///< 0.5um Particle Count particles_05um, ///> 0.5um Particle Count
particles_10um, ///< 1.0um Particle Count particles_10um, ///> 1.0um Particle Count
particles_25um, ///< 2.5um Particle Count particles_25um, ///> 2.5um Particle Count
particles_50um, ///< 5.0um Particle Count particles_50um, ///> 5.0um Particle Count
particles_100um; ///< 10.0um Particle Count particles_100um; ///> 10.0um Particle Count
uint16_t unused; ///< Unused uint16_t unused; ///< Unused
uint16_t checksum; ///< Packet checksum uint16_t checksum; ///< Packet checksum
}; };

View File

@@ -1,5 +1,3 @@
#ifdef USE_ARDUINO
#include "prometheus_handler.h" #include "prometheus_handler.h"
#include "esphome/core/application.h" #include "esphome/core/application.h"
@@ -89,7 +87,7 @@ void PrometheusHandler::sensor_row_(AsyncResponseStream *stream, sensor::Sensor
stream->print(obj->get_unit_of_measurement().c_str()); stream->print(obj->get_unit_of_measurement().c_str());
stream->print(F("\"} ")); stream->print(F("\"} "));
stream->print(value_accuracy_to_string(obj->state, obj->get_accuracy_decimals()).c_str()); stream->print(value_accuracy_to_string(obj->state, obj->get_accuracy_decimals()).c_str());
stream->print('\n'); stream->print(F("\n"));
} else { } else {
// Invalid state // Invalid state
stream->print(F("esphome_sensor_failed{id=\"")); stream->print(F("esphome_sensor_failed{id=\""));
@@ -124,7 +122,7 @@ void PrometheusHandler::binary_sensor_row_(AsyncResponseStream *stream, binary_s
stream->print(relabel_name_(obj).c_str()); stream->print(relabel_name_(obj).c_str());
stream->print(F("\"} ")); stream->print(F("\"} "));
stream->print(obj->state); stream->print(obj->state);
stream->print('\n'); stream->print(F("\n"));
} else { } else {
// Invalid state // Invalid state
stream->print(F("esphome_binary_sensor_failed{id=\"")); stream->print(F("esphome_binary_sensor_failed{id=\""));
@@ -158,7 +156,7 @@ void PrometheusHandler::fan_row_(AsyncResponseStream *stream, fan::Fan *obj) {
stream->print(relabel_name_(obj).c_str()); stream->print(relabel_name_(obj).c_str());
stream->print(F("\"} ")); stream->print(F("\"} "));
stream->print(obj->state); stream->print(obj->state);
stream->print('\n'); stream->print(F("\n"));
// Speed if available // Speed if available
if (obj->get_traits().supports_speed()) { if (obj->get_traits().supports_speed()) {
stream->print(F("esphome_fan_speed{id=\"")); stream->print(F("esphome_fan_speed{id=\""));
@@ -167,7 +165,7 @@ void PrometheusHandler::fan_row_(AsyncResponseStream *stream, fan::Fan *obj) {
stream->print(relabel_name_(obj).c_str()); stream->print(relabel_name_(obj).c_str());
stream->print(F("\"} ")); stream->print(F("\"} "));
stream->print(obj->speed); stream->print(obj->speed);
stream->print('\n'); stream->print(F("\n"));
} }
// Oscillation if available // Oscillation if available
if (obj->get_traits().supports_oscillation()) { if (obj->get_traits().supports_oscillation()) {
@@ -177,7 +175,7 @@ void PrometheusHandler::fan_row_(AsyncResponseStream *stream, fan::Fan *obj) {
stream->print(relabel_name_(obj).c_str()); stream->print(relabel_name_(obj).c_str());
stream->print(F("\"} ")); stream->print(F("\"} "));
stream->print(obj->oscillating); stream->print(obj->oscillating);
stream->print('\n'); stream->print(F("\n"));
} }
} }
#endif #endif
@@ -281,7 +279,7 @@ void PrometheusHandler::cover_row_(AsyncResponseStream *stream, cover::Cover *ob
stream->print(relabel_name_(obj).c_str()); stream->print(relabel_name_(obj).c_str());
stream->print(F("\"} ")); stream->print(F("\"} "));
stream->print(obj->position); stream->print(obj->position);
stream->print('\n'); stream->print(F("\n"));
if (obj->get_traits().get_supports_tilt()) { if (obj->get_traits().get_supports_tilt()) {
stream->print(F("esphome_cover_tilt{id=\"")); stream->print(F("esphome_cover_tilt{id=\""));
stream->print(relabel_id_(obj).c_str()); stream->print(relabel_id_(obj).c_str());
@@ -289,7 +287,7 @@ void PrometheusHandler::cover_row_(AsyncResponseStream *stream, cover::Cover *ob
stream->print(relabel_name_(obj).c_str()); stream->print(relabel_name_(obj).c_str());
stream->print(F("\"} ")); stream->print(F("\"} "));
stream->print(obj->tilt); stream->print(obj->tilt);
stream->print('\n'); stream->print(F("\n"));
} }
} else { } else {
// Invalid state // Invalid state
@@ -322,7 +320,7 @@ void PrometheusHandler::switch_row_(AsyncResponseStream *stream, switch_::Switch
stream->print(relabel_name_(obj).c_str()); stream->print(relabel_name_(obj).c_str());
stream->print(F("\"} ")); stream->print(F("\"} "));
stream->print(obj->state); stream->print(obj->state);
stream->print('\n'); stream->print(F("\n"));
} }
#endif #endif
@@ -346,11 +344,9 @@ void PrometheusHandler::lock_row_(AsyncResponseStream *stream, lock::Lock *obj)
stream->print(relabel_name_(obj).c_str()); stream->print(relabel_name_(obj).c_str());
stream->print(F("\"} ")); stream->print(F("\"} "));
stream->print(obj->state); stream->print(obj->state);
stream->print('\n'); stream->print(F("\n"));
} }
#endif #endif
} // namespace prometheus } // namespace prometheus
} // namespace esphome } // namespace esphome
#endif // USE_ARDUINO

View File

@@ -1,14 +1,12 @@
#pragma once #pragma once
#ifdef USE_ARDUINO
#include <map> #include <map>
#include <utility> #include <utility>
#include "esphome/core/entity_base.h"
#include "esphome/components/web_server_base/web_server_base.h" #include "esphome/components/web_server_base/web_server_base.h"
#include "esphome/core/controller.h"
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/core/controller.h"
#include "esphome/core/entity_base.h"
namespace esphome { namespace esphome {
namespace prometheus { namespace prometheus {
@@ -119,5 +117,3 @@ class PrometheusHandler : public AsyncWebHandler, public Component {
} // namespace prometheus } // namespace prometheus
} // namespace esphome } // namespace esphome
#endif // USE_ARDUINO

View File

@@ -7,10 +7,20 @@ namespace pulse_meter {
static const char *const TAG = "pulse_meter"; static const char *const TAG = "pulse_meter";
void PulseMeterSensor::set_total_pulses(uint32_t pulses) {
this->total_pulses_ = pulses;
if (this->total_sensor_ != nullptr) {
this->total_sensor_->publish_state(this->total_pulses_);
}
}
void PulseMeterSensor::setup() { void PulseMeterSensor::setup() {
this->pin_->setup(); this->pin_->setup();
this->isr_pin_ = pin_->to_isr(); this->isr_pin_ = pin_->to_isr();
// Set the last processed edge to now for the first timeout
this->last_processed_edge_us_ = micros();
if (this->filter_mode_ == FILTER_EDGE) { if (this->filter_mode_ == FILTER_EDGE) {
this->pin_->attach_interrupt(PulseMeterSensor::edge_intr, this, gpio::INTERRUPT_RISING_EDGE); this->pin_->attach_interrupt(PulseMeterSensor::edge_intr, this, gpio::INTERRUPT_RISING_EDGE);
} else if (this->filter_mode_ == FILTER_PULSE) { } else if (this->filter_mode_ == FILTER_PULSE) {
@@ -38,12 +48,16 @@ void PulseMeterSensor::loop() {
} }
// We need to detect at least two edges to have a valid pulse width // We need to detect at least two edges to have a valid pulse width
if (!this->initialized_) { switch (this->meter_state_) {
this->initialized_ = true; case MeterState::INITIAL:
} else { case MeterState::TIMED_OUT: {
this->meter_state_ = MeterState::RUNNING;
} break;
case MeterState::RUNNING: {
uint32_t delta_us = this->get_->last_detected_edge_us_ - this->last_processed_edge_us_; uint32_t delta_us = this->get_->last_detected_edge_us_ - this->last_processed_edge_us_;
float pulse_width_us = delta_us / float(this->get_->count_); float pulse_width_us = delta_us / float(this->get_->count_);
this->publish_state((60.0f * 1000000.0f) / pulse_width_us); this->publish_state((60.0f * 1000000.0f) / pulse_width_us);
} break;
} }
this->last_processed_edge_us_ = this->get_->last_detected_edge_us_; this->last_processed_edge_us_ = this->get_->last_detected_edge_us_;
@@ -53,11 +67,19 @@ void PulseMeterSensor::loop() {
const uint32_t now = micros(); const uint32_t now = micros();
const uint32_t time_since_valid_edge_us = now - this->last_processed_edge_us_; const uint32_t time_since_valid_edge_us = now - this->last_processed_edge_us_;
if (this->initialized_ && time_since_valid_edge_us > this->timeout_us_) { switch (this->meter_state_) {
// Running and initial states can timeout
case MeterState::INITIAL:
case MeterState::RUNNING: {
if (time_since_valid_edge_us > this->timeout_us_) {
this->meter_state_ = MeterState::TIMED_OUT;
ESP_LOGD(TAG, "No pulse detected for %us, assuming 0 pulses/min", time_since_valid_edge_us / 1000000); ESP_LOGD(TAG, "No pulse detected for %us, assuming 0 pulses/min", time_since_valid_edge_us / 1000000);
this->initialized_ = false;
this->publish_state(0.0f); this->publish_state(0.0f);
} }
} break;
default:
break;
}
} }
} }

View File

@@ -20,7 +20,8 @@ class PulseMeterSensor : public sensor::Sensor, public Component {
void set_timeout_us(uint32_t timeout) { this->timeout_us_ = timeout; } void set_timeout_us(uint32_t timeout) { this->timeout_us_ = timeout; }
void set_total_sensor(sensor::Sensor *sensor) { this->total_sensor_ = sensor; } void set_total_sensor(sensor::Sensor *sensor) { this->total_sensor_ = sensor; }
void set_filter_mode(InternalFilterMode mode) { this->filter_mode_ = mode; } void set_filter_mode(InternalFilterMode mode) { this->filter_mode_ = mode; }
void set_total_pulses(uint32_t pulses) { this->total_pulses_ = pulses; }
void set_total_pulses(uint32_t pulses);
void setup() override; void setup() override;
void loop() override; void loop() override;
@@ -38,7 +39,8 @@ class PulseMeterSensor : public sensor::Sensor, public Component {
InternalFilterMode filter_mode_{FILTER_EDGE}; InternalFilterMode filter_mode_{FILTER_EDGE};
// Variables used in the loop // Variables used in the loop
bool initialized_ = false; enum class MeterState { INITIAL, RUNNING, TIMED_OUT };
MeterState meter_state_ = MeterState::INITIAL;
uint32_t total_pulses_ = 0; uint32_t total_pulses_ = 0;
uint32_t last_processed_edge_us_ = 0; uint32_t last_processed_edge_us_ = 0;

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