1
0
mirror of https://github.com/esphome/esphome.git synced 2025-11-01 15:41:52 +00:00

Compare commits

...

179 Commits

Author SHA1 Message Date
Jesse Hills
6124531479 Merge pull request #3944 from esphome/bump-2022.10.1
2022.10.1
2022-10-26 12:49:08 +13:00
Jesse Hills
b8549d323c Bump version to 2022.10.1 2022-10-26 12:08:19 +13:00
Franck Nijhof
01adece673 Add wind_speed sensor device class (#3941) 2022-10-26 12:08:19 +13:00
NP v/d Spek
0220934e4c Fixed touch release issue using the interrupt pin (#3925)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-10-26 12:08:19 +13:00
Franck Nijhof
ca09693efa Add water & precipitation_intensity sensor device classes (#3940) 2022-10-26 12:08:19 +13:00
Jesse Hills
e96d7483b3 Update bluetooth proxy limit as soon as connection requested (#3935) 2022-10-26 12:08:18 +13:00
Jesse Hills
ccbfa20bb9 Merge pull request #3918 from esphome/bump-2022.10.0
2022.10.0
2022-10-19 11:13:16 +13:00
Jesse Hills
58cd754e07 Fix bad merge 2022-10-19 10:50:32 +13:00
Jesse Hills
d1263e583b Bump version to 2022.10.0 2022-10-19 10:30:22 +13:00
Jesse Hills
67c911c37f Merge branch 'beta' into bump-2022.10.0 2022-10-19 10:30:21 +13:00
Jesse Hills
288e3c3e3e Merge pull request #3909 from esphome/bump-2022.10.0b2
2022.10.0b2
2022-10-15 09:12:58 +13:00
Jesse Hills
a84378c6c2 Bump version to 2022.10.0b2 2022-10-15 08:37:11 +13:00
Jesse Hills
b6073408f4 Remove address type map from bluetooth proxy (#3905) 2022-10-15 08:37:11 +13:00
Frédéric Jouault
b2d91ac5de Send true and not RSSI in ble_presence (#3904) 2022-10-15 08:37:11 +13:00
Sergey Dudanov
8bf34e09f4 Modbus QWORD fix (#3856) 2022-10-15 08:37:11 +13:00
Guillermo Ruffino
be914f2c15 fix never calling preset change trigger (#3864)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2022-10-15 08:37:11 +13:00
Jesse Hills
a6c999dea0 Merge pull request #3900 from esphome/bump-2022.10.0b1
2022.10.0b1
2022-10-13 09:50:04 +13:00
Jesse Hills
f422fabab4 Bump version to 2022.10.0b1 2022-10-13 09:18:46 +13:00
Jesse Hills
01b130ec59 Merge branch 'dev' into bump-2022.10.0b1 2022-10-13 09:18:46 +13:00
definitio
48a1797e72 Do not require CS pin for ST7789V (#3888)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-10-13 08:26:28 +13:00
Jesse Hills
b34d24735a Send GATT error events to HA (#3884) 2022-10-12 22:22:07 +13:00
Chris Feenstra
fe38b36c26 Add support for ZHLT01 heatpump IR protocol (#3655)
Co-authored-by: Chris Feenstra <chris@cfeenstra.nl>
2022-10-12 15:29:57 +13:00
cstaahl
03fca8d91e Fix pulse_meter filter logic (#3321) 2022-10-12 15:26:35 +13:00
RoboMagus
9f9980e338 Add ble RSSI sensor for connected devices (#3860) 2022-10-12 15:23:56 +13:00
Quentin Smith
19900b004b Fix type annotation on extract_registry_entry_config (#3623)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-10-12 15:15:03 +13:00
Gustavo Ambrozio
a8ff0a8913 Exposing coordinates from touchscreen binary sensor (#3891) 2022-10-11 12:22:13 +13:00
RoboMagus
45861456f1 Fix default unit for ble_rssi sensor (#3874) 2022-10-11 12:03:54 +13:00
RoboMagus
44b335e7e3 Correctly set ble_write UUIDs based on their lengths. (#3885) 2022-10-11 12:02:53 +13:00
Jesse Hills
de23bbace2 Update webserver index file (#3896) 2022-10-11 12:01:41 +13:00
RoboMagus
edff9ae322 Proxy friendly host url resolution for use_address with path. (#3653) 2022-10-11 12:01:31 +13:00
NP v/d Spek
3c2766448d Refactor xpt2046 to be a touchscreen platform (#3793) 2022-10-11 10:10:22 +13:00
Jesse Hills
786c8b6cfe Add new sensor device classes (#3895) 2022-10-11 09:54:58 +13:00
Jesse Hills
e8de6a3a67 Merge pull request #3883 from esphome/bump-2022.9.4
2022.9.4
2022-10-07 16:56:22 +13:00
Jesse Hills
3c320c4c83 Bump version to 2022.9.4 2022-10-07 16:34:39 +13:00
Jesse Hills
3b83f967e4 Dont add wifi block to yaml if discovered device uses ethernet (#3882) 2022-10-07 16:34:39 +13:00
Jesse Hills
5e96b8ef7c Bump esphome-dashboard to 20221007.0 (#3881) 2022-10-07 16:34:39 +13:00
Jesse Hills
5df0e82c37 Add network type to mdns service message (#3880) 2022-10-07 16:34:38 +13:00
Jesse Hills
fd57b21aff Dont add wifi block to yaml if discovered device uses ethernet (#3882) 2022-10-07 15:35:48 +13:00
Jesse Hills
6087183a0c Bump esphome-dashboard to 20221007.0 (#3881) 2022-10-07 15:20:13 +13:00
Jesse Hills
01b7c4200e Add network type to mdns service message (#3880) 2022-10-07 14:42:28 +13:00
dependabot[bot]
7171286c3c Bump pylint from 2.15.2 to 2.15.3 (#3870)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-06 14:57:07 +13:00
Jesse Hills
2d58239b74 Merge pull request #3876 from esphome/bump-2022.9.3
2022.9.3
2022-10-06 09:57:39 +13:00
Jesse Hills
6b52f62531 Bump version to 2022.9.3 2022-10-06 09:22:01 +13:00
Jesse Hills
1001d9c04e Bluetooth Proxy active connections (#3817) 2022-10-06 09:22:00 +13:00
Jesse Hills
d220d41182 Bump python min to 3.9 (#3871) 2022-10-05 20:09:27 +13:00
Jesse Hills
c3a8972550 Add min_version to esphome config (#3866) 2022-10-05 16:30:56 +13:00
dependabot[bot]
263b603188 Bump pyupgrade from 2.37.3 to 3.0.0 (#3867)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-10-05 16:29:49 +13:00
Javier Peletier
584b722e7e Fix time/automation (cron) wdt crash when time jumps ahead too much (#3844) 2022-10-05 14:52:45 +13:00
Javier Peletier
05edfd0e82 Add cg.with_local_variable (#3577) 2022-10-05 11:50:03 +13:00
Jesse Hills
16249c02a5 Bump CI to python 3.9 (#3869) 2022-10-05 11:21:13 +13:00
Guillermo Ruffino
e8ff36d1f3 fix dump preset string type (#3863) 2022-10-04 10:50:33 +13:00
Jesse Hills
ed443c6153 Bluetooth Proxy active connections (#3817) 2022-10-04 10:45:06 +13:00
Keith Burzinski
f4a84765cd Add display GPIO setup instruction for Aliexpress display (#3851) 2022-09-30 19:10:53 +13:00
J. Nick Koston
106de3530d Add support for parsing the short local name in the tracker (#3854) 2022-09-30 09:15:30 +13:00
Jesse Hills
119c3f6f46 Merge pull request #3853 from esphome/bump-2022.9.2
2022.9.2
2022-09-29 16:18:21 +13:00
Jesse Hills
d2c1c7507c Bump version to 2022.9.2 2022-09-29 16:00:55 +13:00
Guillermo Ruffino
efdb3d1f40 Bump dashboard to 20220925.0 (#3846) 2022-09-29 16:00:55 +13:00
Michael Davidson
8095db6715 Thermostat remove deprecated config (#3643)
* Raise errors for all the now deprecated options

* Fix CONF_DEFAULT_PRESET detection

* Stop attempting to set the non-existent normal_config

* Add support for default presets

* Fix correct detection of Two Point temperature mode

* Fix lint issues

* Fix tests

* Generate correct yaml for equivalent configurations

* Remove debug code

* Only set default preset if the thermostat does not have state to restore

* Add restore_default_preset_on_boot option
If set to True then the default_preset will be applied on every boot. If False (Default) state will be restored from memory as per prior versions

* Apply lint suggestions

* Switch from restore_default_preset_on_boot to an enum for startup_behavior
This gives better self-documentation as well as the option for extending to other options down the track

* Lint fixes

* Rename startup_behavior to on_boot_restore_from
This removes any issues with different English locales

* Fix comparable_preset yaml output alignment

* Add dump of on_boot_restore_from setting

Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2022-09-25 21:59:04 -05:00
Guillermo Ruffino
ce2e161b08 Bump dashboard to 20220925.0 (#3846) 2022-09-25 18:16:30 -03:00
Jesse Hills
9dbc32b85f Merge pull request #3839 from esphome/bump-2022.9.1
2022.9.1
2022-09-22 07:51:52 +12:00
Jesse Hills
66226abb48 Bump version to 2022.9.1 2022-09-22 07:40:15 +12:00
Jesse Hills
34bef2f2ca Revert "fix spi timing issues" (#3838) 2022-09-22 07:40:15 +12:00
Jesse Hills
6ef93452f5 Revert "fix spi timing issues" (#3838) 2022-09-22 07:38:31 +12:00
Jesse Hills
fdd4ca6837 Merge pull request #3837 from esphome/bump-2022.9.0
2022.9.0
2022-09-21 15:59:34 +12:00
Jesse Hills
9655362f23 Bump version to 2022.9.0 2022-09-21 15:34:02 +12:00
Jesse Hills
130c9fad22 Merge branch 'beta' into bump-2022.9.0 2022-09-21 15:34:02 +12:00
Jesse Hills
3de0b601bf Merge pull request #3835 from esphome/bump-2022.9.0b5
2022.9.0b5
2022-09-21 15:23:41 +12:00
Jesse Hills
91560ae4e9 Bump version to 2022.9.0b5 2022-09-21 12:38:55 +12:00
Guillermo Ruffino
fd6135aebb Bump dashboard to 20220920.1 (#3834) 2022-09-21 12:38:55 +12:00
Guillermo Ruffino
68ea59f3ae Bump dashboard to 20220920.1 (#3834) 2022-09-21 12:32:53 +12:00
Jesse Hills
e5fe5d1249 Merge pull request #3833 from esphome/bump-2022.9.0b4
2022.9.0b4
2022-09-21 09:09:20 +12:00
Jesse Hills
9a69769a7e Dont fail fast on CI for docker (#3832) 2022-09-21 07:54:35 +12:00
Jesse Hills
63b42f3608 Bump version to 2022.9.0b4 2022-09-21 07:36:39 +12:00
Paulus Schoutsen
d56107e97f Bump dashboard to 20220920.0 (#3831) 2022-09-21 07:36:39 +12:00
Guillermo Ruffino
33f296e05b Fix-esphome-validation-line-number (#3815) 2022-09-21 07:36:39 +12:00
Paulus Schoutsen
3572c62315 Bump dashboard to 20220920.0 (#3831) 2022-09-21 07:35:46 +12:00
Jesse Hills
97e067a277 Merge pull request #3829 from esphome/bump-2022.9.0b3
2022.9.0b3
2022-09-20 19:28:27 +12:00
Guillermo Ruffino
1444cddda9 Fix-esphome-validation-line-number (#3815) 2022-09-20 17:23:55 +12:00
Jesse Hills
5f56cf3128 Bump version to 2022.9.0b3 2022-09-20 17:14:51 +12:00
Paulus Schoutsen
5c4e83ebdc Bump dashboard to 20220919.1 (#3828) 2022-09-20 17:14:51 +12:00
Keith Burzinski
91f1c25fcc Make sprinkler reset_resume() method public (#3824) 2022-09-20 17:14:51 +12:00
h3ndrik
47a7a239ae [BME280] raise standby time (#3804) 2022-09-20 17:14:51 +12:00
Samuel Sieb
fb9984e21f split pronto codes if they are too long (#3812)
Co-authored-by: Samuel Sieb <samuel@sieb.net>
2022-09-20 17:14:51 +12:00
Paulus Schoutsen
b2db524366 Bump dashboard to 20220919.1 (#3828) 2022-09-20 17:13:59 +12:00
Keith Burzinski
ab8674a5c7 Make sprinkler reset_resume() method public (#3824) 2022-09-20 13:02:55 +12:00
Geek_cat
d1c85fc3fa Allow CORS for web_server (#3819) 2022-09-20 13:01:00 +12:00
h3ndrik
55ad45e3ee [BME280] raise standby time (#3804) 2022-09-19 07:25:59 +12:00
pawel3410
f6e5a8cb2a Fix mcp23s17 addressing beyond 3 (#3797) 2022-09-15 18:19:41 -07:00
Samuel Sieb
7a91ca9809 split pronto codes if they are too long (#3812)
Co-authored-by: Samuel Sieb <samuel@sieb.net>
2022-09-16 07:27:50 +12:00
Jesse Hills
71dd04b09e Merge pull request #3809 from esphome/bump-2022.9.0b2
2022.9.0b2
2022-09-15 13:35:54 +12:00
Jesse Hills
7deabbb512 Bump version to 2022.9.0b2 2022-09-15 13:02:40 +12:00
Azimath
625a575e49 Remove floating point calculation from ac_dimmer ISR (#3770) 2022-09-15 13:02:39 +12:00
Jesse Hills
df4d0da221 Initialize all child sensors to nullptr (#3808) 2022-09-15 13:02:39 +12:00
RoboMagus
6b23b7cad7 Unify 'nullptr' initalization of class members; (#3805) 2022-09-15 13:02:39 +12:00
Guillermo Ruffino
cea7deab91 Sim800l add calls, multiline sms and ussd (#3630)
Co-authored-by: Matus Ivanecky <matus.ivanecky@gmail.com>
Co-authored-by: Matus Ivanecky <maty535@users.noreply.github.com>
2022-09-15 13:02:39 +12:00
RoboMagus
c61abf6aca null initialize total sensor for pulse counter (#3803)
* null initialize total sensor.

* pedantic styling fix

Co-authored-by: Guillermo Ruffino <glm.net@gmail.com>

Co-authored-by: Guillermo Ruffino <glm.net@gmail.com>
2022-09-15 13:02:39 +12:00
Azimath
917bbc669c Remove floating point calculation from ac_dimmer ISR (#3770) 2022-09-15 11:54:33 +12:00
Jesse Hills
0ac4c055de Initialize all child sensors to nullptr (#3808) 2022-09-15 11:53:22 +12:00
RoboMagus
78b55d86e9 Unify 'nullptr' initalization of class members; (#3805) 2022-09-15 11:53:02 +12:00
Guillermo Ruffino
aaf50fc2e6 Sim800l add calls, multiline sms and ussd (#3630)
Co-authored-by: Matus Ivanecky <matus.ivanecky@gmail.com>
Co-authored-by: Matus Ivanecky <maty535@users.noreply.github.com>
2022-09-15 07:43:03 +12:00
RoboMagus
6a8f4e92df null initialize total sensor for pulse counter (#3803)
* null initialize total sensor.

* pedantic styling fix

Co-authored-by: Guillermo Ruffino <glm.net@gmail.com>

Co-authored-by: Guillermo Ruffino <glm.net@gmail.com>
2022-09-14 10:01:28 -03:00
Jesse Hills
6d267fda01 Merge pull request #3802 from esphome/bump-2022.9.0b1
2022.9.0b1
2022-09-14 17:27:01 +12:00
Jesse Hills
88943103a2 Bump version to 2022.10.0-dev 2022-09-14 17:02:22 +12:00
Jesse Hills
e557dc7208 Bump version to 2022.9.0b1 2022-09-14 17:02:22 +12:00
Jesse Hills
3558806b0e Merge branch 'dev' into bump-2022.9.0b1 2022-09-14 17:02:21 +12:00
Ignacio Hernandez-Ros
f1e8cc2cf0 fix spi timing issues (#3763) 2022-09-14 16:53:51 +12:00
Pascal Vizeli
6236db1a27 Add uFire ISE sensor (#3789)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-09-14 16:51:20 +12:00
Jesse Hills
f4b0917239 Allow ble tracker to subscribe to ota start and stop the scanning (#3800) 2022-09-14 16:49:20 +12:00
Aleš Komárek
a5e3cd1a42 Add Prometheus Service Discovery for online devices (#3788) 2022-09-14 15:22:59 +12:00
Jesse Hills
b3cca5dcb6 Add stop action for ble scanning (#3799) 2022-09-14 14:57:45 +12:00
Jonathan V
49465223a4 esp32_ble_tracker continuous and one shot scanning modes (#3649)
Co-authored-by: Jonathan Valdez <@jonofmac>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-09-14 11:10:12 +12:00
dependabot[bot]
15f0e54cbf Bump frenck/action-yamllint from 1.2.0 to 1.3.0 (#3798)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-14 09:57:43 +12:00
Pascal Vizeli
ed8f343aad Remove status_set_error from ufire_ec (#3792) 2022-09-13 11:17:33 +12:00
Jordan W. Cobb
cbd8d70431 Add support for TM1638 Led and Key component (#3340) 2022-09-12 08:30:15 -07:00
dependabot[bot]
9ff187c3f8 Bump zeroconf from 0.39.0 to 0.39.1 (#3782)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-12 10:32:33 +12:00
Carlos Garcia Saura
be473b97c4 [MPU6050] Support devices with WHOAMI 0x98 (#3784) 2022-09-12 10:32:07 +12:00
anatoly-savchenkov
9a5f865eea Add Factory Reset button and switch (#3724)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-09-12 10:23:46 +12:00
dependabot[bot]
790280ace9 Bump pylint from 2.15.0 to 2.15.2 (#3785)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-12 10:16:18 +12:00
David Buezas
8ba207fc7f Add support for BL0942 voltage, current, energy and power Sensor (#3777) 2022-09-12 09:36:09 +12:00
Keilin Bickar
d66b2a1778 Add support for MPL3115A2 Pressure/Altitude and Temperature Sensor (#3371)
* Add support for mpl3115a2

* Add codeowner

* Linter/test updates

* Minor changes

* Made pressure/altitude exclusive

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-09-09 00:18:02 -05:00
Pascal Vizeli
e3f2562047 u-fire EC sensor (#3774)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-09-08 22:30:07 +12:00
Philippe FOUQUET
f77118a90c Add support to tm1621 display (#3737)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-09-08 11:40:27 +12:00
Jesse Hills
041eb8f6cc Merge pull request #3783 from esphome/bump-2022.8.3
2022.8.3
2022-09-06 18:54:28 +12:00
Jesse Hills
733a84df75 Bump version to 2022.8.3 2022-09-06 16:50:17 +12:00
Jesse Hills
acd0b50b40 Fix HA addon auth using HA credentials (#3758) 2022-09-06 16:50:11 +12:00
Jesse Hills
635851807a Fix HA addon auth using HA credentials (#3758) 2022-09-06 16:41:23 +12:00
Jesse Hills
89fd367297 YAML linting (#3779) 2022-09-06 15:48:01 +12:00
Jesse Hills
60e46d485e Merge pull request #3781 from esphome/bump-2022.8.2
2022.8.2
2022-09-06 15:38:46 +12:00
Jesse Hills
5bf0c92318 Bump version to 2022.8.2 2022-09-06 13:12:00 +12:00
Avirsaam
39d493c278 Update modbus_controller.cpp (#3768) 2022-09-06 13:12:00 +12:00
anatoly-savchenkov
d2ce62aa13 Ignore NaN states in the integration component (#3767) 2022-09-06 13:12:00 +12:00
Jesse Hills
c8eb30ef27 Initial bluetooth_proxy support (#3736) 2022-09-06 13:11:54 +12:00
Jesse Hills
c317422ed7 Move crc16 to helpers (#3780) 2022-09-06 12:57:21 +12:00
Jesse Hills
614eb81ad7 Remove unneeded line (spi component adds it) (#3778) 2022-09-06 11:54:32 +12:00
Avirsaam
219c5953f1 Update modbus_controller.cpp (#3768) 2022-09-05 12:50:27 +12:00
dependabot[bot]
5d712c73ea Bump pytest from 7.1.1 to 7.1.3 (#3766)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-04 20:24:26 +12:00
Andrey Yantsen
7097b7677e Add the same docker tags as used in HA (#3752) 2022-09-04 20:23:54 +12:00
anatoly-savchenkov
7a4cf13e0c Ignore NaN states in the integration component (#3767) 2022-09-04 19:21:17 +12:00
NP v/d Spek
4788a6182e I found some issue in the ili9341 driver (#3756) 2022-09-02 15:46:51 +12:00
dependabot[bot]
e3dad7c632 Bump black from 22.6.0 to 22.8.0 (#3760)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-02 13:42:04 +12:00
RoboMagus
1b4156646e Esp32 pulsecounter optional pcnt (#3691)
Co-authored-by: RoboMagus <->
2022-09-02 13:22:34 +12:00
dependabot[bot]
2650441013 Bump pylint from 2.14.5 to 2.15.0 (#3746)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-08-31 17:01:36 +12:00
dependabot[bot]
71697df2b6 Bump aioesphomeapi from 10.11.0 to 10.13.0 (#3740)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-31 16:53:36 +12:00
Fabian Berthold
acd55b9601 Add sum type to binary_sensor_map (#3541) 2022-08-31 16:52:16 +12:00
yuhei mukoyama
0907de8662 Add AEHA IR Protocol (#3726) 2022-08-31 16:43:33 +12:00
Wouter van der Wal
15eb9605a8 ibeacon support for the ble_rssi sensor (#3745) 2022-08-31 16:42:48 +12:00
Joe
6d5cb866db Add BedJet Fan child component (#3735) 2022-08-31 13:53:18 +12:00
Keith Burzinski
768490089e Add IP101 support to Ethernet component (#3751) 2022-08-31 13:45:30 +12:00
Jesse Hills
4d66fab360 Tidy up switch schemas (#3754) 2022-08-31 13:43:46 +12:00
Jesse Hills
bd6bc283b6 Remove unnecessary schema extension on template button (#3753) 2022-08-31 11:45:06 +12:00
anatoly-savchenkov
3120a0ba83 Captive portal show nearby WiFi with no ssid configured (#3748) 2022-08-31 10:19:43 +12:00
andyboeh
b2199d5464 mcp23017: read output latch registers during setup (#3744) 2022-08-31 10:07:40 +12:00
Jan Grewe
84bac8356a Add Prometheus metrics relabeling (#3734)
Co-authored-by: Guillermo Ruffino <glm.net@gmail.com>
2022-08-30 10:55:55 +12:00
Frank Riley
2819166539 Support high update rates and fix several bugs in the cse7766 component. (#3675) 2022-08-25 16:12:45 +12:00
MrEditor97
8fa18ca7c7 Support for MCP9600 Thermocouple Amplifier (#3700)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-08-25 15:49:31 +12:00
NP v/d Spek
63290a265c add color compare operator's (#3730) 2022-08-25 13:41:52 +12:00
Jesse Hills
b854e17995 Initial bluetooth_proxy support (#3736) 2022-08-25 07:13:44 +12:00
Samuel Sieb
5dec9d88f6 add log messages for bad pronto codes (#3738)
Co-authored-by: Samuel Sieb <samuel@sieb.net>
2022-08-23 13:26:36 +12:00
Mike Ryan
3d0a85ee78 Add bitmap font support (#3573)
Co-authored-by: Otto Winter <otto@otto-winter.com>
2022-08-19 10:49:52 +12:00
Keith Burzinski
ac3cdf487f Fix SPI HW selection for ESP32 variants (#3728) 2022-08-19 07:55:10 +12:00
Sebastian Lövdahl
a47e92f2bc Let favicon be cached (#3729) 2022-08-19 07:54:10 +12:00
Keith Burzinski
fc15ddfa91 Add dps310 sensor support (#3704)
Co-authored-by: Greg Arnold <greg@arnoldassociates.com>
2022-08-19 07:49:35 +12:00
Jesse Hills
d546ef941f Add final validate for i2c with mix/max frequency (#3727)
Co-authored-by: MrEditor97 <mr.samuel.hughes@gmail.com>
2022-08-18 11:04:50 +12:00
Stijn Tintel
b4bbe3d7b5 wifi: support 802.11k and 802.11v (#3600) 2022-08-17 21:10:43 +12:00
functionpointer
5561d4eaeb hydreon_rgxx: Support lens_bad, em_sat and temperature (#3642) 2022-08-17 16:07:33 +12:00
Samuel Sieb
0f6dab394a fix grow password setting (#3722)
Co-authored-by: Samuel Sieb <samuel@sieb.net>
2022-08-16 20:41:46 -07:00
Samuel Sieb
43539f2dbf Send CR also for commands for sim800l (#3719) 2022-08-17 10:35:44 +12:00
Ignacio Hernandez-Ros
df6830110d Improve OTA error messages adding return codes (#3698) 2022-08-16 21:51:05 +12:00
rbaron
1a524a5a50 Makes ble_client.ble_write's action value templatable (#3715) 2022-08-16 14:40:58 +12:00
rbaron
1a2288cccf Fixes BLE remote address type when connecting (#3702) 2022-08-15 15:09:33 +12:00
buxtronix
8df27d4c3f Only trigger ble_client on_connect after discovering services (#3710) 2022-08-15 15:06:05 +12:00
puuu
0688deca6b Add support for pvvx mithermometer display via ble client (#3333)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-08-15 10:56:25 +12:00
anatoly-savchenkov
01a4443b6c Webui small fixes (#3713) 2022-08-15 10:28:29 +12:00
Samuel Sieb
e008b054cb support modifying the apds9960 settings (#3708)
Co-authored-by: Samuel Sieb <samuel@sieb.net>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-08-15 10:16:56 +12:00
NP v/d Spek
1cf213dad8 add gradient color V2.0 (#3709) 2022-08-15 08:21:08 +12:00
Peter Galantha
aeb81e547b Add state_class total (#3608)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-08-11 14:00:09 +12:00
Jesse Hills
479f7703a2 Add vector include (#3707) 2022-08-11 13:57:42 +12:00
anatoly-savchenkov
c7dc396b6d Improve Web view for Climate components (#3706) 2022-08-11 13:32:55 +12:00
Jesse Hills
80e3030811 Bump version to 2022.9.0-dev 2022-08-10 15:26:02 +12:00
312 changed files with 11717 additions and 3045 deletions

View File

@@ -25,10 +25,9 @@ indent_size = 2
[*.{yaml,yml}]
indent_style = space
indent_size = 2
quote_type = single
quote_type = double
# JSON
[*.json]
indent_style = space
indent_size = 2

1
.github/FUNDING.yml vendored
View File

@@ -1,3 +1,4 @@
---
# These are supported funding model platforms
custom: https://www.nabucasa.com

View File

@@ -1,3 +1,4 @@
---
blank_issues_enabled: false
contact_links:
- name: Issue Tracker
@@ -5,7 +6,10 @@ contact_links:
about: Please create bug reports in the dedicated issue tracker.
- name: Feature Request Tracker
url: https://github.com/esphome/feature-requests
about: Please create feature requests in the dedicated feature request tracker.
about: |
Please create feature requests in the dedicated feature request tracker.
- name: Frequently Asked Question
url: https://esphome.io/guides/faq.html
about: Please view the FAQ for common questions and what to include in a bug report.
about: |
Please view the FAQ for common questions and what
to include in a bug report.

View File

@@ -1,13 +1,14 @@
---
version: 2
updates:
- package-ecosystem: "pip"
- package-ecosystem: pip
directory: "/"
schedule:
interval: "daily"
interval: daily
ignore:
# Hypotehsis is only used for testing and is updated quite often
- dependency-name: hypothesis
- package-ecosystem: "github-actions"
- package-ecosystem: github-actions
directory: "/"
schedule:
interval: daily

View File

@@ -1,21 +1,23 @@
---
name: CI for docker images
# Only run when docker paths change
# yamllint disable-line rule:truthy
on:
push:
branches: [dev, beta, release]
paths:
- 'docker/**'
- '.github/workflows/**'
- 'requirements*.txt'
- 'platformio.ini'
- "docker/**"
- ".github/workflows/**"
- "requirements*.txt"
- "platformio.ini"
pull_request:
paths:
- 'docker/**'
- '.github/workflows/**'
- 'requirements*.txt'
- 'platformio.ini'
- "docker/**"
- ".github/workflows/**"
- "requirements*.txt"
- "platformio.ini"
permissions:
contents: read
@@ -26,28 +28,29 @@ jobs:
name: Build docker containers
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
arch: [amd64, armv7, aarch64]
build_type: ["ha-addon", "docker", "lint"]
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.9"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set TAG
run: |
echo "TAG=check" >> $GITHUB_ENV
- name: Set TAG
run: |
echo "TAG=check" >> $GITHUB_ENV
- name: Run build
run: |
docker/build.py \
--tag "${TAG}" \
--arch "${{ matrix.arch }}" \
--build-type "${{ matrix.build_type }}" \
build
- name: Run build
run: |
docker/build.py \
--tag "${TAG}" \
--arch "${{ matrix.arch }}" \
--build-type "${{ matrix.build_type }}" \
build

View File

@@ -1,5 +1,7 @@
---
name: CI
# yamllint disable-line rule:truthy
on:
push:
branches: [dev, beta, release]
@@ -10,6 +12,7 @@ permissions:
contents: read
concurrency:
# yamllint disable-line rule:line-length
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
@@ -73,6 +76,8 @@ jobs:
name: Run script/clang-tidy for ESP32 IDF
options: --environment esp32-idf-tidy --grep USE_ESP_IDF
pio_cache_key: tidyesp32-idf
- id: yamllint
name: Run yamllint
steps:
- uses: actions/checkout@v3
@@ -80,17 +85,19 @@ jobs:
uses: actions/setup-python@v4
id: python
with:
python-version: '3.8'
python-version: "3.9"
- name: Cache virtualenv
uses: actions/cache@v3
with:
path: .venv
# yamllint disable-line rule:line-length
key: venv-${{ steps.python.outputs.python-version }}-${{ hashFiles('requirements*.txt') }}
restore-keys: |
venv-${{ steps.python.outputs.python-version }}-
- name: Set up virtualenv
# yamllint disable rule:line-length
run: |
python -m venv .venv
source .venv/bin/activate
@@ -99,12 +106,14 @@ jobs:
pip install -e .
echo "$GITHUB_WORKSPACE/.venv/bin" >> $GITHUB_PATH
echo "VIRTUAL_ENV=$GITHUB_WORKSPACE/.venv" >> $GITHUB_ENV
# yamllint enable rule:line-length
# Use per check platformio cache because checks use different parts
- name: Cache platformio
uses: actions/cache@v3
with:
path: ~/.platformio
# yamllint disable-line rule:line-length
key: platformio-${{ matrix.pio_cache_key }}-${{ hashFiles('platformio.ini') }}
if: matrix.id == 'test' || matrix.id == 'clang-tidy'
@@ -145,8 +154,9 @@ jobs:
pytest -vv --tb=native tests
if: matrix.id == 'pytest'
# Also run git-diff-index so that the step is marked as failed on formatting errors,
# since clang-format doesn't do anything but change files if -i is passed.
# Also run git-diff-index so that the step is marked as failed on
# formatting errors, since clang-format doesn't do anything but
# change files if -i is passed.
- name: Run clang-format
run: |
script/clang-format -i
@@ -161,6 +171,11 @@ jobs:
# Also cache libdeps, store them in a ~/.platformio subfolder
PLATFORMIO_LIBDEPS_DIR: ~/.platformio/libdeps
- name: Run yamllint
if: matrix.id == 'yamllint'
uses: frenck/action-yamllint@v1.3.0
- name: Suggested changes
run: script/ci-suggest-changes
# yamllint disable-line rule:line-length
if: always() && (matrix.id == 'clang-tidy' || matrix.id == 'clang-format' || matrix.id == 'lint-python')

View File

@@ -1,8 +1,10 @@
---
name: Lock
# yamllint disable-line rule:truthy
on:
schedule:
- cron: '30 0 * * *'
- cron: "30 0 * * *"
workflow_dispatch:
permissions:

View File

@@ -1,5 +1,7 @@
---
name: Publish Release
# yamllint disable-line rule:truthy
on:
workflow_dispatch:
release:
@@ -20,6 +22,7 @@ jobs:
- uses: actions/checkout@v3
- name: Get tag
id: tag
# yamllint disable rule:line-length
run: |
if [[ "$GITHUB_EVENT_NAME" = "release" ]]; then
TAG="${GITHUB_REF#refs/tags/}"
@@ -29,6 +32,7 @@ jobs:
TAG="${TAG}${today}"
fi
echo "::set-output name=tag::${TAG}"
# yamllint enable rule:line-length
deploy-pypi:
name: Build and publish to PyPi
@@ -39,7 +43,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.x'
python-version: "3.x"
- name: Set up python environment
run: |
script/setup
@@ -65,37 +69,37 @@ jobs:
arch: [amd64, armv7, aarch64]
build_type: ["ha-addon", "docker", "lint"]
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.9'
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.9"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Log in to docker hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Log in to the GitHub container registry
uses: docker/login-action@v2
with:
- name: Log in to docker hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Log in to the GitHub container registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
run: |
docker/build.py \
--tag "${{ needs.init.outputs.tag }}" \
--arch "${{ matrix.arch }}" \
--build-type "${{ matrix.build_type }}" \
build \
--push
- name: Build and push
run: |
docker/build.py \
--tag "${{ needs.init.outputs.tag }}" \
--arch "${{ matrix.arch }}" \
--build-type "${{ matrix.build_type }}" \
build \
--push
deploy-docker-manifest:
if: github.repository == 'esphome/esphome'
@@ -108,34 +112,34 @@ jobs:
matrix:
build_type: ["ha-addon", "docker", "lint"]
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: Enable experimental manifest support
run: |
mkdir -p ~/.docker
echo "{\"experimental\": \"enabled\"}" > ~/.docker/config.json
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.9"
- name: Enable experimental manifest support
run: |
mkdir -p ~/.docker
echo "{\"experimental\": \"enabled\"}" > ~/.docker/config.json
- name: Log in to docker hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Log in to the GitHub container registry
uses: docker/login-action@v2
with:
- name: Log in to docker hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Log in to the GitHub container registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Run manifest
run: |
docker/build.py \
--tag "${{ needs.init.outputs.tag }}" \
--build-type "${{ matrix.build_type }}" \
manifest
- name: Run manifest
run: |
docker/build.py \
--tag "${{ needs.init.outputs.tag }}" \
--build-type "${{ matrix.build_type }}" \
manifest
deploy-ha-addon-repo:
if: github.repository == 'esphome/esphome' && github.event_name == 'release'
@@ -144,6 +148,7 @@ jobs:
steps:
- env:
TOKEN: ${{ secrets.DEPLOY_HA_ADDON_REPO_TOKEN }}
# yamllint disable rule:line-length
run: |
TAG="${GITHUB_REF#refs/tags/}"
curl \
@@ -152,3 +157,4 @@ jobs:
-H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/esphome/home-assistant-addon/actions/workflows/bump-version.yml/dispatches \
-d "{\"ref\":\"main\",\"inputs\":{\"version\":\"$TAG\"}}"
# yamllint enable rule:line-length

View File

@@ -1,8 +1,10 @@
---
name: Stale
# yamllint disable-line rule:truthy
on:
schedule:
- cron: '30 0 * * *'
- cron: "30 0 * * *"
workflow_dispatch:
permissions:
@@ -31,7 +33,8 @@ jobs:
and will be closed if no further activity occurs within 7 days.
Thank you for your contributions.
# Use stale to automatically close issues with a reference to the issue tracker
# Use stale to automatically close issues with a
# reference to the issue tracker
close-issues:
runs-on: ubuntu-latest
steps:

View File

@@ -1,6 +1,8 @@
---
ports:
- port: 6052
onOpen: open-preview
- port: 6052
onOpen: open-preview
tasks:
- before: pyenv local $(pyenv version | grep '^3\.' | cut -d ' ' -f 1) && script/setup
command: python -m esphome dashboard config
# yamllint disable-line rule:line-length
- before: pyenv local $(pyenv version | grep '^3\.' | cut -d ' ' -f 1) && script/setup
command: python -m esphome dashboard config

View File

@@ -1,14 +1,15 @@
---
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/ambv/black
rev: 22.6.0
hooks:
- id: black
args:
- --safe
- --quiet
files: ^((esphome|script|tests)/.+)?[^/]+\.py$
- id: black
args:
- --safe
- --quiet
files: ^((esphome|script|tests)/.+)?[^/]+\.py$
- repo: https://gitlab.com/pycqa/flake8
rev: 4.0.1
hooks:
@@ -26,7 +27,7 @@ repos:
- --branch=release
- --branch=beta
- repo: https://github.com/asottile/pyupgrade
rev: v2.37.3
rev: v3.0.0
hooks:
- id: pyupgrade
args: [--py38-plus]
args: [--py39-plus]

3
.yamllint Normal file
View File

@@ -0,0 +1,3 @@
---
ignore: |
venv/

View File

@@ -29,11 +29,15 @@ esphome/components/b_parasite/* @rbaron
esphome/components/ballu/* @bazuchan
esphome/components/bang_bang/* @OttoWinter
esphome/components/bedjet/* @jhansche
esphome/components/bedjet/climate/* @jhansche
esphome/components/bedjet/fan/* @jhansche
esphome/components/bh1750/* @OttoWinter
esphome/components/binary_sensor/* @esphome/core
esphome/components/bl0939/* @ziceva
esphome/components/bl0940/* @tobias-
esphome/components/bl0942/* @dbuezas
esphome/components/ble_client/* @buxtronix
esphome/components/bluetooth_proxy/* @jesserockz
esphome/components/bme680_bsec/* @trvrnrth
esphome/components/bmp3xx/* @martgras
esphome/components/button/* @esphome/core
@@ -59,12 +63,14 @@ esphome/components/debug/* @OttoWinter
esphome/components/delonghi/* @grob6000
esphome/components/dfplayer/* @glmnet
esphome/components/dht/* @OttoWinter
esphome/components/dps310/* @kbx81
esphome/components/ds1307/* @badbadc0ffee
esphome/components/dsmr/* @glmnet @zuidwijk
esphome/components/ektf2232/* @jesserockz
esphome/components/ens210/* @itn3rd77
esphome/components/esp32/* @esphome/core
esphome/components/esp32_ble/* @jesserockz
esphome/components/esp32_ble_client/* @jesserockz
esphome/components/esp32_ble_server/* @jesserockz
esphome/components/esp32_camera_web_server/* @ayufan
esphome/components/esp32_can/* @Sympatron
@@ -72,6 +78,7 @@ esphome/components/esp32_improv/* @jesserockz
esphome/components/esp8266/* @esphome/core
esphome/components/exposure_notifications/* @OttoWinter
esphome/components/ezo/* @ssieb
esphome/components/factory_reset/* @anatoly-savchenkov
esphome/components/fastled_base/* @OttoWinter
esphome/components/feedback/* @ianchi
esphome/components/fingerprint_grow/* @OnFreund @loongyh
@@ -120,6 +127,7 @@ esphome/components/mcp2515/* @danielschramm @mvturnho
esphome/components/mcp3204/* @rsumner
esphome/components/mcp4728/* @berfenger
esphome/components/mcp47a1/* @jesserockz
esphome/components/mcp9600/* @MrEditor97
esphome/components/mcp9808/* @k7hpn
esphome/components/md5/* @esphome/core
esphome/components/mdns/* @esphome/core
@@ -138,6 +146,7 @@ esphome/components/modbus_controller/switch/* @martgras
esphome/components/modbus_controller/text_sensor/* @martgras
esphome/components/mopeka_ble/* @spbrogan
esphome/components/mopeka_pro_check/* @spbrogan
esphome/components/mpl3115a2/* @kbickar
esphome/components/mpu6886/* @fabaff
esphome/components/network/* @esphome/core
esphome/components/nextion/* @senexcrenshaw
@@ -220,7 +229,9 @@ esphome/components/teleinfo/* @0hax
esphome/components/thermostat/* @kbx81
esphome/components/time/* @OttoWinter
esphome/components/tlc5947/* @rnauber
esphome/components/tm1621/* @Philippe12
esphome/components/tm1637/* @glmnet
esphome/components/tm1638/* @skykingjwc
esphome/components/tmp102/* @timsavage
esphome/components/tmp117/* @Azimath
esphome/components/tof10120/* @wstrzalka
@@ -235,6 +246,8 @@ esphome/components/tuya/sensor/* @jesserockz
esphome/components/tuya/switch/* @jesserockz
esphome/components/tuya/text_sensor/* @dentra
esphome/components/uart/* @esphome/core
esphome/components/ufire_ec/* @pvizeli
esphome/components/ufire_ise/* @pvizeli
esphome/components/ultrasonic/* @OttoWinter
esphome/components/version/* @esphome/core
esphome/components/wake_on_lan/* @willwill2will54
@@ -245,4 +258,4 @@ esphome/components/xiaomi_lywsd03mmc/* @ahpohl
esphome/components/xiaomi_mhoc303/* @drug123
esphome/components/xiaomi_mhoc401/* @vevsvevs
esphome/components/xiaomi_rtcgq02lm/* @jesserockz
esphome/components/xpt2046/* @numo68
esphome/components/xpt2046/* @nielsnl68 @numo68

View File

@@ -88,10 +88,12 @@ def main():
sys.exit(1)
# detect channel from tag
match = re.match(r'^\d+\.\d+(?:\.\d+)?(b\d+)?$', args.tag)
match = re.match(r"^(\d+\.\d+)(?:\.\d+)?(b\d+)?$", args.tag)
major_minor_version = None
if match is None:
channel = CHANNEL_DEV
elif match.group(1) is None:
elif match.group(2) is None:
major_minor_version = match.group(1)
channel = CHANNEL_RELEASE
else:
channel = CHANNEL_BETA
@@ -106,6 +108,11 @@ def main():
tags_to_push.append("beta")
tags_to_push.append("latest")
# Compatibility with HA tags
if major_minor_version:
tags_to_push.append("stable")
tags_to_push.append(major_minor_version)
if args.command == "build":
# 1. pull cache image
params = DockerParams.for_type_arch(args.build_type, args.arch)

View File

@@ -22,6 +22,7 @@ from esphome.cpp_generator import ( # noqa
static_const_array,
statement,
variable,
with_local_variable,
new_variable,
Pvariable,
new_Pvariable,

View File

@@ -121,11 +121,8 @@ void IRAM_ATTR HOT AcDimmerDataStore::gpio_intr() {
// calculate time until enable in µs: (1.0-value)*cycle_time, but with integer arithmetic
// also take into account min_power
auto min_us = this->cycle_time_us * this->min_power / 1000;
// calculate required value to provide a true RMS voltage output
this->enable_time_us =
std::max((uint32_t) 1, (uint32_t)((65535 - (acos(1 - (2 * this->value / 65535.0)) / 3.14159 * 65535)) *
(this->cycle_time_us - min_us)) /
65535);
this->enable_time_us = std::max((uint32_t) 1, ((65535 - this->value) * (this->cycle_time_us - min_us)) / 65535);
if (this->method == DIM_METHOD_LEADING_PULSE) {
// Minimum pulse time should be enough for the triac to trigger when it is close to the ZC zone
// this is for brightness near 99%
@@ -206,6 +203,7 @@ void AcDimmer::setup() {
#endif
}
void AcDimmer::write_state(float state) {
state = std::acos(1 - (2 * state)) / 3.14159; // RMS power compensation
auto new_value = static_cast<uint16_t>(roundf(state * 65535));
if (new_value != 0 && this->store_.value == 0)
this->store_.init_cycle = this->init_with_half_cycle_;

View File

@@ -82,7 +82,7 @@ class ADE7953 : public i2c::I2CDevice, public PollingComponent {
return i2c::ERROR_OK;
}
InternalGPIOPin *irq_pin_ = nullptr;
InternalGPIOPin *irq_pin_{nullptr};
bool is_setup_{false};
sensor::Sensor *voltage_sensor_{nullptr};
sensor::Sensor *current_a_sensor_{nullptr};

View File

@@ -18,8 +18,8 @@ class AHT10Component : public PollingComponent, public i2c::I2CDevice {
void set_humidity_sensor(sensor::Sensor *humidity_sensor) { humidity_sensor_ = humidity_sensor; }
protected:
sensor::Sensor *temperature_sensor_;
sensor::Sensor *humidity_sensor_;
sensor::Sensor *temperature_sensor_{nullptr};
sensor::Sensor *humidity_sensor_{nullptr};
};
} // namespace aht10

View File

@@ -38,7 +38,7 @@ void AirthingsWaveMini::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt
}
case ESP_GATTC_READ_CHAR_EVT: {
if (param->read.conn_id != this->parent()->conn_id)
if (param->read.conn_id != this->parent()->get_conn_id())
break;
if (param->read.status != ESP_GATT_OK) {
ESP_LOGW(TAG, "Error reading char at handle %d, status=%d", param->read.handle, param->read.status);
@@ -88,8 +88,8 @@ void AirthingsWaveMini::update() {
}
void AirthingsWaveMini::request_read_values_() {
auto status =
esp_ble_gattc_read_char(this->parent()->gattc_if, this->parent()->conn_id, this->handle_, ESP_GATT_AUTH_REQ_NONE);
auto status = esp_ble_gattc_read_char(this->parent()->get_gattc_if(), this->parent()->get_conn_id(), this->handle_,
ESP_GATT_AUTH_REQ_NONE);
if (status) {
ESP_LOGW(TAG, "Error sending read request for sensor, status=%d", status);
}

View File

@@ -38,7 +38,7 @@ void AirthingsWavePlus::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt
}
case ESP_GATTC_READ_CHAR_EVT: {
if (param->read.conn_id != this->parent()->conn_id)
if (param->read.conn_id != this->parent()->get_conn_id())
break;
if (param->read.status != ESP_GATT_OK) {
ESP_LOGW(TAG, "Error reading char at handle %d, status=%d", param->read.handle, param->read.status);
@@ -109,8 +109,8 @@ void AirthingsWavePlus::update() {
}
void AirthingsWavePlus::request_read_values_() {
auto status =
esp_ble_gattc_read_char(this->parent()->gattc_if, this->parent()->conn_id, this->handle_, ESP_GATT_AUTH_REQ_NONE);
auto status = esp_ble_gattc_read_char(this->parent()->get_gattc_if(), this->parent()->get_conn_id(), this->handle_,
ESP_GATT_AUTH_REQ_NONE);
if (status) {
ESP_LOGW(TAG, "Error sending read request for sensor, status=%d", status);
}

View File

@@ -4,33 +4,15 @@
// - Arduino - AM2320: https://github.com/EngDial/AM2320/blob/master/src/AM2320.cpp
#include "am2320.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace am2320 {
static const char *const TAG = "am2320";
// ---=== Calc CRC16 ===---
uint16_t crc_16(uint8_t *ptr, uint8_t length) {
uint16_t crc = 0xFFFF;
uint8_t i;
//------------------------------
while (length--) {
crc ^= *ptr++;
for (i = 0; i < 8; i++) {
if ((crc & 0x01) != 0) {
crc >>= 1;
crc ^= 0xA001;
} else {
crc >>= 1;
}
}
}
return crc;
}
void AM2320Component::update() {
uint8_t data[8];
data[0] = 0;
@@ -98,7 +80,7 @@ bool AM2320Component::read_data_(uint8_t *data) {
checksum = data[7] << 8;
checksum += data[6];
if (crc_16(data, 6) != checksum) {
if (crc16(data, 6) != checksum) {
ESP_LOGW(TAG, "AM2320 Checksum invalid!");
return false;
}

View File

@@ -21,8 +21,8 @@ class AM2320Component : public PollingComponent, public i2c::I2CDevice {
bool read_data_(uint8_t *data);
bool read_bytes_(uint8_t a_register, uint8_t *data, uint8_t len, uint32_t conversion = 0);
sensor::Sensor *temperature_sensor_;
sensor::Sensor *humidity_sensor_;
sensor::Sensor *temperature_sensor_{nullptr};
sensor::Sensor *humidity_sensor_{nullptr};
};
} // namespace am2320

View File

@@ -76,9 +76,9 @@ void Am43::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_i
if (this->current_sensor_ > 0) {
if (this->illuminance_ != nullptr) {
auto *packet = this->encoder_->get_light_level_request();
auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_,
packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP,
ESP_GATT_AUTH_REQ_NONE);
auto status = esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(),
this->char_handle_, packet->length, packet->data,
ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
if (status) {
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(),
status);
@@ -102,8 +102,8 @@ void Am43::update() {
if (this->battery_ != nullptr) {
auto *packet = this->encoder_->get_battery_level_request();
auto status =
esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length,
packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_,
packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
if (status)
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status);
}

View File

@@ -27,8 +27,8 @@ void Am43Component::loop() {
if (this->node_state == espbt::ClientState::ESTABLISHED && !this->logged_in_) {
auto *packet = this->encoder_->get_send_pin_request(this->pin_);
auto status =
esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length,
packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_,
packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
ESP_LOGI(TAG, "[%s] Logging into AM43", this->get_name().c_str());
if (status) {
ESP_LOGW(TAG, "[%s] Error writing set_pin to device, error = %d", this->get_name().c_str(), status);
@@ -54,8 +54,8 @@ void Am43Component::control(const CoverCall &call) {
if (call.get_stop()) {
auto *packet = this->encoder_->get_stop_request();
auto status =
esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length,
packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_,
packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
if (status)
ESP_LOGW(TAG, "[%s] Error writing stop command to device, error = %d", this->get_name().c_str(), status);
}
@@ -66,8 +66,8 @@ void Am43Component::control(const CoverCall &call) {
pos = 1 - pos;
auto *packet = this->encoder_->get_set_position_request(100 - (uint8_t)(pos * 100));
auto status =
esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length,
packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_,
packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
if (status)
ESP_LOGW(TAG, "[%s] Error writing set_position command to device, error = %d", this->get_name().c_str(), status);
}
@@ -92,7 +92,8 @@ void Am43Component::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
}
this->char_handle_ = chr->handle;
auto status = esp_ble_gattc_register_for_notify(this->parent_->gattc_if, this->parent_->remote_bda, chr->handle);
auto status = esp_ble_gattc_register_for_notify(this->parent_->get_gattc_if(), this->parent_->get_remote_bda(),
chr->handle);
if (status) {
ESP_LOGW(TAG, "[%s] esp_ble_gattc_register_for_notify failed, status=%d", this->get_name().c_str(), status);
}
@@ -122,9 +123,9 @@ void Am43Component::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
if (this->decoder_->pin_ok_) {
ESP_LOGI(TAG, "[%s] AM43 pin accepted.", this->get_name().c_str());
auto *packet = this->encoder_->get_position_request();
auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_,
packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP,
ESP_GATT_AUTH_REQ_NONE);
auto status = esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(),
this->char_handle_, packet->length, packet->data,
ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
if (status)
ESP_LOGW(TAG, "[%s] Error writing set_position to device, error = %d", this->get_name().c_str(), status);
} else {

View File

@@ -13,7 +13,7 @@ _LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ["display"]
MULTI_CONF = True
Animation_ = display.display_ns.class_("Animation")
Animation_ = display.display_ns.class_("Animation", espImage.Image_)
ANIMATION_SCHEMA = cv.Schema(
{

View File

@@ -34,15 +34,17 @@ void Anova::control(const ClimateCall &call) {
ESP_LOGW(TAG, "Unsupported mode: %d", mode);
return;
}
auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_,
pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
auto status =
esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_,
pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
if (status)
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status);
}
if (call.get_target_temperature().has_value()) {
auto *pkt = this->codec_->get_set_target_temp_request(*call.get_target_temperature());
auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_,
pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
auto status =
esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_,
pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
if (status)
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status);
}
@@ -65,7 +67,8 @@ void Anova::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_
}
this->char_handle_ = chr->handle;
auto status = esp_ble_gattc_register_for_notify(this->parent_->gattc_if, this->parent_->remote_bda, chr->handle);
auto status = esp_ble_gattc_register_for_notify(this->parent_->get_gattc_if(), this->parent_->get_remote_bda(),
chr->handle);
if (status) {
ESP_LOGW(TAG, "[%s] esp_ble_gattc_register_for_notify failed, status=%d", this->get_name().c_str(), status);
}
@@ -112,8 +115,8 @@ void Anova::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_
}
if (pkt != nullptr) {
auto status =
esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, pkt->length,
pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_,
pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
if (status) {
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(),
status);
@@ -137,8 +140,9 @@ void Anova::update() {
auto *pkt = this->codec_->get_read_device_status_request();
if (this->current_request_ == 0)
this->codec_->get_set_unit_request(this->fahrenheit_ ? 'f' : 'c');
auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_,
pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
auto status =
esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_,
pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
if (status)
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status);
this->current_request_++;

View File

@@ -8,6 +8,27 @@ AUTO_LOAD = ["sensor", "binary_sensor"]
MULTI_CONF = True
CONF_APDS9960_ID = "apds9960_id"
CONF_LED_DRIVE = "led_drive"
CONF_PROXIMITY_GAIN = "proximity_gain"
CONF_AMBIENT_LIGHT_GAIN = "ambient_light_gain"
CONF_GESTURE_LED_DRIVE = "gesture_led_drive"
CONF_GESTURE_GAIN = "gesture_gain"
CONF_GESTURE_WAIT_TIME = "gesture_wait_time"
DRIVE_LEVELS = {"100ma": 0, "50ma": 1, "25ma": 2, "12.5ma": 3}
PROXIMITY_LEVELS = {"1x": 0, "2x": 1, "4x": 2, "8x": 3}
AMBIENT_LEVELS = {"1x": 0, "4x": 1, "16x": 2, "64x": 3}
GESTURE_LEVELS = {"1x": 0, "2x": 1, "4x": 2, "8x": 3}
GESTURE_WAIT_TIMES = {
"0ms": 0,
"2.8ms": 1,
"5.6ms": 2,
"8.4ms": 3,
"14ms": 4,
"22.4ms": 5,
"30.8ms": 6,
"39.2ms": 7,
}
apds9960_nds = cg.esphome_ns.namespace("apds9960")
APDS9960 = apds9960_nds.class_("APDS9960", cg.PollingComponent, i2c.I2CDevice)
@@ -16,6 +37,20 @@ CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(APDS9960),
cv.Optional(CONF_LED_DRIVE, "100mA"): cv.enum(DRIVE_LEVELS, lower=True),
cv.Optional(CONF_PROXIMITY_GAIN, "4x"): cv.enum(
PROXIMITY_LEVELS, lower=True
),
cv.Optional(CONF_AMBIENT_LIGHT_GAIN, "4x"): cv.enum(
AMBIENT_LEVELS, lower=True
),
cv.Optional(CONF_GESTURE_LED_DRIVE, "100mA"): cv.enum(
DRIVE_LEVELS, lower=True
),
cv.Optional(CONF_GESTURE_GAIN, "4x"): cv.enum(GESTURE_LEVELS, lower=True),
cv.Optional(CONF_GESTURE_WAIT_TIME, "2.8ms"): cv.enum(
GESTURE_WAIT_TIMES, lower=True
),
}
)
.extend(cv.polling_component_schema("60s"))
@@ -27,3 +62,9 @@ async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
cg.add(var.set_led_drive(config[CONF_LED_DRIVE]))
cg.add(var.set_proximity_gain(config[CONF_PROXIMITY_GAIN]))
cg.add(var.set_ambient_gain(config[CONF_AMBIENT_LIGHT_GAIN]))
cg.add(var.set_gesture_led_drive(config[CONF_GESTURE_LED_DRIVE]))
cg.add(var.set_gesture_gain(config[CONF_GESTURE_GAIN]))
cg.add(var.set_gesture_wait_time(config[CONF_GESTURE_WAIT_TIME]))

View File

@@ -46,16 +46,16 @@ void APDS9960::setup() {
uint8_t val = 0;
APDS9960_ERROR_CHECK(this->read_byte(0x8F, &val));
val &= 0b00111111;
uint8_t led_drive = 0; // led drive, 0 -> 100mA, 1 -> 50mA, 2 -> 25mA, 3 -> 12.5mA
val |= (led_drive & 0b11) << 6;
// led drive, 0 -> 100mA, 1 -> 50mA, 2 -> 25mA, 3 -> 12.5mA
val |= (this->led_drive_ & 0b11) << 6;
val &= 0b11110011;
uint8_t proximity_gain = 2; // proximity gain, 0 -> 1x, 1 -> 2X, 2 -> 4X, 4 -> 8X
val |= (proximity_gain & 0b11) << 2;
// proximity gain, 0 -> 1x, 1 -> 2X, 2 -> 4X, 3 -> 8X
val |= (this->proximity_gain_ & 0b11) << 2;
val &= 0b11111100;
uint8_t ambient_gain = 1; // ambient light gain, 0 -> 1x, 1 -> 4x, 2 -> 16x, 3 -> 64x
val |= (ambient_gain & 0b11) << 0;
// ambient light gain, 0 -> 1x, 1 -> 4x, 2 -> 16x, 3 -> 64x
val |= (this->ambient_gain_ & 0b11) << 0;
APDS9960_WRITE_BYTE(0x8F, val);
// Pers (0x8C) -> 0x11 (2 consecutive proximity or ALS for interrupt)
@@ -75,19 +75,18 @@ void APDS9960::setup() {
// GConf 2 (0xA3, gesture config 2) ->
APDS9960_ERROR_CHECK(this->read_byte(0xA3, &val));
val &= 0b10011111;
uint8_t gesture_gain = 2; // gesture gain, 0 -> 1x, 1 -> 2x, 2 -> 4x, 3 -> 8x
val |= (gesture_gain & 0b11) << 5;
// gesture gain, 0 -> 1x, 1 -> 2x, 2 -> 4x, 3 -> 8x
val |= (this->gesture_gain_ & 0b11) << 5;
val &= 0b11100111;
uint8_t gesture_led_drive = 0; // gesture led drive, 0 -> 100mA, 1 -> 50mA, 2 -> 25mA, 3 -> 12.5mA
val |= (gesture_led_drive & 0b11) << 3;
// gesture led drive, 0 -> 100mA, 1 -> 50mA, 2 -> 25mA, 3 -> 12.5mA
val |= (this->gesture_led_drive_ & 0b11) << 3;
val &= 0b11111000;
// gesture wait time
// 0 -> 0ms, 1 -> 2.8ms, 2 -> 5.6ms, 3 -> 8.4ms
// 4 -> 14.0ms, 5 -> 22.4 ms, 6 -> 30.8ms, 7 -> 39.2 ms
uint8_t gesture_wait_time = 1; // gesture wait time
val |= (gesture_wait_time & 0b111) << 0;
val |= (this->gesture_wait_time_ & 0b111) << 0;
APDS9960_WRITE_BYTE(0xA3, val);
// GOffsetU (0xA4) -> 0x00 (no offset)

View File

@@ -16,6 +16,13 @@ class APDS9960 : public PollingComponent, public i2c::I2CDevice {
void update() override;
void loop() override;
void set_led_drive(uint8_t level) { this->led_drive_ = level; }
void set_proximity_gain(uint8_t gain) { this->proximity_gain_ = gain; }
void set_ambient_gain(uint8_t gain) { this->ambient_gain_ = gain; }
void set_gesture_led_drive(uint8_t level) { this->gesture_led_drive_ = level; }
void set_gesture_gain(uint8_t gain) { this->gesture_gain_ = gain; }
void set_gesture_wait_time(uint8_t wait_time) { this->gesture_wait_time_ = wait_time; }
void set_red_channel(sensor::Sensor *red_channel) { red_channel_ = red_channel; }
void set_green_channel(sensor::Sensor *green_channel) { green_channel_ = green_channel; }
void set_blue_channel(sensor::Sensor *blue_channel) { blue_channel_ = blue_channel; }
@@ -36,6 +43,13 @@ class APDS9960 : public PollingComponent, public i2c::I2CDevice {
void report_gesture_(int gesture);
void process_dataset_(int up, int down, int left, int right);
uint8_t led_drive_;
uint8_t proximity_gain_;
uint8_t ambient_gain_;
uint8_t gesture_led_drive_;
uint8_t gesture_gain_;
uint8_t gesture_wait_time_;
sensor::Sensor *red_channel_{nullptr};
sensor::Sensor *green_channel_{nullptr};
sensor::Sensor *blue_channel_{nullptr};

View File

@@ -43,6 +43,16 @@ service APIConnection {
rpc button_command (ButtonCommandRequest) returns (void) {}
rpc lock_command (LockCommandRequest) returns (void) {}
rpc media_player_command (MediaPlayerCommandRequest) returns (void) {}
rpc subscribe_bluetooth_le_advertisements(SubscribeBluetoothLEAdvertisementsRequest) returns (void) {}
rpc bluetooth_device_request(BluetoothDeviceRequest) returns (void) {}
rpc bluetooth_gatt_get_services(BluetoothGATTGetServicesRequest) returns (void) {}
rpc bluetooth_gatt_read(BluetoothGATTReadRequest) returns (void) {}
rpc bluetooth_gatt_write(BluetoothGATTWriteRequest) returns (void) {}
rpc bluetooth_gatt_read_descriptor(BluetoothGATTReadDescriptorRequest) returns (void) {}
rpc bluetooth_gatt_write_descriptor(BluetoothGATTWriteDescriptorRequest) returns (void) {}
rpc bluetooth_gatt_notify(BluetoothGATTNotifyRequest) returns (void) {}
rpc subscribe_bluetooth_connections_free(SubscribeBluetoothConnectionsFreeRequest) returns (BluetoothConnectionsFreeResponse) {}
}
@@ -77,6 +87,8 @@ message HelloRequest {
// Not strictly necessary to send but nice for debugging
// purposes.
string client_info = 1;
uint32 api_version_major = 2;
uint32 api_version_minor = 3;
}
// Confirmation of successful connection request.
@@ -190,6 +202,8 @@ message DeviceInfoResponse {
string project_version = 9;
uint32 webserver_port = 10;
uint32 bluetooth_proxy_version = 11;
}
message ListEntitiesRequest {
@@ -1099,3 +1113,216 @@ message MediaPlayerCommandRequest {
bool has_media_url = 6;
string media_url = 7;
}
// ==================== BLUETOOTH ====================
message SubscribeBluetoothLEAdvertisementsRequest {
option (id) = 66;
option (source) = SOURCE_CLIENT;
}
message BluetoothServiceData {
string uuid = 1;
repeated uint32 legacy_data = 2 [deprecated = true];
bytes data = 3; // Changed in proto version 1.7
}
message BluetoothLEAdvertisementResponse {
option (id) = 67;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_BLUETOOTH_PROXY";
option (no_delay) = true;
uint64 address = 1;
string name = 2;
sint32 rssi = 3;
repeated string service_uuids = 4;
repeated BluetoothServiceData service_data = 5;
repeated BluetoothServiceData manufacturer_data = 6;
}
enum BluetoothDeviceRequestType {
BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT = 0;
BLUETOOTH_DEVICE_REQUEST_TYPE_DISCONNECT = 1;
BLUETOOTH_DEVICE_REQUEST_TYPE_PAIR = 2;
BLUETOOTH_DEVICE_REQUEST_TYPE_UNPAIR = 3;
}
message BluetoothDeviceRequest {
option (id) = 68;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_BLUETOOTH_PROXY";
uint64 address = 1;
BluetoothDeviceRequestType request_type = 2;
}
message BluetoothDeviceConnectionResponse {
option (id) = 69;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_BLUETOOTH_PROXY";
uint64 address = 1;
bool connected = 2;
uint32 mtu = 3;
int32 error = 4;
}
message BluetoothGATTGetServicesRequest {
option (id) = 70;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_BLUETOOTH_PROXY";
uint64 address = 1;
}
message BluetoothGATTDescriptor {
repeated uint64 uuid = 1;
uint32 handle = 2;
}
message BluetoothGATTCharacteristic {
repeated uint64 uuid = 1;
uint32 handle = 2;
uint32 properties = 3;
repeated BluetoothGATTDescriptor descriptors = 4;
}
message BluetoothGATTService {
repeated uint64 uuid = 1;
uint32 handle = 2;
repeated BluetoothGATTCharacteristic characteristics = 3;
}
message BluetoothGATTGetServicesResponse {
option (id) = 71;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_BLUETOOTH_PROXY";
uint64 address = 1;
repeated BluetoothGATTService services = 2;
}
message BluetoothGATTGetServicesDoneResponse {
option (id) = 72;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_BLUETOOTH_PROXY";
uint64 address = 1;
}
message BluetoothGATTReadRequest {
option (id) = 73;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_BLUETOOTH_PROXY";
uint64 address = 1;
uint32 handle = 2;
}
message BluetoothGATTReadResponse {
option (id) = 74;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_BLUETOOTH_PROXY";
uint64 address = 1;
uint32 handle = 2;
bytes data = 3;
}
message BluetoothGATTWriteRequest {
option (id) = 75;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_BLUETOOTH_PROXY";
uint64 address = 1;
uint32 handle = 2;
bool response = 3;
bytes data = 4;
}
message BluetoothGATTReadDescriptorRequest {
option (id) = 76;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_BLUETOOTH_PROXY";
uint64 address = 1;
uint32 handle = 2;
}
message BluetoothGATTWriteDescriptorRequest {
option (id) = 77;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_BLUETOOTH_PROXY";
uint64 address = 1;
uint32 handle = 2;
bytes data = 3;
}
message BluetoothGATTNotifyRequest {
option (id) = 78;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_BLUETOOTH_PROXY";
uint64 address = 1;
uint32 handle = 2;
bool enable = 3;
}
message BluetoothGATTNotifyDataResponse {
option (id) = 79;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_BLUETOOTH_PROXY";
uint64 address = 1;
uint32 handle = 2;
bytes data = 3;
}
message SubscribeBluetoothConnectionsFreeRequest {
option (id) = 80;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_BLUETOOTH_PROXY";
}
message BluetoothConnectionsFreeResponse {
option (id) = 81;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_BLUETOOTH_PROXY";
uint32 free = 1;
uint32 limit = 2;
}
message BluetoothGATTErrorResponse {
option (id) = 82;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_BLUETOOTH_PROXY";
uint64 address = 1;
uint32 handle = 2;
int32 error = 3;
}
message BluetoothGATTWriteResponse {
option (id) = 83;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_BLUETOOTH_PROXY";
uint64 address = 1;
uint32 handle = 2;
}
message BluetoothGATTNotifyResponse {
option (id) = 84;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_BLUETOOTH_PROXY";
uint64 address = 1;
uint32 handle = 2;
}

View File

@@ -1,10 +1,10 @@
#include "api_connection.h"
#include "esphome/core/entity_base.h"
#include "esphome/core/log.h"
#include "esphome/components/network/util.h"
#include "esphome/core/version.h"
#include "esphome/core/hal.h"
#include <cerrno>
#include "esphome/components/network/util.h"
#include "esphome/core/entity_base.h"
#include "esphome/core/hal.h"
#include "esphome/core/log.h"
#include "esphome/core/version.h"
#ifdef USE_DEEP_SLEEP
#include "esphome/components/deep_sleep/deep_sleep_component.h"
@@ -12,6 +12,9 @@
#ifdef USE_HOMEASSISTANT_TIME
#include "esphome/components/homeassistant/time/homeassistant_time.h"
#endif
#ifdef USE_BLUETOOTH_PROXY
#include "esphome/components/bluetooth_proxy/bluetooth_proxy.h"
#endif
namespace esphome {
namespace api {
@@ -823,6 +826,56 @@ void APIConnection::on_get_time_response(const GetTimeResponse &value) {
}
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIConnection::send_bluetooth_le_advertisement(const BluetoothLEAdvertisementResponse &msg) {
if (!this->bluetooth_le_advertisement_subscription_)
return false;
if (this->client_api_version_major_ < 1 || this->client_api_version_minor_ < 7) {
BluetoothLEAdvertisementResponse resp = msg;
for (auto &service : resp.service_data) {
service.legacy_data.assign(service.data.begin(), service.data.end());
service.data.clear();
}
for (auto &manufacturer_data : resp.manufacturer_data) {
manufacturer_data.legacy_data.assign(manufacturer_data.data.begin(), manufacturer_data.data.end());
manufacturer_data.data.clear();
}
return this->send_bluetooth_le_advertisement_response(resp);
}
return this->send_bluetooth_le_advertisement_response(msg);
}
void APIConnection::bluetooth_device_request(const BluetoothDeviceRequest &msg) {
bluetooth_proxy::global_bluetooth_proxy->bluetooth_device_request(msg);
}
void APIConnection::bluetooth_gatt_read(const BluetoothGATTReadRequest &msg) {
bluetooth_proxy::global_bluetooth_proxy->bluetooth_gatt_read(msg);
}
void APIConnection::bluetooth_gatt_write(const BluetoothGATTWriteRequest &msg) {
bluetooth_proxy::global_bluetooth_proxy->bluetooth_gatt_write(msg);
}
void APIConnection::bluetooth_gatt_read_descriptor(const BluetoothGATTReadDescriptorRequest &msg) {
bluetooth_proxy::global_bluetooth_proxy->bluetooth_gatt_read_descriptor(msg);
}
void APIConnection::bluetooth_gatt_write_descriptor(const BluetoothGATTWriteDescriptorRequest &msg) {
bluetooth_proxy::global_bluetooth_proxy->bluetooth_gatt_write_descriptor(msg);
}
void APIConnection::bluetooth_gatt_get_services(const BluetoothGATTGetServicesRequest &msg) {
bluetooth_proxy::global_bluetooth_proxy->bluetooth_gatt_send_services(msg);
}
void APIConnection::bluetooth_gatt_notify(const BluetoothGATTNotifyRequest &msg) {
bluetooth_proxy::global_bluetooth_proxy->bluetooth_gatt_notify(msg);
}
BluetoothConnectionsFreeResponse APIConnection::subscribe_bluetooth_connections_free(
const SubscribeBluetoothConnectionsFreeRequest &msg) {
BluetoothConnectionsFreeResponse resp;
resp.free = bluetooth_proxy::global_bluetooth_proxy->get_bluetooth_connections_free();
resp.limit = bluetooth_proxy::global_bluetooth_proxy->get_bluetooth_connections_limit();
return resp;
}
#endif
bool APIConnection::send_log_message(int level, const char *tag, const char *line) {
if (this->log_subscription_ < level)
return false;
@@ -840,11 +893,14 @@ bool APIConnection::send_log_message(int level, const char *tag, const char *lin
HelloResponse APIConnection::hello(const HelloRequest &msg) {
this->client_info_ = msg.client_info + " (" + this->helper_->getpeername() + ")";
this->helper_->set_log_info(client_info_);
ESP_LOGV(TAG, "Hello from client: '%s'", this->client_info_.c_str());
this->client_api_version_major_ = msg.api_version_major;
this->client_api_version_minor_ = msg.api_version_minor;
ESP_LOGV(TAG, "Hello from client: '%s' | API Version %d.%d", this->client_info_.c_str(),
this->client_api_version_major_, this->client_api_version_minor_);
HelloResponse resp;
resp.api_version_major = 1;
resp.api_version_minor = 6;
resp.api_version_minor = 7;
resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")";
resp.name = App.get_name();
@@ -886,6 +942,9 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
#endif
#ifdef USE_WEBSERVER
resp.webserver_port = USE_WEBSERVER_PORT;
#endif
#ifdef USE_BLUETOOTH_PROXY
resp.bluetooth_proxy_version = bluetooth_proxy::global_bluetooth_proxy->has_active() ? 2 : 1;
#endif
return resp;
}

View File

@@ -1,11 +1,11 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/application.h"
#include "api_frame_helper.h"
#include "api_pb2.h"
#include "api_pb2_service.h"
#include "api_server.h"
#include "api_frame_helper.h"
#include "esphome/core/application.h"
#include "esphome/core/component.h"
namespace esphome {
namespace api {
@@ -94,6 +94,20 @@ class APIConnection : public APIServerConnection {
return;
this->send_homeassistant_service_response(call);
}
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_le_advertisement(const BluetoothLEAdvertisementResponse &msg);
void bluetooth_device_request(const BluetoothDeviceRequest &msg) override;
void bluetooth_gatt_read(const BluetoothGATTReadRequest &msg) override;
void bluetooth_gatt_write(const BluetoothGATTWriteRequest &msg) override;
void bluetooth_gatt_read_descriptor(const BluetoothGATTReadDescriptorRequest &msg) override;
void bluetooth_gatt_write_descriptor(const BluetoothGATTWriteDescriptorRequest &msg) override;
void bluetooth_gatt_get_services(const BluetoothGATTGetServicesRequest &msg) override;
void bluetooth_gatt_notify(const BluetoothGATTNotifyRequest &msg) override;
BluetoothConnectionsFreeResponse subscribe_bluetooth_connections_free(
const SubscribeBluetoothConnectionsFreeRequest &msg) override;
#endif
#ifdef USE_HOMEASSISTANT_TIME
void send_time_request() {
GetTimeRequest req;
@@ -134,6 +148,9 @@ class APIConnection : public APIServerConnection {
return {};
}
void execute_service(const ExecuteServiceRequest &msg) override;
void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) override {
this->bluetooth_le_advertisement_subscription_ = true;
}
bool is_authenticated() override { return this->connection_state_ == ConnectionState::AUTHENTICATED; }
bool is_connection_setup() override {
return this->connection_state_ == ConnectionState ::CONNECTED || this->is_authenticated();
@@ -167,6 +184,8 @@ class APIConnection : public APIServerConnection {
std::unique_ptr<APIFrameHelper> helper_;
std::string client_info_;
uint32_t client_api_version_major_{0};
uint32_t client_api_version_minor_{0};
#ifdef USE_ESP32_CAMERA
esp32_camera::CameraImageReader image_reader_;
#endif
@@ -176,6 +195,7 @@ class APIConnection : public APIServerConnection {
uint32_t last_traffic_;
bool sent_ping_{false};
bool service_call_subscription_{false};
bool bluetooth_le_advertisement_subscription_{false};
bool next_close_ = false;
APIServer *parent_;
InitialStateIterator initial_state_iterator_;

View File

@@ -116,9 +116,9 @@ class APINoiseFrameHelper : public APIFrameHelper {
std::vector<uint8_t> prologue_;
std::shared_ptr<APINoiseContext> ctx_;
NoiseHandshakeState *handshake_ = nullptr;
NoiseCipherState *send_cipher_ = nullptr;
NoiseCipherState *recv_cipher_ = nullptr;
NoiseHandshakeState *handshake_{nullptr};
NoiseCipherState *send_cipher_{nullptr};
NoiseCipherState *recv_cipher_{nullptr};
NoiseProtocolId nid_;
enum class State {

File diff suppressed because it is too large Load Diff

View File

@@ -155,12 +155,20 @@ enum MediaPlayerCommand : uint32_t {
MEDIA_PLAYER_COMMAND_MUTE = 3,
MEDIA_PLAYER_COMMAND_UNMUTE = 4,
};
enum BluetoothDeviceRequestType : uint32_t {
BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT = 0,
BLUETOOTH_DEVICE_REQUEST_TYPE_DISCONNECT = 1,
BLUETOOTH_DEVICE_REQUEST_TYPE_PAIR = 2,
BLUETOOTH_DEVICE_REQUEST_TYPE_UNPAIR = 3,
};
} // namespace enums
class HelloRequest : public ProtoMessage {
public:
std::string client_info{};
uint32_t api_version_major{0};
uint32_t api_version_minor{0};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
@@ -168,6 +176,7 @@ class HelloRequest : public ProtoMessage {
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class HelloResponse : public ProtoMessage {
public:
@@ -263,6 +272,7 @@ class DeviceInfoResponse : public ProtoMessage {
std::string project_name{};
std::string project_version{};
uint32_t webserver_port{0};
uint32_t bluetooth_proxy_version{0};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
@@ -1214,6 +1224,300 @@ class MediaPlayerCommandRequest : public ProtoMessage {
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class SubscribeBluetoothLEAdvertisementsRequest : public ProtoMessage {
public:
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
};
class BluetoothServiceData : public ProtoMessage {
public:
std::string uuid{};
std::vector<uint32_t> legacy_data{};
std::string data{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class BluetoothLEAdvertisementResponse : public ProtoMessage {
public:
uint64_t address{0};
std::string name{};
int32_t rssi{0};
std::vector<std::string> service_uuids{};
std::vector<BluetoothServiceData> service_data{};
std::vector<BluetoothServiceData> manufacturer_data{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class BluetoothDeviceRequest : public ProtoMessage {
public:
uint64_t address{0};
enums::BluetoothDeviceRequestType request_type{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class BluetoothDeviceConnectionResponse : public ProtoMessage {
public:
uint64_t address{0};
bool connected{false};
uint32_t mtu{0};
int32_t error{0};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class BluetoothGATTGetServicesRequest : public ProtoMessage {
public:
uint64_t address{0};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class BluetoothGATTDescriptor : public ProtoMessage {
public:
std::vector<uint64_t> uuid{};
uint32_t handle{0};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class BluetoothGATTCharacteristic : public ProtoMessage {
public:
std::vector<uint64_t> uuid{};
uint32_t handle{0};
uint32_t properties{0};
std::vector<BluetoothGATTDescriptor> descriptors{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class BluetoothGATTService : public ProtoMessage {
public:
std::vector<uint64_t> uuid{};
uint32_t handle{0};
std::vector<BluetoothGATTCharacteristic> characteristics{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class BluetoothGATTGetServicesResponse : public ProtoMessage {
public:
uint64_t address{0};
std::vector<BluetoothGATTService> services{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class BluetoothGATTGetServicesDoneResponse : public ProtoMessage {
public:
uint64_t address{0};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class BluetoothGATTReadRequest : public ProtoMessage {
public:
uint64_t address{0};
uint32_t handle{0};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class BluetoothGATTReadResponse : public ProtoMessage {
public:
uint64_t address{0};
uint32_t handle{0};
std::string data{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class BluetoothGATTWriteRequest : public ProtoMessage {
public:
uint64_t address{0};
uint32_t handle{0};
bool response{false};
std::string data{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class BluetoothGATTReadDescriptorRequest : public ProtoMessage {
public:
uint64_t address{0};
uint32_t handle{0};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class BluetoothGATTWriteDescriptorRequest : public ProtoMessage {
public:
uint64_t address{0};
uint32_t handle{0};
std::string data{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class BluetoothGATTNotifyRequest : public ProtoMessage {
public:
uint64_t address{0};
uint32_t handle{0};
bool enable{false};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class BluetoothGATTNotifyDataResponse : public ProtoMessage {
public:
uint64_t address{0};
uint32_t handle{0};
std::string data{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class SubscribeBluetoothConnectionsFreeRequest : public ProtoMessage {
public:
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
};
class BluetoothConnectionsFreeResponse : public ProtoMessage {
public:
uint32_t free{0};
uint32_t limit{0};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class BluetoothGATTErrorResponse : public ProtoMessage {
public:
uint64_t address{0};
uint32_t handle{0};
int32_t error{0};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class BluetoothGATTWriteResponse : public ProtoMessage {
public:
uint64_t address{0};
uint32_t handle{0};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class BluetoothGATTNotifyResponse : public ProtoMessage {
public:
uint64_t address{0};
uint32_t handle{0};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
} // namespace api
} // namespace esphome

View File

@@ -328,6 +328,103 @@ bool APIServerConnectionBase::send_media_player_state_response(const MediaPlayer
#endif
#ifdef USE_MEDIA_PLAYER
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_le_advertisement_response(const BluetoothLEAdvertisementResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_le_advertisement_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothLEAdvertisementResponse>(msg, 67);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_device_connection_response(const BluetoothDeviceConnectionResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_device_connection_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothDeviceConnectionResponse>(msg, 69);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_gatt_get_services_response(const BluetoothGATTGetServicesResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_gatt_get_services_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothGATTGetServicesResponse>(msg, 71);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_gatt_get_services_done_response(
const BluetoothGATTGetServicesDoneResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_gatt_get_services_done_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothGATTGetServicesDoneResponse>(msg, 72);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_gatt_read_response(const BluetoothGATTReadResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_gatt_read_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothGATTReadResponse>(msg, 74);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
#endif
#ifdef USE_BLUETOOTH_PROXY
#endif
#ifdef USE_BLUETOOTH_PROXY
#endif
#ifdef USE_BLUETOOTH_PROXY
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_gatt_notify_data_response(const BluetoothGATTNotifyDataResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_gatt_notify_data_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothGATTNotifyDataResponse>(msg, 79);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_connections_free_response(const BluetoothConnectionsFreeResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_connections_free_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothConnectionsFreeResponse>(msg, 81);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_gatt_error_response(const BluetoothGATTErrorResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_gatt_error_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothGATTErrorResponse>(msg, 82);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_gatt_write_response(const BluetoothGATTWriteResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_gatt_write_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothGATTWriteResponse>(msg, 83);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_gatt_notify_response(const BluetoothGATTNotifyResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_gatt_notify_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothGATTNotifyResponse>(msg, 84);
}
#endif
bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {
switch (msg_type) {
case 1: {
@@ -592,6 +689,103 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
ESP_LOGVV(TAG, "on_media_player_command_request: %s", msg.dump().c_str());
#endif
this->on_media_player_command_request(msg);
#endif
break;
}
case 66: {
SubscribeBluetoothLEAdvertisementsRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_subscribe_bluetooth_le_advertisements_request: %s", msg.dump().c_str());
#endif
this->on_subscribe_bluetooth_le_advertisements_request(msg);
break;
}
case 68: {
#ifdef USE_BLUETOOTH_PROXY
BluetoothDeviceRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_bluetooth_device_request: %s", msg.dump().c_str());
#endif
this->on_bluetooth_device_request(msg);
#endif
break;
}
case 70: {
#ifdef USE_BLUETOOTH_PROXY
BluetoothGATTGetServicesRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_bluetooth_gatt_get_services_request: %s", msg.dump().c_str());
#endif
this->on_bluetooth_gatt_get_services_request(msg);
#endif
break;
}
case 73: {
#ifdef USE_BLUETOOTH_PROXY
BluetoothGATTReadRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_bluetooth_gatt_read_request: %s", msg.dump().c_str());
#endif
this->on_bluetooth_gatt_read_request(msg);
#endif
break;
}
case 75: {
#ifdef USE_BLUETOOTH_PROXY
BluetoothGATTWriteRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_bluetooth_gatt_write_request: %s", msg.dump().c_str());
#endif
this->on_bluetooth_gatt_write_request(msg);
#endif
break;
}
case 76: {
#ifdef USE_BLUETOOTH_PROXY
BluetoothGATTReadDescriptorRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_bluetooth_gatt_read_descriptor_request: %s", msg.dump().c_str());
#endif
this->on_bluetooth_gatt_read_descriptor_request(msg);
#endif
break;
}
case 77: {
#ifdef USE_BLUETOOTH_PROXY
BluetoothGATTWriteDescriptorRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_bluetooth_gatt_write_descriptor_request: %s", msg.dump().c_str());
#endif
this->on_bluetooth_gatt_write_descriptor_request(msg);
#endif
break;
}
case 78: {
#ifdef USE_BLUETOOTH_PROXY
BluetoothGATTNotifyRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_bluetooth_gatt_notify_request: %s", msg.dump().c_str());
#endif
this->on_bluetooth_gatt_notify_request(msg);
#endif
break;
}
case 80: {
#ifdef USE_BLUETOOTH_PROXY
SubscribeBluetoothConnectionsFreeRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_subscribe_bluetooth_connections_free_request: %s", msg.dump().c_str());
#endif
this->on_subscribe_bluetooth_connections_free_request(msg);
#endif
break;
}
@@ -855,6 +1049,126 @@ void APIServerConnection::on_media_player_command_request(const MediaPlayerComma
this->media_player_command(msg);
}
#endif
void APIServerConnection::on_subscribe_bluetooth_le_advertisements_request(
const SubscribeBluetoothLEAdvertisementsRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->subscribe_bluetooth_le_advertisements(msg);
}
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_bluetooth_device_request(const BluetoothDeviceRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->bluetooth_device_request(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_bluetooth_gatt_get_services_request(const BluetoothGATTGetServicesRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->bluetooth_gatt_get_services(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_bluetooth_gatt_read_request(const BluetoothGATTReadRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->bluetooth_gatt_read(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_bluetooth_gatt_write_request(const BluetoothGATTWriteRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->bluetooth_gatt_write(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_bluetooth_gatt_read_descriptor_request(const BluetoothGATTReadDescriptorRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->bluetooth_gatt_read_descriptor(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_bluetooth_gatt_write_descriptor_request(const BluetoothGATTWriteDescriptorRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->bluetooth_gatt_write_descriptor(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_bluetooth_gatt_notify_request(const BluetoothGATTNotifyRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->bluetooth_gatt_notify(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_subscribe_bluetooth_connections_free_request(
const SubscribeBluetoothConnectionsFreeRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
BluetoothConnectionsFreeResponse ret = this->subscribe_bluetooth_connections_free(msg);
if (!this->send_bluetooth_connections_free_response(ret)) {
this->on_fatal_error();
}
}
#endif
} // namespace api
} // namespace esphome

View File

@@ -153,6 +153,62 @@ class APIServerConnectionBase : public ProtoService {
#endif
#ifdef USE_MEDIA_PLAYER
virtual void on_media_player_command_request(const MediaPlayerCommandRequest &value){};
#endif
virtual void on_subscribe_bluetooth_le_advertisements_request(
const SubscribeBluetoothLEAdvertisementsRequest &value){};
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_le_advertisement_response(const BluetoothLEAdvertisementResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void on_bluetooth_device_request(const BluetoothDeviceRequest &value){};
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_device_connection_response(const BluetoothDeviceConnectionResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void on_bluetooth_gatt_get_services_request(const BluetoothGATTGetServicesRequest &value){};
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_gatt_get_services_response(const BluetoothGATTGetServicesResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_gatt_get_services_done_response(const BluetoothGATTGetServicesDoneResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void on_bluetooth_gatt_read_request(const BluetoothGATTReadRequest &value){};
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_gatt_read_response(const BluetoothGATTReadResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void on_bluetooth_gatt_write_request(const BluetoothGATTWriteRequest &value){};
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void on_bluetooth_gatt_read_descriptor_request(const BluetoothGATTReadDescriptorRequest &value){};
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void on_bluetooth_gatt_write_descriptor_request(const BluetoothGATTWriteDescriptorRequest &value){};
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void on_bluetooth_gatt_notify_request(const BluetoothGATTNotifyRequest &value){};
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_gatt_notify_data_response(const BluetoothGATTNotifyDataResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void on_subscribe_bluetooth_connections_free_request(const SubscribeBluetoothConnectionsFreeRequest &value){};
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_connections_free_response(const BluetoothConnectionsFreeResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_gatt_error_response(const BluetoothGATTErrorResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_gatt_write_response(const BluetoothGATTWriteResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_gatt_notify_response(const BluetoothGATTNotifyResponse &msg);
#endif
protected:
bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;
@@ -204,6 +260,32 @@ class APIServerConnection : public APIServerConnectionBase {
#endif
#ifdef USE_MEDIA_PLAYER
virtual void media_player_command(const MediaPlayerCommandRequest &msg) = 0;
#endif
virtual void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) = 0;
#ifdef USE_BLUETOOTH_PROXY
virtual void bluetooth_device_request(const BluetoothDeviceRequest &msg) = 0;
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void bluetooth_gatt_get_services(const BluetoothGATTGetServicesRequest &msg) = 0;
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void bluetooth_gatt_read(const BluetoothGATTReadRequest &msg) = 0;
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void bluetooth_gatt_write(const BluetoothGATTWriteRequest &msg) = 0;
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void bluetooth_gatt_read_descriptor(const BluetoothGATTReadDescriptorRequest &msg) = 0;
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void bluetooth_gatt_write_descriptor(const BluetoothGATTWriteDescriptorRequest &msg) = 0;
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void bluetooth_gatt_notify(const BluetoothGATTNotifyRequest &msg) = 0;
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual BluetoothConnectionsFreeResponse subscribe_bluetooth_connections_free(
const SubscribeBluetoothConnectionsFreeRequest &msg) = 0;
#endif
protected:
void on_hello_request(const HelloRequest &msg) override;
@@ -250,6 +332,31 @@ class APIServerConnection : public APIServerConnectionBase {
#endif
#ifdef USE_MEDIA_PLAYER
void on_media_player_command_request(const MediaPlayerCommandRequest &msg) override;
#endif
void on_subscribe_bluetooth_le_advertisements_request(const SubscribeBluetoothLEAdvertisementsRequest &msg) override;
#ifdef USE_BLUETOOTH_PROXY
void on_bluetooth_device_request(const BluetoothDeviceRequest &msg) override;
#endif
#ifdef USE_BLUETOOTH_PROXY
void on_bluetooth_gatt_get_services_request(const BluetoothGATTGetServicesRequest &msg) override;
#endif
#ifdef USE_BLUETOOTH_PROXY
void on_bluetooth_gatt_read_request(const BluetoothGATTReadRequest &msg) override;
#endif
#ifdef USE_BLUETOOTH_PROXY
void on_bluetooth_gatt_write_request(const BluetoothGATTWriteRequest &msg) override;
#endif
#ifdef USE_BLUETOOTH_PROXY
void on_bluetooth_gatt_read_descriptor_request(const BluetoothGATTReadDescriptorRequest &msg) override;
#endif
#ifdef USE_BLUETOOTH_PROXY
void on_bluetooth_gatt_write_descriptor_request(const BluetoothGATTWriteDescriptorRequest &msg) override;
#endif
#ifdef USE_BLUETOOTH_PROXY
void on_bluetooth_gatt_notify_request(const BluetoothGATTNotifyRequest &msg) override;
#endif
#ifdef USE_BLUETOOTH_PROXY
void on_subscribe_bluetooth_connections_free_request(const SubscribeBluetoothConnectionsFreeRequest &msg) override;
#endif
};

View File

@@ -291,6 +291,79 @@ void APIServer::send_homeassistant_service_call(const HomeassistantServiceRespon
client->send_homeassistant_service_call(call);
}
}
#ifdef USE_BLUETOOTH_PROXY
void APIServer::send_bluetooth_le_advertisement(const BluetoothLEAdvertisementResponse &call) {
for (auto &client : this->clients_) {
client->send_bluetooth_le_advertisement(call);
}
}
void APIServer::send_bluetooth_device_connection(uint64_t address, bool connected, uint16_t mtu, esp_err_t error) {
BluetoothDeviceConnectionResponse call;
call.address = address;
call.connected = connected;
call.mtu = mtu;
call.error = error;
for (auto &client : this->clients_) {
client->send_bluetooth_device_connection_response(call);
}
}
void APIServer::send_bluetooth_connections_free(uint8_t free, uint8_t limit) {
BluetoothConnectionsFreeResponse call;
call.free = free;
call.limit = limit;
for (auto &client : this->clients_) {
client->send_bluetooth_connections_free_response(call);
}
}
void APIServer::send_bluetooth_gatt_read_response(const BluetoothGATTReadResponse &call) {
for (auto &client : this->clients_) {
client->send_bluetooth_gatt_read_response(call);
}
}
void APIServer::send_bluetooth_gatt_write_response(const BluetoothGATTWriteResponse &call) {
for (auto &client : this->clients_) {
client->send_bluetooth_gatt_write_response(call);
}
}
void APIServer::send_bluetooth_gatt_notify_data_response(const BluetoothGATTNotifyDataResponse &call) {
for (auto &client : this->clients_) {
client->send_bluetooth_gatt_notify_data_response(call);
}
}
void APIServer::send_bluetooth_gatt_notify_response(const BluetoothGATTNotifyResponse &call) {
for (auto &client : this->clients_) {
client->send_bluetooth_gatt_notify_response(call);
}
}
void APIServer::send_bluetooth_gatt_services(const BluetoothGATTGetServicesResponse &call) {
for (auto &client : this->clients_) {
client->send_bluetooth_gatt_get_services_response(call);
}
}
void APIServer::send_bluetooth_gatt_services_done(uint64_t address) {
BluetoothGATTGetServicesDoneResponse call;
call.address = address;
for (auto &client : this->clients_) {
client->send_bluetooth_gatt_get_services_done_response(call);
}
}
void APIServer::send_bluetooth_gatt_error(uint64_t address, uint16_t handle, esp_err_t error) {
BluetoothGATTErrorResponse call;
call.address = address;
call.handle = handle;
call.error = error;
for (auto &client : this->clients_) {
client->send_bluetooth_gatt_error_response(call);
}
}
#endif
APIServer::APIServer() { global_api_server = this; }
void APIServer::subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute,
std::function<void(std::string)> f) {

View File

@@ -73,6 +73,18 @@ class APIServer : public Component, public Controller {
void on_media_player_update(media_player::MediaPlayer *obj) override;
#endif
void send_homeassistant_service_call(const HomeassistantServiceResponse &call);
#ifdef USE_BLUETOOTH_PROXY
void send_bluetooth_le_advertisement(const BluetoothLEAdvertisementResponse &call);
void send_bluetooth_device_connection(uint64_t address, bool connected, uint16_t mtu, esp_err_t error = ESP_OK);
void send_bluetooth_connections_free(uint8_t free, uint8_t limit);
void send_bluetooth_gatt_read_response(const BluetoothGATTReadResponse &call);
void send_bluetooth_gatt_write_response(const BluetoothGATTWriteResponse &call);
void send_bluetooth_gatt_notify_data_response(const BluetoothGATTNotifyDataResponse &call);
void send_bluetooth_gatt_notify_response(const BluetoothGATTNotifyResponse &call);
void send_bluetooth_gatt_services(const BluetoothGATTGetServicesResponse &call);
void send_bluetooth_gatt_services_done(uint64_t address);
void send_bluetooth_gatt_error(uint64_t address, uint16_t handle, esp_err_t error);
#endif
void register_user_service(UserServiceDescriptor *descriptor) { this->user_services_.push_back(descriptor); }
#ifdef USE_HOMEASSISTANT_TIME
void request_time();

View File

@@ -70,7 +70,7 @@ class ProtoVarInt {
}
}
void encode(std::vector<uint8_t> &out) {
uint32_t val = this->value_;
uint64_t val = this->value_;
if (val <= 0x7F) {
out.push_back(val);
return;

View File

@@ -92,9 +92,9 @@ class AS3935Component : public Component {
virtual void write_register(uint8_t reg, uint8_t mask, uint8_t bits, uint8_t start_position) = 0;
sensor::Sensor *distance_sensor_;
sensor::Sensor *energy_sensor_;
binary_sensor::BinarySensor *thunder_alert_binary_sensor_;
sensor::Sensor *distance_sensor_{nullptr};
sensor::Sensor *energy_sensor_{nullptr};
binary_sensor::BinarySensor *thunder_alert_binary_sensor_{nullptr};
GPIOPin *irq_pin_;
bool indoor_;

View File

@@ -89,8 +89,10 @@ enum BedjetCommand : uint8_t {
"85%", "90%", "95%", "100%" \
}
static const char *const BEDJET_FAN_STEP_NAMES[20] = BEDJET_FAN_STEP_NAMES_;
static const std::string BEDJET_FAN_STEP_NAME_STRINGS[20] = BEDJET_FAN_STEP_NAMES_;
static const uint8_t BEDJET_FAN_SPEED_COUNT = 20;
static const char *const BEDJET_FAN_STEP_NAMES[BEDJET_FAN_SPEED_COUNT] = BEDJET_FAN_STEP_NAMES_;
static const std::string BEDJET_FAN_STEP_NAME_STRINGS[BEDJET_FAN_SPEED_COUNT] = BEDJET_FAN_STEP_NAMES_;
static const std::set<std::string> BEDJET_FAN_STEP_NAMES_SET BEDJET_FAN_STEP_NAMES_;
} // namespace bedjet

View File

@@ -128,9 +128,9 @@ uint8_t BedJetHub::write_bedjet_packet_(BedjetPacket *pkt) {
}
return -1;
}
auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_cmd_,
pkt->data_length + 1, (uint8_t *) &pkt->command, ESP_GATT_WRITE_TYPE_NO_RSP,
ESP_GATT_AUTH_REQ_NONE);
auto status = esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(),
this->char_handle_cmd_, pkt->data_length + 1, (uint8_t *) &pkt->command,
ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
return status;
}
@@ -138,13 +138,13 @@ uint8_t BedJetHub::write_bedjet_packet_(BedjetPacket *pkt) {
uint8_t BedJetHub::set_notify_(const bool enable) {
uint8_t status;
if (enable) {
status = esp_ble_gattc_register_for_notify(this->parent_->gattc_if, this->parent_->remote_bda,
status = esp_ble_gattc_register_for_notify(this->parent_->get_gattc_if(), this->parent_->get_remote_bda(),
this->char_handle_status_);
if (status) {
ESP_LOGW(TAG, "[%s] esp_ble_gattc_register_for_notify failed, status=%d", this->get_name().c_str(), status);
}
} else {
status = esp_ble_gattc_unregister_for_notify(this->parent_->gattc_if, this->parent_->remote_bda,
status = esp_ble_gattc_unregister_for_notify(this->parent_->get_gattc_if(), this->parent_->get_remote_bda(),
this->char_handle_status_);
if (status) {
ESP_LOGW(TAG, "[%s] esp_ble_gattc_unregister_for_notify failed, status=%d", this->get_name().c_str(), status);
@@ -204,8 +204,8 @@ bool BedJetHub::discover_characteristics_() {
result = false;
} else {
this->char_handle_name_ = chr->handle;
auto status = esp_ble_gattc_read_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_name_,
ESP_GATT_AUTH_REQ_NONE);
auto status = esp_ble_gattc_read_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(),
this->char_handle_name_, ESP_GATT_AUTH_REQ_NONE);
if (status) {
ESP_LOGI(TAG, "[%s] Unable to read name characteristic: %d", this->get_name().c_str(), status);
}
@@ -230,22 +230,6 @@ void BedJetHub::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
this->dispatch_state_(false);
break;
}
case ESP_GATTC_OPEN_EVT: {
// FIXME: bug in BLEClient
this->parent_->conn_id = param->open.conn_id;
this->open_conn_id_ = param->open.conn_id;
break;
}
case ESP_GATTC_CONNECT_EVT: {
if (this->parent_->conn_id != param->connect.conn_id && this->open_conn_id_ != 0xff) {
// FIXME: bug in BLEClient
ESP_LOGW(TAG, "[%s] CONNECT_EVT unexpected conn_id; open=%d, parent=%d, param=%d", this->get_name().c_str(),
this->open_conn_id_, this->parent_->conn_id, param->connect.conn_id);
this->parent_->conn_id = this->open_conn_id_;
}
break;
}
case ESP_GATTC_SEARCH_CMPL_EVT: {
auto result = this->discover_characteristics_();
@@ -301,7 +285,7 @@ void BedJetHub::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
break;
}
case ESP_GATTC_READ_CHAR_EVT: {
if (param->read.conn_id != this->parent_->conn_id)
if (param->read.conn_id != this->parent_->get_conn_id())
break;
if (param->read.status != ESP_GATT_OK) {
ESP_LOGW(TAG, "Error reading char at handle %d, status=%d", param->read.handle, param->read.status);
@@ -358,9 +342,9 @@ void BedJetHub::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
if (this->processing_)
break;
if (param->notify.conn_id != this->parent_->conn_id) {
if (param->notify.conn_id != this->parent_->get_conn_id()) {
ESP_LOGW(TAG, "[%s] Received notify event for unexpected parent conn: expect %x, got %x",
this->get_name().c_str(), this->parent_->conn_id, param->notify.conn_id);
this->get_name().c_str(), this->parent_->get_conn_id(), param->notify.conn_id);
// FIXME: bug in BLEClient holding wrong conn_id.
}
@@ -394,7 +378,7 @@ void BedJetHub::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
if (needs_extra) {
// This means the packet was partial, so read the status characteristic to get the second part.
// Ideally this will complete quickly. We won't process additional notification events until it does.
auto status = esp_ble_gattc_read_char(this->parent_->gattc_if, this->parent_->conn_id,
auto status = esp_ble_gattc_read_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(),
this->char_handle_status_, ESP_GATT_AUTH_REQ_NONE);
if (status) {
ESP_LOGI(TAG, "[%s] Unable to read extended status packet", this->get_name().c_str());
@@ -438,15 +422,15 @@ uint8_t BedJetHub::write_notify_config_descriptor_(bool enable) {
// NOTE: BLEClient uses `uint8_t*` of length 1, but BLE spec requires 16 bits.
uint16_t notify_en = enable ? 1 : 0;
auto status =
esp_ble_gattc_write_char_descr(this->parent_->gattc_if, this->parent_->conn_id, handle, sizeof(notify_en),
(uint8_t *) &notify_en, ESP_GATT_WRITE_TYPE_RSP, ESP_GATT_AUTH_REQ_NONE);
auto status = esp_ble_gattc_write_char_descr(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), handle,
sizeof(notify_en), (uint8_t *) &notify_en, ESP_GATT_WRITE_TYPE_RSP,
ESP_GATT_AUTH_REQ_NONE);
if (status) {
ESP_LOGW(TAG, "esp_ble_gattc_write_char_descr error, status=%d", status);
return status;
}
ESP_LOGD(TAG, "[%s] wrote notify=%s to status config 0x%04x, for conn %d", this->get_name().c_str(),
enable ? "true" : "false", handle, this->parent_->conn_id);
enable ? "true" : "false", handle, this->parent_->get_conn_id());
return ESP_GATT_OK;
}
@@ -500,7 +484,7 @@ void BedJetHub::update() { this->dispatch_status_(); }
void BedJetHub::dump_config() {
ESP_LOGCONFIG(TAG, "BedJet Hub '%s'", this->get_name().c_str());
ESP_LOGCONFIG(TAG, " ble_client.app_id: %d", this->parent()->app_id);
ESP_LOGCONFIG(TAG, " ble_client.conn_id: %d", this->parent()->conn_id);
ESP_LOGCONFIG(TAG, " ble_client.conn_id: %d", this->parent()->get_conn_id());
LOG_UPDATE_INTERVAL(this)
ESP_LOGCONFIG(TAG, " Child components (%d):", this->children_.size());
for (auto *child : this->children_) {

View File

@@ -167,8 +167,6 @@ class BedJetHub : public esphome::ble_client::BLEClientNode, public PollingCompo
uint16_t char_handle_status_;
uint16_t config_descr_status_;
uint8_t open_conn_id_ = -1;
uint8_t write_notify_config_descriptor_(bool enable);
};

View File

@@ -9,19 +9,17 @@ from esphome.const import (
CONF_RECEIVE_TIMEOUT,
CONF_TIME_ID,
)
from . import (
from .. import (
BEDJET_CLIENT_SCHEMA,
bedjet_ns,
register_bedjet_child,
)
_LOGGER = logging.getLogger(__name__)
CODEOWNERS = ["@jhansche"]
DEPENDENCIES = ["ble_client"]
DEPENDENCIES = ["bedjet"]
bedjet_ns = cg.esphome_ns.namespace("bedjet")
BedJetClimate = bedjet_ns.class_(
"BedJetClimate", climate.Climate, ble_client.BLEClientNode, cg.PollingComponent
)
BedJetClimate = bedjet_ns.class_("BedJetClimate", climate.Climate, cg.PollingComponent)
BedjetHeatMode = bedjet_ns.enum("BedjetHeatMode")
BEDJET_HEAT_MODES = {
"heat": BedjetHeatMode.HEAT_MODE_HEAT,

View File

@@ -15,13 +15,13 @@ float bedjet_temp_to_c(const uint8_t temp) {
}
static const std::string *bedjet_fan_step_to_fan_mode(const uint8_t fan_step) {
if (fan_step <= 19)
if (fan_step < BEDJET_FAN_SPEED_COUNT)
return &BEDJET_FAN_STEP_NAME_STRINGS[fan_step];
return nullptr;
}
static uint8_t bedjet_fan_speed_to_step(const std::string &fan_step_percent) {
for (int i = 0; i < sizeof(BEDJET_FAN_STEP_NAME_STRINGS); i++) {
for (int i = 0; i < BEDJET_FAN_SPEED_COUNT; i++) {
if (fan_step_percent == BEDJET_FAN_STEP_NAME_STRINGS[i]) {
return i;
}

View File

@@ -1,12 +1,12 @@
#pragma once
#include "esphome/components/climate/climate.h"
#include "esphome/core/component.h"
#include "esphome/core/defines.h"
#include "esphome/core/hal.h"
#include "bedjet_child.h"
#include "bedjet_codec.h"
#include "bedjet_hub.h"
#include "esphome/components/bedjet/bedjet_child.h"
#include "esphome/components/bedjet/bedjet_codec.h"
#include "esphome/components/bedjet/bedjet_hub.h"
#include "esphome/components/climate/climate.h"
#ifdef USE_ESP32

View File

@@ -0,0 +1,36 @@
import logging
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import fan
from esphome.const import (
CONF_ID,
)
from .. import (
BEDJET_CLIENT_SCHEMA,
bedjet_ns,
register_bedjet_child,
)
_LOGGER = logging.getLogger(__name__)
CODEOWNERS = ["@jhansche"]
DEPENDENCIES = ["bedjet"]
BedJetFan = bedjet_ns.class_("BedJetFan", fan.Fan, cg.PollingComponent)
CONFIG_SCHEMA = (
fan.FAN_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(BedJetFan),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(BEDJET_CLIENT_SCHEMA)
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await fan.register_fan(var, config)
await register_bedjet_child(var, config)

View File

@@ -0,0 +1,108 @@
#include "bedjet_fan.h"
#include "esphome/core/log.h"
#ifdef USE_ESP32
namespace esphome {
namespace bedjet {
using namespace esphome::fan;
void BedJetFan::dump_config() { LOG_FAN("", "BedJet Fan", this); }
std::string BedJetFan::describe() { return "BedJet Fan"; }
void BedJetFan::control(const fan::FanCall &call) {
ESP_LOGD(TAG, "Received BedJetFan::control");
if (!this->parent_->is_connected()) {
ESP_LOGW(TAG, "Not connected, cannot handle control call yet.");
return;
}
bool did_change = false;
if (call.get_state().has_value() && this->state != *call.get_state()) {
// Turning off is easy:
if (this->state && this->parent_->button_off()) {
this->state = false;
this->publish_state();
return;
}
// Turning on, we have to choose a specific mode; for now, use "COOL" mode
// In the future we could configure the mode to use for fan.turn_on.
if (this->parent_->button_cool()) {
this->state = true;
did_change = true;
}
}
// ignore speed changes if not on or turning on
if (this->state && call.get_speed().has_value()) {
this->speed = *call.get_speed();
this->parent_->set_fan_index(this->speed);
did_change = true;
}
if (did_change) {
this->publish_state();
}
}
void BedJetFan::on_status(const BedjetStatusPacket *data) {
ESP_LOGVV(TAG, "[%s] Handling on_status with data=%p", this->get_name().c_str(), (void *) data);
bool did_change = false;
bool new_state = data->mode != MODE_STANDBY && data->mode != MODE_WAIT;
if (new_state != this->state) {
this->state = new_state;
did_change = true;
}
if (data->fan_step != this->speed) {
this->speed = data->fan_step;
did_change = true;
}
if (did_change) {
this->publish_state();
}
}
/** Attempts to update the fan device from the last received BedjetStatusPacket.
*
* This will be called from #on_status() when the parent dispatches new status packets,
* and from #update() when the polling interval is triggered.
*
* @return `true` if the status has been applied; `false` if there is nothing to apply.
*/
bool BedJetFan::update_status_() {
if (!this->parent_->is_connected())
return false;
if (!this->parent_->has_status())
return false;
auto *status = this->parent_->get_status_packet();
if (status == nullptr)
return false;
this->on_status(status);
return true;
}
void BedJetFan::update() {
ESP_LOGD(TAG, "[%s] update()", this->get_name().c_str());
// TODO: if the hub component is already polling, do we also need to include polling?
// We're already going to get on_status() at the hub's polling interval.
auto result = this->update_status_();
ESP_LOGD(TAG, "[%s] update_status result=%s", this->get_name().c_str(), result ? "true" : "false");
}
/** Resets states to defaults. */
void BedJetFan::reset_state_() {
this->state = false;
this->publish_state();
}
} // namespace bedjet
} // namespace esphome
#endif

View File

@@ -0,0 +1,40 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/defines.h"
#include "esphome/core/hal.h"
#include "esphome/components/bedjet/bedjet_child.h"
#include "esphome/components/bedjet/bedjet_codec.h"
#include "esphome/components/bedjet/bedjet_hub.h"
#include "esphome/components/fan/fan.h"
#ifdef USE_ESP32
namespace esphome {
namespace bedjet {
class BedJetFan : public fan::Fan, public BedJetClient, public PollingComponent {
public:
void update() override;
void dump_config() override;
float get_setup_priority() const override { return setup_priority::AFTER_WIFI; }
/* BedJetClient status update */
void on_status(const BedjetStatusPacket *data) override;
void on_bedjet_state(bool is_ready) override{};
std::string describe() override;
fan::FanTraits get_traits() override { return fan::FanTraits(false, true, false, BEDJET_FAN_SPEED_COUNT); }
protected:
void control(const fan::FanCall &call) override;
private:
void reset_state_();
bool update_status_();
};
} // namespace bedjet
} // namespace esphome
#endif

View File

@@ -13,6 +13,9 @@ void BinarySensorMap::loop() {
case BINARY_SENSOR_MAP_TYPE_GROUP:
this->process_group_();
break;
case BINARY_SENSOR_MAP_TYPE_SUM:
this->process_sum_();
break;
}
}
@@ -46,6 +49,34 @@ void BinarySensorMap::process_group_() {
this->last_mask_ = mask;
}
void BinarySensorMap::process_sum_() {
float total_current_value = 0.0;
uint64_t mask = 0x00;
// check all binary_sensors for its state. when active add its value to total_current_value.
// create a bitmask for the binary_sensor status on all channels
for (size_t i = 0; i < this->channels_.size(); i++) {
auto bs = this->channels_[i];
if (bs.binary_sensor->state) {
total_current_value += bs.sensor_value;
mask |= 1 << i;
}
}
// check if the sensor map was touched
if (mask != 0ULL) {
// did the bit_mask change or is it a new sensor touch
if (this->last_mask_ != mask) {
float publish_value = total_current_value;
ESP_LOGD(TAG, "'%s' - Publishing %.2f", this->name_.c_str(), publish_value);
this->publish_state(publish_value);
}
} else if (this->last_mask_ != 0ULL) {
// is this a new sensor release
ESP_LOGD(TAG, "'%s' - No binary sensor active, publishing 0", this->name_.c_str());
this->publish_state(0.0);
}
this->last_mask_ = mask;
}
void BinarySensorMap::add_channel(binary_sensor::BinarySensor *sensor, float value) {
BinarySensorMapChannel sensor_channel{
.binary_sensor = sensor,

View File

@@ -9,6 +9,7 @@ namespace binary_sensor_map {
enum BinarySensorMapType {
BINARY_SENSOR_MAP_TYPE_GROUP,
BINARY_SENSOR_MAP_TYPE_SUM,
};
struct BinarySensorMapChannel {
@@ -50,8 +51,10 @@ class BinarySensorMap : public sensor::Sensor, public Component {
/**
* methods to process the types of binary_sensor_maps
* GROUP: process_group_() just map to a value
* ADD: process_add_() adds all the values
* */
void process_group_();
void process_sum_();
};
} // namespace binary_sensor_map

View File

@@ -9,6 +9,7 @@ from esphome.const import (
ICON_CHECK_CIRCLE_OUTLINE,
CONF_BINARY_SENSOR,
CONF_GROUP,
CONF_SUM,
)
DEPENDENCIES = ["binary_sensor"]
@@ -21,6 +22,7 @@ SensorMapType = binary_sensor_map_ns.enum("SensorMapType")
SENSOR_MAP_TYPES = {
CONF_GROUP: SensorMapType.BINARY_SENSOR_MAP_TYPE_GROUP,
CONF_SUM: SensorMapType.BINARY_SENSOR_MAP_TYPE_SUM,
}
entry = {
@@ -41,6 +43,17 @@ CONFIG_SCHEMA = cv.typed_schema(
),
}
),
CONF_SUM: sensor.sensor_schema(
BinarySensorMap,
icon=ICON_CHECK_CIRCLE_OUTLINE,
accuracy_decimals=0,
).extend(
{
cv.Required(CONF_CHANNELS): cv.All(
cv.ensure_list(entry), cv.Length(min=1)
),
}
),
},
lower=True,
)

View File

@@ -75,16 +75,16 @@ class BL0939 : public PollingComponent, public uart::UARTDevice {
void dump_config() override;
protected:
sensor::Sensor *voltage_sensor_;
sensor::Sensor *current_sensor_1_;
sensor::Sensor *current_sensor_2_;
sensor::Sensor *voltage_sensor_{nullptr};
sensor::Sensor *current_sensor_1_{nullptr};
sensor::Sensor *current_sensor_2_{nullptr};
// NB This may be negative as the circuits is seemingly able to measure
// power in both directions
sensor::Sensor *power_sensor_1_;
sensor::Sensor *power_sensor_2_;
sensor::Sensor *energy_sensor_1_;
sensor::Sensor *energy_sensor_2_;
sensor::Sensor *energy_sensor_sum_;
sensor::Sensor *power_sensor_1_{nullptr};
sensor::Sensor *power_sensor_2_{nullptr};
sensor::Sensor *energy_sensor_1_{nullptr};
sensor::Sensor *energy_sensor_2_{nullptr};
sensor::Sensor *energy_sensor_sum_{nullptr};
// Divide by this to turn into Watt
float power_reference_ = BL0939_PREF;

View File

@@ -75,14 +75,14 @@ class BL0940 : public PollingComponent, public uart::UARTDevice {
void dump_config() override;
protected:
sensor::Sensor *voltage_sensor_;
sensor::Sensor *current_sensor_;
sensor::Sensor *voltage_sensor_{nullptr};
sensor::Sensor *current_sensor_{nullptr};
// NB This may be negative as the circuits is seemingly able to measure
// power in both directions
sensor::Sensor *power_sensor_;
sensor::Sensor *energy_sensor_;
sensor::Sensor *internal_temperature_sensor_;
sensor::Sensor *external_temperature_sensor_;
sensor::Sensor *power_sensor_{nullptr};
sensor::Sensor *energy_sensor_{nullptr};
sensor::Sensor *internal_temperature_sensor_{nullptr};
sensor::Sensor *external_temperature_sensor_{nullptr};
// Max difference between two measurements of the temperature. Used to avoid noise.
float max_temperature_diff_{0};

View File

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

View File

@@ -0,0 +1,121 @@
#include "bl0942.h"
#include "esphome/core/log.h"
namespace esphome {
namespace bl0942 {
static const char *const TAG = "bl0942";
static const uint8_t BL0942_READ_COMMAND = 0x58;
static const uint8_t BL0942_FULL_PACKET = 0xAA;
static const uint8_t BL0942_PACKET_HEADER = 0x55;
static const uint8_t BL0942_WRITE_COMMAND = 0xA8;
static const uint8_t BL0942_REG_I_FAST_RMS_CTRL = 0x10;
static const uint8_t BL0942_REG_MODE = 0x18;
static const uint8_t BL0942_REG_SOFT_RESET = 0x19;
static const uint8_t BL0942_REG_USR_WRPROT = 0x1A;
static const uint8_t BL0942_REG_TPS_CTRL = 0x1B;
// TODO: Confirm insialisation works as intended
const uint8_t BL0942_INIT[5][6] = {
// Reset to default
{BL0942_WRITE_COMMAND, BL0942_REG_SOFT_RESET, 0x5A, 0x5A, 0x5A, 0x38},
// Enable User Operation Write
{BL0942_WRITE_COMMAND, BL0942_REG_USR_WRPROT, 0x55, 0x00, 0x00, 0xF0},
// 0x0100 = CF_UNABLE energy pulse, AC_FREQ_SEL 50Hz, RMS_UPDATE_SEL 800mS
{BL0942_WRITE_COMMAND, BL0942_REG_MODE, 0x00, 0x10, 0x00, 0x37},
// 0x47FF = Over-current and leakage alarm on, Automatic temperature measurement, Interval 100mS
{BL0942_WRITE_COMMAND, BL0942_REG_TPS_CTRL, 0xFF, 0x47, 0x00, 0xFE},
// 0x181C = Half cycle, Fast RMS threshold 6172
{BL0942_WRITE_COMMAND, BL0942_REG_I_FAST_RMS_CTRL, 0x1C, 0x18, 0x00, 0x1B}};
void BL0942::loop() {
DataPacket buffer;
if (!this->available()) {
return;
}
if (read_array((uint8_t *) &buffer, sizeof(buffer))) {
if (validate_checksum(&buffer)) {
received_package_(&buffer);
}
} else {
ESP_LOGW(TAG, "Junk on wire. Throwing away partial message");
while (read() >= 0)
;
}
}
bool BL0942::validate_checksum(DataPacket *data) {
uint8_t checksum = BL0942_READ_COMMAND;
// Whole package but checksum
uint8_t *raw = (uint8_t *) data;
for (uint32_t i = 0; i < sizeof(*data) - 1; i++) {
checksum += raw[i];
}
checksum ^= 0xFF;
if (checksum != data->checksum) {
ESP_LOGW(TAG, "BL0942 invalid checksum! 0x%02X != 0x%02X", checksum, data->checksum);
}
return checksum == data->checksum;
}
void BL0942::update() {
this->flush();
this->write_byte(BL0942_READ_COMMAND);
this->write_byte(BL0942_FULL_PACKET);
}
void BL0942::setup() {
for (auto *i : BL0942_INIT) {
this->write_array(i, 6);
delay(1);
}
this->flush();
}
void BL0942::received_package_(DataPacket *data) {
// Bad header
if (data->frame_header != BL0942_PACKET_HEADER) {
ESP_LOGI(TAG, "Invalid data. Header mismatch: %d", data->frame_header);
return;
}
float v_rms = (uint24_t) data->v_rms / voltage_reference_;
float i_rms = (uint24_t) data->i_rms / current_reference_;
float watt = (int24_t) data->watt / power_reference_;
uint32_t cf_cnt = (uint24_t) data->cf_cnt;
float total_energy_consumption = cf_cnt / energy_reference_;
float frequency = 1000000.0f / data->frequency;
if (voltage_sensor_ != nullptr) {
voltage_sensor_->publish_state(v_rms);
}
if (current_sensor_ != nullptr) {
current_sensor_->publish_state(i_rms);
}
if (power_sensor_ != nullptr) {
power_sensor_->publish_state(watt);
}
if (energy_sensor_ != nullptr) {
energy_sensor_->publish_state(total_energy_consumption);
}
if (frequency_sensor_ != nullptr) {
frequency_sensor_->publish_state(frequency);
}
ESP_LOGV(TAG, "BL0942: U %fV, I %fA, P %fW, Cnt %d, ∫P %fkWh, frequency %f°Hz, status 0x%08X", v_rms, i_rms, watt,
cf_cnt, total_energy_consumption, frequency, data->status);
}
void BL0942::dump_config() { // NOLINT(readability-function-cognitive-complexity)
ESP_LOGCONFIG(TAG, "BL0942:");
LOG_SENSOR("", "Voltage", this->voltage_sensor_);
LOG_SENSOR("", "Current", this->current_sensor_);
LOG_SENSOR("", "Power", this->power_sensor_);
LOG_SENSOR("", "Energy", this->energy_sensor_);
LOG_SENSOR("", "frequency", this->frequency_sensor_);
}
} // namespace bl0942
} // namespace esphome

View File

@@ -0,0 +1,68 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/datatypes.h"
#include "esphome/components/uart/uart.h"
#include "esphome/components/sensor/sensor.h"
namespace esphome {
namespace bl0942 {
static const float BL0942_PREF = 596; // taken from tasmota
static const float BL0942_UREF = 15873.35944299; // should be 73989/1.218
static const float BL0942_IREF = 251213.46469622; // 305978/1.218
static const float BL0942_EREF = 3304.61127328; // Measured
struct DataPacket {
uint8_t frame_header;
uint24_le_t i_rms;
uint24_le_t v_rms;
uint24_le_t i_fast_rms;
int24_le_t watt;
uint24_le_t cf_cnt;
uint16_le_t frequency;
uint8_t reserved1;
uint8_t status;
uint8_t reserved2;
uint8_t reserved3;
uint8_t checksum;
} __attribute__((packed));
class BL0942 : public PollingComponent, public uart::UARTDevice {
public:
void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; }
void set_current_sensor(sensor::Sensor *current_sensor) { current_sensor_ = current_sensor; }
void set_power_sensor(sensor::Sensor *power_sensor) { power_sensor_ = power_sensor; }
void set_energy_sensor(sensor::Sensor *energy_sensor) { energy_sensor_ = energy_sensor; }
void set_frequency_sensor(sensor::Sensor *frequency_sensor) { frequency_sensor_ = frequency_sensor; }
void loop() override;
void update() override;
void setup() override;
void dump_config() override;
protected:
sensor::Sensor *voltage_sensor_{nullptr};
sensor::Sensor *current_sensor_{nullptr};
// NB This may be negative as the circuits is seemingly able to measure
// power in both directions
sensor::Sensor *power_sensor_{nullptr};
sensor::Sensor *energy_sensor_{nullptr};
sensor::Sensor *frequency_sensor_{nullptr};
// Divide by this to turn into Watt
float power_reference_ = BL0942_PREF;
// Divide by this to turn into Volt
float voltage_reference_ = BL0942_UREF;
// Divide by this to turn into Ampere
float current_reference_ = BL0942_IREF;
// Divide by this to turn into kWh
float energy_reference_ = BL0942_EREF;
static bool validate_checksum(DataPacket *data);
void received_package_(DataPacket *data);
};
} // namespace bl0942
} // namespace esphome

View File

@@ -0,0 +1,93 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor, uart
from esphome.const import (
CONF_CURRENT,
CONF_ENERGY,
CONF_ID,
CONF_POWER,
CONF_VOLTAGE,
CONF_FREQUENCY,
DEVICE_CLASS_CURRENT,
DEVICE_CLASS_ENERGY,
DEVICE_CLASS_POWER,
DEVICE_CLASS_VOLTAGE,
DEVICE_CLASS_FREQUENCY,
STATE_CLASS_MEASUREMENT,
UNIT_AMPERE,
UNIT_KILOWATT_HOURS,
UNIT_VOLT,
UNIT_WATT,
UNIT_HERTZ,
)
DEPENDENCIES = ["uart"]
bl0942_ns = cg.esphome_ns.namespace("bl0942")
BL0942 = bl0942_ns.class_("BL0942", cg.PollingComponent, uart.UARTDevice)
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(BL0942),
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
unit_of_measurement=UNIT_VOLT,
accuracy_decimals=1,
device_class=DEVICE_CLASS_VOLTAGE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_CURRENT): sensor.sensor_schema(
unit_of_measurement=UNIT_AMPERE,
accuracy_decimals=2,
device_class=DEVICE_CLASS_CURRENT,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_POWER): sensor.sensor_schema(
unit_of_measurement=UNIT_WATT,
accuracy_decimals=0,
device_class=DEVICE_CLASS_POWER,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_ENERGY): sensor.sensor_schema(
unit_of_measurement=UNIT_KILOWATT_HOURS,
accuracy_decimals=0,
device_class=DEVICE_CLASS_ENERGY,
),
cv.Optional(CONF_FREQUENCY): sensor.sensor_schema(
unit_of_measurement=UNIT_HERTZ,
accuracy_decimals=0,
device_class=DEVICE_CLASS_FREQUENCY,
state_class=STATE_CLASS_MEASUREMENT,
),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(uart.UART_DEVICE_SCHEMA)
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await uart.register_uart_device(var, config)
if CONF_VOLTAGE in config:
conf = config[CONF_VOLTAGE]
sens = await sensor.new_sensor(conf)
cg.add(var.set_voltage_sensor(sens))
if CONF_CURRENT in config:
conf = config[CONF_CURRENT]
sens = await sensor.new_sensor(conf)
cg.add(var.set_current_sensor(sens))
if CONF_POWER in config:
conf = config[CONF_POWER]
sens = await sensor.new_sensor(conf)
cg.add(var.set_power_sensor(sens))
if CONF_ENERGY in config:
conf = config[CONF_ENERGY]
sens = await sensor.new_sensor(conf)
cg.add(var.set_energy_sensor(sens))
if CONF_FREQUENCY in config:
conf = config[CONF_FREQUENCY]
sens = await sensor.new_sensor(conf)
cg.add(var.set_frequency_sensor(sens))

View File

@@ -1,6 +1,6 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import esp32_ble_tracker
from esphome.components import esp32_ble_tracker, esp32_ble_client
from esphome.const import (
CONF_CHARACTERISTIC_UUID,
CONF_ID,
@@ -14,13 +14,12 @@ from esphome.const import (
)
from esphome import automation
AUTO_LOAD = ["esp32_ble_client"]
CODEOWNERS = ["@buxtronix"]
DEPENDENCIES = ["esp32_ble_tracker"]
ble_client_ns = cg.esphome_ns.namespace("ble_client")
BLEClient = ble_client_ns.class_(
"BLEClient", cg.Component, esp32_ble_tracker.ESPBTClient
)
BLEClient = ble_client_ns.class_("BLEClient", esp32_ble_client.BLEClientBase)
BLEClientNode = ble_client_ns.class_("BLEClientNode")
BLEClientNodeConstRef = BLEClientNode.operator("ref").operator("const")
# Triggers
@@ -101,12 +100,40 @@ async def ble_write_to_code(config, action_id, template_arg, args):
else:
cg.add(var.set_value_simple(value))
serv_uuid128 = esp32_ble_tracker.as_reversed_hex_array(config[CONF_SERVICE_UUID])
cg.add(var.set_service_uuid128(serv_uuid128))
char_uuid128 = esp32_ble_tracker.as_reversed_hex_array(
config[CONF_CHARACTERISTIC_UUID]
)
cg.add(var.set_char_uuid128(char_uuid128))
if len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
cg.add(
var.set_service_uuid16(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]))
)
elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid32_format):
cg.add(
var.set_service_uuid32(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]))
)
elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid128_format):
uuid128 = esp32_ble_tracker.as_reversed_hex_array(config[CONF_SERVICE_UUID])
cg.add(var.set_service_uuid128(uuid128))
if len(config[CONF_CHARACTERISTIC_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
cg.add(
var.set_char_uuid16(
esp32_ble_tracker.as_hex(config[CONF_CHARACTERISTIC_UUID])
)
)
elif len(config[CONF_CHARACTERISTIC_UUID]) == len(
esp32_ble_tracker.bt_uuid32_format
):
cg.add(
var.set_char_uuid32(
esp32_ble_tracker.as_hex(config[CONF_CHARACTERISTIC_UUID])
)
)
elif len(config[CONF_CHARACTERISTIC_UUID]) == len(
esp32_ble_tracker.bt_uuid128_format
):
uuid128 = esp32_ble_tracker.as_reversed_hex_array(
config[CONF_CHARACTERISTIC_UUID]
)
cg.add(var.set_char_uuid128(uuid128))
return var

View File

@@ -1,8 +1,8 @@
#include "automation.h"
#include <esp_bt_defs.h>
#include <esp_gap_ble_api.h>
#include <esp_gattc_api.h>
#include <esp_bt_defs.h>
#include "esphome/core/log.h"
@@ -31,8 +31,8 @@ void BLEWriterClientNode::write(const std::vector<uint8_t> &value) {
}
ESP_LOGVV(TAG, "Will write %d bytes: %s", value.size(), format_hex_pretty(value).c_str());
esp_err_t err =
esp_ble_gattc_write_char(this->parent()->gattc_if, this->parent()->conn_id, this->ble_char_handle_, value.size(),
const_cast<uint8_t *>(value.data()), write_type, ESP_GATT_AUTH_REQ_NONE);
esp_ble_gattc_write_char(this->parent()->get_gattc_if(), this->parent()->get_conn_id(), this->ble_char_handle_,
value.size(), const_cast<uint8_t *>(value.data()), write_type, ESP_GATT_AUTH_REQ_NONE);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Error writing to characteristic: %s!", esp_err_to_name(err));
}

View File

@@ -28,7 +28,8 @@ class BLEClientDisconnectTrigger : public Trigger<>, public BLEClientNode {
void loop() override {}
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) override {
if (event == ESP_GATTC_DISCONNECT_EVT && memcmp(param->disconnect.remote_bda, this->parent_->remote_bda, 6) == 0)
if (event == ESP_GATTC_DISCONNECT_EVT &&
memcmp(param->disconnect.remote_bda, this->parent_->get_remote_bda(), 6) == 0)
this->trigger();
if (event == ESP_GATTC_SEARCH_CMPL_EVT)
this->node_state = espbt::ClientState::ESTABLISHED;
@@ -45,10 +46,14 @@ class BLEWriterClientNode : public BLEClientNode {
// Attempts to write the contents of value to char_uuid_.
void write(const std::vector<uint8_t> &value);
void set_char_uuid128(uint8_t *uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
void set_service_uuid16(uint16_t uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_uint16(uuid); }
void set_service_uuid32(uint32_t uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); }
void set_service_uuid128(uint8_t *uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
void set_char_uuid16(uint16_t uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_uint16(uuid); }
void set_char_uuid32(uint32_t uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); }
void set_char_uuid128(uint8_t *uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) override;

View File

@@ -1,8 +1,9 @@
#include "esphome/core/log.h"
#include "ble_client.h"
#include "esphome/components/esp32_ble_client/ble_client_base.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include "esphome/core/application.h"
#include "esphome/core/helpers.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include "ble_client.h"
#include "esphome/core/log.h"
#ifdef USE_ESP32
@@ -11,22 +12,13 @@ namespace ble_client {
static const char *const TAG = "ble_client";
float BLEClient::get_setup_priority() const { return setup_priority::AFTER_BLUETOOTH; }
void BLEClient::setup() {
auto ret = esp_ble_gattc_app_register(this->app_id);
if (ret) {
ESP_LOGE(TAG, "gattc app register failed. app_id=%d code=%d", this->app_id, ret);
this->mark_failed();
}
this->set_states_(espbt::ClientState::IDLE);
BLEClientBase::setup();
this->enabled = true;
}
void BLEClient::loop() {
if (this->state() == espbt::ClientState::DISCOVERED) {
this->connect();
}
BLEClientBase::loop();
for (auto *node : this->nodes_)
node->loop();
}
@@ -39,34 +31,7 @@ void BLEClient::dump_config() {
bool BLEClient::parse_device(const espbt::ESPBTDevice &device) {
if (!this->enabled)
return false;
if (device.address_uint64() != this->address)
return false;
if (this->state() != espbt::ClientState::IDLE)
return false;
ESP_LOGD(TAG, "Found device at MAC address [%s]", device.address_str().c_str());
this->set_states_(espbt::ClientState::DISCOVERED);
auto addr = device.address_uint64();
this->remote_bda[0] = (addr >> 40) & 0xFF;
this->remote_bda[1] = (addr >> 32) & 0xFF;
this->remote_bda[2] = (addr >> 24) & 0xFF;
this->remote_bda[3] = (addr >> 16) & 0xFF;
this->remote_bda[4] = (addr >> 8) & 0xFF;
this->remote_bda[5] = (addr >> 0) & 0xFF;
this->remote_addr_type = device.get_address_type();
return true;
}
std::string BLEClient::address_str() const {
char buf[20];
sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", (uint8_t)(this->address >> 40) & 0xff,
(uint8_t)(this->address >> 32) & 0xff, (uint8_t)(this->address >> 24) & 0xff,
(uint8_t)(this->address >> 16) & 0xff, (uint8_t)(this->address >> 8) & 0xff,
(uint8_t)(this->address >> 0) & 0xff);
std::string ret;
ret = buf;
return ret;
return BLEClientBase::parse_device(device);
}
void BLEClient::set_enabled(bool enabled) {
@@ -74,7 +39,7 @@ void BLEClient::set_enabled(bool enabled) {
return;
if (!enabled && this->state() != espbt::ClientState::IDLE) {
ESP_LOGI(TAG, "[%s] Disabling BLE client.", this->address_str().c_str());
auto ret = esp_ble_gattc_close(this->gattc_if, this->conn_id);
auto ret = esp_ble_gattc_close(this->gattc_if_, this->conn_id_);
if (ret) {
ESP_LOGW(TAG, "esp_ble_gattc_close error, address=%s status=%d", this->address_str().c_str(), ret);
}
@@ -82,125 +47,12 @@ void BLEClient::set_enabled(bool enabled) {
this->enabled = enabled;
}
void BLEClient::connect() {
ESP_LOGI(TAG, "Attempting BLE connection to %s", this->address_str().c_str());
auto ret = esp_ble_gattc_open(this->gattc_if, this->remote_bda, this->remote_addr_type, true);
if (ret) {
ESP_LOGW(TAG, "esp_ble_gattc_open error, address=%s status=%d", this->address_str().c_str(), ret);
this->set_states_(espbt::ClientState::IDLE);
} else {
this->set_states_(espbt::ClientState::CONNECTING);
}
}
void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t esp_gattc_if,
esp_ble_gattc_cb_param_t *param) {
if (event == ESP_GATTC_REG_EVT && this->app_id != param->reg.app_id)
return;
if (event != ESP_GATTC_REG_EVT && esp_gattc_if != ESP_GATT_IF_NONE && esp_gattc_if != this->gattc_if)
return;
bool all_established = this->all_nodes_established_();
switch (event) {
case ESP_GATTC_REG_EVT: {
if (param->reg.status == ESP_GATT_OK) {
ESP_LOGV(TAG, "gattc registered app id %d", this->app_id);
this->gattc_if = esp_gattc_if;
} else {
ESP_LOGE(TAG, "gattc app registration failed id=%d code=%d", param->reg.app_id, param->reg.status);
}
break;
}
case ESP_GATTC_OPEN_EVT: {
ESP_LOGV(TAG, "[%s] ESP_GATTC_OPEN_EVT", this->address_str().c_str());
this->conn_id = param->open.conn_id;
if (param->open.status != ESP_GATT_OK) {
ESP_LOGW(TAG, "connect to %s failed, status=%d", this->address_str().c_str(), param->open.status);
this->set_states_(espbt::ClientState::IDLE);
break;
}
break;
}
case ESP_GATTC_CONNECT_EVT: {
ESP_LOGV(TAG, "[%s] ESP_GATTC_CONNECT_EVT", this->address_str().c_str());
if (this->conn_id != param->connect.conn_id) {
ESP_LOGD(TAG, "[%s] Unexpected conn_id in CONNECT_EVT: param conn=%d, open conn=%d",
this->address_str().c_str(), param->connect.conn_id, this->conn_id);
}
auto ret = esp_ble_gattc_send_mtu_req(this->gattc_if, param->connect.conn_id);
if (ret) {
ESP_LOGW(TAG, "esp_ble_gattc_send_mtu_req failed, status=%x", ret);
}
break;
}
case ESP_GATTC_CFG_MTU_EVT: {
if (param->cfg_mtu.status != ESP_GATT_OK) {
ESP_LOGW(TAG, "cfg_mtu to %s failed, mtu %d, status %d", this->address_str().c_str(), param->cfg_mtu.mtu,
param->cfg_mtu.status);
this->set_states_(espbt::ClientState::IDLE);
break;
}
ESP_LOGV(TAG, "cfg_mtu status %d, mtu %d", param->cfg_mtu.status, param->cfg_mtu.mtu);
esp_ble_gattc_search_service(esp_gattc_if, param->cfg_mtu.conn_id, nullptr);
break;
}
case ESP_GATTC_DISCONNECT_EVT: {
if (memcmp(param->disconnect.remote_bda, this->remote_bda, 6) != 0) {
return;
}
ESP_LOGV(TAG, "[%s] ESP_GATTC_DISCONNECT_EVT, reason %d", this->address_str().c_str(), param->disconnect.reason);
for (auto &svc : this->services_)
delete svc; // NOLINT(cppcoreguidelines-owning-memory)
this->services_.clear();
this->set_states_(espbt::ClientState::IDLE);
break;
}
case ESP_GATTC_SEARCH_RES_EVT: {
BLEService *ble_service = new BLEService(); // NOLINT(cppcoreguidelines-owning-memory)
ble_service->uuid = espbt::ESPBTUUID::from_uuid(param->search_res.srvc_id.uuid);
ble_service->start_handle = param->search_res.start_handle;
ble_service->end_handle = param->search_res.end_handle;
ble_service->client = this;
this->services_.push_back(ble_service);
break;
}
case ESP_GATTC_SEARCH_CMPL_EVT: {
ESP_LOGV(TAG, "[%s] ESP_GATTC_SEARCH_CMPL_EVT", this->address_str().c_str());
for (auto &svc : this->services_) {
ESP_LOGI(TAG, "Service UUID: %s", svc->uuid.to_string().c_str());
ESP_LOGI(TAG, " start_handle: 0x%x end_handle: 0x%x", svc->start_handle, svc->end_handle);
svc->parse_characteristics();
}
this->set_states_(espbt::ClientState::CONNECTED);
this->set_state(espbt::ClientState::ESTABLISHED);
break;
}
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
auto *descr = this->get_config_descriptor(param->reg_for_notify.handle);
if (descr == nullptr) {
ESP_LOGW(TAG, "No descriptor found for notify of handle 0x%x", param->reg_for_notify.handle);
break;
}
if (descr->uuid.get_uuid().len != ESP_UUID_LEN_16 ||
descr->uuid.get_uuid().uuid.uuid16 != ESP_GATT_UUID_CHAR_CLIENT_CONFIG) {
ESP_LOGW(TAG, "Handle 0x%x (uuid %s) is not a client config char uuid", param->reg_for_notify.handle,
descr->uuid.to_string().c_str());
break;
}
uint16_t notify_en = 1;
auto status =
esp_ble_gattc_write_char_descr(this->gattc_if, this->conn_id, descr->handle, sizeof(notify_en),
(uint8_t *) &notify_en, ESP_GATT_WRITE_TYPE_RSP, ESP_GATT_AUTH_REQ_NONE);
if (status) {
ESP_LOGW(TAG, "esp_ble_gattc_write_char_descr error, status=%d", status);
}
break;
}
BLEClientBase::gattc_event_handler(event, esp_gattc_if, param);
default:
break;
}
for (auto *node : this->nodes_)
node->gattc_event_handler(event, esp_gattc_if, param);
@@ -213,236 +65,26 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
}
void BLEClient::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
switch (event) {
// This event is sent by the server when it requests security
case ESP_GAP_BLE_SEC_REQ_EVT:
ESP_LOGV(TAG, "ESP_GAP_BLE_SEC_REQ_EVT %x", event);
esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true);
break;
// This event is sent once authentication has completed
case ESP_GAP_BLE_AUTH_CMPL_EVT:
esp_bd_addr_t bd_addr;
memcpy(bd_addr, param->ble_security.auth_cmpl.bd_addr, sizeof(esp_bd_addr_t));
ESP_LOGI(TAG, "auth complete. remote BD_ADDR: %s", format_hex(bd_addr, 6).c_str());
if (!param->ble_security.auth_cmpl.success) {
ESP_LOGE(TAG, "auth fail reason = 0x%x", param->ble_security.auth_cmpl.fail_reason);
} else {
ESP_LOGV(TAG, "auth success. address type = %d auth mode = %d", param->ble_security.auth_cmpl.addr_type,
param->ble_security.auth_cmpl.auth_mode);
}
break;
// There are other events we'll want to implement at some point to support things like pass key
// https://github.com/espressif/esp-idf/blob/cba69dd088344ed9d26739f04736ae7a37541b3a/examples/bluetooth/bluedroid/ble/gatt_security_client/tutorial/Gatt_Security_Client_Example_Walkthrough.md
default:
break;
BLEClientBase::gap_event_handler(event, param);
for (auto *node : this->nodes_)
node->gap_event_handler(event, param);
}
void BLEClient::set_state(espbt::ClientState state) {
BLEClientBase::set_state(state);
for (auto &node : nodes_)
node->node_state = state;
}
bool BLEClient::all_nodes_established_() {
if (this->state() != espbt::ClientState::ESTABLISHED)
return false;
for (auto &node : nodes_) {
if (node->node_state != espbt::ClientState::ESTABLISHED)
return false;
}
}
// Parse GATT values into a float for a sensor.
// Ref: https://www.bluetooth.com/specifications/assigned-numbers/format-types/
float BLEClient::parse_char_value(uint8_t *value, uint16_t length) {
// A length of one means a single octet value.
if (length == 0)
return 0;
if (length == 1)
return (float) ((uint8_t) value[0]);
switch (value[0]) {
case 0x1: // boolean.
case 0x2: // 2bit.
case 0x3: // nibble.
case 0x4: // uint8.
return (float) ((uint8_t) value[1]);
case 0x5: // uint12.
case 0x6: // uint16.
if (length > 2) {
return (float) ((uint16_t)(value[1] << 8) + (uint16_t) value[2]);
}
case 0x7: // uint24.
if (length > 3) {
return (float) ((uint32_t)(value[1] << 16) + (uint32_t)(value[2] << 8) + (uint32_t)(value[3]));
}
case 0x8: // uint32.
if (length > 4) {
return (float) ((uint32_t)(value[1] << 24) + (uint32_t)(value[2] << 16) + (uint32_t)(value[3] << 8) +
(uint32_t)(value[4]));
}
case 0xC: // int8.
return (float) ((int8_t) value[1]);
case 0xD: // int12.
case 0xE: // int16.
if (length > 2) {
return (float) ((int16_t)(value[1] << 8) + (int16_t) value[2]);
}
case 0xF: // int24.
if (length > 3) {
return (float) ((int32_t)(value[1] << 16) + (int32_t)(value[2] << 8) + (int32_t)(value[3]));
}
case 0x10: // int32.
if (length > 4) {
return (float) ((int32_t)(value[1] << 24) + (int32_t)(value[2] << 16) + (int32_t)(value[3] << 8) +
(int32_t)(value[4]));
}
}
ESP_LOGW(TAG, "Cannot parse characteristic value of type 0x%x length %d", value[0], length);
return NAN;
}
BLEService *BLEClient::get_service(espbt::ESPBTUUID uuid) {
for (auto *svc : this->services_) {
if (svc->uuid == uuid)
return svc;
}
return nullptr;
}
BLEService *BLEClient::get_service(uint16_t uuid) { return this->get_service(espbt::ESPBTUUID::from_uint16(uuid)); }
BLECharacteristic *BLEClient::get_characteristic(espbt::ESPBTUUID service, espbt::ESPBTUUID chr) {
auto *svc = this->get_service(service);
if (svc == nullptr)
return nullptr;
return svc->get_characteristic(chr);
}
BLECharacteristic *BLEClient::get_characteristic(uint16_t service, uint16_t chr) {
return this->get_characteristic(espbt::ESPBTUUID::from_uint16(service), espbt::ESPBTUUID::from_uint16(chr));
}
BLEDescriptor *BLEClient::get_config_descriptor(uint16_t handle) {
for (auto &svc : this->services_) {
for (auto &chr : svc->characteristics) {
if (chr->handle == handle) {
for (auto &desc : chr->descriptors) {
if (desc->uuid == espbt::ESPBTUUID::from_uint16(0x2902))
return desc;
}
}
}
}
return nullptr;
}
BLECharacteristic *BLEService::get_characteristic(espbt::ESPBTUUID uuid) {
for (auto &chr : this->characteristics) {
if (chr->uuid == uuid)
return chr;
}
return nullptr;
}
BLECharacteristic *BLEService::get_characteristic(uint16_t uuid) {
return this->get_characteristic(espbt::ESPBTUUID::from_uint16(uuid));
}
BLEDescriptor *BLEClient::get_descriptor(espbt::ESPBTUUID service, espbt::ESPBTUUID chr, espbt::ESPBTUUID descr) {
auto *svc = this->get_service(service);
if (svc == nullptr)
return nullptr;
auto *ch = svc->get_characteristic(chr);
if (ch == nullptr)
return nullptr;
return ch->get_descriptor(descr);
}
BLEDescriptor *BLEClient::get_descriptor(uint16_t service, uint16_t chr, uint16_t descr) {
return this->get_descriptor(espbt::ESPBTUUID::from_uint16(service), espbt::ESPBTUUID::from_uint16(chr),
espbt::ESPBTUUID::from_uint16(descr));
}
BLEService::~BLEService() {
for (auto &chr : this->characteristics)
delete chr; // NOLINT(cppcoreguidelines-owning-memory)
}
void BLEService::parse_characteristics() {
uint16_t offset = 0;
esp_gattc_char_elem_t result;
while (true) {
uint16_t count = 1;
esp_gatt_status_t status = esp_ble_gattc_get_all_char(
this->client->gattc_if, this->client->conn_id, this->start_handle, this->end_handle, &result, &count, offset);
if (status == ESP_GATT_INVALID_OFFSET || status == ESP_GATT_NOT_FOUND) {
break;
}
if (status != ESP_GATT_OK) {
ESP_LOGW(TAG, "esp_ble_gattc_get_all_char error, status=%d", status);
break;
}
if (count == 0) {
break;
}
BLECharacteristic *characteristic = new BLECharacteristic(); // NOLINT(cppcoreguidelines-owning-memory)
characteristic->uuid = espbt::ESPBTUUID::from_uuid(result.uuid);
characteristic->properties = result.properties;
characteristic->handle = result.char_handle;
characteristic->service = this;
this->characteristics.push_back(characteristic);
ESP_LOGI(TAG, " characteristic %s, handle 0x%x, properties 0x%x", characteristic->uuid.to_string().c_str(),
characteristic->handle, characteristic->properties);
characteristic->parse_descriptors();
offset++;
}
}
BLECharacteristic::~BLECharacteristic() {
for (auto &desc : this->descriptors)
delete desc; // NOLINT(cppcoreguidelines-owning-memory)
}
void BLECharacteristic::parse_descriptors() {
uint16_t offset = 0;
esp_gattc_descr_elem_t result;
while (true) {
uint16_t count = 1;
esp_gatt_status_t status = esp_ble_gattc_get_all_descr(
this->service->client->gattc_if, this->service->client->conn_id, this->handle, &result, &count, offset);
if (status == ESP_GATT_INVALID_OFFSET || status == ESP_GATT_NOT_FOUND) {
break;
}
if (status != ESP_GATT_OK) {
ESP_LOGW(TAG, "esp_ble_gattc_get_all_descr error, status=%d", status);
break;
}
if (count == 0) {
break;
}
BLEDescriptor *desc = new BLEDescriptor(); // NOLINT(cppcoreguidelines-owning-memory)
desc->uuid = espbt::ESPBTUUID::from_uuid(result.uuid);
desc->handle = result.handle;
desc->characteristic = this;
this->descriptors.push_back(desc);
ESP_LOGV(TAG, " descriptor %s, handle 0x%x", desc->uuid.to_string().c_str(), desc->handle);
offset++;
}
}
BLEDescriptor *BLECharacteristic::get_descriptor(espbt::ESPBTUUID uuid) {
for (auto &desc : this->descriptors) {
if (desc->uuid == uuid)
return desc;
}
return nullptr;
}
BLEDescriptor *BLECharacteristic::get_descriptor(uint16_t uuid) {
return this->get_descriptor(espbt::ESPBTUUID::from_uint16(uuid));
}
void BLECharacteristic::write_value(uint8_t *new_val, int16_t new_val_size, esp_gatt_write_type_t write_type) {
auto *client = this->service->client;
auto status = esp_ble_gattc_write_char(client->gattc_if, client->conn_id, this->handle, new_val_size, new_val,
write_type, ESP_GATT_AUTH_REQ_NONE);
if (status) {
ESP_LOGW(TAG, "Error sending write value to BLE gattc server, status=%d", status);
}
}
void BLECharacteristic::write_value(uint8_t *new_val, int16_t new_val_size) {
write_value(new_val, new_val_size, ESP_GATT_WRITE_TYPE_NO_RSP);
return true;
}
} // namespace ble_client

View File

@@ -1,8 +1,9 @@
#pragma once
#include "esphome/components/esp32_ble_client/ble_client_base.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include "esphome/core/component.h"
#include "esphome/core/helpers.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#ifdef USE_ESP32
@@ -18,15 +19,16 @@ namespace ble_client {
namespace espbt = esphome::esp32_ble_tracker;
using namespace esp32_ble_client;
class BLEClient;
class BLEService;
class BLECharacteristic;
class BLEClientNode {
public:
virtual void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) = 0;
virtual void loop(){};
virtual void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {}
virtual void loop() {}
void set_address(uint64_t address) { address_ = address; }
espbt::ESPBTClient *client;
// This should be transitioned to Established once the node no longer needs
@@ -42,57 +44,17 @@ class BLEClientNode {
uint64_t address_;
};
class BLEDescriptor {
public:
espbt::ESPBTUUID uuid;
uint16_t handle;
BLECharacteristic *characteristic;
};
class BLECharacteristic {
public:
~BLECharacteristic();
espbt::ESPBTUUID uuid;
uint16_t handle;
esp_gatt_char_prop_t properties;
std::vector<BLEDescriptor *> descriptors;
void parse_descriptors();
BLEDescriptor *get_descriptor(espbt::ESPBTUUID uuid);
BLEDescriptor *get_descriptor(uint16_t uuid);
void write_value(uint8_t *new_val, int16_t new_val_size);
void write_value(uint8_t *new_val, int16_t new_val_size, esp_gatt_write_type_t write_type);
BLEService *service;
};
class BLEService {
public:
~BLEService();
espbt::ESPBTUUID uuid;
uint16_t start_handle;
uint16_t end_handle;
std::vector<BLECharacteristic *> characteristics;
BLEClient *client;
void parse_characteristics();
BLECharacteristic *get_characteristic(espbt::ESPBTUUID uuid);
BLECharacteristic *get_characteristic(uint16_t uuid);
};
class BLEClient : public espbt::ESPBTClient, public Component {
class BLEClient : public BLEClientBase {
public:
void setup() override;
void dump_config() override;
void loop() override;
float get_setup_priority() const override;
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) override;
void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override;
bool parse_device(const espbt::ESPBTDevice &device) override;
void on_scan_end() override {}
void connect() override;
void set_address(uint64_t address) { this->address = address; }
void set_enabled(bool enabled);
@@ -102,43 +64,14 @@ class BLEClient : public espbt::ESPBTClient, public Component {
this->nodes_.push_back(node);
}
BLEService *get_service(espbt::ESPBTUUID uuid);
BLEService *get_service(uint16_t uuid);
BLECharacteristic *get_characteristic(espbt::ESPBTUUID service, espbt::ESPBTUUID chr);
BLECharacteristic *get_characteristic(uint16_t service, uint16_t chr);
BLEDescriptor *get_descriptor(espbt::ESPBTUUID service, espbt::ESPBTUUID chr, espbt::ESPBTUUID descr);
BLEDescriptor *get_descriptor(uint16_t service, uint16_t chr, uint16_t descr);
// Get the configuration descriptor for the given characteristic handle.
BLEDescriptor *get_config_descriptor(uint16_t handle);
float parse_char_value(uint8_t *value, uint16_t length);
int gattc_if;
esp_bd_addr_t remote_bda;
esp_ble_addr_type_t remote_addr_type;
uint16_t conn_id;
uint64_t address;
bool enabled;
std::string address_str() const;
void set_state(espbt::ClientState state) override;
protected:
void set_states_(espbt::ClientState st) {
this->set_state(st);
for (auto &node : nodes_)
node->node_state = st;
}
bool all_nodes_established_() {
if (this->state() != espbt::ClientState::ESTABLISHED)
return false;
for (auto &node : nodes_) {
if (node->node_state != espbt::ClientState::ESTABLISHED)
return false;
}
return true;
}
bool all_nodes_established_();
std::vector<BLEClientNode *> nodes_;
std::vector<BLEService *> services_;
};
} // namespace ble_client

View File

@@ -5,7 +5,11 @@ from esphome.const import (
CONF_CHARACTERISTIC_UUID,
CONF_LAMBDA,
CONF_TRIGGER_ID,
CONF_TYPE,
CONF_SERVICE_UUID,
DEVICE_CLASS_SIGNAL_STRENGTH,
STATE_CLASS_MEASUREMENT,
UNIT_DECIBEL_MILLIWATT,
)
from esphome import automation
from .. import ble_client_ns
@@ -16,6 +20,8 @@ CONF_DESCRIPTOR_UUID = "descriptor_uuid"
CONF_NOTIFY = "notify"
CONF_ON_NOTIFY = "on_notify"
TYPE_CHARACTERISTIC = "characteristic"
TYPE_RSSI = "rssi"
adv_data_t = cg.std_vector.template(cg.uint8)
adv_data_t_const_ref = adv_data_t.operator("ref").operator("const")
@@ -27,33 +33,67 @@ BLESensorNotifyTrigger = ble_client_ns.class_(
"BLESensorNotifyTrigger", automation.Trigger.template(cg.float_)
)
CONFIG_SCHEMA = cv.All(
sensor.sensor_schema(
BLESensor,
accuracy_decimals=0,
)
.extend(
{
cv.Required(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid,
cv.Required(CONF_CHARACTERISTIC_UUID): esp32_ble_tracker.bt_uuid,
cv.Optional(CONF_DESCRIPTOR_UUID): esp32_ble_tracker.bt_uuid,
cv.Optional(CONF_LAMBDA): cv.returning_lambda,
cv.Optional(CONF_NOTIFY, default=False): cv.boolean,
cv.Optional(CONF_ON_NOTIFY): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
BLESensorNotifyTrigger
),
}
),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(ble_client.BLE_CLIENT_SCHEMA)
BLEClientRssiSensor = ble_client_ns.class_(
"BLEClientRSSISensor", sensor.Sensor, cg.PollingComponent, ble_client.BLEClientNode
)
async def to_code(config):
def checkType(value):
if CONF_TYPE not in value and CONF_SERVICE_UUID in value:
raise cv.Invalid(
"Looks like you're trying to create a ble characteristic sensor. Please add `type: characteristic` to your sensor config."
)
return value
CONFIG_SCHEMA = cv.All(
checkType,
cv.typed_schema(
{
TYPE_CHARACTERISTIC: sensor.sensor_schema(
BLESensor,
accuracy_decimals=0,
)
.extend(cv.polling_component_schema("60s"))
.extend(ble_client.BLE_CLIENT_SCHEMA)
.extend(
{
cv.Required(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid,
cv.Required(CONF_CHARACTERISTIC_UUID): esp32_ble_tracker.bt_uuid,
cv.Optional(CONF_DESCRIPTOR_UUID): esp32_ble_tracker.bt_uuid,
cv.Optional(CONF_LAMBDA): cv.returning_lambda,
cv.Optional(CONF_NOTIFY, default=False): cv.boolean,
cv.Optional(CONF_ON_NOTIFY): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
BLESensorNotifyTrigger
),
}
),
}
),
TYPE_RSSI: sensor.sensor_schema(
BLEClientRssiSensor,
accuracy_decimals=0,
unit_of_measurement=UNIT_DECIBEL_MILLIWATT,
device_class=DEVICE_CLASS_SIGNAL_STRENGTH,
state_class=STATE_CLASS_MEASUREMENT,
)
.extend(cv.polling_component_schema("60s"))
.extend(ble_client.BLE_CLIENT_SCHEMA),
},
lower=True,
),
)
async def rssi_sensor_to_code(config):
var = await sensor.new_sensor(config)
await cg.register_component(var, config)
await ble_client.register_ble_node(var, config)
async def characteristic_sensor_to_code(config):
var = await sensor.new_sensor(config)
if len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
cg.add(
@@ -125,3 +165,10 @@ async def to_code(config):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await ble_client.register_ble_node(trigger, config)
await automation.build_automation(trigger, [(float, "x")], conf)
async def to_code(config):
if config[CONF_TYPE] == TYPE_RSSI:
await rssi_sensor_to_code(config)
elif config[CONF_TYPE] == TYPE_CHARACTERISTIC:
await characteristic_sensor_to_code(config)

View File

@@ -19,7 +19,8 @@ class BLESensorNotifyTrigger : public Trigger<float>, public BLESensor {
break;
}
case ESP_GATTC_NOTIFY_EVT: {
if (param->notify.conn_id != this->sensor_->parent()->conn_id || param->notify.handle != this->sensor_->handle)
if (param->notify.conn_id != this->sensor_->parent()->get_conn_id() ||
param->notify.handle != this->sensor_->handle)
break;
this->trigger(this->sensor_->parent()->parse_char_value(param->notify.value, param->notify.value_len));
}

View File

@@ -0,0 +1,78 @@
#include "ble_rssi_sensor.h"
#include "esphome/core/log.h"
#include "esphome/core/application.h"
#include "esphome/core/helpers.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#ifdef USE_ESP32
namespace esphome {
namespace ble_client {
static const char *const TAG = "ble_rssi_sensor";
void BLEClientRSSISensor::loop() {}
void BLEClientRSSISensor::dump_config() {
LOG_SENSOR("", "BLE Client RSSI Sensor", this);
ESP_LOGCONFIG(TAG, " MAC address : %s", this->parent()->address_str().c_str());
LOG_UPDATE_INTERVAL(this);
}
void BLEClientRSSISensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) {
switch (event) {
case ESP_GATTC_OPEN_EVT: {
if (param->open.status == ESP_GATT_OK) {
ESP_LOGI(TAG, "[%s] Connected successfully!", this->get_name().c_str());
break;
}
break;
}
case ESP_GATTC_DISCONNECT_EVT: {
ESP_LOGW(TAG, "[%s] Disconnected!", this->get_name().c_str());
this->status_set_warning();
this->publish_state(NAN);
break;
}
case ESP_GATTC_SEARCH_CMPL_EVT:
this->node_state = espbt::ClientState::ESTABLISHED;
break;
default:
break;
}
}
void BLEClientRSSISensor::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
switch (event) {
// server response on RSSI request:
case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT:
if (param->read_rssi_cmpl.status == ESP_BT_STATUS_SUCCESS) {
int8_t rssi = param->read_rssi_cmpl.rssi;
ESP_LOGI(TAG, "ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT RSSI: %d", rssi);
this->publish_state(rssi);
}
break;
default:
break;
}
}
void BLEClientRSSISensor::update() {
if (this->node_state != espbt::ClientState::ESTABLISHED) {
ESP_LOGW(TAG, "[%s] Cannot poll, not connected", this->get_name().c_str());
return;
}
ESP_LOGV(TAG, "requesting rssi from %s", this->parent()->address_str().c_str());
auto status = esp_ble_gap_read_rssi(this->parent()->get_remote_bda());
if (status != ESP_OK) {
ESP_LOGW(TAG, "esp_ble_gap_read_rssi error, address=%s, status=%d", this->parent()->address_str().c_str(), status);
this->status_set_warning();
this->publish_state(NAN);
}
}
} // namespace ble_client
} // namespace esphome
#endif

View File

@@ -0,0 +1,31 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/ble_client/ble_client.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include "esphome/components/sensor/sensor.h"
#ifdef USE_ESP32
#include <esp_gattc_api.h>
namespace esphome {
namespace ble_client {
namespace espbt = esphome::esp32_ble_tracker;
class BLEClientRSSISensor : public sensor::Sensor, public PollingComponent, public BLEClientNode {
public:
void loop() override;
void update() override;
void dump_config() override;
float get_setup_priority() const override { return setup_priority::DATA; }
void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override;
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) override;
};
} // namespace ble_client
} // namespace esphome
#endif

View File

@@ -63,8 +63,8 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
this->handle = descr->handle;
}
if (this->notify_) {
auto status =
esp_ble_gattc_register_for_notify(this->parent()->gattc_if, this->parent()->remote_bda, chr->handle);
auto status = esp_ble_gattc_register_for_notify(this->parent()->get_gattc_if(),
this->parent()->get_remote_bda(), chr->handle);
if (status) {
ESP_LOGW(TAG, "esp_ble_gattc_register_for_notify failed, status=%d", status);
}
@@ -74,7 +74,7 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
break;
}
case ESP_GATTC_READ_CHAR_EVT: {
if (param->read.conn_id != this->parent()->conn_id)
if (param->read.conn_id != this->parent()->get_conn_id())
break;
if (param->read.status != ESP_GATT_OK) {
ESP_LOGW(TAG, "Error reading char at handle %d, status=%d", param->read.handle, param->read.status);
@@ -87,7 +87,7 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
break;
}
case ESP_GATTC_NOTIFY_EVT: {
if (param->notify.conn_id != this->parent()->conn_id || param->notify.handle != this->handle)
if (param->notify.conn_id != this->parent()->get_conn_id() || param->notify.handle != this->handle)
break;
ESP_LOGV(TAG, "[%s] ESP_GATTC_NOTIFY_EVT: handle=0x%x, value=0x%x", this->get_name().c_str(),
param->notify.handle, param->notify.value[0]);
@@ -122,8 +122,8 @@ void BLESensor::update() {
return;
}
auto status =
esp_ble_gattc_read_char(this->parent()->gattc_if, this->parent()->conn_id, this->handle, ESP_GATT_AUTH_REQ_NONE);
auto status = esp_ble_gattc_read_char(this->parent()->get_gattc_if(), this->parent()->get_conn_id(), this->handle,
ESP_GATT_AUTH_REQ_NONE);
if (status) {
this->status_set_warning();
this->publish_state(NAN);

View File

@@ -1,7 +1,7 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import switch, ble_client
from esphome.const import CONF_ICON, CONF_ID, CONF_INVERTED, ICON_BLUETOOTH
from esphome.const import ICON_BLUETOOTH
from .. import ble_client_ns
BLEClientSwitch = ble_client_ns.class_(
@@ -9,22 +9,13 @@ BLEClientSwitch = ble_client_ns.class_(
)
CONFIG_SCHEMA = (
switch.SWITCH_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(BLEClientSwitch),
cv.Optional(CONF_INVERTED): cv.invalid(
"BLE client switches do not support inverted mode!"
),
cv.Optional(CONF_ICON, default=ICON_BLUETOOTH): switch.icon,
}
)
switch.switch_schema(BLEClientSwitch, icon=ICON_BLUETOOTH, block_inverted=True)
.extend(ble_client.BLE_CLIENT_SCHEMA)
.extend(cv.COMPONENT_SCHEMA)
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
var = await switch.new_switch(config)
await cg.register_component(var, config)
await switch.register_switch(var, config)
await ble_client.register_ble_node(var, config)

View File

@@ -19,7 +19,8 @@ class BLETextSensorNotifyTrigger : public Trigger<std::string>, public BLETextSe
break;
}
case ESP_GATTC_NOTIFY_EVT: {
if (param->notify.conn_id != this->sensor_->parent()->conn_id || param->notify.handle != this->sensor_->handle)
if (param->notify.conn_id != this->sensor_->parent()->get_conn_id() ||
param->notify.handle != this->sensor_->handle)
break;
this->trigger(this->sensor_->parse_data(param->notify.value, param->notify.value_len));
}

View File

@@ -66,8 +66,8 @@ void BLETextSensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
this->handle = descr->handle;
}
if (this->notify_) {
auto status =
esp_ble_gattc_register_for_notify(this->parent()->gattc_if, this->parent()->remote_bda, chr->handle);
auto status = esp_ble_gattc_register_for_notify(this->parent()->get_gattc_if(),
this->parent()->get_remote_bda(), chr->handle);
if (status) {
ESP_LOGW(TAG, "esp_ble_gattc_register_for_notify failed, status=%d", status);
}
@@ -77,7 +77,7 @@ void BLETextSensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
break;
}
case ESP_GATTC_READ_CHAR_EVT: {
if (param->read.conn_id != this->parent()->conn_id)
if (param->read.conn_id != this->parent()->get_conn_id())
break;
if (param->read.status != ESP_GATT_OK) {
ESP_LOGW(TAG, "Error reading char at handle %d, status=%d", param->read.handle, param->read.status);
@@ -90,7 +90,7 @@ void BLETextSensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
break;
}
case ESP_GATTC_NOTIFY_EVT: {
if (param->notify.conn_id != this->parent()->conn_id || param->notify.handle != this->handle)
if (param->notify.conn_id != this->parent()->get_conn_id() || param->notify.handle != this->handle)
break;
ESP_LOGV(TAG, "[%s] ESP_GATTC_NOTIFY_EVT: handle=0x%x, value=0x%x", this->get_name().c_str(),
param->notify.handle, param->notify.value[0]);
@@ -121,8 +121,8 @@ void BLETextSensor::update() {
return;
}
auto status =
esp_ble_gattc_read_char(this->parent()->gattc_if, this->parent()->conn_id, this->handle, ESP_GATT_AUTH_REQ_NONE);
auto status = esp_ble_gattc_read_char(this->parent()->get_gattc_if(), this->parent()->get_conn_id(), this->handle,
ESP_GATT_AUTH_REQ_NONE);
if (status) {
this->status_set_warning();
this->publish_state(EMPTY);

View File

@@ -58,7 +58,7 @@ class BLEPresenceDevice : public binary_sensor::BinarySensorInitiallyOff,
case MATCH_BY_SERVICE_UUID:
for (auto uuid : device.get_service_uuids()) {
if (this->uuid_ == uuid) {
this->publish_state(device.get_rssi());
this->publish_state(true);
this->found_ = true;
return true;
}
@@ -83,7 +83,7 @@ class BLEPresenceDevice : public binary_sensor::BinarySensorInitiallyOff,
return false;
}
this->publish_state(device.get_rssi());
this->publish_state(true);
this->found_ = true;
return true;
}

View File

@@ -12,41 +12,78 @@ namespace ble_rssi {
class BLERSSISensor : public sensor::Sensor, public esp32_ble_tracker::ESPBTDeviceListener, public Component {
public:
void set_address(uint64_t address) {
this->by_address_ = true;
this->match_by_ = MATCH_BY_MAC_ADDRESS;
this->address_ = address;
}
void set_service_uuid16(uint16_t uuid) {
this->by_address_ = false;
this->match_by_ = MATCH_BY_SERVICE_UUID;
this->uuid_ = esp32_ble_tracker::ESPBTUUID::from_uint16(uuid);
}
void set_service_uuid32(uint32_t uuid) {
this->by_address_ = false;
this->match_by_ = MATCH_BY_SERVICE_UUID;
this->uuid_ = esp32_ble_tracker::ESPBTUUID::from_uint32(uuid);
}
void set_service_uuid128(uint8_t *uuid) {
this->by_address_ = false;
this->match_by_ = MATCH_BY_SERVICE_UUID;
this->uuid_ = esp32_ble_tracker::ESPBTUUID::from_raw(uuid);
}
void set_ibeacon_uuid(uint8_t *uuid) {
this->match_by_ = MATCH_BY_IBEACON_UUID;
this->ibeacon_uuid_ = esp32_ble_tracker::ESPBTUUID::from_raw(uuid);
}
void set_ibeacon_major(uint16_t major) {
this->check_ibeacon_major_ = true;
this->ibeacon_major_ = major;
}
void set_ibeacon_minor(uint16_t minor) {
this->check_ibeacon_minor_ = true;
this->ibeacon_minor_ = minor;
}
void on_scan_end() override {
if (!this->found_)
this->publish_state(NAN);
this->found_ = false;
}
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override {
if (this->by_address_) {
if (device.address_uint64() == this->address_) {
this->publish_state(device.get_rssi());
this->found_ = true;
return true;
}
} else {
for (auto uuid : device.get_service_uuids()) {
if (this->uuid_ == uuid) {
switch (this->match_by_) {
case MATCH_BY_MAC_ADDRESS:
if (device.address_uint64() == this->address_) {
this->publish_state(device.get_rssi());
this->found_ = true;
return true;
}
}
break;
case MATCH_BY_SERVICE_UUID:
for (auto uuid : device.get_service_uuids()) {
if (this->uuid_ == uuid) {
this->publish_state(device.get_rssi());
this->found_ = true;
return true;
}
}
break;
case MATCH_BY_IBEACON_UUID:
if (!device.get_ibeacon().has_value()) {
return false;
}
auto ibeacon = device.get_ibeacon().value();
if (this->ibeacon_uuid_ != ibeacon.get_uuid()) {
return false;
}
if (this->check_ibeacon_major_ && this->ibeacon_major_ != ibeacon.get_major()) {
return false;
}
if (this->check_ibeacon_minor_ && this->ibeacon_minor_ != ibeacon.get_minor()) {
return false;
}
this->publish_state(device.get_rssi());
this->found_ = true;
return true;
}
return false;
}
@@ -54,10 +91,20 @@ class BLERSSISensor : public sensor::Sensor, public esp32_ble_tracker::ESPBTDevi
float get_setup_priority() const override { return setup_priority::DATA; }
protected:
enum MatchType { MATCH_BY_MAC_ADDRESS, MATCH_BY_SERVICE_UUID, MATCH_BY_IBEACON_UUID };
MatchType match_by_;
bool found_{false};
bool by_address_{false};
uint64_t address_;
esp32_ble_tracker::ESPBTUUID uuid_;
esp32_ble_tracker::ESPBTUUID ibeacon_uuid_;
uint16_t ibeacon_major_;
bool check_ibeacon_major_;
uint16_t ibeacon_minor_;
bool check_ibeacon_minor_;
};
} // namespace ble_rssi

View File

@@ -2,11 +2,14 @@ import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor, esp32_ble_tracker
from esphome.const import (
CONF_IBEACON_MAJOR,
CONF_IBEACON_MINOR,
CONF_IBEACON_UUID,
CONF_SERVICE_UUID,
CONF_MAC_ADDRESS,
DEVICE_CLASS_SIGNAL_STRENGTH,
STATE_CLASS_MEASUREMENT,
UNIT_DECIBEL,
UNIT_DECIBEL_MILLIWATT,
)
DEPENDENCIES = ["esp32_ble_tracker"]
@@ -16,10 +19,19 @@ BLERSSISensor = ble_rssi_ns.class_(
"BLERSSISensor", sensor.Sensor, cg.Component, esp32_ble_tracker.ESPBTDeviceListener
)
def _validate(config):
if CONF_IBEACON_MAJOR in config and CONF_IBEACON_UUID not in config:
raise cv.Invalid("iBeacon major identifier requires iBeacon UUID")
if CONF_IBEACON_MINOR in config and CONF_IBEACON_UUID not in config:
raise cv.Invalid("iBeacon minor identifier requires iBeacon UUID")
return config
CONFIG_SCHEMA = cv.All(
sensor.sensor_schema(
BLERSSISensor,
unit_of_measurement=UNIT_DECIBEL,
unit_of_measurement=UNIT_DECIBEL_MILLIWATT,
accuracy_decimals=0,
device_class=DEVICE_CLASS_SIGNAL_STRENGTH,
state_class=STATE_CLASS_MEASUREMENT,
@@ -28,11 +40,15 @@ CONFIG_SCHEMA = cv.All(
{
cv.Optional(CONF_MAC_ADDRESS): cv.mac_address,
cv.Optional(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid,
cv.Optional(CONF_IBEACON_MAJOR): cv.uint16_t,
cv.Optional(CONF_IBEACON_MINOR): cv.uint16_t,
cv.Optional(CONF_IBEACON_UUID): cv.uuid,
}
)
.extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
.extend(cv.COMPONENT_SCHEMA),
cv.has_exactly_one_key(CONF_MAC_ADDRESS, CONF_SERVICE_UUID),
cv.has_exactly_one_key(CONF_MAC_ADDRESS, CONF_SERVICE_UUID, CONF_IBEACON_UUID),
_validate,
)
@@ -60,3 +76,13 @@ async def to_code(config):
elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid128_format):
uuid128 = esp32_ble_tracker.as_reversed_hex_array(config[CONF_SERVICE_UUID])
cg.add(var.set_service_uuid128(uuid128))
if CONF_IBEACON_UUID in config:
ibeacon_uuid = esp32_ble_tracker.as_hex_array(str(config[CONF_IBEACON_UUID]))
cg.add(var.set_ibeacon_uuid(ibeacon_uuid))
if CONF_IBEACON_MAJOR in config:
cg.add(var.set_ibeacon_major(config[CONF_IBEACON_MAJOR]))
if CONF_IBEACON_MINOR in config:
cg.add(var.set_ibeacon_minor(config[CONF_IBEACON_MINOR]))

View File

@@ -0,0 +1,33 @@
from esphome.components import esp32_ble_tracker, esp32_ble_client
import esphome.config_validation as cv
import esphome.codegen as cg
from esphome.const import CONF_ACTIVE, CONF_ID
AUTO_LOAD = ["esp32_ble_client", "esp32_ble_tracker"]
DEPENDENCIES = ["api", "esp32"]
CODEOWNERS = ["@jesserockz"]
bluetooth_proxy_ns = cg.esphome_ns.namespace("bluetooth_proxy")
BluetoothProxy = bluetooth_proxy_ns.class_(
"BluetoothProxy", esp32_ble_client.BLEClientBase
)
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(BluetoothProxy),
cv.Optional(CONF_ACTIVE, default=False): cv.boolean,
}
).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
cg.add(var.set_active(config[CONF_ACTIVE]))
await esp32_ble_tracker.register_client(var, config)
cg.add_define("USE_BLUETOOTH_PROXY")

View File

@@ -0,0 +1,406 @@
#include "bluetooth_proxy.h"
#include "esphome/core/log.h"
#ifdef USE_ESP32
#include "esphome/components/api/api_server.h"
namespace esphome {
namespace bluetooth_proxy {
static const char *const TAG = "bluetooth_proxy";
static const esp_err_t ESP_GATT_NOT_CONNECTED = -1;
static const esp_err_t ESP_GATT_WRONG_ADDRESS = -2;
BluetoothProxy::BluetoothProxy() {
global_bluetooth_proxy = this;
this->address_ = 0;
}
bool BluetoothProxy::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
if (!api::global_api_server->is_connected())
return false;
ESP_LOGV(TAG, "Proxying packet from %s - %s. RSSI: %d dB", device.get_name().c_str(), device.address_str().c_str(),
device.get_rssi());
this->send_api_packet_(device);
if (this->address_ == 0)
return true;
BLEClientBase::parse_device(device);
return true;
}
void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device) {
api::BluetoothLEAdvertisementResponse resp;
resp.address = device.address_uint64();
if (!device.get_name().empty())
resp.name = device.get_name();
resp.rssi = device.get_rssi();
for (auto uuid : device.get_service_uuids()) {
resp.service_uuids.push_back(uuid.to_string());
}
for (auto &data : device.get_service_datas()) {
api::BluetoothServiceData service_data;
service_data.uuid = data.uuid.to_string();
service_data.data.assign(data.data.begin(), data.data.end());
resp.service_data.push_back(std::move(service_data));
}
for (auto &data : device.get_manufacturer_datas()) {
api::BluetoothServiceData manufacturer_data;
manufacturer_data.uuid = data.uuid.to_string();
manufacturer_data.data.assign(data.data.begin(), data.data.end());
resp.manufacturer_data.push_back(std::move(manufacturer_data));
}
api::global_api_server->send_bluetooth_le_advertisement(resp);
}
void BluetoothProxy::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) {
BLEClientBase::gattc_event_handler(event, gattc_if, param);
switch (event) {
case ESP_GATTC_DISCONNECT_EVT: {
api::global_api_server->send_bluetooth_device_connection(this->address_, false, this->mtu_,
param->disconnect.reason);
api::global_api_server->send_bluetooth_connections_free(this->get_bluetooth_connections_free(),
this->get_bluetooth_connections_limit());
this->address_ = 0;
}
case ESP_GATTC_OPEN_EVT: {
if (param->open.status != ESP_GATT_OK && param->open.status != ESP_GATT_ALREADY_OPEN) {
api::global_api_server->send_bluetooth_device_connection(this->address_, false, this->mtu_, param->open.status);
break;
}
break;
}
case ESP_GATTC_SEARCH_CMPL_EVT: {
api::global_api_server->send_bluetooth_device_connection(this->address_, true, this->mtu_);
api::global_api_server->send_bluetooth_connections_free(this->get_bluetooth_connections_free(),
this->get_bluetooth_connections_limit());
break;
}
case ESP_GATTC_READ_DESCR_EVT:
case ESP_GATTC_READ_CHAR_EVT: {
if (param->read.conn_id != this->conn_id_)
break;
if (param->read.status != ESP_GATT_OK) {
ESP_LOGW(TAG, "Error reading char/descriptor at handle 0x%2X, status=%d", param->read.handle,
param->read.status);
api::global_api_server->send_bluetooth_gatt_error(this->address_, param->read.handle, param->read.status);
break;
}
api::BluetoothGATTReadResponse resp;
resp.address = this->address_;
resp.handle = param->read.handle;
resp.data.reserve(param->read.value_len);
for (uint16_t i = 0; i < param->read.value_len; i++) {
resp.data.push_back(param->read.value[i]);
}
api::global_api_server->send_bluetooth_gatt_read_response(resp);
break;
}
case ESP_GATTC_WRITE_CHAR_EVT:
case ESP_GATTC_WRITE_DESCR_EVT: {
if (param->write.conn_id != this->conn_id_)
break;
if (param->write.status != ESP_GATT_OK) {
ESP_LOGW(TAG, "Error writing char/descriptor at handle 0x%2X, status=%d", param->write.handle,
param->write.status);
api::global_api_server->send_bluetooth_gatt_error(this->address_, param->write.handle, param->write.status);
break;
}
api::BluetoothGATTWriteResponse resp;
resp.address = this->address_;
resp.handle = param->write.handle;
api::global_api_server->send_bluetooth_gatt_write_response(resp);
break;
}
case ESP_GATTC_UNREG_FOR_NOTIFY_EVT: {
if (param->unreg_for_notify.status != ESP_GATT_OK) {
ESP_LOGW(TAG, "Error unregistering notifications for handle 0x%2X, status=%d", param->unreg_for_notify.handle,
param->unreg_for_notify.status);
api::global_api_server->send_bluetooth_gatt_error(this->address_, param->unreg_for_notify.handle,
param->unreg_for_notify.status);
break;
}
api::BluetoothGATTNotifyResponse resp;
resp.address = this->address_;
resp.handle = param->unreg_for_notify.handle;
api::global_api_server->send_bluetooth_gatt_notify_response(resp);
break;
}
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
if (param->reg_for_notify.status != ESP_GATT_OK) {
ESP_LOGW(TAG, "Error registering notifications for handle 0x%2X, status=%d", param->reg_for_notify.handle,
param->reg_for_notify.status);
api::global_api_server->send_bluetooth_gatt_error(this->address_, param->reg_for_notify.handle,
param->reg_for_notify.status);
break;
}
api::BluetoothGATTNotifyResponse resp;
resp.address = this->address_;
resp.handle = param->reg_for_notify.handle;
api::global_api_server->send_bluetooth_gatt_notify_response(resp);
break;
}
case ESP_GATTC_NOTIFY_EVT: {
if (param->notify.conn_id != this->conn_id_)
break;
ESP_LOGV(TAG, "ESP_GATTC_NOTIFY_EVT: handle=0x%2X", param->notify.handle);
api::BluetoothGATTNotifyDataResponse resp;
resp.address = this->address_;
resp.handle = param->notify.handle;
resp.data.reserve(param->notify.value_len);
for (uint16_t i = 0; i < param->notify.value_len; i++) {
resp.data.push_back(param->notify.value[i]);
}
api::global_api_server->send_bluetooth_gatt_notify_data_response(resp);
break;
}
default:
break;
}
}
void BluetoothProxy::dump_config() { ESP_LOGCONFIG(TAG, "Bluetooth Proxy:"); }
void BluetoothProxy::loop() {
BLEClientBase::loop();
if (this->state_ != espbt::ClientState::IDLE && !api::global_api_server->is_connected()) {
ESP_LOGI(TAG, "[%s] Disconnecting.", this->address_str().c_str());
auto err = esp_ble_gattc_close(this->gattc_if_, this->conn_id_);
if (err != ERR_OK) {
ESP_LOGW(TAG, "esp_ble_gattc_close error, address=%s err=%d", this->address_str().c_str(), err);
}
}
if (this->send_service_ == this->services_.size()) {
this->send_service_ = -1;
api::global_api_server->send_bluetooth_gatt_services_done(this->address_);
} else if (this->send_service_ >= 0) {
auto &service = this->services_[this->send_service_];
api::BluetoothGATTGetServicesResponse resp;
resp.address = this->address_;
api::BluetoothGATTService service_resp;
service_resp.uuid = {service->uuid.get_128bit_high(), service->uuid.get_128bit_low()};
service_resp.handle = service->start_handle;
for (auto &characteristic : service->characteristics) {
api::BluetoothGATTCharacteristic characteristic_resp;
characteristic_resp.uuid = {characteristic->uuid.get_128bit_high(), characteristic->uuid.get_128bit_low()};
characteristic_resp.handle = characteristic->handle;
characteristic_resp.properties = characteristic->properties;
for (auto &descriptor : characteristic->descriptors) {
api::BluetoothGATTDescriptor descriptor_resp;
descriptor_resp.uuid = {descriptor->uuid.get_128bit_high(), descriptor->uuid.get_128bit_low()};
descriptor_resp.handle = descriptor->handle;
characteristic_resp.descriptors.push_back(std::move(descriptor_resp));
}
service_resp.characteristics.push_back(std::move(characteristic_resp));
}
resp.services.push_back(std::move(service_resp));
api::global_api_server->send_bluetooth_gatt_services(resp);
this->send_service_++;
}
}
void BluetoothProxy::bluetooth_device_request(const api::BluetoothDeviceRequest &msg) {
switch (msg.request_type) {
case api::enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT: {
this->address_ = msg.address;
this->set_state(espbt::ClientState::SEARCHING);
api::global_api_server->send_bluetooth_connections_free(this->get_bluetooth_connections_free(),
this->get_bluetooth_connections_limit());
break;
}
case api::enums::BLUETOOTH_DEVICE_REQUEST_TYPE_DISCONNECT: {
if (this->state() != espbt::ClientState::IDLE) {
ESP_LOGI(TAG, "[%s] Disconnecting.", this->address_str().c_str());
auto err = esp_ble_gattc_close(this->gattc_if_, this->conn_id_);
if (err != ERR_OK) {
ESP_LOGW(TAG, "esp_ble_gattc_close error, address=%s err=%d", this->address_str().c_str(), err);
}
}
break;
}
case api::enums::BLUETOOTH_DEVICE_REQUEST_TYPE_PAIR:
case api::enums::BLUETOOTH_DEVICE_REQUEST_TYPE_UNPAIR:
break;
}
}
void BluetoothProxy::bluetooth_gatt_read(const api::BluetoothGATTReadRequest &msg) {
if (this->state_ != espbt::ClientState::ESTABLISHED) {
ESP_LOGW(TAG, "Cannot read GATT characteristic, not connected.");
api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_NOT_CONNECTED);
return;
}
if (this->address_ != msg.address) {
ESP_LOGW(TAG, "Address mismatch for read GATT characteristic request");
api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_WRONG_ADDRESS);
return;
}
auto *characteristic = this->get_characteristic(msg.handle);
if (characteristic == nullptr) {
ESP_LOGW(TAG, "Cannot read GATT characteristic, not found.");
api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_INVALID_HANDLE);
return;
}
ESP_LOGV(TAG, "Reading GATT characteristic %s", characteristic->uuid.to_string().c_str());
esp_err_t err =
esp_ble_gattc_read_char(this->gattc_if_, this->conn_id_, characteristic->handle, ESP_GATT_AUTH_REQ_NONE);
if (err != ERR_OK) {
ESP_LOGW(TAG, "esp_ble_gattc_read_char error, err=%d", err);
api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, err);
}
}
void BluetoothProxy::bluetooth_gatt_write(const api::BluetoothGATTWriteRequest &msg) {
if (this->state_ != espbt::ClientState::ESTABLISHED) {
ESP_LOGW(TAG, "Cannot write GATT characteristic, not connected.");
api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_NOT_CONNECTED);
return;
}
if (this->address_ != msg.address) {
ESP_LOGW(TAG, "Address mismatch for write GATT characteristic request");
api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_WRONG_ADDRESS);
return;
}
auto *characteristic = this->get_characteristic(msg.handle);
if (characteristic == nullptr) {
ESP_LOGW(TAG, "Cannot write GATT characteristic, not found.");
api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_INVALID_HANDLE);
return;
}
ESP_LOGV(TAG, "Writing GATT characteristic %s", characteristic->uuid.to_string().c_str());
auto err = characteristic->write_value((uint8_t *) msg.data.data(), msg.data.size(),
msg.response ? ESP_GATT_WRITE_TYPE_RSP : ESP_GATT_WRITE_TYPE_NO_RSP);
if (err != ERR_OK) {
api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, err);
}
}
void BluetoothProxy::bluetooth_gatt_read_descriptor(const api::BluetoothGATTReadDescriptorRequest &msg) {
if (this->state_ != espbt::ClientState::ESTABLISHED) {
ESP_LOGW(TAG, "Cannot read GATT characteristic descriptor, not connected.");
api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_NOT_CONNECTED);
return;
}
if (this->address_ != msg.address) {
ESP_LOGW(TAG, "Address mismatch for read GATT characteristic descriptor request");
api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_WRONG_ADDRESS);
return;
}
auto *descriptor = this->get_descriptor(msg.handle);
if (descriptor == nullptr) {
ESP_LOGW(TAG, "Cannot read GATT characteristic descriptor, not found.");
api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_INVALID_HANDLE);
return;
}
ESP_LOGV(TAG, "Reading GATT characteristic descriptor %s -> %s", descriptor->characteristic->uuid.to_string().c_str(),
descriptor->uuid.to_string().c_str());
esp_err_t err =
esp_ble_gattc_read_char_descr(this->gattc_if_, this->conn_id_, descriptor->handle, ESP_GATT_AUTH_REQ_NONE);
if (err != ERR_OK) {
ESP_LOGW(TAG, "esp_ble_gattc_read_char error, err=%d", err);
api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, err);
}
}
void BluetoothProxy::bluetooth_gatt_write_descriptor(const api::BluetoothGATTWriteDescriptorRequest &msg) {
if (this->state_ != espbt::ClientState::ESTABLISHED) {
ESP_LOGW(TAG, "Cannot write GATT characteristic descriptor, not connected.");
api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_NOT_CONNECTED);
return;
}
if (this->address_ != msg.address) {
ESP_LOGW(TAG, "Address mismatch for write GATT characteristic descriptor request");
api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_WRONG_ADDRESS);
return;
}
auto *descriptor = this->get_descriptor(msg.handle);
if (descriptor == nullptr) {
ESP_LOGW(TAG, "Cannot write GATT characteristic descriptor, not found.");
api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_INVALID_HANDLE);
return;
}
ESP_LOGV(TAG, "Writing GATT characteristic descriptor %s -> %s", descriptor->characteristic->uuid.to_string().c_str(),
descriptor->uuid.to_string().c_str());
esp_err_t err =
esp_ble_gattc_write_char_descr(this->gattc_if_, this->conn_id_, descriptor->handle, msg.data.size(),
(uint8_t *) msg.data.data(), ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
if (err != ERR_OK) {
ESP_LOGW(TAG, "esp_ble_gattc_write_char_descr error, err=%d", err);
api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, err);
}
}
void BluetoothProxy::bluetooth_gatt_send_services(const api::BluetoothGATTGetServicesRequest &msg) {
if (this->state_ != espbt::ClientState::ESTABLISHED) {
ESP_LOGW(TAG, "Cannot get GATT services, not connected.");
api::global_api_server->send_bluetooth_gatt_error(msg.address, 0, ESP_GATT_NOT_CONNECTED);
return;
}
if (this->address_ != msg.address) {
ESP_LOGW(TAG, "Address mismatch for service list request");
api::global_api_server->send_bluetooth_gatt_error(msg.address, 0, ESP_GATT_WRONG_ADDRESS);
return;
}
this->send_service_ = 0;
}
void BluetoothProxy::bluetooth_gatt_notify(const api::BluetoothGATTNotifyRequest &msg) {
if (this->state_ != espbt::ClientState::ESTABLISHED) {
ESP_LOGW(TAG, "Cannot configure notify, not connected.");
api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_NOT_CONNECTED);
return;
}
if (this->address_ != msg.address) {
ESP_LOGW(TAG, "Address mismatch for notify");
api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_WRONG_ADDRESS);
return;
}
auto *characteristic = this->get_characteristic(msg.handle);
if (characteristic == nullptr) {
ESP_LOGW(TAG, "Cannot notify GATT characteristic, not found.");
api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_INVALID_HANDLE);
return;
}
esp_err_t err;
if (msg.enable) {
err = esp_ble_gattc_register_for_notify(this->gattc_if_, this->remote_bda_, characteristic->handle);
if (err != ESP_OK) {
ESP_LOGW(TAG, "esp_ble_gattc_register_for_notify failed, err=%d", err);
api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, err);
}
} else {
err = esp_ble_gattc_unregister_for_notify(this->gattc_if_, this->remote_bda_, characteristic->handle);
if (err != ESP_OK) {
ESP_LOGW(TAG, "esp_ble_gattc_unregister_for_notify failed, err=%d", err);
api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, err);
}
}
}
BluetoothProxy *global_bluetooth_proxy = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
} // namespace bluetooth_proxy
} // namespace esphome
#endif // USE_ESP32

View File

@@ -0,0 +1,57 @@
#pragma once
#ifdef USE_ESP32
#include <map>
#include "esphome/components/api/api_pb2.h"
#include "esphome/components/esp32_ble_client/ble_client_base.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include "esphome/core/automation.h"
#include "esphome/core/component.h"
#include "esphome/core/defines.h"
#include <map>
namespace esphome {
namespace bluetooth_proxy {
using namespace esp32_ble_client;
class BluetoothProxy : public BLEClientBase {
public:
BluetoothProxy();
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
void dump_config() override;
void loop() override;
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) override;
void bluetooth_device_request(const api::BluetoothDeviceRequest &msg);
void bluetooth_gatt_read(const api::BluetoothGATTReadRequest &msg);
void bluetooth_gatt_write(const api::BluetoothGATTWriteRequest &msg);
void bluetooth_gatt_read_descriptor(const api::BluetoothGATTReadDescriptorRequest &msg);
void bluetooth_gatt_write_descriptor(const api::BluetoothGATTWriteDescriptorRequest &msg);
void bluetooth_gatt_send_services(const api::BluetoothGATTGetServicesRequest &msg);
void bluetooth_gatt_notify(const api::BluetoothGATTNotifyRequest &msg);
int get_bluetooth_connections_free() { return this->state_ == espbt::ClientState::IDLE ? 1 : 0; }
int get_bluetooth_connections_limit() { return 1; }
void set_active(bool active) { this->active_ = active; }
bool has_active() { return this->active_; }
protected:
void send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device);
int16_t send_service_{-1};
bool active_;
};
extern BluetoothProxy *global_bluetooth_proxy; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
} // namespace bluetooth_proxy
} // namespace esphome
#endif // USE_ESP32

View File

@@ -163,7 +163,7 @@ void BME280Component::setup() {
return;
}
config_register &= ~0b11111100;
config_register |= 0b000 << 5; // 0.5 ms standby time
config_register |= 0b101 << 5; // 1000 ms standby time
config_register |= (this->iir_filter_ & 0b111) << 2;
if (!this->write_byte(BME280_REGISTER_CONFIG, config_register)) {
this->mark_failed();

View File

@@ -96,9 +96,9 @@ class BME280Component : public PollingComponent, public i2c::I2CDevice {
BME280Oversampling pressure_oversampling_{BME280_OVERSAMPLING_16X};
BME280Oversampling humidity_oversampling_{BME280_OVERSAMPLING_16X};
BME280IIRFilter iir_filter_{BME280_IIR_FILTER_OFF};
sensor::Sensor *temperature_sensor_;
sensor::Sensor *pressure_sensor_;
sensor::Sensor *humidity_sensor_;
sensor::Sensor *temperature_sensor_{nullptr};
sensor::Sensor *pressure_sensor_{nullptr};
sensor::Sensor *humidity_sensor_{nullptr};
enum ErrorCode {
NONE = 0,
COMMUNICATION_FAILED,

View File

@@ -129,10 +129,10 @@ class BME680Component : public PollingComponent, public i2c::I2CDevice {
uint16_t heater_temperature_{320};
uint16_t heater_duration_{150};
sensor::Sensor *temperature_sensor_;
sensor::Sensor *pressure_sensor_;
sensor::Sensor *humidity_sensor_;
sensor::Sensor *gas_resistance_sensor_;
sensor::Sensor *temperature_sensor_{nullptr};
sensor::Sensor *pressure_sensor_{nullptr};
sensor::Sensor *humidity_sensor_{nullptr};
sensor::Sensor *gas_resistance_sensor_{nullptr};
};
} // namespace bme680

View File

@@ -100,15 +100,15 @@ class BME680BSECComponent : public Component, public i2c::I2CDevice {
SampleRate pressure_sample_rate_{SAMPLE_RATE_DEFAULT};
SampleRate humidity_sample_rate_{SAMPLE_RATE_DEFAULT};
sensor::Sensor *temperature_sensor_;
sensor::Sensor *pressure_sensor_;
sensor::Sensor *humidity_sensor_;
sensor::Sensor *gas_resistance_sensor_;
sensor::Sensor *iaq_sensor_;
text_sensor::TextSensor *iaq_accuracy_text_sensor_;
sensor::Sensor *iaq_accuracy_sensor_;
sensor::Sensor *co2_equivalent_sensor_;
sensor::Sensor *breath_voc_equivalent_sensor_;
sensor::Sensor *temperature_sensor_{nullptr};
sensor::Sensor *pressure_sensor_{nullptr};
sensor::Sensor *humidity_sensor_{nullptr};
sensor::Sensor *gas_resistance_sensor_{nullptr};
sensor::Sensor *iaq_sensor_{nullptr};
text_sensor::TextSensor *iaq_accuracy_text_sensor_{nullptr};
sensor::Sensor *iaq_accuracy_sensor_{nullptr};
sensor::Sensor *co2_equivalent_sensor_{nullptr};
sensor::Sensor *breath_voc_equivalent_sensor_{nullptr};
};
#endif
} // namespace bme680_bsec

View File

@@ -81,8 +81,8 @@ class BMP280Component : public PollingComponent, public i2c::I2CDevice {
BMP280Oversampling temperature_oversampling_{BMP280_OVERSAMPLING_16X};
BMP280Oversampling pressure_oversampling_{BMP280_OVERSAMPLING_16X};
BMP280IIRFilter iir_filter_{BMP280_IIR_FILTER_OFF};
sensor::Sensor *temperature_sensor_;
sensor::Sensor *pressure_sensor_;
sensor::Sensor *temperature_sensor_{nullptr};
sensor::Sensor *pressure_sensor_{nullptr};
enum ErrorCode {
NONE = 0,
COMMUNICATION_FAILED,

View File

@@ -125,8 +125,8 @@ class BMP3XXComponent : public PollingComponent, public i2c::I2CDevice {
Oversampling pressure_oversampling_{OVERSAMPLING_X16};
IIRFilter iir_filter_{IIR_FILTER_OFF};
OperationMode operation_mode_{FORCED_MODE};
sensor::Sensor *temperature_sensor_;
sensor::Sensor *pressure_sensor_;
sensor::Sensor *temperature_sensor_{nullptr};
sensor::Sensor *pressure_sensor_{nullptr};
enum ErrorCode {
NONE = 0,
ERROR_COMMUNICATION_FAILED,

View File

@@ -5,7 +5,6 @@ from esphome.const import (
CONF_DEVICE_CLASS,
CONF_ENTITY_CATEGORY,
CONF_ICON,
CONF_ID,
CONF_SOURCE_ID,
)
from esphome.core.entity_helpers import inherit_property_from
@@ -15,12 +14,15 @@ from .. import copy_ns
CopySwitch = copy_ns.class_("CopySwitch", switch.Switch, cg.Component)
CONFIG_SCHEMA = switch.SWITCH_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(CopySwitch),
cv.Required(CONF_SOURCE_ID): cv.use_id(switch.Switch),
}
).extend(cv.COMPONENT_SCHEMA)
CONFIG_SCHEMA = (
switch.switch_schema(CopySwitch)
.extend(
{
cv.Required(CONF_SOURCE_ID): cv.use_id(switch.Switch),
}
)
.extend(cv.COMPONENT_SCHEMA)
)
FINAL_VALIDATE_SCHEMA = cv.All(
inherit_property_from(CONF_ICON, CONF_SOURCE_ID),
@@ -30,8 +32,7 @@ FINAL_VALIDATE_SCHEMA = cv.All(
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await switch.register_switch(var, config)
var = await switch.new_switch(config)
await cg.register_component(var, config)
source = await cg.get_variable(config[CONF_SOURCE_ID])

View File

@@ -13,8 +13,9 @@ void CSE7766Component::loop() {
this->raw_data_index_ = 0;
}
if (this->available() == 0)
if (this->available() == 0) {
return;
}
this->last_transmission_ = now;
while (this->available() != 0) {
@@ -22,6 +23,7 @@ void CSE7766Component::loop() {
if (!this->check_byte_()) {
this->raw_data_index_ = 0;
this->status_set_warning();
continue;
}
if (this->raw_data_index_ == 23) {
@@ -51,8 +53,9 @@ bool CSE7766Component::check_byte_() {
if (index == 23) {
uint8_t checksum = 0;
for (uint8_t i = 2; i < 23; i++)
for (uint8_t i = 2; i < 23; i++) {
checksum += this->raw_data_[i];
}
if (checksum != this->raw_data_[23]) {
ESP_LOGW(TAG, "Invalid checksum from CSE7766: 0x%02X != 0x%02X", checksum, this->raw_data_[23]);
@@ -66,20 +69,34 @@ bool CSE7766Component::check_byte_() {
void CSE7766Component::parse_data_() {
ESP_LOGVV(TAG, "CSE7766 Data: ");
for (uint8_t i = 0; i < 23; i++) {
ESP_LOGVV(TAG, " i=%u: 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", i, BYTE_TO_BINARY(this->raw_data_[i]),
ESP_LOGVV(TAG, " %u: 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", i + 1, BYTE_TO_BINARY(this->raw_data_[i]),
this->raw_data_[i]);
}
uint8_t header1 = this->raw_data_[0];
if (header1 == 0xAA) {
ESP_LOGW(TAG, "CSE7766 not calibrated!");
ESP_LOGE(TAG, "CSE7766 not calibrated!");
return;
}
if ((header1 & 0xF0) == 0xF0 && ((header1 >> 0) & 1) == 1) {
ESP_LOGW(TAG, "CSE7766 reports abnormal hardware: (0x%02X)", header1);
ESP_LOGW(TAG, " Coefficient storage area is abnormal.");
return;
bool power_cycle_exceeds_range = false;
if ((header1 & 0xF0) == 0xF0) {
if (header1 & 0xD) {
ESP_LOGE(TAG, "CSE7766 reports abnormal external circuit or chip damage: (0x%02X)", header1);
if (header1 & (1 << 3)) {
ESP_LOGE(TAG, " Voltage cycle exceeds range.");
}
if (header1 & (1 << 2)) {
ESP_LOGE(TAG, " Current cycle exceeds range.");
}
if (header1 & (1 << 0)) {
ESP_LOGE(TAG, " Coefficient storage area is abnormal.");
}
return;
}
power_cycle_exceeds_range = header1 & (1 << 1);
}
uint32_t voltage_calib = this->get_24_bit_uint_(2);
@@ -92,46 +109,29 @@ void CSE7766Component::parse_data_() {
uint8_t adj = this->raw_data_[20];
uint32_t cf_pulses = (this->raw_data_[21] << 8) + this->raw_data_[22];
bool power_ok = true;
bool voltage_ok = true;
bool current_ok = true;
if (header1 > 0xF0) {
// ESP_LOGV(TAG, "CSE7766 reports abnormal hardware: (0x%02X)", byte);
if ((header1 >> 3) & 1) {
ESP_LOGV(TAG, " Voltage cycle exceeds range.");
voltage_ok = false;
}
if ((header1 >> 2) & 1) {
ESP_LOGV(TAG, " Current cycle exceeds range.");
current_ok = false;
}
if ((header1 >> 1) & 1) {
ESP_LOGV(TAG, " Power cycle exceeds range.");
power_ok = false;
}
if ((header1 >> 0) & 1) {
ESP_LOGV(TAG, " Coefficient storage area is abnormal.");
return;
}
}
if ((adj & 0x40) == 0x40 && voltage_ok && current_ok) {
bool have_voltage = adj & 0x40;
if (have_voltage) {
// voltage cycle of serial port outputted is a complete cycle;
this->voltage_acc_ += voltage_calib / float(voltage_cycle);
this->voltage_counts_ += 1;
}
float power = 0;
if ((adj & 0x10) == 0x10 && voltage_ok && current_ok && power_ok) {
bool have_power = adj & 0x10;
float power = 0.0f;
if (have_power) {
// power cycle of serial port outputted is a complete cycle;
power = power_calib / float(power_cycle);
// According to the user manual, power cycle exceeding range means the measured power is 0
if (!power_cycle_exceeds_range) {
power = power_calib / float(power_cycle);
}
this->power_acc_ += power;
this->power_counts_ += 1;
uint32_t difference;
if (this->cf_pulses_last_ == 0)
if (this->cf_pulses_last_ == 0) {
this->cf_pulses_last_ = cf_pulses;
}
if (cf_pulses < this->cf_pulses_last_) {
difference = cf_pulses + (0x10000 - this->cf_pulses_last_);
@@ -139,41 +139,52 @@ void CSE7766Component::parse_data_() {
difference = cf_pulses - this->cf_pulses_last_;
}
this->cf_pulses_last_ = cf_pulses;
this->energy_total_ += difference * float(power_calib) / 1000000.0 / 3600.0;
this->energy_total_ += difference * float(power_calib) / 1000000.0f / 3600.0f;
this->energy_total_counts_ += 1;
}
if ((adj & 0x20) == 0x20 && current_ok && voltage_ok && power != 0.0) {
if (adj & 0x20) {
// indicates current cycle of serial port outputted is a complete cycle;
this->current_acc_ += current_calib / float(current_cycle);
float current = 0.0f;
if (have_voltage && !have_power) {
// Testing has shown that when we have voltage and current but not power, that means the power is 0.
// We report a power of 0, which in turn means we should report a current of 0.
this->power_counts_ += 1;
} else if (power != 0.0f) {
current = current_calib / float(current_cycle);
}
this->current_acc_ += current;
this->current_counts_ += 1;
}
}
void CSE7766Component::update() {
float voltage = this->voltage_counts_ > 0 ? this->voltage_acc_ / this->voltage_counts_ : 0.0f;
float current = this->current_counts_ > 0 ? this->current_acc_ / this->current_counts_ : 0.0f;
float power = this->power_counts_ > 0 ? this->power_acc_ / this->power_counts_ : 0.0f;
const auto publish_state = [](const char *name, sensor::Sensor *sensor, float &acc, uint32_t &counts) {
if (counts != 0) {
const auto avg = acc / counts;
ESP_LOGV(TAG, "Got voltage_acc=%.2f current_acc=%.2f power_acc=%.2f", this->voltage_acc_, this->current_acc_,
this->power_acc_);
ESP_LOGV(TAG, "Got voltage_counts=%d current_counts=%d power_counts=%d", this->voltage_counts_, this->current_counts_,
this->power_counts_);
ESP_LOGD(TAG, "Got voltage=%.1fV current=%.1fA power=%.1fW", voltage, current, power);
ESP_LOGV(TAG, "Got %s_acc=%.2f %s_counts=%d %s=%.1f", name, acc, name, counts, name, avg);
if (this->voltage_sensor_ != nullptr)
this->voltage_sensor_->publish_state(voltage);
if (this->current_sensor_ != nullptr)
this->current_sensor_->publish_state(current);
if (this->power_sensor_ != nullptr)
this->power_sensor_->publish_state(power);
if (this->energy_sensor_ != nullptr)
this->energy_sensor_->publish_state(this->energy_total_);
if (sensor != nullptr) {
sensor->publish_state(avg);
}
this->voltage_acc_ = 0.0f;
this->current_acc_ = 0.0f;
this->power_acc_ = 0.0f;
this->voltage_counts_ = 0;
this->power_counts_ = 0;
this->current_counts_ = 0;
acc = 0.0f;
counts = 0;
}
};
publish_state("voltage", this->voltage_sensor_, this->voltage_acc_, this->voltage_counts_);
publish_state("current", this->current_sensor_, this->current_acc_, this->current_counts_);
publish_state("power", this->power_sensor_, this->power_acc_, this->power_counts_);
if (this->energy_total_counts_ != 0) {
ESP_LOGV(TAG, "Got energy_total=%.2f energy_total_counts=%d", this->energy_total_, this->energy_total_counts_);
if (this->energy_sensor_ != nullptr) {
this->energy_sensor_->publish_state(this->energy_total_);
}
this->energy_total_counts_ = 0;
}
}
uint32_t CSE7766Component::get_24_bit_uint_(uint8_t start_index) {

View File

@@ -39,6 +39,8 @@ class CSE7766Component : public PollingComponent, public uart::UARTDevice {
uint32_t voltage_counts_{0};
uint32_t current_counts_{0};
uint32_t power_counts_{0};
// Setting this to 1 means it will always publish 0 once at startup
uint32_t energy_total_counts_{1};
};
} // namespace cse7766

View File

@@ -10,13 +10,7 @@ CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(CustomSwitchConstructor),
cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.Required(CONF_SWITCHES): cv.ensure_list(
switch.SWITCH_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(switch.Switch),
}
)
),
cv.Required(CONF_SWITCHES): cv.ensure_list(switch.switch_schema(switch.Switch)),
}
)

View File

@@ -3,6 +3,7 @@ from pathlib import Path
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components.packages import validate_source_shorthand
from esphome.const import CONF_WIFI
from esphome.wizard import wizard_file
from esphome.yaml_util import dump
@@ -43,7 +44,9 @@ async def to_code(config):
cg.add(dashboard_import_ns.set_package_import_url(config[CONF_PACKAGE_IMPORT_URL]))
def import_config(path: str, name: str, project_name: str, import_url: str) -> None:
def import_config(
path: str, name: str, project_name: str, import_url: str, network: str = CONF_WIFI
) -> None:
p = Path(path)
if p.exists():
@@ -69,7 +72,9 @@ def import_config(path: str, name: str, project_name: str, import_url: str) -> N
"name_add_mac_suffix": False,
},
}
p.write_text(
dump(config) + WIFI_CONFIG,
encoding="utf8",
)
output = dump(config)
if network == CONF_WIFI:
output += WIFI_CONFIG
p.write_text(output, encoding="utf8")

View File

@@ -349,13 +349,7 @@ CONFIG_SCHEMA = cv.Schema(
CONF_ICON: ICON_BLUETOOTH,
},
],
): [
switch.SWITCH_SCHEMA.extend(cv.COMPONENT_SCHEMA).extend(
{
cv.GenerateID(): cv.declare_id(DemoSwitch),
}
)
],
): [switch.switch_schema(DemoSwitch).extend(cv.COMPONENT_SCHEMA)],
cv.Optional(
CONF_TEXT_SENSORS,
default=[
@@ -422,9 +416,8 @@ async def to_code(config):
await cg.register_component(var, conf)
for conf in config[CONF_SWITCHES]:
var = cg.new_Pvariable(conf[CONF_ID])
var = await switch.new_switch(conf)
await cg.register_component(var, conf)
await switch.register_switch(var, conf)
for conf in config[CONF_TEXT_SENSORS]:
var = await text_sensor.new_text_sensor(conf)

View File

@@ -20,8 +20,8 @@ class DHT12Component : public PollingComponent, public i2c::I2CDevice {
protected:
bool read_data_(uint8_t *data);
sensor::Sensor *temperature_sensor_;
sensor::Sensor *humidity_sensor_;
sensor::Sensor *temperature_sensor_{nullptr};
sensor::Sensor *humidity_sensor_{nullptr};
};
} // namespace dht12

View File

View File

@@ -0,0 +1,189 @@
#include "dps310.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
namespace esphome {
namespace dps310 {
static const char *const TAG = "dps310";
void DPS310Component::setup() {
uint8_t coef_data_raw[DPS310_NUM_COEF_REGS];
auto timer = DPS310_INIT_TIMEOUT;
uint8_t reg = 0;
ESP_LOGCONFIG(TAG, "Setting up DPS310...");
// first, reset the sensor
if (!this->write_byte(DPS310_REG_RESET, DPS310_CMD_RESET)) {
this->mark_failed();
return;
}
delay(10);
// wait for the sensor and its coefficients to be ready
while (timer-- && (!(reg & DPS310_BIT_SENSOR_RDY) || !(reg & DPS310_BIT_COEF_RDY))) {
reg = this->read_byte(DPS310_REG_MEAS_CFG).value_or(0);
delay(5);
}
if (!(reg & DPS310_BIT_SENSOR_RDY) || !(reg & DPS310_BIT_COEF_RDY)) { // the flags were not set in time
this->mark_failed();
return;
}
// read device ID
if (!this->read_byte(DPS310_REG_PROD_REV_ID, &this->prod_rev_id_)) {
this->mark_failed();
return;
}
// read in coefficients used to calculate the compensated pressure and temperature values
if (!this->read_bytes(DPS310_REG_COEF, coef_data_raw, DPS310_NUM_COEF_REGS)) {
this->mark_failed();
return;
}
// read in coefficients source register, too -- we need this a few lines down
if (!this->read_byte(DPS310_REG_TMP_COEF_SRC, &reg)) {
this->mark_failed();
return;
}
// set up operational stuff
if (!this->write_byte(DPS310_REG_PRS_CFG, DPS310_VAL_PRS_CFG)) {
this->mark_failed();
return;
}
if (!this->write_byte(DPS310_REG_TMP_CFG, DPS310_VAL_TMP_CFG | (reg & DPS310_BIT_TMP_COEF_SRC))) {
this->mark_failed();
return;
}
if (!this->write_byte(DPS310_REG_CFG, DPS310_VAL_REG_CFG)) {
this->mark_failed();
return;
}
if (!this->write_byte(DPS310_REG_MEAS_CFG, 0x07)) { // enable background mode
this->mark_failed();
return;
}
this->c0_ = // we only ever use c0/2, so just divide by 2 here to save time later
DPS310Component::twos_complement(
int16_t(((uint16_t) coef_data_raw[0] << 4) | (((uint16_t) coef_data_raw[1] >> 4) & 0x0F)), 12) /
2;
this->c1_ =
DPS310Component::twos_complement(int16_t((((uint16_t) coef_data_raw[1] & 0x0F) << 8) | coef_data_raw[2]), 12);
this->c00_ = ((uint32_t) coef_data_raw[3] << 12) | ((uint32_t) coef_data_raw[4] << 4) |
(((uint32_t) coef_data_raw[5] >> 4) & 0x0F);
this->c00_ = DPS310Component::twos_complement(c00_, 20);
this->c10_ =
(((uint32_t) coef_data_raw[5] & 0x0F) << 16) | ((uint32_t) coef_data_raw[6] << 8) | (uint32_t) coef_data_raw[7];
this->c10_ = DPS310Component::twos_complement(c10_, 20);
this->c01_ = int16_t(((uint16_t) coef_data_raw[8] << 8) | (uint16_t) coef_data_raw[9]);
this->c11_ = int16_t(((uint16_t) coef_data_raw[10] << 8) | (uint16_t) coef_data_raw[11]);
this->c20_ = int16_t(((uint16_t) coef_data_raw[12] << 8) | (uint16_t) coef_data_raw[13]);
this->c21_ = int16_t(((uint16_t) coef_data_raw[14] << 8) | (uint16_t) coef_data_raw[15]);
this->c30_ = int16_t(((uint16_t) coef_data_raw[16] << 8) | (uint16_t) coef_data_raw[17]);
}
void DPS310Component::dump_config() {
ESP_LOGCONFIG(TAG, "DPS310:");
ESP_LOGCONFIG(TAG, " Product ID: %u", this->prod_rev_id_ & 0x0F);
ESP_LOGCONFIG(TAG, " Revision ID: %u", (this->prod_rev_id_ >> 4) & 0x0F);
LOG_I2C_DEVICE(this);
if (this->is_failed()) {
ESP_LOGE(TAG, "Communication with DPS310 failed!");
}
LOG_UPDATE_INTERVAL(this);
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
LOG_SENSOR(" ", "Pressure", this->pressure_sensor_);
}
float DPS310Component::get_setup_priority() const { return setup_priority::DATA; }
void DPS310Component::update() {
if (!this->update_in_progress_) {
this->update_in_progress_ = true;
this->read_();
}
}
void DPS310Component::read_() {
uint8_t reg = 0;
if (!this->read_byte(DPS310_REG_MEAS_CFG, &reg)) {
this->status_set_warning();
return;
}
if ((!this->got_pres_) && (reg & DPS310_BIT_PRS_RDY)) {
this->read_pressure_();
}
if ((!this->got_temp_) && (reg & DPS310_BIT_TMP_RDY)) {
this->read_temperature_();
}
if (this->got_pres_ && this->got_temp_) {
this->calculate_values_(this->raw_temperature_, this->raw_pressure_);
this->got_pres_ = false;
this->got_temp_ = false;
this->update_in_progress_ = false;
this->status_clear_warning();
} else {
auto f = std::bind(&DPS310Component::read_, this);
this->set_timeout("dps310", 10, f);
}
}
void DPS310Component::read_pressure_() {
uint8_t bytes[3];
if (!this->read_bytes(DPS310_REG_PRS_B2, bytes, 3)) {
this->status_set_warning();
return;
}
this->got_pres_ = true;
this->raw_pressure_ = DPS310Component::twos_complement(
int32_t((uint32_t(bytes[0]) << 16) | (uint32_t(bytes[1]) << 8) | (uint32_t(bytes[2]))), 24);
}
void DPS310Component::read_temperature_() {
uint8_t bytes[3];
if (!this->read_bytes(DPS310_REG_TMP_B2, bytes, 3)) {
this->status_set_warning();
return;
}
this->got_temp_ = true;
this->raw_temperature_ = DPS310Component::twos_complement(
int32_t((uint32_t(bytes[0]) << 16) | (uint32_t(bytes[1]) << 8) | (uint32_t(bytes[2]))), 24);
}
// Calculations are taken from the datasheet which can be found here:
// https://www.infineon.com/dgdl/Infineon-DPS310-DataSheet-v01_02-EN.pdf?fileId=5546d462576f34750157750826c42242
// Sections "How to Calculate Compensated Pressure Values" and "How to Calculate Compensated Temperature Values"
// Variable names below match variable names from the datasheet but lowercased
void DPS310Component::calculate_values_(int32_t raw_temperature, int32_t raw_pressure) {
const float t_raw_sc = (float) raw_temperature / DPS310_SCALE_FACTOR;
const float p_raw_sc = (float) raw_pressure / DPS310_SCALE_FACTOR;
const float temperature = t_raw_sc * this->c1_ + this->c0_; // c0/2 done earlier!
const float pressure = (this->c00_ + p_raw_sc * (this->c10_ + p_raw_sc * (this->c20_ + p_raw_sc * this->c30_)) +
t_raw_sc * this->c01_ + t_raw_sc * p_raw_sc * (this->c11_ + p_raw_sc * this->c21_)) /
100; // divide by 100 for hPa
if (this->temperature_sensor_ != nullptr) {
this->temperature_sensor_->publish_state(temperature);
}
if (this->pressure_sensor_ != nullptr) {
this->pressure_sensor_->publish_state(pressure);
}
}
int32_t DPS310Component::twos_complement(int32_t val, uint8_t bits) {
if (val & ((uint32_t) 1 << (bits - 1))) {
val -= (uint32_t) 1 << bits;
}
return val;
}
} // namespace dps310
} // namespace esphome

View File

@@ -0,0 +1,65 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/i2c/i2c.h"
namespace esphome {
namespace dps310 {
static const uint8_t DPS310_REG_PRS_B2 = 0x00; // Highest byte of pressure data
static const uint8_t DPS310_REG_TMP_B2 = 0x03; // Highest byte of temperature data
static const uint8_t DPS310_REG_PRS_CFG = 0x06; // Pressure configuration
static const uint8_t DPS310_REG_TMP_CFG = 0x07; // Temperature configuration
static const uint8_t DPS310_REG_MEAS_CFG = 0x08; // Sensor configuration
static const uint8_t DPS310_REG_CFG = 0x09; // Interrupt/FIFO configuration
static const uint8_t DPS310_REG_RESET = 0x0C; // Soft reset
static const uint8_t DPS310_REG_PROD_REV_ID = 0x0D; // Register that contains the part ID
static const uint8_t DPS310_REG_COEF = 0x10; // Top of calibration coefficient data space
static const uint8_t DPS310_REG_TMP_COEF_SRC = 0x28; // Temperature calibration src
static const uint8_t DPS310_BIT_PRS_RDY = 0x10; // Pressure measurement is ready
static const uint8_t DPS310_BIT_TMP_RDY = 0x20; // Temperature measurement is ready
static const uint8_t DPS310_BIT_SENSOR_RDY = 0x40; // Sensor initialization complete when bit is set
static const uint8_t DPS310_BIT_COEF_RDY = 0x80; // Coefficients are available when bit is set
static const uint8_t DPS310_BIT_TMP_COEF_SRC = 0x80; // Temperature measurement source (0 = ASIC, 1 = MEMS element)
static const uint8_t DPS310_BIT_REQ_PRES = 0x01; // Set this bit to request pressure reading
static const uint8_t DPS310_BIT_REQ_TEMP = 0x02; // Set this bit to request temperature reading
static const uint8_t DPS310_CMD_RESET = 0x89; // What to write to reset the device
static const uint8_t DPS310_VAL_PRS_CFG = 0x01; // Value written to DPS310_REG_PRS_CFG at startup
static const uint8_t DPS310_VAL_TMP_CFG = 0x01; // Value written to DPS310_REG_TMP_CFG at startup
static const uint8_t DPS310_VAL_REG_CFG = 0x00; // Value written to DPS310_REG_CFG at startup
static const uint8_t DPS310_INIT_TIMEOUT = 20; // How long to wait for DPS310 start-up to complete
static const uint8_t DPS310_NUM_COEF_REGS = 18; // Number of coefficients we need to read from the device
static const int32_t DPS310_SCALE_FACTOR = 1572864; // Measurement compensation scale factor
class DPS310Component : public PollingComponent, public i2c::I2CDevice {
public:
void setup() override;
void dump_config() override;
float get_setup_priority() const override;
void update() override;
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; }
void set_pressure_sensor(sensor::Sensor *pressure_sensor) { pressure_sensor_ = pressure_sensor; }
protected:
void read_();
void read_pressure_();
void read_temperature_();
void calculate_values_(int32_t raw_temperature, int32_t raw_pressure);
static int32_t twos_complement(int32_t val, uint8_t bits);
sensor::Sensor *temperature_sensor_{nullptr};
sensor::Sensor *pressure_sensor_{nullptr};
int32_t raw_pressure_, raw_temperature_, c00_, c10_;
int16_t c0_, c1_, c01_, c11_, c20_, c21_, c30_;
uint8_t prod_rev_id_;
bool got_pres_, got_temp_, update_in_progress_;
};
} // namespace dps310
} // namespace esphome

View File

@@ -0,0 +1,62 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c, sensor
from esphome.const import (
CONF_ID,
CONF_PRESSURE,
CONF_TEMPERATURE,
DEVICE_CLASS_PRESSURE,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
ICON_GAUGE,
ICON_THERMOMETER,
UNIT_HECTOPASCAL,
)
CODEOWNERS = ["@kbx81"]
DEPENDENCIES = ["i2c"]
dps310_ns = cg.esphome_ns.namespace("dps310")
DPS310Component = dps310_ns.class_(
"DPS310Component", cg.PollingComponent, i2c.I2CDevice
)
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(DPS310Component),
cv.Required(CONF_TEMPERATURE): sensor.sensor_schema(
unit_of_measurement=UNIT_CELSIUS,
icon=ICON_THERMOMETER,
accuracy_decimals=1,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Required(CONF_PRESSURE): sensor.sensor_schema(
unit_of_measurement=UNIT_HECTOPASCAL,
icon=ICON_GAUGE,
accuracy_decimals=1,
device_class=DEVICE_CLASS_PRESSURE,
state_class=STATE_CLASS_MEASUREMENT,
),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(i2c.i2c_device_schema(0x77))
)
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 CONF_TEMPERATURE in config:
sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
cg.add(var.set_temperature_sensor(sens))
if CONF_PRESSURE in config:
sens = await sensor.new_sensor(config[CONF_PRESSURE])
cg.add(var.set_pressure_sensor(sens))

View File

@@ -31,8 +31,8 @@ class ENS210Component : public PollingComponent, public i2c::I2CDevice {
bool set_low_power_(bool enable);
void extract_measurement_(uint32_t val, int *data, int *status);
sensor::Sensor *temperature_sensor_;
sensor::Sensor *humidity_sensor_;
sensor::Sensor *temperature_sensor_{nullptr};
sensor::Sensor *humidity_sensor_{nullptr};
};
} // namespace ens210

View File

@@ -141,7 +141,7 @@ class ESP32Preferences : public ESPPreferences {
ESP_LOGD(TAG, "Saving %d preferences to flash: %d cached, %d written, %d failed", cached + written + failed, cached,
written, failed);
if (failed > 0) {
ESP_LOGD(TAG, "Error saving %d preferences to flash. Last error=%s for key=%s", failed, esp_err_to_name(last_err),
ESP_LOGE(TAG, "Error saving %d preferences to flash. Last error=%s for key=%s", failed, esp_err_to_name(last_err),
last_key.c_str());
}
@@ -170,6 +170,17 @@ class ESP32Preferences : public ESPPreferences {
}
return to_save.data != stored_data.data;
}
bool reset() override {
ESP_LOGD(TAG, "Cleaning up preferences in flash...");
s_pending_save.clear();
nvs_flash_deinit();
nvs_flash_erase();
// Make the handle invalid to prevent any saves until restart
nvs_handle = 0;
return true;
}
};
void setup_preferences() {

View File

@@ -0,0 +1,12 @@
import esphome.codegen as cg
from esphome.components import esp32_ble_tracker
AUTO_LOAD = ["esp32_ble_tracker"]
CODEOWNERS = ["@jesserockz"]
DEPENDENCIES = ["esp32"]
esp32_ble_client_ns = cg.esphome_ns.namespace("esp32_ble_client")
BLEClientBase = esp32_ble_client_ns.class_(
"BLEClientBase", esp32_ble_tracker.ESPBTClient, cg.Component
)

View File

@@ -0,0 +1,84 @@
#include "ble_characteristic.h"
#include "ble_client_base.h"
#include "ble_service.h"
#include "esphome/core/log.h"
#ifdef USE_ESP32
namespace esphome {
namespace esp32_ble_client {
static const char *const TAG = "esp32_ble_client.characteristic";
BLECharacteristic::~BLECharacteristic() {
for (auto &desc : this->descriptors)
delete desc; // NOLINT(cppcoreguidelines-owning-memory)
}
void BLECharacteristic::parse_descriptors() {
uint16_t offset = 0;
esp_gattc_descr_elem_t result;
while (true) {
uint16_t count = 1;
esp_gatt_status_t status =
esp_ble_gattc_get_all_descr(this->service->client->get_gattc_if(), this->service->client->get_conn_id(),
this->handle, &result, &count, offset);
if (status == ESP_GATT_INVALID_OFFSET || status == ESP_GATT_NOT_FOUND) {
break;
}
if (status != ESP_GATT_OK) {
ESP_LOGW(TAG, "esp_ble_gattc_get_all_descr error, status=%d", status);
break;
}
if (count == 0) {
break;
}
BLEDescriptor *desc = new BLEDescriptor(); // NOLINT(cppcoreguidelines-owning-memory)
desc->uuid = espbt::ESPBTUUID::from_uuid(result.uuid);
desc->handle = result.handle;
desc->characteristic = this;
this->descriptors.push_back(desc);
ESP_LOGV(TAG, " descriptor %s, handle 0x%x", desc->uuid.to_string().c_str(), desc->handle);
offset++;
}
}
BLEDescriptor *BLECharacteristic::get_descriptor(espbt::ESPBTUUID uuid) {
for (auto &desc : this->descriptors) {
if (desc->uuid == uuid)
return desc;
}
return nullptr;
}
BLEDescriptor *BLECharacteristic::get_descriptor(uint16_t uuid) {
return this->get_descriptor(espbt::ESPBTUUID::from_uint16(uuid));
}
BLEDescriptor *BLECharacteristic::get_descriptor_by_handle(uint16_t handle) {
for (auto &desc : this->descriptors) {
if (desc->handle == handle)
return desc;
}
return nullptr;
}
esp_err_t BLECharacteristic::write_value(uint8_t *new_val, int16_t new_val_size, esp_gatt_write_type_t write_type) {
auto *client = this->service->client;
auto status = esp_ble_gattc_write_char(client->get_gattc_if(), client->get_conn_id(), this->handle, new_val_size,
new_val, write_type, ESP_GATT_AUTH_REQ_NONE);
if (status) {
ESP_LOGW(TAG, "Error sending write value to BLE gattc server, status=%d", status);
}
return status;
}
esp_err_t BLECharacteristic::write_value(uint8_t *new_val, int16_t new_val_size) {
return write_value(new_val, new_val_size, ESP_GATT_WRITE_TYPE_NO_RSP);
}
} // namespace esp32_ble_client
} // namespace esphome
#endif // USE_ESP32

View File

@@ -0,0 +1,35 @@
#pragma once
#ifdef USE_ESP32
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include "ble_descriptor.h"
namespace esphome {
namespace esp32_ble_client {
namespace espbt = esphome::esp32_ble_tracker;
class BLEService;
class BLECharacteristic {
public:
~BLECharacteristic();
espbt::ESPBTUUID uuid;
uint16_t handle;
esp_gatt_char_prop_t properties;
std::vector<BLEDescriptor *> descriptors;
void parse_descriptors();
BLEDescriptor *get_descriptor(espbt::ESPBTUUID uuid);
BLEDescriptor *get_descriptor(uint16_t uuid);
BLEDescriptor *get_descriptor_by_handle(uint16_t handle);
esp_err_t write_value(uint8_t *new_val, int16_t new_val_size);
esp_err_t write_value(uint8_t *new_val, int16_t new_val_size, esp_gatt_write_type_t write_type);
BLEService *service;
};
} // namespace esp32_ble_client
} // namespace esphome
#endif // USE_ESP32

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