Compare commits

...

620 Commits

Author SHA1 Message Date
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
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
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
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
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
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
Jesse Hills
97e067a277 Merge pull request #3829 from esphome/bump-2022.9.0b3
2022.9.0b3
2022-09-20 19:28:27 +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
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
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
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
Jesse Hills
31ad75d01b Merge pull request #3757 from esphome/bump-2022.8.1
2022.8.1
2022-09-01 13:59:03 +12:00
Jesse Hills
aa2eb29274 Bump version to 2022.8.1 2022-09-01 11:26:52 +12:00
Keith Burzinski
22eb4f9cb9 Fix SPI HW selection for ESP32 variants (#3728) 2022-09-01 11:26:51 +12:00
Samuel Sieb
3acc8e7479 fix grow password setting (#3722)
Co-authored-by: Samuel Sieb <samuel@sieb.net>
2022-09-01 11:26:51 +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
Jesse Hills
adc8c1aa38 Merge pull request #3721 from esphome/bump-2022.8.0
2022.8.0
2022-08-17 13:31:24 +12:00
Jesse Hills
3a82f500d4 Bump version to 2022.8.0 2022-08-17 12:58:30 +12:00
Jesse Hills
7d1d4831a8 Merge branch 'beta' into bump-2022.8.0 2022-08-17 12:58:29 +12: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
Jesse Hills
1a98e882dc Merge pull request #3718 from esphome/bump-2022.8.0b3
2022.8.0b3
2022-08-16 15:16:37 +12:00
Jesse Hills
c943d84036 Bump version to 2022.8.0b3 2022-08-16 14:47:48 +12:00
rbaron
d07a6704d5 Makes ble_client.ble_write's action value templatable (#3715) 2022-08-16 14:47:48 +12:00
rbaron
8cfcd5904c Fixes BLE remote address type when connecting (#3702) 2022-08-16 14:47:48 +12:00
buxtronix
fb8846bb45 Only trigger ble_client on_connect after discovering services (#3710) 2022-08-16 14:47:48 +12:00
anatoly-savchenkov
0d0733dd94 Webui small fixes (#3713) 2022-08-16 14:47:47 +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
Jesse Hills
a67d58948d Merge pull request #3714 from esphome/bump-2022.8.0b2
2022.8.0b2
2022-08-15 09:08:44 +12:00
Jesse Hills
84c051d097 Bump version to 2022.8.0b2 2022-08-15 08:23:35 +12:00
NP v/d Spek
b918abfd54 add gradient color V2.0 (#3709) 2022-08-15 08:23:35 +12:00
Peter Galantha
7f41b7cd93 Add state_class total (#3608)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-08-15 08:23:35 +12:00
Jesse Hills
4759b4fe2e Add vector include (#3707) 2022-08-15 08:23:35 +12:00
anatoly-savchenkov
e2c8e69d12 Improve Web view for Climate components (#3706) 2022-08-15 08:23:35 +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
917e8e155c Merge pull request #3705 from esphome/bump-2022.8.0b1
2022.8.0b1
2022-08-10 15:45:17 +12:00
Jesse Hills
80e3030811 Bump version to 2022.9.0-dev 2022-08-10 15:26:02 +12:00
Jesse Hills
a97e3d827d Bump version to 2022.8.0b1 2022-08-10 15:26:02 +12:00
RoboMagus
029014d9d6 Add priority to on_shutdown trigger (#3644) 2022-08-10 13:10:49 +12:00
dependabot[bot]
e4c2922536 Bump pylint from 2.14.4 to 2.14.5 (#3697)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-10 12:34:56 +12:00
dependabot[bot]
7133ae6aaa Bump flake8 from 4.0.1 to 5.0.4 (#3703)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-10 09:21:25 +12:00
dependabot[bot]
2f7f0ff3a1 Bump pytest-mock from 3.8.1 to 3.8.2 (#3622)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-10 08:03:42 +12:00
dependabot[bot]
df853bf61e Bump zeroconf from 0.38.7 to 0.39.0 (#3694)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-09 19:19:13 +12:00
dependabot[bot]
f0ac753f9b Bump pytest-asyncio from 0.18.3 to 0.19.0 (#3695)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-09 19:16:49 +12:00
Adrián Panella
4d56a975e6 Create feedback cover component (#3253) 2022-08-09 17:21:27 +12:00
dependabot[bot]
d56c53c848 Bump aioesphomeapi from 10.10.0 to 10.11.0 (#3669)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-09 13:53:30 +12:00
dependabot[bot]
f83b16320d Bump pyupgrade from 2.34.0 to 2.37.3 (#3670)
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-09 13:53:18 +12:00
anatoly-savchenkov
ac10e27f08 [Nextion] Add on_page trigger (#3673) 2022-08-09 12:44:20 +12:00
NP v/d Spek
34df7a6072 add gradient color (#3687) 2022-08-09 12:29:41 +12:00
Keith Burzinski
e2cddf1005 Sprinkler controller component (#2249)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-08-09 12:19:42 +12:00
Ian Blais
ced423748e Adding support for Whynter ARC-14S/SH Air Conditioners (#3641) 2022-08-09 10:49:03 +12:00
davestubbs
77fb02729e Added support for setting the current animation frame (#3681) 2022-08-09 10:28:05 +12:00
Joe
fef39b9fbe Refactor BedJet climate into Hub component (#3522) 2022-08-09 10:14:37 +12:00
Jesse Hills
02810105fb Add helpers to switch python for schema and codegen (#3693)
* Add helpers to switch python for schema and codegen

* Add icon back
2022-08-07 22:01:17 -05:00
rbaron
baad92515b Introduces ble_client.ble_write Action (#3398) 2022-08-08 12:59:55 +12:00
Jesse Hills
ab86ddcf02 Merge pull request #3692 from esphome/bump-2022.6.3
2022.6.3
2022-08-08 11:48:35 +12:00
Jesse Hills
bf8eddb13b Bump version to 2022.6.3 2022-08-08 08:24:12 +12:00
Samuel Sieb
e5eaf7a3fe Use correct struct members. (#3672) 2022-08-08 08:24:12 +12:00
Samuel Sieb
50f32a3aa5 Use application/json instead of text/json (#3671) 2022-08-08 08:24:11 +12:00
Bryan Berg
eb878710c1 Add CO device class to binary_sensor (#3656) 2022-08-08 08:24:11 +12:00
Samuel Sieb
311980e0e4 Update inkbird_ibsth1_mini.cpp (#3664) 2022-08-08 08:24:11 +12:00
Javier Peletier
df73170e5a modbus: fix queue deduplicator erasing custom commands (#3650) 2022-08-08 08:24:11 +12:00
dentra
a12c6b5f35 Fix panic abort when BLEClient reconnects (#3594) 2022-08-08 07:57:43 +12:00
Keith Burzinski
522646c64d Extend ST7789V component to support additional displays (#3651)
Co-authored-by: definitio <37266727+definitio@users.noreply.github.com>
2022-08-08 07:54:48 +12:00
anatoly-savchenkov
4791093e48 Add get_ap() method to WiFi (#3684) 2022-08-08 07:51:09 +12:00
Donovan Baarda
599a455150 Add a soft reset in setup() for bme280. (#3615) 2022-08-08 07:50:15 +12:00
Samuel Sieb
2deef16ebe fix sx1509 use of pullup and pulldown (#3689)
Co-authored-by: Samuel Sieb <samuel@sieb.net>
2022-08-08 07:39:41 +12:00
RoboMagus
989b7be99b Pass optional args to 'new_Pvariable' when creating new entities (#3690) 2022-08-08 07:31:50 +12:00
Derek M
cd473e1395 Adding option to report signal strength on pvvx_mithermometer (#3688)
Co-authored-by: doolbneerg <doolbneerg@outlook.com>
2022-08-08 07:30:30 +12:00
Jesse Hills
e246ebfb2e Remove old unused code in mcp23xxx_base (#3685) 2022-08-07 09:56:52 +12:00
Borys Pierov
8546ae56da Allow GPIO20 for ESP32 (#3680) 2022-08-04 07:30:19 +12:00
Sybren A. Stüvel
9e227b0192 Upgrade rweather/Crypto from 0.2.0 to 0.4.0 (#3593)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-08-03 07:58:03 +12:00
Samuel Sieb
ba7737e9f8 dalybms: support changing the address (#3676) 2022-08-03 07:48:02 +12:00
Jesse Hills
98aa3d51ed Show changes from python linting (#3677) 2022-08-03 07:27:45 +12:00
Samuel Sieb
e7cfb5492e Use correct struct members. (#3672) 2022-08-02 17:09:17 +12:00
Samuel Sieb
9ed136dc3a Use application/json instead of text/json (#3671) 2022-08-02 17:07:32 +12:00
Bryan Berg
9217216723 Add CO device class to binary_sensor (#3656) 2022-08-02 11:32:02 +12:00
RoboMagus
936c408a58 List webserver service on MDNS if enabled. (#3662) 2022-08-02 11:31:06 +12:00
Samuel Sieb
54427eac9a Update inkbird_ibsth1_mini.cpp (#3664) 2022-08-01 13:08:19 +12:00
Samuel Sieb
e809488cc0 Remove deprecated adc init call. (#3667) 2022-08-01 09:44:58 +12:00
DAVe3283
ed26c57d99 Rework NaN handling in sensor filters (#3610)
Co-authored-by: Otto Winter <otto@otto-winter.com>
2022-07-28 16:39:11 +12:00
piechade
2a49811f6e Add support for SMT100 Soil Moisture Sensor (#3654)
Co-authored-by: Dennis Piecha <d.piecha@ymail.com>
2022-07-28 11:22:49 +12:00
Peter Galantha
c95acd2568 update packages: nginx-light-1.18.0-6.1+deb11u2 nano-5.4-2+deb11u1 (#3631) 2022-07-25 16:04:21 +12:00
Samuel Sieb
6a4e0cf667 add option to publish initial state of binary sensors (#3636)
Co-authored-by: Samuel Sieb <samuel@sieb.net>
2022-07-25 15:55:32 +12:00
RoboMagus
04f4dd8a22 Add 'set_total_pulses' action to 'pulse_counter' sensor. (#3640) 2022-07-25 14:20:15 +12:00
Javier Peletier
f33d829ce9 FIX: NVS preferences being written even if unchanged (#3647) 2022-07-25 10:10:37 +12:00
RoboMagus
578671ea94 Fix configuration constants in core. (#3652) 2022-07-25 08:23:55 +12:00
Javier Peletier
d10300c330 modbus: fix queue deduplicator erasing custom commands (#3650) 2022-07-25 08:06:18 +12:00
Andreas Hergert
e0555e140f Improvement pipsolar crc (#3316)
Co-authored-by: Andreas <andreas.hergert@otrs.com>
2022-07-07 12:54:19 +12:00
Luca Adrian L
093989406f Added more sensor device classes (#3624) 2022-07-07 11:32:38 +12:00
dab0g
cdb16f08f6 Update dfplayer condition function name (#3619) 2022-07-04 09:22:04 +12:00
dependabot[bot]
53139c293b Bump pytest-mock from 3.7.0 to 3.8.1 (#3616) 2022-07-01 14:54:37 +02:00
dependabot[bot]
6a8bdcc315 Bump pylint from 2.14.3 to 2.14.4 (#3617) 2022-07-01 14:54:13 +02:00
dependabot[bot]
fe535939a3 Bump colorama from 0.4.4 to 0.4.5 (#3614) 2022-06-30 12:42:35 +02:00
dependabot[bot]
09e6c11d73 Bump black from 22.3.0 to 22.6.0 (#3613) 2022-06-30 12:42:28 +02:00
dependabot[bot]
8112bdfaa8 Bump pyupgrade from 2.32.1 to 2.34.0 (#3591)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Otto Winter <otto@otto-winter.com>
2022-06-30 11:59:53 +02:00
marshn
f7db9aaa9f Fix slow_pwm output glitch (#3601) 2022-06-30 11:28:19 +02:00
Javier Peletier
435f972357 fix EDict to dict mapping in helpers.py (#3599) 2022-06-30 11:24:24 +02:00
dependabot[bot]
f82b46c16b Bump zeroconf from 0.38.4 to 0.38.7 (#3562)
Bumps [zeroconf](https://github.com/jstasiak/python-zeroconf) from 0.38.4 to 0.38.7.
- [Release notes](https://github.com/jstasiak/python-zeroconf/releases)
- [Commits](https://github.com/jstasiak/python-zeroconf/compare/0.38.4...0.38.7)

---
updated-dependencies:
- dependency-name: zeroconf
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-06-30 11:06:22 +02:00
Jesse Hills
bca96f91b2 Remove min_save_interval from intergration and total_daily_energy (#3498) 2022-06-30 16:47:56 +12:00
Benoit3
6f83a49c63 #3358 Correct temperature validity detection issue in some conditions of pu… (#3545)
Co-authored-by: Otto Winter <otto@otto-winter.com>
2022-06-29 21:30:44 +02:00
André Klitzing
72cce391ab Fix some typos found by codespell (#3598) 2022-06-27 18:02:46 -03:00
Jesse Hills
ccc13cc9e1 Merge pull request #3596 from esphome/bump-2022.6.2
2022.6.2
2022-06-23 11:30:31 +12:00
Jesse Hills
020b2c05c8 Bump version to 2022.6.2 2022-06-23 10:34:10 +12:00
Joe
4c37c17df1 Fix 2 small issues in BLEClient (#3544) 2022-06-23 10:34:09 +12:00
Sergey Dudanov
8f67acadd8 Media Player: added play_media action (#3579)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-06-23 10:34:09 +12:00
kahrendt
ca13c4c1a6 Fix wrong type for voc_state*_ in sgp4x component (#3581)
Co-authored-by: Martin <25747549+martgras@users.noreply.github.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-06-23 10:34:09 +12:00
ShellAddicted
d0c646c721 Fix: Make MQTT over TLS actually work (#3580) 2022-06-23 10:34:09 +12:00
lkomurcu
8a055675af Move gas mbus config option being a define to being a build flag since its used in external libraries. (#3575) 2022-06-23 10:34:09 +12:00
Jesse Hills
28d2949ebe Add github-actions to dependabot (#3595) 2022-06-23 07:51:05 +12:00
Sven Serlier
c4a0015997 Update actions (#3592)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-06-23 07:50:55 +12:00
Jesse Hills
f564be6aea Do two substitutions passes to allow substitutions inside substitutions (#3583) 2022-06-22 08:21:32 +12:00
dependabot[bot]
988f15e6af Bump aioesphomeapi from 10.8.2 to 10.10.0 (#3590)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-06-21 17:19:41 +12:00
Zimmermann Zsolt
37b6d442bd Correcting ESP32 flash save/load key calculation algorithm (#3416)
Co-authored-by: Otto Winter <otto@otto-winter.com>
Co-authored-by: Dr. Zimmermann Zsolt <cina@storage1.cina.hu>
2022-06-21 17:17:51 +12:00
Nick B
fb2467f6f0 DAC7678 support (#3441)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-06-21 17:12:15 +12:00
dependabot[bot]
29045b0435 Bump pylint from 2.13.9 to 2.14.3 (#3589)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-06-21 16:27:33 +12:00
Jesse Hills
311a48c64e Bump platformio to 6.0.2 (#3566) 2022-06-21 11:53:51 +12:00
Mateus Demboski
01b3815f27 Map LOLIN C3 mini board pins (#3587) 2022-06-21 11:33:20 +12:00
Joe
b0d1c801bd Fix 2 small issues in BLEClient (#3544) 2022-06-21 11:31:17 +12:00
RoboMagus
5aaac06f5b RestoringGlobalsComponent: Store value on shutdown (#3586) 2022-06-21 11:27:53 +12:00
RoboMagus
34adbf0588 Fix / Reverse order shutdown (#3585) 2022-06-21 11:26:34 +12:00
Jesse Hills
0a4213182e Merge branch 'release' into dev 2022-06-21 07:18:41 +12:00
Sergey Dudanov
b0c0258e70 Media Player: added play_media action (#3579)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-06-20 12:17:20 +12:00
kahrendt
8110e591d0 Fix wrong type for voc_state*_ in sgp4x component (#3581)
Co-authored-by: Martin <25747549+martgras@users.noreply.github.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-06-20 11:27:00 +12:00
ShellAddicted
fe05d7aec1 Fix: Make MQTT over TLS actually work (#3580) 2022-06-20 11:17:58 +12:00
lkomurcu
57f5884070 Move gas mbus config option being a define to being a build flag since its used in external libraries. (#3575) 2022-06-20 07:39:40 +12:00
Jesse Hills
f329c74a15 Merge pull request #3578 from esphome/bump-2022.6.1
2022.6.1
2022-06-18 22:31:45 +12:00
Jesse Hills
7c86f3fa9e Bump version to 2022.6.1 2022-06-18 17:25:52 +12:00
Sergey Dudanov
203b8b01bf Media Player: added triggers (#3576) 2022-06-18 17:25:51 +12:00
Benjamin Klotz
8a1034a92f Bugfix for ExternalRAMAllocator copy constructor (#3571) 2022-06-18 17:25:51 +12:00
Jesse Hills
aa0c2dedd9 Setup the mute pin if configured (#3568) 2022-06-18 17:25:51 +12:00
Sergey Dudanov
d045908e05 Media Player: added triggers (#3576) 2022-06-18 17:21:42 +12:00
Guillermo Ruffino
f002a23d2d Language schema 202204 (#3492) 2022-06-17 13:46:20 +12:00
gazoodle
29d6d0a906 Fix modbus user-defined function handling (#3527) 2022-06-17 13:35:25 +12:00
Benjamin Klotz
c8b58b5c23 Bugfix for ExternalRAMAllocator copy constructor (#3571) 2022-06-17 13:32:43 +12:00
Jesse Hills
01bfafc5f1 Setup the mute pin if configured (#3568) 2022-06-17 13:30:21 +12:00
Jesse Hills
8c9948bb56 Merge pull request #3567 from esphome/bump-2022.6.0
2022.6.0
2022-06-16 12:59:26 +12:00
Jesse Hills
2d1abaa68e Bump version to 2022.6.0 2022-06-16 11:31:38 +12:00
Jesse Hills
664a3df0b4 Merge branch 'beta' into bump-2022.6.0 2022-06-16 11:31:38 +12:00
Jesse Hills
9ff893881c Merge pull request #3560 from esphome/bump-2022.6.0b4
2022.6.0b4
2022-06-14 22:42:06 +12:00
Jesse Hills
94f6c6861a Bump version to 2022.6.0b4 2022-06-14 20:41:46 +12:00
Martin
b1d614e6c4 Bm3xx: Fix typo (#3559) 2022-06-14 20:41:46 +12:00
André Klitzing
7fceb070e5 Fix compilation with ESP32-S3 (#3543)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-06-14 20:41:46 +12:00
Martin
06440d0202 Bm3xx: Fix typo (#3559) 2022-06-14 20:38:09 +12:00
André Klitzing
0ecf9f4f2f Fix compilation with ESP32-S3 (#3543)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-06-14 20:36:38 +12:00
Jesse Hills
5c7c0834c0 Merge pull request #3554 from esphome/bump-2022.6.0b3
2022.6.0b3
2022-06-13 17:06:45 +12:00
Jesse Hills
f3a25de11d Bump version to 2022.6.0b3 2022-06-13 13:32:43 +12:00
Jesse Hills
041bef8bcd Implement media player volume actions (#3551) 2022-06-13 13:32:43 +12:00
Jesse Hills
8998c5f6dd Implement media player volume actions (#3551) 2022-06-13 13:28:55 +12:00
Jesse Hills
6e83790308 Merge pull request #3540 from esphome/bump-2022.6.0b2
2022.6.0b2
2022-06-09 21:16:23 +12:00
Jesse Hills
d2d4eb4eae Bump version to 2022.6.0b2 2022-06-09 20:27:22 +12:00
Viktor Nagy
5942a3898c Nextion brightness setting requires an assignment (#3533) 2022-06-09 20:27:22 +12:00
Samuel Sieb
93421f0fa7 publish fan speed count for discovery (#3537)
Co-authored-by: Samuel Sieb <samuel@sieb.net>
2022-06-09 20:27:22 +12:00
RoboMagus
3a9ab50dd2 Refactor vl53l0x to remove code from header. (#3536) 2022-06-09 16:24:56 +12:00
Viktor Nagy
5abd91d6d5 Nextion brightness setting requires an assignment (#3533) 2022-06-09 16:20:05 +12:00
Samuel Sieb
c3da42516b publish fan speed count for discovery (#3537)
Co-authored-by: Samuel Sieb <samuel@sieb.net>
2022-06-09 15:27:04 +12:00
Jesse Hills
6cb5cd48c2 Merge pull request #3535 from esphome/bump-2022.6.0b1
2022.6.0b1
2022-06-08 23:26:21 +12:00
Jesse Hills
ec1fae6883 Bump version to 2022.7.0-dev 2022-06-08 22:46:20 +12:00
Jesse Hills
746fd1122f Bump version to 2022.6.0b1 2022-06-08 22:46:20 +12:00
Jesse Hills
9663760ec5 Merge branch 'dev' into bump-2022.6.0b1 2022-06-08 22:46:20 +12:00
Wolfgang Tremmel
a3d73d1e23 RG15 data is float/double, not int (#3512)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-06-08 22:34:50 +12:00
Jesse Hills
d63e14a4b6 Implement the media player actions (#3534) 2022-06-08 22:33:21 +12:00
DAVe3283
03944e6cd8 Fix bogus reading on no communication with MAX31865 (#3505) 2022-06-08 09:58:32 +12:00
Maurice Makaay
0d1028be2e Cleanup deprecated EntityBase::hash_base() (#3525)
Co-authored-by: Maurice Makaay <mmakaay1@xs4all.net>
2022-06-08 09:13:11 +12:00
VitaliyKurokhtin
6a85259e4d Block Tuya light from reacting to dp changes if transitioning (#3076) 2022-06-07 23:07:08 +12:00
Maurice Makaay
ebca936b7e Fix percentage validation for wrong data type input (#3524)
Co-authored-by: Maurice Makaay <mmakaay1@xs4all.net>
2022-06-07 23:00:27 +12:00
Nicholas Peters
31c4551890 Fix sdp3x error checking (#3531) 2022-06-07 22:43:46 +12:00
Samuel Sieb
dd470d4197 support rotated ILI9341 (ILI9342) (#3526)
Co-authored-by: Samuel Sieb <samuel@sieb.net>
2022-06-07 22:42:13 +12:00
Maurice Makaay
612822490b Fix endless 'WiFi Unknown connection status 0' loop (#3530)
Co-authored-by: Maurice Makaay <mmakaay1@xs4all.net>
2022-06-07 12:08:29 +12:00
Maurice Makaay
f8969605e8 Suppress first rotary encoder event (#3532)
Co-authored-by: Maurice Makaay <mmakaay1@xs4all.net>
2022-06-07 11:36:54 +12:00
Carlos Garcia Saura
dd24ffa24e Correct ADC auto-range for ESP32-S2 variant (13 bit adc) (#3158)
Co-authored-by: Otto Winter <otto@otto-winter.com>
2022-06-03 16:07:35 +12:00
guillempages
d0dda48932 Add display_type property to DisplayBuffer (#3430)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-06-03 15:39:04 +12:00
Eric van Blokland
6349b5f654 Added RC6 protocol support (#3514) 2022-06-03 15:37:04 +12:00
Joe
a6ff02a3cf Refactor clock syncing (#3503)
* Expose `send_local_time()` as public, for use in lambdas.
  This will send the current time configured in `time_id`.
* Add a new `set_clock()` public method, separate from time_id.
  This allows setting the clock manually, without syncing from a Time
  Component. Again this can only be called from ESPHome; i.e.,
  generally from a lambda.
2022-06-03 13:53:20 +12:00
jimtng
4f57bf786b Add mqtt.on_connect and mqtt.on_disconnect triggers (#3520) 2022-06-03 13:51:50 +12:00
Jesse Hills
6221f6d47d Implement Media Player and I2S Media player (#3487) 2022-06-02 17:00:17 +12:00
Wolfgang Tremmel
a922efeafa Change rain intensity sensor string (#3511) 2022-05-31 16:49:18 +12:00
jimtng
5aa42e5e66 Add variable substitutions for !include (#3510) 2022-05-31 16:45:18 +12:00
Joe
708672ec7e [BedJet] Add configurable heating strategy (#3519)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-05-31 15:45:01 +12:00
Jan Grewe
d2cefbf224 Allow Prometheus component to export internal components (#3508)
Co-authored-by: Jan Grewe <jan.grewe@flixbus.com>
2022-05-31 07:29:57 +12:00
Michael Davidson
adb7aa6950 Thermostat preset with modes (#3298)
* Rework HOME/AWAY support to being driven via a map of ClimatePreset/ThermostatClimateTargetTempConfig
This opens up to theoretically being able to support other presets (ECO, SLEEP, etc)

* Add support for additional presets
Configuration takes the form;
```
climate:
  platform: preset
  ...
  preset:
    [eco | away | boost | comfort | home | sleep | activity]:
      default_target_temperature_low: 20
      default_target_temperature_high: 24
```

These will be available in the Home Assistant UI and, like the existing Home/Away config will reset the temperature in line with these defaults when selected. The existing away_config/home_config is still respected (although preset->home/preset->away will be applied after them and override them if both styles are specified)

* Add support for specifying MODE, FAN_MODE and SWING_MODE on a preset
When switching presets these will implicitly flow through to the controller. However calls to climate.control which specify any of these will take precedence even when changing the mode (think of the preset version as the default for that preset)

* Add `preset_change` mode trigger
When defined this trigger will fire when the preset for the thermostat has been changed. The intent of this is similar to `auto_mode` - it's not intended to be used to control the preset's state (eg. communicate with the physical thermostat) but instead might be used to update a visual indicator, for instance.

* Apply lint, clang-format, and clang-tidy fixes

* Additional clang-format fixes

* Wrap log related strings in LOG_STR_ARG

* Add support for custom presets
This also changes the configuration syntax to;
```yaml
  preset:
    # Standard preset
    - name: [eco | away | boost | comfort | home | sleep | activity]
      default_target_temperature_low: 20
      ...
    # Custom preset
    - name: My custom preset
      default_target_temperature_low: 18
```

For the end user there is no difference between a custom and built in preset. For developers custom presets are set via `climate.control` `custom_preset` property instead of the `preset`

* Lint/clang-format/clang-tidy fixes

* Additional lint/clang-format/clang-tidy fixes

* Clang-tidy changes

* Sort imports

* Improve configuration validation for presets
- Unify temperature validation across default, away, and preset configuration
- Validate modes for presets have the required actions

* Trigger a refresh after changing internals of the thermostat

* Apply formatting fixes

* Validate mode, fan_mode, and swing_mode on presets

* Add preset temperature validation against visual min/max configuration

* Apply code formatting fixes

* Fix preset temperature validation
2022-05-24 22:44:26 -05:00
Jesse Hills
77f322166e Merge pull request #3499 from esphome/bump-2022.5.1
2022.5.1
2022-05-25 07:29:42 +12:00
Jesse Hills
f3f6e54818 Bump version to 2022.5.1 2022-05-24 21:56:18 +12:00
Martin
fb0fec1f25 esp32: fix NVS (#3497) 2022-05-24 21:56:18 +12:00
Jesse Hills
b66af9fb4d Add missing import to bedjet (#3490) 2022-05-24 21:56:18 +12:00
user897943
6617d576a7 Update bedjet_const.h to remove blank spaces before speed steps, fixes Unknown Error when using climate.set_fan_mode in HA (#3476) 2022-05-24 21:56:18 +12:00
Jesse Hills
420dacb22d Merge pull request #3488 from esphome/bump-2022.5.0
2022.5.0
2022-05-18 16:56:16 +12:00
Jesse Hills
ae2f6ad4d1 Bump version to 2022.5.0 2022-05-18 16:30:20 +12:00
Jesse Hills
2c28d79bf8 Merge branch 'beta' into bump-2022.5.0 2022-05-18 16:30:19 +12:00
Jesse Hills
c5069edc78 Merge pull request #3484 from esphome/bump-2022.5.0b4
2022.5.0b4
2022-05-17 23:42:51 +12:00
Jesse Hills
282d9e138c Revert adding spaces 2022-05-17 23:31:55 +12:00
Jesse Hills
72fcf2cbe1 Bump version to 2022.5.0b4 2022-05-17 23:23:37 +12:00
Samuel Sieb
6f49f5465b Retry Tuya init commands (#3482)
Co-authored-by: Samuel Sieb <samuel@sieb.net>
2022-05-17 23:23:33 +12:00
Martin
17b8bd8316 ESP32: Only save to NVS if data was changed (#3479) 2022-05-17 23:16:33 +12:00
Jesse Hills
7e88938932 Merge pull request #3478 from esphome/bump-2022.5.0b3
2022.5.0b3
2022-05-16 13:42:05 +12:00
Jesse Hills
c707e64685 Bump version to 2022.5.0b3 2022-05-16 13:07:12 +12:00
Jesse Hills
a639690716 Mark improv_serial and ESP-IDF usb based serial on c3/s2/s3 unsupported (#3477) 2022-05-16 13:07:12 +12:00
[pʲɵs]
01222dbab7 Increase JSON buffer size on overflow (#3475) 2022-05-16 13:07:12 +12:00
Jesse Hills
ff72d6a146 Merge pull request #3465 from esphome/bump-2022.5.0b2
2022.5.0b2
2022-05-12 22:15:21 +12:00
Jesse Hills
603d0d0c7c Bump version to 2022.5.0b2 2022-05-12 17:00:14 +12:00
Brian Kaufman
28883f711b Update captive portal canHandle function (#3360) 2022-05-12 17:00:13 +12:00
Michael Davidson
e914828add Make custom_fan and custom_preset templatable as per documentation (#3330) 2022-05-12 17:00:13 +12:00
James Szalay
c1480029fb Use heat mode for heat. Move EXT HT to custom presets. (#3437)
* Use heat mode for heat. Move EXT HT to custom presets.

* Fix syntax error.
2022-05-12 17:00:13 +12:00
Niclas Larsson
40f622949e Shelly dimmer: Use unique_ptr to handle the lifetime of stm32_t (#3400)
Co-authored-by: Martin <25747549+martgras@users.noreply.github.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-05-12 17:00:13 +12:00
Maurice Makaay
63096ac2bc On epoch sync, restore local TZ (#3462)
Co-authored-by: Maurice Makaay <mmakaay1@xs4all.net>
2022-05-12 17:00:13 +12:00
Jesse Hills
c2a59cb476 Merge pull request #3460 from esphome/bump-2022.5.0b1
2022.5.0b1
2022-05-11 15:51:44 +12:00
Jesse Hills
d6e039a1d1 Bump version to 2022.5.0b1 2022-05-11 12:50:42 +12:00
Jesse Hills
0f1a7c2b69 Merge branch 'dev' into bump-2022.5.0b1 2022-05-11 12:50:41 +12:00
Jesse Hills
993044c870 Merge pull request #3408 from esphome/bump-2022.4.0
2022.4.0
2022-04-21 07:42:58 +12:00
Jesse Hills
a8c1b63edb Bump version to 2022.4.0 2022-04-20 17:06:08 +12:00
Jesse Hills
db7d946e1b Merge branch 'beta' into bump-2022.4.0 2022-04-20 17:06:08 +12:00
Jesse Hills
41d9059a2f Merge pull request #3407 from esphome/bump-2022.4.0b4
2022.4.0b4
2022-04-20 16:55:11 +12:00
Jesse Hills
e26e0d7c01 Bump version to 2022.4.0b4 2022-04-20 16:35:43 +12:00
Jesse Hills
ad41c07a1f Dont require {} for wifi ap with defaults (#3404) 2022-04-20 16:35:42 +12:00
Jesse Hills
5732f3b044 Merge pull request #3402 from esphome/bump-2022.4.0b3
2022.4.0b3
2022-04-19 15:31:05 +12:00
Jesse Hills
712115b6ce Bump version to 2022.4.0b3 2022-04-19 12:33:38 +12:00
rnauber
9283559c6b Shelly Dimmer: Delete obsolete LICENSE.txt (#3394) 2022-04-19 12:33:38 +12:00
Michel van de Wetering
6b393438e9 Fix power_delivered/produced_phase sensor deviceclass in DSMR (#3395) 2022-04-19 12:33:38 +12:00
Jesse Hills
343b9ab455 Merge pull request #3390 from esphome/bump-2022.4.0b2
2022.4.0b2
2022-04-14 15:37:31 +12:00
Jesse Hills
dcb226b202 Bump version to 2022.4.0b2 2022-04-14 13:48:35 +12:00
Janez Troha
2243021b58 Allocate smaller amount of buffer for JSON (#3384) 2022-04-14 13:48:35 +12:00
rnauber
d5134e88b1 Add support for Shelly Dimmer 2 (#2954)
Co-authored-by: Niclas Larsson <niclas@edgesystems.se>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
Co-authored-by: Jernej Kos <jernej@kos.mx>
Co-authored-by: Richard Nauber <richard@nauber.dev>
2022-04-14 13:48:35 +12:00
matthias882
c59adf612f Changes accuracy of single cell voltage (#3387) 2022-04-14 13:48:35 +12:00
Jesse Hills
a82d8ea0c3 Merge pull request #3381 from esphome/bump-2022.4.0b1
2022.4.0b1
2022-04-13 16:14:08 +12:00
Jesse Hills
ad57faa9a9 Bump version to 2022.4.0b1 2022-04-13 13:42:28 +12:00
Jesse Hills
a9b5e8d036 Merge branch 'dev' into bump-2022.4.0b1 2022-04-13 13:42:27 +12:00
Jesse Hills
fc7348d46d Merge pull request #3346 from esphome/bump-2022.3.2
2022.3.2
2022-03-30 13:28:58 +13:00
Jesse Hills
8be2456c7e Bump version to 2022.3.2 2022-03-30 08:15:50 +13:00
Jesse Hills
bb5f7249a6 Actually increase request memory for json parsing (#3331) 2022-03-30 08:15:50 +13:00
Jesse Hills
fc94a5d0ee Merge pull request #3324 from esphome/bump-2022.3.1
2022.3.1
2022-03-24 16:45:14 +13:00
Jesse Hills
24029cc918 Bump version to 2022.3.1 2022-03-23 12:26:02 +13:00
Jesse Hills
9a9d5964ee Add small delay before setting up app in safe mode (#3323) 2022-03-23 12:26:02 +13:00
Jesse Hills
4e4a512107 Reserve less memory for json (#3289) 2022-03-23 12:26:02 +13:00
Jesse Hills
0729ed538e Webserver utilize Component Iterator to not overload eventstream (#3310) 2022-03-23 12:26:01 +13:00
wysiwyng
24b75b7ed6 Fix WDT reset during dallas search algorithm (#3293) 2022-03-23 12:26:01 +13:00
Jesse Hills
ec3618ecb8 Merge pull request #3304 from esphome/bump-2022.3.0
2022.3.0
2022-03-16 23:38:01 +13:00
Jesse Hills
792a24f38d Bump version to 2022.3.0 2022-03-16 22:32:24 +13:00
Jesse Hills
652e8a015b Merge branch 'beta' into bump-2022.3.0 2022-03-16 22:32:24 +13:00
Jesse Hills
59e6e798dd Merge pull request #3302 from esphome/bump-2022.3.0b2
2022.3.0b2
2022-03-16 15:38:32 +13:00
Jesse Hills
e5c2dbc7ec Bump version to 2022.3.0b2 2022-03-16 14:06:00 +13:00
Jesse Hills
756f71c382 Allow custom register type for modbus number (#3202) 2022-03-16 14:06:00 +13:00
Jesse Hills
b7535693fa Add helper overloads for hex print 16-bit (#3297) 2022-03-16 14:06:00 +13:00
stegm
06a3505698 Add optimistic config flag to modbus select. (#3267) 2022-03-16 14:06:00 +13:00
Jesse Hills
efa8f0730d Merge pull request #3278 from esphome/bump-2022.3.0b1
2022.3.0b1
2022-03-09 20:57:07 +13:00
Jesse Hills
023d26f521 Bump version to 2022.3.0b1 2022-03-09 20:07:50 +13:00
Jesse Hills
5068619f1b Merge branch 'dev' into bump-2022.3.0b1 2022-03-09 20:07:49 +13:00
Jesse Hills
1ef6fd8fb0 Merge pull request #3261 from esphome/bump-2022.2.6
2022.2.6
2022-03-02 22:54:51 +13:00
Jesse Hills
942b0de7fd Bump version to 2022.2.6 2022-03-02 17:07:08 +13:00
Jesse Hills
859cca49d1 Only get free memory size from internal (#3259) 2022-03-02 17:07:08 +13:00
Jesse Hills
8f7ff25624 Merge pull request #3252 from esphome/bump-2022.2.5
2022.2.5
2022-02-24 07:28:56 +13:00
Jesse Hills
97aca8e54c Bump version to 2022.2.5 2022-02-23 11:20:48 +13:00
Nicholas Peters
95acf19067 Fix regression caused by TSL2591 auto gain (#3249) 2022-02-23 11:20:48 +13:00
Martin Weinelt
3d0899aa58 Respect ESPHOME_USE_SUBPROCESS in esp32 post_build script (#3246) 2022-02-23 11:20:48 +13:00
Jesse Hills
138d6e505b Merge pull request #3245 from esphome/bump-2022.2.4
2022.2.4
2022-02-21 10:54:54 +13:00
Jesse Hills
2748e6ba29 Bump version to 2022.2.4 2022-02-21 10:17:25 +13:00
Jesse Hills
dbd4e927d8 Fix fatal erroring in addon startup script (#3244) 2022-02-21 10:17:24 +13:00
Jesse Hills
e73d47918f Fix lilygo touchscreen rotation (#3221) 2022-02-21 10:17:24 +13:00
Tyler Bules
b881bc071e ESP32-C3 deep sleep fix (#3066) 2022-02-21 10:17:24 +13:00
Otto Winter
1d0395d1c7 Improve ESP8266 iram usage (#3223) 2022-02-21 10:17:24 +13:00
Otto Winter
616c787e37 Fix ESP8266 climate memaccess warning (#3226) 2022-02-21 10:17:24 +13:00
Otto Winter
0c4de2bc97 Publish NAN when dallas conversion failed (#3227) 2022-02-21 10:17:24 +13:00
Jesse Hills
c2f5ac9eba Merge pull request #3220 from esphome/bump-2022.2.3
2022.2.3
2022-02-18 12:06:19 +13:00
Jesse Hills
5764c988af Bump version to 2022.2.3 2022-02-18 11:51:56 +13:00
dependabot[bot]
ccc2fbfd67 Bump platformio from 5.2.4 to 5.2.5 (#3188)
* Bump platformio from 5.2.4 to 5.2.5

Bumps [platformio](https://github.com/platformio/platformio) from 5.2.4 to 5.2.5.
- [Release notes](https://github.com/platformio/platformio/releases)
- [Changelog](https://github.com/platformio/platformio-core/blob/develop/HISTORY.rst)
- [Commits](https://github.com/platformio/platformio/commits)

---
updated-dependencies:
- dependency-name: platformio
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update requirements.txt

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Otto Winter <otto@otto-winter.com>
2022-02-18 11:51:56 +13:00
Jesse Hills
10b4adb8e6 Merge pull request #3219 from esphome/bump-2022.2.2
2022.2.2
2022-02-18 10:36:33 +13:00
Jesse Hills
83b7181bcb Bump version to 2022.2.2 2022-02-18 10:16:11 +13:00
Jesse Hills
8886b7e141 Add LONG LONG flag for arduinojson (#3212) 2022-02-18 10:16:11 +13:00
Otto Winter
7dcc4d030b Fix platformio docker version mismstch (#3215) 2022-02-18 10:16:11 +13:00
Stewart
b9398897c1 Set entity-category to diagnostic for debug component (#3209)
Co-authored-by: Stewart Morgan <stewart@arnos-vale.net>
Co-authored-by: root <root@build.servers.arnos-vale.net>
2022-02-18 10:16:11 +13:00
Jesse Hills
657b1c60ae Merge pull request #3208 from esphome/bump-2022.2.1
2022.2.1
2022-02-17 08:00:12 +13:00
Jesse Hills
dc54b17778 Bump version to 2022.2.1 2022-02-17 07:36:14 +13:00
Stewart
1fb214165b Fix missed ARDUINO_VERSION_CODE to USE_ARDUINO_VERSION_CODE changes (#3206)
Co-authored-by: Stewart Morgan <stewart@arnos-vale.net>
2022-02-17 07:36:14 +13:00
Jesse Hills
81b2fd78f5 Merge pull request #3204 from esphome/bump-2022.2.0
2022.2.0
2022-02-16 21:12:45 +13:00
Jesse Hills
69002fb1e6 Bump version to 2022.2.0 2022-02-16 20:13:14 +13:00
Jesse Hills
75332a752d Merge branch 'beta' into bump-2022.2.0 2022-02-16 20:13:13 +13:00
Jesse Hills
b528f48417 Merge pull request #3201 from esphome/bump-2022.2.0b3
2022.2.0b3
2022-02-16 11:10:31 +13:00
Jesse Hills
ec7a79049a Bump version to 2022.2.0b3 2022-02-16 09:45:05 +13:00
Jesse Hills
6ddad6b299 Update HA addon token (#3200) 2022-02-16 09:45:05 +13:00
Maurice Makaay
16dc7762f9 Fix strlcpy() uses to make long SSIDs and passwords work (#3199)
Co-authored-by: Maurice Makaay <mmakaay1@xs4all.net>
2022-02-16 09:45:05 +13:00
Jesse Hills
dc0ed8857f Merge pull request #3198 from esphome/bump-2022.2.0b2
2022.2.0b2
2022-02-15 12:48:34 +13:00
Jesse Hills
bb6b77bd98 Bump version to 2022.2.0b2 2022-02-15 12:00:12 +13:00
Jesse Hills
dcc80f9032 Allow framework version validator to be maximum version (#3197) 2022-02-15 12:00:12 +13:00
Otto Winter
dd554bcdf4 Make generating combined binary output verbose (#3127) 2022-02-15 12:00:12 +13:00
Jesse Hills
f376a39e55 Clamp rotary_encoder restored value to min and max (#3184) 2022-02-15 12:00:12 +13:00
dependabot[bot]
8dcc9d6b66 Bump aioesphomeapi from 10.8.1 to 10.8.2 (#3182) 2022-02-15 12:00:11 +13:00
Jesse Hills
a576c9f21f Merge pull request #3180 from esphome/bump-2022.2.0b1
2022.2.0b1
2022-02-10 00:20:46 +13:00
Jesse Hills
71a438e2cb Bump version to 2022.2.0b1 2022-02-09 23:47:36 +13:00
Jesse Hills
272d6f2a8b Merge branch 'dev' into bump-2022.2.0b1 2022-02-09 23:47:36 +13:00
Jesse Hills
09ed1aed93 Merge pull request #3177 from esphome/bump-2022.1.4
2022.1.4
2022-02-09 12:42:47 +13:00
Jesse Hills
53d3718028 Bump version to 2022.1.4 2022-02-09 11:54:41 +13:00
Jesse Hills
2b5dce5232 Try fix canbus config validation (#3173) 2022-02-09 11:54:40 +13:00
Otto Winter
9ad84150aa Enable mDNS during OTA safe mode (#3146) 2022-02-09 11:54:40 +13:00
Jesse Hills
c0523590b4 Merge pull request #3154 from esphome/bump-2022.1.3
2022.1.3
2022-02-03 07:17:52 +13:00
Jesse Hills
c7f091ab10 Bump version to 2022.1.3 2022-02-02 22:16:24 +13:00
Jesse Hills
7479e0aada Fix backwards string case helpers (#3126) 2022-02-02 22:16:16 +13:00
Jesse Hills
5bbee1a1fe Merge pull request #3111 from esphome/bump-2022.1.2
2022.1.2
2022-01-25 09:36:20 +13:00
Jesse Hills
bdb9546ca3 Bump version to 2022.1.2 2022-01-25 09:11:20 +13:00
Plácido Revilla
46af4cad6e Set the wrapped single light in light partition to internal (#3092) 2022-01-25 09:11:19 +13:00
Martin
76a238912b [modbus_controller] fix incorrect start address for number write (#3073) 2022-01-25 09:11:19 +13:00
Jesse Hills
909a526967 Merge pull request #3075 from esphome/bump-2022.1.1
2022.1.1
2022-01-20 09:08:57 +13:00
Jesse Hills
cd6f4fb93f Bump version to 2022.1.1 2022-01-20 08:34:18 +13:00
Jesse Hills
c19458696e Add *.py.script files to distributions (#3074) 2022-01-20 08:34:18 +13:00
Jesse Hills
318b930e9f Merge pull request #3070 from esphome/bump-2022.1.0
2022.1.0
2022-01-19 19:43:54 +13:00
Jesse Hills
9296a078a7 Bump version to 2022.1.0 2022-01-19 16:08:27 +13:00
Jesse Hills
5dc776e55f Merge pull request #3068 from esphome/bump-2022.1.0b4
2022.1.0b4
2022-01-19 07:40:37 +13:00
Jesse Hills
72d60f30f7 Bump version to 2022.1.0b4 2022-01-18 15:49:31 +13:00
Oxan van Leeuwen
869743a742 Fail hard if no random bytes available for encryption (#3067) 2022-01-18 15:49:31 +13:00
Martin
7b03e07908 [modbus_controller] add missing skip_updates (#3063) 2022-01-18 15:49:31 +13:00
Paulus Schoutsen
348f880e15 bump dashboard to 20220116.0 (#3061) 2022-01-18 15:49:31 +13:00
Jesse Hills
ead597d0fb Merge pull request #3060 from esphome/bump-2022.1.0b3
2022.1.0b3
2022-01-17 13:13:40 +13:00
Jesse Hills
afbf989715 Bump version to 2022.1.0b3 2022-01-17 12:40:07 +13:00
Jesse Hills
01b62a16c3 Add number setting to web_server/rest_api (#3055) 2022-01-17 12:40:07 +13:00
Oxan van Leeuwen
c5eba04517 Remove deprecated attribute from virtual entity methods (#3056) 2022-01-17 12:40:07 +13:00
Oxan van Leeuwen
282313ab52 Rename post_build scripts to fix codeowners script (#3057) 2022-01-17 12:40:07 +13:00
Ohad Lutzky
d274545e77 Disable caching for binary download (#3054) 2022-01-17 12:40:07 +13:00
Jesse Hills
d3fda37615 Merge pull request #3042 from esphome/bump-2022.1.0b2
2022.1.0b2
2022-01-13 22:21:45 +13:00
Jesse Hills
cbe3092404 Bump version to 2022.1.0b2 2022-01-13 21:28:45 +13:00
Paulus Schoutsen
6dfe3039d0 Bump dashboard to 20220113.2 (#3041) 2022-01-13 21:28:45 +13:00
Paulus Schoutsen
d6009453df Add factory to download name (#3040) 2022-01-13 21:28:45 +13:00
Jesse Hills
c81323ef91 Merge pull request #3039 from esphome/bump-2022.1.0b1
2022.1.0b1
2022-01-13 12:12:07 +13:00
Jesse Hills
961c27f1c2 Bump version to 2022.1.0b1 2022-01-13 11:02:07 +13:00
Jesse Hills
fe4a14e6cc Merge branch 'dev' into bump-2022.1.0b1 2022-01-13 11:02:07 +13:00
Jesse Hills
50848c2f4d Merge pull request #2966 from esphome/bump-2021.12.3
2021.12.3
2021-12-30 14:54:49 +13:00
Jesse Hills
d32633b3c7 Update curl package version in docker (#2939) 2021-12-30 14:32:29 +13:00
Jesse Hills
b37739eec2 Bump version to 2021.12.3 2021-12-30 13:58:47 +13:00
Jesse Hills
28f87dc804 Remove -e for hassio images (#2964) 2021-12-30 13:58:47 +13:00
Jesse Hills
41879e41e6 Workaround installing as editable package not working (#2936) 2021-12-30 13:58:47 +13:00
Jesse Hills
fc0a6546a2 Only allow internal pins for dht sensor (#2940) 2021-12-30 13:58:47 +13:00
Jesse Hills
ffd4280d6c Require arduino in webserver for better validation (#2941) 2021-12-30 13:58:46 +13:00
Jesse Hills
db3b955b0f Merge pull request #2932 from esphome/bump-2021.12.2
2021.12.2
2021-12-21 12:45:27 +13:00
Jesse Hills
5516f65971 Bump version to 2021.12.2 2021-12-21 08:24:08 +13:00
Oxan van Leeuwen
9471df0a1b Fix MQTT button press action (#2917) 2021-12-21 08:24:07 +13:00
Oxan van Leeuwen
6d39f64be7 Don't disable idle task WDT when it's not enabled (#2856) 2021-12-21 08:24:07 +13:00
Jesse Hills
b89d0a9a73 Merge pull request #2915 from esphome/bump-2021.12.1
2021.12.1
2021-12-15 16:36:39 +13:00
Jesse Hills
4bb779d9a5 Bump version to 2021.12.1 2021-12-15 14:57:32 +13:00
wilberforce
386a5b6362 Allow button POST on press from web server (#2913) 2021-12-15 14:57:32 +13:00
Oxan van Leeuwen
e32a999cd0 Set text sensor state property to filter output (#2893) 2021-12-15 14:57:32 +13:00
Jesse Hills
bfbc6a4bad Merge pull request #2907 from esphome/bump-2021.12.0
2021.12.0
2021-12-12 07:59:37 +13:00
Jesse Hills
8c9e0e552d Bump version to 2021.12.0 2021-12-12 07:10:51 +13:00
Jesse Hills
8aaf9fd83f Merge pull request #2905 from esphome/bump-2021.12.0b6
2021.12.0b6
2021-12-11 21:54:49 +13:00
Jesse Hills
08057720b8 Bump version to 2021.12.0b6 2021-12-11 21:07:07 +13:00
Jesse Hills
bfaa648837 Bump esphome-dashboard to 20211211.0 (#2904) 2021-12-11 21:07:07 +13:00
Keith Burzinski
d504daef91 Fix for two points setting when fan_only_cooling is disabled (#2903)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
Co-authored-by: Keith Burzinski <kburzinski@kbx-mbp2021.ad.kbx81.net>
2021-12-11 21:07:07 +13:00
Jesse Hills
b8d3ef2f49 Merge pull request #2899 from esphome/bump-2021.12.0b5
2021.12.0b5
2021-12-10 10:55:55 +13:00
Jesse Hills
3bf6320030 Bump version to 2021.12.0b5 2021-12-10 09:55:48 +13:00
Guillermo Ruffino
708b928c73 Modbus number/output use write single (#2896)
Co-authored-by: Martin <25747549+martgras@users.noreply.github.com>
2021-12-10 09:55:48 +13:00
Jesse Hills
649366ff44 Fix published state for modbus number (#2894) 2021-12-10 09:55:47 +13:00
Jesse Hills
e5c9e87fad Merge pull request #2890 from esphome/bump-2021.12.0b4
2021.12.0b4
2021-12-10 09:50:29 +13:00
Jesse Hills
f3d9d707b6 Bump version to 2021.12.0b4 2021-12-08 12:58:14 +13:00
Jesse Hills
090e10730c Bump esphome-dashboard to 20211208.0 (#2887) 2021-12-08 12:58:14 +13:00
Jesse Hills
fbc84861c7 Use new platform component config blocks for wizard (#2885) 2021-12-08 12:58:14 +13:00
Carlos Garcia Saura
e763469af8 Feed watchdog while setting up OTA (#2876) 2021-12-08 12:58:14 +13:00
Jesse Hills
3c0c514e44 Merge pull request #2880 from esphome/bump-2021.12.0b3
2021.12.0b3
2021-12-07 15:27:08 +13:00
Jesse Hills
ed5e2dd332 Bump version to 2021.12.0b3 2021-12-07 07:47:48 +13:00
Jesse Hills
09b7c6f550 Bump esphome-dashboard to 20211207.0 (#2877) 2021-12-07 07:47:48 +13:00
Oxan van Leeuwen
df315a1f51 Feed watchdog when no component loops (#2857) 2021-12-07 07:47:48 +13:00
Jesse Hills
7ee4bb621c Allow wizard to specify secrets (#2875) 2021-12-07 07:47:48 +13:00
Jesse Hills
24874f4c3c Adopt using wifi secrets that should exist at this point (#2874) 2021-12-07 07:47:48 +13:00
Jesse Hills
c128880033 Add endpoint to fetch secrets keys (#2873) 2021-12-07 07:47:48 +13:00
Massimiliano Ravelli
a66e94a0b0 Ignore already stopped dhcp for ethernet (#2862)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-12-07 07:47:48 +13:00
Oxan van Leeuwen
56870ed4a8 Fix MCP23x17 not disabling pullup after config change (#2855) 2021-12-07 07:47:48 +13:00
Martin
3ac720df47 SPS30 : fix i2c read size (#2866) 2021-12-07 07:47:48 +13:00
Carlos Garcia Saura
1bc757ad06 ADC: Turn verbose the debugging "got voltage" (#2863) 2021-12-07 07:47:48 +13:00
Martin
f72abc6f3d tlc59208f : fix compilation error (#2867) 2021-12-07 07:47:48 +13:00
Jesse Hills
5ac88de985 Bump esphome-dashboard to 20211206.0 (#2870) 2021-12-07 07:47:48 +13:00
Jesse Hills
0826b367d6 Merge pull request #2853 from esphome/bump-2021.12.0b2
2021.12.0b2
2021-12-03 08:07:30 +13:00
Jesse Hills
329bf861d6 Bump version to 2021.12.0b2 2021-12-03 07:54:34 +13:00
Oxan van Leeuwen
9dcd3d18a0 Update ota_component.cpp (#2852) 2021-12-03 07:54:34 +13:00
Jesse Hills
db66cd88b6 Merge pull request #2851 from esphome/bump-2021.12.0b1
2021.12.0b1
2021-12-02 21:32:43 +13:00
Jesse Hills
86c205fe43 Remove blank line 2021-12-02 21:08:11 +13:00
Jesse Hills
c6414138c7 Bump version to 2021.12.0b1 2021-12-02 19:38:49 +13:00
Jesse Hills
36b355eb82 Merge branch 'dev' into bump-2021.12.0b1 2021-12-02 19:38:44 +13:00
Jesse Hills
7be9291b13 Merge pull request #2821 from esphome/bump-2021.11.4
2021.11.4
2021-11-29 13:23:47 +13:00
Jesse Hills
ea9e75039b Bump version to 2021.11.4 2021-11-29 10:18:49 +13:00
Conclusio
a5fb036011 Add delay to improve stability (#2793) 2021-11-29 10:18:48 +13:00
Dave T
e55506f9db Correct bitmask for third color (blue) scaling. (#2817) 2021-11-29 10:18:48 +13:00
Carlos Garcia Saura
50ec1d0445 Fix compilation error for WPA enterprise in ESP-IDF (#2815) 2021-11-29 10:18:48 +13:00
Oxan van Leeuwen
3d5e1d8d91 Fix parsing of multiple values in EZO sensor (#2814)
Co-authored-by: Lydia Sevelt <LydiaSevelt@gmail.com>
2021-11-29 10:18:48 +13:00
Oxan van Leeuwen
db2128a344 Fix parsing numbers in Anova (#2816) 2021-11-29 10:18:48 +13:00
Jesse Hills
21db43db06 Merge pull request #2808 from esphome/bump-2021.11.3
2021.11.3
2021-11-28 00:01:16 +13:00
Jesse Hills
5009b3029f Bump version to 2021.11.3 2021-11-27 21:13:01 +13:00
Maurice Makaay
57a029189c Add missing nvs_flash_init() to ESP32 preferences code (#2805)
Co-authored-by: Maurice Makaay <mmakaay1@xs4all.net>
2021-11-27 21:13:01 +13:00
Jesse Hills
0cb715bb76 Merge pull request #2799 from esphome/bump-2021.11.2
2021.11.2
2021-11-26 09:25:37 +13:00
Jesse Hills
7d03823afd Bump version to 2021.11.2 2021-11-26 09:02:54 +13:00
Oxan van Leeuwen
8e1c9f5042 Fix parsing numbers from null-terminated buffers (#2755) 2021-11-26 09:02:54 +13:00
Samuel Sieb
980b7cda8f Remove floating point ops from the ISR (#2751)
Co-authored-by: Samuel Sieb <samuel@sieb.net>
2021-11-26 09:02:53 +13:00
Kamil Trzciński
3a72dd5cb6 esp32_camera_web_server: Improve support for MotionEye (#2777) 2021-11-26 09:02:53 +13:00
Dave T
3178243811 Fix frame scaling for animated gifs (#2750) 2021-11-26 09:02:53 +13:00
Maurice Makaay
d30e2f2a4f Allow UART debug configuration with no after: definition (#2753) 2021-11-26 09:02:53 +13:00
Jesse Hills
6226dae05c Merge pull request #2744 from esphome/bump-2021.11.1
2021.11.1
2021-11-17 23:45:43 +13:00
Jesse Hills
9c6a475a6e Bump version to 2021.11.1 2021-11-17 23:31:38 +13:00
Franck Nijhof
8294d10d5b Re-instate device class update for binary sensors (#2743) 2021-11-17 23:31:38 +13:00
Evgeny
67558bec47 Fix HM3301 AQI index calculator (#2739) 2021-11-17 23:31:38 +13:00
Jesse Hills
84873d4074 Merge pull request #2742 from esphome/bump-2021.11.0
2021.11.0
2021-11-17 22:18:29 +13:00
Jesse Hills
58a0b28a39 Bump version to 2021.11.0 2021-11-17 21:58:30 +13:00
Jesse Hills
b37d3a66cc Merge pull request #2738 from esphome/bump-2021.11.0b9
2021.11.0b9
2021-11-17 10:27:41 +13:00
Jesse Hills
7e495a5e27 Bump version to 2021.11.0b9 2021-11-17 08:00:26 +13:00
rotarykite
c41547fd4a Fix senseair component uart read timeout (#2658)
Co-authored-by: DAVe3283 <DAVe3283+GitHub@gmail.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
Co-authored-by: Chua Jun Chieh <junchieh.chua@softspace.com.my>
2021-11-17 08:00:26 +13:00
Ryan Hoffman
0d47d41c85 Use as_reversed_hex_array in ble_sensor to fix UUID parsing (#2737)
#1627 renamed as_hex_array to as_reversed_hex_array but forgot to rename these users.
2021-11-17 08:00:26 +13:00
Jesse Hills
41a3a17456 Merge pull request #2734 from esphome/bump-2021.11.0b8
2021.11.0b8
2021-11-16 13:50:10 +13:00
Jesse Hills
cbbafbcca2 Bump version to 2021.11.0b8 2021-11-16 12:53:56 +13:00
Jesse Hills
c75566b374 Fix zeroconf time comparisons (#2733)
Co-authored-by: J. Nick Koston <nick@koston.org>
2021-11-16 12:53:56 +13:00
Jesse Hills
7279f1fcc1 Merge pull request #2732 from esphome/bump-2021.11.0b7
2021.11.0b7
2021-11-16 12:19:07 +13:00
Jesse Hills
d7432f7c10 Bump version to 2021.11.0b7 2021-11-16 11:05:51 +13:00
Jesse Hills
b0a0a153f3 Improv serial/checksum changes (#2731)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2021-11-16 11:05:51 +13:00
Jesse Hills
024632dbd0 Merge pull request #2730 from esphome/bump-2021.11.0b6
2021.11.0b6
2021-11-16 10:53:11 +13:00
Jesse Hills
0a545a28b9 Bump version to 2021.11.0b6 2021-11-16 09:59:00 +13:00
Jesse Hills
0f2df59998 Add zeroconf as a direct dependency and lock the version (#2729) 2021-11-16 09:58:59 +13:00
Jesse Hills
29a7d32f77 Merge pull request #2725 from esphome/bump-2021.11.0b5
2021.11.0b5
2021-11-15 13:42:59 +13:00
Jesse Hills
687a7e9b2f Bump version to 2021.11.0b5 2021-11-15 12:02:18 +13:00
Alexandre-Jacques St-Jacques
09e8782318 Remove unnecessary duplicate touch_pad_filter_start (#2724) 2021-11-15 12:02:18 +13:00
Jesse Hills
f2aea02210 Merge pull request #2723 from esphome/bump-2021.11.0b4
2021.11.0b4
2021-11-15 11:42:59 +13:00
Jesse Hills
194f922312 Bump version to 2021.11.0b4 2021-11-15 11:03:40 +13:00
Jesse Hills
fea3c48098 Fix indentation of write_lambda for modbus_controller number (#2722) 2021-11-15 11:03:39 +13:00
Sergey V. DUDANOV
c2f57baec2 RemoteTransmitter fix. Bug from version 2021.10. Some changes. (#2706) 2021-11-15 11:03:39 +13:00
Oxan van Leeuwen
f4a140e126 Feed WDT between doing ESP32 touchpad measurements (#2720) 2021-11-15 11:03:39 +13:00
Oxan van Leeuwen
ab506b09fe Restore InterruptLock on wifi-less ESP8266 (#2712) 2021-11-15 11:03:39 +13:00
Krzysztof Białek
87e1cdeedb Allow setting custom command_topic for Select and Number components (#2714) 2021-11-15 11:03:39 +13:00
Jesse Hills
81a36146ef Bump ESPAsyncWebServer to 2.1.0 (#2686) 2021-11-15 11:03:39 +13:00
Jesse Hills
7fa4a68a27 Merge pull request #2704 from esphome/bump-2021.11.0b3
2021.11.0b3
2021-11-12 17:21:58 +13:00
Jesse Hills
f1c5e2ef81 Bump version to 2021.11.0b3 2021-11-12 16:12:31 +13:00
lcavalli
b526155cce Update device classes for binary sensors (#2703) 2021-11-12 16:12:31 +13:00
Jesse Hills
62c3f301e7 Only allow prometheus when using arduino (#2697) 2021-11-12 16:12:31 +13:00
Jesse Hills
38cb988809 Remove my.ha links from improv (#2695) 2021-11-12 16:12:31 +13:00
Jesse Hills
b976ac54c8 Merge pull request #2693 from esphome/bump-2021.11.0b2
2021.11.0b2
2021-11-11 12:52:35 +13:00
Jesse Hills
78026e766f Bump version to 2021.11.0b2 2021-11-11 12:25:41 +13:00
Oxan van Leeuwen
b4cd8d21a5 Enable addressable light power supply based on raw values (#2690) 2021-11-11 12:25:41 +13:00
Maurice Makaay
7552893311 Uart debugging support (#2478)
Co-authored-by: Maurice Makaay <mmakaay1@xs4all.net>
Co-authored-by: Maurice Makaay <account-github@makaay.nl>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-11-11 12:25:41 +13:00
Carlos Garcia Saura
21c896d8f8 [remote_transmitter] accurate pulse timing for ESP8266 (#2476) 2021-11-11 12:25:40 +13:00
Jesse Hills
4b7fe202ec Fix template number initial value being NaN (#2692) 2021-11-11 12:25:40 +13:00
Jesse Hills
9f4519210f Merge pull request #2691 from esphome/bump-2021.11.0b1
2021.11.0b1
2021-11-11 11:05:45 +13:00
Jesse Hills
b0506afa5b Merge branch 'beta' into bump-2021.11.0b1 2021-11-11 10:48:23 +13:00
Jesse Hills
8cbb379898 Remove import (not sure how it got there) 2021-11-11 10:35:18 +13:00
Jesse Hills
b226215593 Bump version to 2021.11.0b1 2021-11-11 10:10:05 +13:00
Jesse Hills
19970729a9 Merge branch 'dev' into bump-2021.11.0b1 2021-11-11 10:10:04 +13:00
Jesse Hills
d2ebfd2833 Merge pull request #2634 from esphome/bump-2021.10.3
2021.10.3
2021-10-27 11:21:08 +13:00
Jesse Hills
bd782fc828 Bump version to 2021.10.3 2021-10-27 10:49:11 +13:00
Jesse Hills
23560e608c Fix select.set using lambda (#2633) 2021-10-27 10:49:10 +13:00
Jan Čermák
f1377b560e Fix pin number validation for sn74hc595 (#2621) 2021-10-27 10:49:10 +13:00
Martin
72108684ea fix modbus output (#2630) 2021-10-27 10:49:10 +13:00
Jesse Hills
c6adaaea97 Remove power and energy from sensors that are not true power (#2628) 2021-10-27 10:49:10 +13:00
Otto Winter
91999a38ca Fix glue code missing micros() (#2623) 2021-10-27 10:49:10 +13:00
0hax
b34eed125d Teleinfo ptec (#2599)
* teleinfo: handle historical mode correctly.

In historical mode, tags like PTEC leads to an issue where we detect a
timestamp wheras this is not possible in historical mode.

PTEC teleinfo tag looks like:
    PTEC HP..
Instead of the usual format
    IINST1 001 I

This make our data parsing fails.

While at here, make sure we continue parsing other tags even if parsing
one of the tag fails.

Signed-off-by: 0hax <0hax@protonmail.com>

* teleinfo: fix compilation with loglevel set to debug.

Signed-off-by: 0hax <0hax@protonmail.com>
2021-10-27 10:49:10 +13:00
Stefan Agner
2abe09529a Autodetect flash size (#2615) 2021-10-27 10:49:10 +13:00
Otto Winter
9aaaf4dd4b Bump platform-espressif8266 from 2.6.2 to 2.6.3 (#2620) 2021-10-27 10:49:09 +13:00
Andreas Hergert
cbfbcf7f1b fixed dependency for pca9685 component (#2614)
Co-authored-by: Otto Winter <otto@otto-winter.com>
Co-authored-by: Andreas <andreas.hergert@otrs.com>
2021-10-27 10:49:09 +13:00
Otto Winter
c7651dc40d Merge pull request #2613 from esphome/bump-2021.10.2
2021.10.2
2021-10-22 18:35:28 +02:00
Otto winter
eda1c471ad Bump version to 2021.10.2 2021-10-22 18:26:24 +02:00
Andreas Hergert
c7ef18fbc4 Bugfix tca9548a and idf refactor anh (#2612)
Co-authored-by: Andreas Hergert <andreas.hergert@otrs.com>
2021-10-22 18:26:23 +02:00
Otto Winter
901ec918b1 Fix ESP8266 OTA compression only starting framework v2.7.0 (#2610) 2021-10-22 18:26:23 +02:00
Otto Winter
6bdae55ee1 Fix compiler warnings and update platformio line filter (#2607) 2021-10-22 18:26:23 +02:00
Otto Winter
dfb96e4b7f Add owner to all libraries used (#2604) 2021-10-22 18:26:22 +02:00
Otto Winter
ff2c316b18 Re-raise keyboardinterrupt (#2603) 2021-10-22 18:26:22 +02:00
Otto Winter
5be52f71f9 Add OTA upload compression for ESP8266 (#2601) 2021-10-22 18:26:22 +02:00
Otto Winter
42873dd37c Bump noise-c from 0.1.3 to 0.1.4 (#2602) 2021-10-22 18:26:21 +02:00
Otto Winter
f93e7d4e3a Fix socket connection closed not detected (#2587) 2021-10-22 18:26:21 +02:00
Oxan van Leeuwen
bbcd523967 Fix validation of addressable light IDs (#2588) 2021-10-22 18:26:21 +02:00
dependabot[bot]
68cbe58d00 Bump esphome-dashboard from 20211021.0 to 20211021.1 (#2594)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-22 18:26:12 +02:00
Oxan van Leeuwen
115bca98f1 Fix old-style arduino_version on ESP8266 and with magic values (#2591) 2021-10-22 18:24:48 +02:00
Oxan van Leeuwen
ed0b34b2fe Fix pin/component switchup in SX1509 pin configuration (#2593) 2021-10-22 18:24:48 +02:00
Oxan van Leeuwen
ab34401421 Fix PlatformIO version for latest Arduino framework (#2590) 2021-10-22 18:24:48 +02:00
Otto Winter
eed0c18d65 Fix HeatpumpIR pin (#2585) 2021-10-22 18:24:47 +02:00
Otto Winter
e5a38ce748 Merge pull request #2580 from esphome/bump-2021.10.1
2021.10.1
2021-10-21 15:31:26 +02:00
Otto Winter
7d9d9fcf36 Fix platformio_install_deps no longer installing all lib_deps (#2584) 2021-10-21 15:29:53 +02:00
Otto Winter
f0aba6ceb2 Fix platformio version in Dockerfile doesn't match requirements (#2582) 2021-10-21 14:53:22 +02:00
Otto Winter
ab07ee57c6 Fix ESP8266 dallas GPIO16 INPUT_PULLUP (#2581) 2021-10-21 14:39:55 +02:00
Otto winter
eae3d72a4d Bump version to 2021.10.1 2021-10-21 14:23:08 +02:00
Otto Winter
7b8d826704 Fix ESP8266 OTA adds unnecessary Update library (#2579) 2021-10-21 14:23:07 +02:00
Otto Winter
e7baa42e63 Arduino global delay/millis/... symbols workaround (#2575) 2021-10-21 14:23:07 +02:00
Otto Winter
2f32833a22 Fix wifi ble coexistence check (#2573) 2021-10-21 14:23:07 +02:00
Otto Winter
f6935a4b4b Fix ESP8266 GPIO0 Pullup Validation (#2572) 2021-10-21 14:23:06 +02:00
Maurice Makaay
332c9e891b Fix MDNS for ESP8266 devices (#2571)
Co-authored-by: Maurice Makaay <account-github@makaay.nl>
Co-authored-by: Otto winter <otto@otto-winter.com>
Co-authored-by: Maurice Makaay <mmakaay1@xs4all.net>
2021-10-21 14:23:06 +02:00
Jesse Hills
b91ee4847f Merge pull request #2570 from esphome/bump-2021.10.0
2021.10.0
2021-10-21 08:37:26 +13:00
Jesse Hills
625463d871 Bump version to 2021.10.0 2021-10-21 07:57:10 +13:00
Jesse Hills
6433a01e07 Merge pull request #2567 from esphome/bump-2021.10.0b11
2021.10.0b11
2021-10-21 07:47:22 +13:00
Jesse Hills
56cc31e8e7 Bump version to 2021.10.0b11 2021-10-21 07:32:27 +13:00
Otto Winter
3af297aa76 Revert nextion clang-tidy changes (#2566) 2021-10-21 07:32:27 +13:00
Otto Winter
996ec59d28 Move running process log line to debug level (#2565) 2021-10-21 07:32:26 +13:00
Jesse Hills
95593eeeab Bump esphome-dashboard to 20211021.0 (#2564) 2021-10-21 07:32:20 +13:00
Jesse Hills
dad244fb7a A few esp32_ble_server/improv fixes (#2562) 2021-10-21 07:31:55 +13:00
Jesse Hills
adb5d27d95 Merge pull request #2561 from esphome/bump-2021.10.0b10
2021.10.0b10
2021-10-20 17:28:37 +13:00
Jesse Hills
8456a8cecb Bump version to 2021.10.0b10 2021-10-20 16:39:24 +13:00
Jesse Hills
b9f66373c1 Bump esphome-dashboard to 20211020.1 (#2559) 2021-10-20 16:39:16 +13:00
Jesse Hills
9ac365feef Fix HA addon so it does not have logout button (#2558) 2021-10-20 16:38:46 +13:00
Jesse Hills
43bbd58a44 Merge pull request #2557 from esphome/bump-2021.10.0b9
2021.10.0b9
2021-10-20 11:18:40 +13:00
Jesse Hills
7feffa64f3 Bump version to 2021.10.0b9 2021-10-20 11:00:02 +13:00
Jesse Hills
ea0977abb4 Bump dashboard to 20211020.0 (#2556) 2021-10-20 10:59:53 +13:00
Jesse Hills
4c83dc7c28 Merge pull request #2555 from esphome/bump-2021.10.0b8
2021.10.0b8
2021-10-20 10:30:59 +13:00
Jesse Hills
e10ab1da78 Bump version to 2021.10.0b8 2021-10-20 10:14:30 +13:00
Martin
1b0e60374b ignore exception when not waiting for a response (#2552) 2021-10-20 10:14:30 +13:00
Oxan van Leeuwen
3a760fbb44 Fix ADC pin validation on ESP32-C3 (#2551) 2021-10-20 10:14:30 +13:00
Jesse Hills
6ef57a2973 Merge pull request #2550 from esphome/bump-2021.10.0b7
2021.10.0b7
2021-10-19 16:22:29 +13:00
Jesse Hills
3e9c7f2e9f Bump version to 2021.10.0b7 2021-10-19 15:47:31 +13:00
Jesse Hills
430598b7a1 Bump dashboard to 20211019.0 (#2549) 2021-10-19 15:47:26 +13:00
Jesse Hills
91611b09b4 Merge pull request #2545 from esphome/bump-2021.10.0b6
2021.10.0b6
2021-10-18 21:41:22 +13:00
Jesse Hills
ecd115851f Bump version to 2021.10.0b6 2021-10-18 21:26:36 +13:00
Maurice Makaay
4a1e50fed1 OTA firmware MD5 check + password support for esp-idf (#2507)
Co-authored-by: Maurice Makaay <account-github@makaay.nl>
2021-10-18 21:26:36 +13:00
Jesse Hills
d6d037047b Merge pull request #2544 from esphome/bump-2021.10.0b5
2021.10.0b5
2021-10-18 16:37:43 +13:00
Jesse Hills
b5734c2b20 Bump version to 2021.10.0b5 2021-10-18 15:31:01 +13:00
Oxan van Leeuwen
723fb7eaac Autodetect ESP32 variant (#2530)
Co-authored-by: Otto winter <otto@otto-winter.com>
2021-10-18 15:31:01 +13:00
Jesse Hills
63a9acaa19 Fix Bluetooth setup_priorities (#2458)
Co-authored-by: Otto Winter <otto@otto-winter.com>
2021-10-18 15:31:00 +13:00
Jesse Hills
0524f8c677 Fix const used for IDF recommended version (#2542) 2021-10-18 15:31:00 +13:00
Otto Winter
70b62f272e Only show timestamp for dashboard access logs (#2540) 2021-10-18 15:31:00 +13:00
Jesse Hills
f0089b7940 Merge pull request #2539 from esphome/bump-2021.10.0b4
2021.10.0b4
2021-10-17 21:17:54 +13:00
Jesse Hills
4b44280d53 Bump version to 2021.10.0b4 2021-10-17 20:34:07 +13:00
Paulus Schoutsen
f045382d20 Bump dashboard to 20211015.0 (#2525)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-10-17 20:34:03 +13:00
Jesse Hills
db3fa1ade7 Allow downloading all bin files from backend in dashboard (#2514)
Co-authored-by: Otto Winter <otto@otto-winter.com>
2021-10-17 20:28:43 +13:00
Oxan van Leeuwen
f83950fd75 Fix bitshift on read in ADE7953 (#2537) 2021-10-17 20:28:43 +13:00
Oxan van Leeuwen
4dd1bf920d Replace framework version_hint with source option (#2529) 2021-10-17 20:28:43 +13:00
Martin
98755f3621 Fix bug in register name definition (#2526) 2021-10-17 20:28:43 +13:00
Keith Burzinski
c3a8a044b9 Fix Nextion HTTPClient error for ESP32 (#2524) 2021-10-17 20:28:42 +13:00
Martin
15b5ea43a7 Add pressure compensation during runtime (#2493)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2021-10-17 20:28:42 +13:00
Jesse Hills
ec683fc227 Merge pull request #2523 from esphome/bump-2021.10.0b3
2021.10.0b3
2021-10-15 10:15:26 +13:00
Jesse Hills
d4e65eb82a Bump version to 2021.10.0b3 2021-10-15 09:42:44 +13:00
Paul Monigatti
10c6601b0a Revert "Added test for bme680_bsec" (#2518)
This reverts commit 7f6a50d291 due to BSEC library license restrictions.
2021-10-15 09:42:44 +13:00
Oxan van Leeuwen
73940bc1bd Don't define UART_SELECTION_UART2 when UART2 is unavailable (#2512) 2021-10-15 09:42:43 +13:00
Paul Monigatti
9b7fb829f9 Fix: Color modes not being correctly used in light partitions (#2513) 2021-10-15 09:42:43 +13:00
Dmitriy Lopatko
c51d8c9021 add missing include in sgp30 (#2517) 2021-10-15 09:42:43 +13:00
Paul Monigatti
d8a6dfe5ce Fix BME680_BSEC compilation issue with ESP32 (#2516) 2021-10-15 09:42:43 +13:00
Oxan van Leeuwen
5f7cef0b06 Disallow using UART2 for logger on ESP-32 variants that lack it (#2510) 2021-10-15 09:42:43 +13:00
Paul Monigatti
48ff2ffc68 Fix: Light flash not restoring previous LightState (#2383)
* Update light state when transformer has finished

* Revert writing direct to output

* Correct handling of zero-length light transformers

* Allow transformers to handle zero-length transitions, and check more boundary conditions when transitioning back to start state

* Removed log.h

* Fixed race condition between LightFlashTransformer.apply() and is_finished()

* clang-format

* Step progress from 0.0f to 1.0f at t=start_time for zero-length transforms to avoid divide-by-zero
2021-10-15 09:42:43 +13:00
Oxan van Leeuwen
b3b9ccd314 Fix light state remaining on after turn off with transition (#2509) 2021-10-15 09:42:43 +13:00
Jesse Hills
e63c7b483b Merge pull request #2505 from esphome/bump-2021.10.0b2
2021.10.0b2
2021-10-13 22:45:29 +13:00
Jesse Hills
f57980b069 Bump version to 2021.10.0b2 2021-10-13 22:30:29 +13:00
Jesse Hills
7006aa0d2a Merge branch 'dev' into beta 2021-10-13 22:11:53 +13:00
Jesse Hills
8051c1ca99 Merge pull request #2504 from esphome/bump-2021.10.0b1
2021.10.0b1
2021-10-13 21:38:41 +13:00
Jesse Hills
a779592414 Bump version to 2021.10.0b1 2021-10-13 16:40:46 +13:00
Jesse Hills
112215848d Merge branch 'dev' into bump-2021.10.0b1 2021-10-13 16:40:46 +13:00
470 changed files with 20199 additions and 5261 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,9 +1,15 @@
---
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
directory: "/"
schedule:
interval: daily
open-pull-requests-limit: 10

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
@@ -30,24 +32,24 @@ jobs:
arch: [amd64, armv7, aarch64]
build_type: ["ha-addon", "docker", "lint"]
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- 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,24 +76,28 @@ 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@v2
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v2
uses: actions/setup-python@v4
id: python
with:
python-version: '3.8'
python-version: "3.8"
- name: Cache virtualenv
uses: actions/cache@v2
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@v2
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'
@@ -131,7 +140,7 @@ jobs:
if: matrix.id == 'ci-custom'
- name: Lint Python
run: script/lint-python
run: script/lint-python -a
if: matrix.id == 'lint-python'
- run: esphome compile ${{ matrix.file }}
@@ -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
if: always() && (matrix.id == 'clang-tidy' || matrix.id == 'clang-format')
# 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:
@@ -17,9 +19,10 @@ jobs:
outputs:
tag: ${{ steps.tag.outputs.tag }}
steps:
- uses: actions/checkout@v2
- 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,17 +32,18 @@ jobs:
TAG="${TAG}${today}"
fi
echo "::set-output name=tag::${TAG}"
# yamllint enable rule:line-length
deploy-pypi:
name: Build and publish to PyPi
if: github.repository == 'esphome/esphome' && github.event_name == 'release'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v1
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@v2
- name: Set up Python
uses: actions/setup-python@v2
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@v1
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- 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@v1
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Log in to the GitHub container registry
uses: docker/login-action@v1
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@v2
- name: Set up Python
uses: actions/setup-python@v2
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@v1
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Log in to the GitHub container registry
uses: docker/login-action@v1
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:
@@ -16,7 +18,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v4
- uses: actions/stale@v5
with:
days-before-pr-stale: 90
days-before-pr-close: 7
@@ -31,11 +33,12 @@ 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:
- uses: actions/stale@v4
- uses: actions/stale@v5
with:
days-before-pr-stale: -1
days-before-pr-close: -1

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.3.0
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.31.1
rev: v2.37.3
hooks:
- id: pyupgrade
args: [--py38-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
@@ -52,18 +56,21 @@ esphome/components/cs5460a/* @balrog-kun
esphome/components/cse7761/* @berfenger
esphome/components/ct_clamp/* @jesserockz
esphome/components/current_based/* @djwmarcx
esphome/components/dac7678/* @NickB1
esphome/components/daly_bms/* @s1lvi0
esphome/components/dashboard_import/* @esphome/core
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
@@ -71,7 +78,9 @@ 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
esphome/components/globals/* @esphome/core
esphome/components/gpio/* @esphome/core
@@ -88,6 +97,7 @@ esphome/components/honeywellabp/* @RubyBailey
esphome/components/hrxl_maxsonar_wr/* @netmikey
esphome/components/hydreon_rgxx/* @functionpointer
esphome/components/i2c/* @esphome/core
esphome/components/i2s_audio/* @jesserockz
esphome/components/improv_serial/* @esphome/core
esphome/components/ina260/* @MrEditor97
esphome/components/inkbird_ibsth1_mini/* @fkirill
@@ -102,6 +112,7 @@ esphome/components/lilygo_t5_47/touchscreen/* @jesserockz
esphome/components/lock/* @esphome/core
esphome/components/logger/* @esphome/core
esphome/components/ltr390/* @sjtrny
esphome/components/max31865/* @DAVe3283
esphome/components/max44009/* @berfenger
esphome/components/max7219digit/* @rspaargaren
esphome/components/max9611/* @mckaymatthew
@@ -116,9 +127,11 @@ 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
esphome/components/media_player/* @jesserockz
esphome/components/midea/* @dudanov
esphome/components/midea_ir/* @dudanov
esphome/components/mitsubishi/* @RubyBailey
@@ -133,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
@@ -185,9 +199,11 @@ esphome/components/shutdown/* @esphome/core @jsuanet
esphome/components/sim800l/* @glmnet
esphome/components/sm2135/* @BoukeHaarsma23
esphome/components/sml/* @alengwenus
esphome/components/smt100/* @piechade
esphome/components/socket/* @esphome/core
esphome/components/sonoff_d1/* @anatoly-savchenkov
esphome/components/spi/* @esphome/core
esphome/components/sprinkler/* @kbx81
esphome/components/sps30/* @martgras
esphome/components/ssd1322_base/* @kbx81
esphome/components/ssd1322_spi/* @kbx81
@@ -213,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
@@ -228,11 +246,14 @@ 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
esphome/components/web_server_base/* @OttoWinter
esphome/components/whirlpool/* @glmnet
esphome/components/whynter/* @aeonsablaze
esphome/components/xiaomi_lywsd03mmc/* @ahpohl
esphome/components/xiaomi_mhoc303/* @drug123
esphome/components/xiaomi_mhoc401/* @vevsvevs

View File

@@ -46,12 +46,10 @@ RUN \
# Ubuntu python3-pip is missing wheel
pip3 install --no-cache-dir \
wheel==0.37.1 \
platformio==5.2.5 \
platformio==6.0.2 \
# Change some platformio settings
&& platformio settings set enable_telemetry No \
&& platformio settings set check_libraries_interval 1000000 \
&& platformio settings set check_platformio_interval 1000000 \
&& platformio settings set check_platforms_interval 1000000 \
&& mkdir -p /piolibs
@@ -96,7 +94,7 @@ RUN \
apt-get update \
# Use pinned versions so that we get updates with build caching
&& apt-get install -y --no-install-recommends \
nginx-light=1.18.0-6.1 \
nginx-light=1.18.0-6.1+deb11u2 \
&& rm -rf \
/tmp/* \
/var/{cache,log}/* \
@@ -136,7 +134,7 @@ RUN \
clang-tidy-11=1:11.0.1-2 \
patch=2.7.6-7 \
software-properties-common=0.96.20.2-2.1 \
nano=5.4-2 \
nano=5.4-2+deb11u1 \
build-essential=12.9 \
python3-dev=3.9.2-3 \
&& rm -rf \

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

@@ -12,7 +12,7 @@ from esphome.const import (
CONF_TYPE_ID,
CONF_TIME,
)
from esphome.jsonschema import jschema_extractor
from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor
from esphome.util import Registry
@@ -23,11 +23,10 @@ def maybe_simple_id(*validators):
def maybe_conf(conf, *validators):
validator = cv.All(*validators)
@jschema_extractor("maybe")
@schema_extractor("maybe")
def validate(value):
# pylint: disable=comparison-with-callable
if value == jschema_extractor:
return validator
if value == SCHEMA_EXTRACT:
return (validator, conf)
if isinstance(value, dict):
return validator(value)
@@ -111,11 +110,9 @@ def validate_automation(extra_schema=None, extra_validators=None, single=False):
# This should only happen with invalid configs, but let's have a nice error message.
return [schema(value)]
@jschema_extractor("automation")
@schema_extractor("automation")
def validator(value):
# hack to get the schema
# pylint: disable=comparison-with-callable
if value == jschema_extractor:
if value == SCHEMA_EXTRACT:
return schema
value = validator_(value)

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

@@ -15,10 +15,21 @@ namespace esphome {
namespace adc {
static const char *const TAG = "adc";
// 13 bits for S3 / 12 bit for all other esp32 variants
// create a const to avoid the repated cast to enum
// 13bit for S2, and 12bit for all other esp32 variants
#ifdef USE_ESP32
static const adc_bits_width_t ADC_WIDTH_MAX_SOC_BITS = static_cast<adc_bits_width_t>(ADC_WIDTH_MAX - 1);
#ifndef SOC_ADC_RTC_MAX_BITWIDTH
#if USE_ESP32_VARIANT_ESP32S2
static const int SOC_ADC_RTC_MAX_BITWIDTH = 13;
#else
static const int SOC_ADC_RTC_MAX_BITWIDTH = 12;
#endif
#endif
static const int ADC_MAX = (1 << SOC_ADC_RTC_MAX_BITWIDTH) - 1; // 4095 (12 bit) or 8191 (13 bit)
static const int ADC_HALF = (1 << SOC_ADC_RTC_MAX_BITWIDTH) >> 1; // 2048 (12 bit) or 4096 (13 bit)
#endif
void ADCSensor::setup() {
@@ -51,10 +62,6 @@ void ADCSensor::setup() {
}
}
// adc_gpio_init doesn't exist on ESP32-S2, ESP32-C3 or ESP32-H2
#if !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32H2) && !defined(USE_ESP32_VARIANT_ESP32S2)
adc_gpio_init(ADC_UNIT_1, (adc_channel_t) channel_);
#endif
#endif // USE_ESP32
}
@@ -75,16 +82,16 @@ void ADCSensor::dump_config() {
} else {
switch (this->attenuation_) {
case ADC_ATTEN_DB_0:
ESP_LOGCONFIG(TAG, " Attenuation: 0db (max 1.1V)");
ESP_LOGCONFIG(TAG, " Attenuation: 0db");
break;
case ADC_ATTEN_DB_2_5:
ESP_LOGCONFIG(TAG, " Attenuation: 2.5db (max 1.5V)");
ESP_LOGCONFIG(TAG, " Attenuation: 2.5db");
break;
case ADC_ATTEN_DB_6:
ESP_LOGCONFIG(TAG, " Attenuation: 6db (max 2.2V)");
ESP_LOGCONFIG(TAG, " Attenuation: 6db");
break;
case ADC_ATTEN_DB_11:
ESP_LOGCONFIG(TAG, " Attenuation: 11db (max 3.9V)");
ESP_LOGCONFIG(TAG, " Attenuation: 11db");
break;
default: // This is to satisfy the unused ADC_ATTEN_MAX
break;
@@ -129,16 +136,16 @@ float ADCSensor::sample() {
return mv / 1000.0f;
}
int raw11, raw6 = 4095, raw2 = 4095, raw0 = 4095;
int raw11, raw6 = ADC_MAX, raw2 = ADC_MAX, raw0 = ADC_MAX;
adc1_config_channel_atten(channel_, ADC_ATTEN_DB_11);
raw11 = adc1_get_raw(channel_);
if (raw11 < 4095) {
if (raw11 < ADC_MAX) {
adc1_config_channel_atten(channel_, ADC_ATTEN_DB_6);
raw6 = adc1_get_raw(channel_);
if (raw6 < 4095) {
if (raw6 < ADC_MAX) {
adc1_config_channel_atten(channel_, ADC_ATTEN_DB_2_5);
raw2 = adc1_get_raw(channel_);
if (raw2 < 4095) {
if (raw2 < ADC_MAX) {
adc1_config_channel_atten(channel_, ADC_ATTEN_DB_0);
raw0 = adc1_get_raw(channel_);
}
@@ -154,15 +161,15 @@ float ADCSensor::sample() {
uint32_t mv2 = esp_adc_cal_raw_to_voltage(raw2, &cal_characteristics_[(int) ADC_ATTEN_DB_2_5]);
uint32_t mv0 = esp_adc_cal_raw_to_voltage(raw0, &cal_characteristics_[(int) ADC_ATTEN_DB_0]);
// Contribution of each value, in range 0-2048
uint32_t c11 = std::min(raw11, 2048);
uint32_t c6 = 2048 - std::abs(raw6 - 2048);
uint32_t c2 = 2048 - std::abs(raw2 - 2048);
uint32_t c0 = std::min(4095 - raw0, 2048);
// max theoretical csum value is 2048*4 = 8192
// Contribution of each value, in range 0-2048 (12 bit ADC) or 0-4096 (13 bit ADC)
uint32_t c11 = std::min(raw11, ADC_HALF);
uint32_t c6 = ADC_HALF - std::abs(raw6 - ADC_HALF);
uint32_t c2 = ADC_HALF - std::abs(raw2 - ADC_HALF);
uint32_t c0 = std::min(ADC_MAX - raw0, ADC_HALF);
// max theoretical csum value is 4096*4 = 16384
uint32_t csum = c11 + c6 + c2 + c0;
// each mv is max 3900; so max value is 3900*2048*4, fits in unsigned
// each mv is max 3900; so max value is 3900*4096*4, fits in unsigned32
uint32_t mv_scaled = (mv11 * c11) + (mv6 * c6) + (mv2 * c2) + (mv0 * c0);
return mv_scaled / (float) (csum * 1000U);
}

View File

@@ -40,6 +40,8 @@ class AddressableLightDisplay : public display::DisplayBuffer, public PollingCom
void setup() override;
void display();
display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_COLOR; }
protected:
int get_width_internal() override;
int get_height_internal() override;

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

@@ -76,6 +76,8 @@ async def to_code(config):
pos = 0
for frameIndex in range(frames):
image.seek(frameIndex)
if CONF_RESIZE in config:
image.thumbnail(config[CONF_RESIZE])
frame = image.convert("RGB")
if CONF_RESIZE in config:
frame = frame.resize([width, height])

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);
}
@@ -92,7 +95,7 @@ void Anova::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_
}
if (this->codec_->has_unit()) {
this->fahrenheit_ = (this->codec_->unit_ == 'f');
ESP_LOGD(TAG, "Anova units is %s", this->fahrenheit_ ? "fahrenheit" : "celcius");
ESP_LOGD(TAG, "Anova units is %s", this->fahrenheit_ ? "fahrenheit" : "celsius");
this->current_request_++;
}
this->publish_state();
@@ -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

@@ -42,6 +42,17 @@ service APIConnection {
rpc select_command (SelectCommandRequest) returns (void) {}
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) {}
}
@@ -76,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.
@@ -189,6 +202,8 @@ message DeviceInfoResponse {
string project_version = 9;
uint32 webserver_port = 10;
uint32 bluetooth_proxy_version = 11;
}
message ListEntitiesRequest {
@@ -472,6 +487,7 @@ enum SensorStateClass {
STATE_CLASS_NONE = 0;
STATE_CLASS_MEASUREMENT = 1;
STATE_CLASS_TOTAL_INCREASING = 2;
STATE_CLASS_TOTAL = 3;
}
enum SensorLastResetType {
@@ -991,7 +1007,7 @@ message ListEntitiesLockResponse {
bool supports_open = 9;
bool requires_code = 10;
# Not yet implemented:
// Not yet implemented:
string code_format = 11;
}
message LockStateResponse {
@@ -1010,7 +1026,7 @@ message LockCommandRequest {
fixed32 key = 1;
LockCommand command = 2;
# Not yet implemented:
// Not yet implemented:
bool has_code = 3;
string code = 4;
}
@@ -1040,3 +1056,245 @@ message ButtonCommandRequest {
fixed32 key = 1;
}
// ==================== MEDIA PLAYER ====================
enum MediaPlayerState {
MEDIA_PLAYER_STATE_NONE = 0;
MEDIA_PLAYER_STATE_IDLE = 1;
MEDIA_PLAYER_STATE_PLAYING = 2;
MEDIA_PLAYER_STATE_PAUSED = 3;
}
enum MediaPlayerCommand {
MEDIA_PLAYER_COMMAND_PLAY = 0;
MEDIA_PLAYER_COMMAND_PAUSE = 1;
MEDIA_PLAYER_COMMAND_STOP = 2;
MEDIA_PLAYER_COMMAND_MUTE = 3;
MEDIA_PLAYER_COMMAND_UNMUTE = 4;
}
message ListEntitiesMediaPlayerResponse {
option (id) = 63;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_MEDIA_PLAYER";
string object_id = 1;
fixed32 key = 2;
string name = 3;
string unique_id = 4;
string icon = 5;
bool disabled_by_default = 6;
EntityCategory entity_category = 7;
bool supports_pause = 8;
}
message MediaPlayerStateResponse {
option (id) = 64;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_MEDIA_PLAYER";
option (no_delay) = true;
fixed32 key = 1;
MediaPlayerState state = 2;
float volume = 3;
bool muted = 4;
}
message MediaPlayerCommandRequest {
option (id) = 65;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_MEDIA_PLAYER";
option (no_delay) = true;
fixed32 key = 1;
bool has_command = 2;
MediaPlayerCommand command = 3;
bool has_volume = 4;
float volume = 5;
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;
}

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 {
@@ -733,6 +736,52 @@ void APIConnection::lock_command(const LockCommandRequest &msg) {
}
#endif
#ifdef USE_MEDIA_PLAYER
bool APIConnection::send_media_player_state(media_player::MediaPlayer *media_player) {
if (!this->state_subscription_)
return false;
MediaPlayerStateResponse resp{};
resp.key = media_player->get_object_id_hash();
resp.state = static_cast<enums::MediaPlayerState>(media_player->state);
resp.volume = media_player->volume;
resp.muted = media_player->is_muted();
return this->send_media_player_state_response(resp);
}
bool APIConnection::send_media_player_info(media_player::MediaPlayer *media_player) {
ListEntitiesMediaPlayerResponse msg;
msg.key = media_player->get_object_id_hash();
msg.object_id = media_player->get_object_id();
msg.name = media_player->get_name();
msg.unique_id = get_default_unique_id("media_player", media_player);
msg.icon = media_player->get_icon();
msg.disabled_by_default = media_player->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(media_player->get_entity_category());
auto traits = media_player->get_traits();
msg.supports_pause = traits.get_supports_pause();
return this->send_list_entities_media_player_response(msg);
}
void APIConnection::media_player_command(const MediaPlayerCommandRequest &msg) {
media_player::MediaPlayer *media_player = App.get_media_player_by_key(msg.key);
if (media_player == nullptr)
return;
auto call = media_player->make_call();
if (msg.has_command) {
call.set_command(static_cast<media_player::MediaPlayerCommand>(msg.command));
}
if (msg.has_volume) {
call.set_volume(msg.volume);
}
if (msg.has_media_url) {
call.set_media_url(msg.media_url);
}
call.perform();
}
#endif
#ifdef USE_ESP32_CAMERA
void APIConnection::send_camera_state(std::shared_ptr<esp32_camera::CameraImage> image) {
if (!this->state_subscription_)
@@ -777,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;
@@ -794,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();
@@ -840,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 {
@@ -82,6 +82,11 @@ class APIConnection : public APIServerConnection {
bool send_lock_state(lock::Lock *a_lock, lock::LockState state);
bool send_lock_info(lock::Lock *a_lock);
void lock_command(const LockCommandRequest &msg) override;
#endif
#ifdef USE_MEDIA_PLAYER
bool send_media_player_state(media_player::MediaPlayer *media_player);
bool send_media_player_info(media_player::MediaPlayer *media_player);
void media_player_command(const MediaPlayerCommandRequest &msg) override;
#endif
bool send_log_message(int level, const char *tag, const char *line);
void send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
@@ -89,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;
@@ -129,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();
@@ -162,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
@@ -171,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

@@ -270,7 +270,7 @@ APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
*
* If the handshake is still active when this method returns and a read/write can't take place at
* the moment, returns WOULD_BLOCK.
* If an error occured, returns that error. Only returns OK if the transport is ready for data
* If an error occurred, returns that error. Only returns OK if the transport is ready for data
* traffic.
*/
APIError APINoiseFrameHelper::state_action_() {
@@ -586,7 +586,7 @@ APIError APINoiseFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) {
}
return APIError::OK;
} else if (sent == -1) {
// an error occured
// an error occurred
state_ = State::FAILED;
HELPER_LOG("Socket write failed with errno %d", errno);
return APIError::SOCKET_WRITE_FAILED;
@@ -980,7 +980,7 @@ APIError APIPlaintextFrameHelper::write_raw_(const struct iovec *iov, int iovcnt
}
return APIError::OK;
} else if (sent == -1) {
// an error occured
// an error occurred
state_ = State::FAILED;
HELPER_LOG("Socket write failed with errno %d", errno);
return APIError::SOCKET_WRITE_FAILED;

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

@@ -53,6 +53,7 @@ enum SensorStateClass : uint32_t {
STATE_CLASS_NONE = 0,
STATE_CLASS_MEASUREMENT = 1,
STATE_CLASS_TOTAL_INCREASING = 2,
STATE_CLASS_TOTAL = 3,
};
enum SensorLastResetType : uint32_t {
LAST_RESET_NONE = 0,
@@ -141,12 +142,33 @@ enum LockCommand : uint32_t {
LOCK_LOCK = 1,
LOCK_OPEN = 2,
};
enum MediaPlayerState : uint32_t {
MEDIA_PLAYER_STATE_NONE = 0,
MEDIA_PLAYER_STATE_IDLE = 1,
MEDIA_PLAYER_STATE_PLAYING = 2,
MEDIA_PLAYER_STATE_PAUSED = 3,
};
enum MediaPlayerCommand : uint32_t {
MEDIA_PLAYER_COMMAND_PLAY = 0,
MEDIA_PLAYER_COMMAND_PAUSE = 1,
MEDIA_PLAYER_COMMAND_STOP = 2,
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;
@@ -154,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:
@@ -249,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;
@@ -1146,6 +1170,317 @@ class ButtonCommandRequest : public ProtoMessage {
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
};
class ListEntitiesMediaPlayerResponse : public ProtoMessage {
public:
std::string object_id{};
uint32_t key{0};
std::string name{};
std::string unique_id{};
std::string icon{};
bool disabled_by_default{false};
enums::EntityCategory entity_category{};
bool supports_pause{false};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class MediaPlayerStateResponse : public ProtoMessage {
public:
uint32_t key{0};
enums::MediaPlayerState state{};
float volume{0.0f};
bool muted{false};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class MediaPlayerCommandRequest : public ProtoMessage {
public:
uint32_t key{0};
bool has_command{false};
enums::MediaPlayerCommand command{};
bool has_volume{false};
float volume{0.0f};
bool has_media_url{false};
std::string media_url{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_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;
};
} // namespace api
} // namespace esphome

View File

@@ -310,6 +310,97 @@ bool APIServerConnectionBase::send_list_entities_button_response(const ListEntit
#endif
#ifdef USE_BUTTON
#endif
#ifdef USE_MEDIA_PLAYER
bool APIServerConnectionBase::send_list_entities_media_player_response(const ListEntitiesMediaPlayerResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_media_player_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesMediaPlayerResponse>(msg, 63);
}
#endif
#ifdef USE_MEDIA_PLAYER
bool APIServerConnectionBase::send_media_player_state_response(const MediaPlayerStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_media_player_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<MediaPlayerStateResponse>(msg, 64);
}
#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
bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {
switch (msg_type) {
case 1: {
@@ -563,6 +654,114 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
ESP_LOGVV(TAG, "on_button_command_request: %s", msg.dump().c_str());
#endif
this->on_button_command_request(msg);
#endif
break;
}
case 65: {
#ifdef USE_MEDIA_PLAYER
MediaPlayerCommandRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
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;
}
@@ -813,6 +1012,139 @@ void APIServerConnection::on_lock_command_request(const LockCommandRequest &msg)
this->lock_command(msg);
}
#endif
#ifdef USE_MEDIA_PLAYER
void APIServerConnection::on_media_player_command_request(const MediaPlayerCommandRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
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

@@ -144,6 +144,62 @@ class APIServerConnectionBase : public ProtoService {
#endif
#ifdef USE_BUTTON
virtual void on_button_command_request(const ButtonCommandRequest &value){};
#endif
#ifdef USE_MEDIA_PLAYER
bool send_list_entities_media_player_response(const ListEntitiesMediaPlayerResponse &msg);
#endif
#ifdef USE_MEDIA_PLAYER
bool send_media_player_state_response(const MediaPlayerStateResponse &msg);
#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
protected:
bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;
@@ -192,6 +248,35 @@ class APIServerConnection : public APIServerConnectionBase {
#endif
#ifdef USE_LOCK
virtual void lock_command(const LockCommandRequest &msg) = 0;
#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;
@@ -236,6 +321,34 @@ class APIServerConnection : public APIServerConnectionBase {
#ifdef USE_LOCK
void on_lock_command_request(const LockCommandRequest &msg) override;
#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
};
} // namespace api

View File

@@ -272,6 +272,15 @@ void APIServer::on_lock_update(lock::Lock *obj) {
}
#endif
#ifdef USE_MEDIA_PLAYER
void APIServer::on_media_player_update(media_player::MediaPlayer *obj) {
if (obj->is_internal())
return;
for (auto &c : this->clients_)
c->send_media_player_state(obj);
}
#endif
float APIServer::get_setup_priority() const { return setup_priority::AFTER_WIFI; }
void APIServer::set_port(uint16_t port) { this->port_ = port; }
APIServer *global_api_server = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
@@ -282,6 +291,58 @@ 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_notify_data_response(const BluetoothGATTNotifyDataResponse &call) {
for (auto &client : this->clients_) {
client->send_bluetooth_gatt_notify_data_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);
}
}
#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

@@ -68,8 +68,20 @@ class APIServer : public Component, public Controller {
#endif
#ifdef USE_LOCK
void on_lock_update(lock::Lock *obj) override;
#endif
#ifdef USE_MEDIA_PLAYER
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_notify_data_response(const BluetoothGATTNotifyDataResponse &call);
void send_bluetooth_gatt_services(const BluetoothGATTGetServicesResponse &call);
void send_bluetooth_gatt_services_done(uint64_t address);
#endif
void register_user_service(UserServiceDescriptor *descriptor) { this->user_services_.push_back(descriptor); }
#ifdef USE_HOMEASSISTANT_TIME
void request_time();

View File

@@ -64,5 +64,11 @@ bool ListEntitiesIterator::on_number(number::Number *number) { return this->clie
bool ListEntitiesIterator::on_select(select::Select *select) { return this->client_->send_select_info(select); }
#endif
#ifdef USE_MEDIA_PLAYER
bool ListEntitiesIterator::on_media_player(media_player::MediaPlayer *media_player) {
return this->client_->send_media_player_info(media_player);
}
#endif
} // namespace api
} // namespace esphome

View File

@@ -51,6 +51,9 @@ class ListEntitiesIterator : public ComponentIterator {
#endif
#ifdef USE_LOCK
bool on_lock(lock::Lock *a_lock) override;
#endif
#ifdef USE_MEDIA_PLAYER
bool on_media_player(media_player::MediaPlayer *media_player) override;
#endif
bool on_end() override;

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

@@ -50,6 +50,11 @@ bool InitialStateIterator::on_select(select::Select *select) {
#ifdef USE_LOCK
bool InitialStateIterator::on_lock(lock::Lock *a_lock) { return this->client_->send_lock_state(a_lock, a_lock->state); }
#endif
#ifdef USE_MEDIA_PLAYER
bool InitialStateIterator::on_media_player(media_player::MediaPlayer *media_player) {
return this->client_->send_media_player_state(media_player);
}
#endif
InitialStateIterator::InitialStateIterator(APIConnection *client) : client_(client) {}
} // namespace api

View File

@@ -48,6 +48,9 @@ class InitialStateIterator : public ComponentIterator {
#endif
#ifdef USE_LOCK
bool on_lock(lock::Lock *a_lock) override;
#endif
#ifdef USE_MEDIA_PLAYER
bool on_media_player(media_player::MediaPlayer *media_player) override;
#endif
protected:
APIConnection *client_;

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

@@ -1 +1,52 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import ble_client, time
from esphome.const import (
CONF_ID,
CONF_RECEIVE_TIMEOUT,
CONF_TIME_ID,
)
CODEOWNERS = ["@jhansche"]
DEPENDENCIES = ["ble_client"]
MULTI_CONF = True
CONF_BEDJET_ID = "bedjet_id"
bedjet_ns = cg.esphome_ns.namespace("bedjet")
BedJetHub = bedjet_ns.class_("BedJetHub", ble_client.BLEClientNode, cg.PollingComponent)
CONFIG_SCHEMA = (
cv.COMPONENT_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(BedJetHub),
cv.Optional(CONF_TIME_ID): cv.use_id(time.RealTimeClock),
cv.Optional(
CONF_RECEIVE_TIMEOUT, default="0s"
): cv.positive_time_period_milliseconds,
}
)
.extend(ble_client.BLE_CLIENT_SCHEMA)
.extend(cv.polling_component_schema("15s"))
)
BEDJET_CLIENT_SCHEMA = cv.Schema(
{
cv.Required(CONF_BEDJET_ID): cv.use_id(BedJetHub),
}
)
async def register_bedjet_child(var, config):
parent = await cg.get_variable(config[CONF_BEDJET_ID])
cg.add(parent.register_child(var))
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await ble_client.register_ble_node(var, config)
if CONF_TIME_ID in config:
time_ = await cg.get_variable(config[CONF_TIME_ID])
cg.add(var.set_time_id(time_))
if CONF_RECEIVE_TIMEOUT in config:
cg.add(var.set_status_timeout(config[CONF_RECEIVE_TIMEOUT]))

View File

@@ -1,644 +0,0 @@
#include "bedjet.h"
#include "esphome/core/log.h"
#ifdef USE_ESP32
namespace esphome {
namespace bedjet {
using namespace esphome::climate;
/// Converts a BedJet temp step into degrees Celsius.
float bedjet_temp_to_c(const uint8_t temp) {
// BedJet temp is "C*2"; to get C, divide by 2.
return temp / 2.0f;
}
/// Converts a BedJet fan step to a speed percentage, in the range of 5% to 100%.
uint8_t bedjet_fan_step_to_speed(const uint8_t fan) {
// 0 = 5%
// 19 = 100%
return 5 * fan + 5;
}
static const std::string *bedjet_fan_step_to_fan_mode(const uint8_t fan_step) {
if (fan_step >= 0 && fan_step <= 19)
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++) {
if (fan_step_percent == BEDJET_FAN_STEP_NAME_STRINGS[i]) {
return i;
}
}
return -1;
}
void Bedjet::upgrade_firmware() {
auto *pkt = this->codec_->get_button_request(MAGIC_UPDATE);
auto status = this->write_bedjet_packet_(pkt);
if (status) {
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status);
}
}
void Bedjet::dump_config() {
LOG_CLIMATE("", "BedJet Climate", this);
auto traits = this->get_traits();
ESP_LOGCONFIG(TAG, " Supported modes:");
for (auto mode : traits.get_supported_modes()) {
ESP_LOGCONFIG(TAG, " - %s", LOG_STR_ARG(climate_mode_to_string(mode)));
}
ESP_LOGCONFIG(TAG, " Supported fan modes:");
for (const auto &mode : traits.get_supported_fan_modes()) {
ESP_LOGCONFIG(TAG, " - %s", LOG_STR_ARG(climate_fan_mode_to_string(mode)));
}
for (const auto &mode : traits.get_supported_custom_fan_modes()) {
ESP_LOGCONFIG(TAG, " - %s (c)", mode.c_str());
}
ESP_LOGCONFIG(TAG, " Supported presets:");
for (auto preset : traits.get_supported_presets()) {
ESP_LOGCONFIG(TAG, " - %s", LOG_STR_ARG(climate_preset_to_string(preset)));
}
for (const auto &preset : traits.get_supported_custom_presets()) {
ESP_LOGCONFIG(TAG, " - %s (c)", preset.c_str());
}
}
void Bedjet::setup() {
this->codec_ = make_unique<BedjetCodec>();
// restore set points
auto restore = this->restore_state_();
if (restore.has_value()) {
ESP_LOGI(TAG, "Restored previous saved state.");
restore->apply(this);
} else {
// Initial status is unknown until we connect
this->reset_state_();
}
#ifdef USE_TIME
this->setup_time_();
#endif
}
/** Resets states to defaults. */
void Bedjet::reset_state_() {
this->mode = climate::CLIMATE_MODE_OFF;
this->action = climate::CLIMATE_ACTION_IDLE;
this->target_temperature = NAN;
this->current_temperature = NAN;
this->preset.reset();
this->custom_preset.reset();
this->publish_state();
}
void Bedjet::loop() {}
void Bedjet::control(const ClimateCall &call) {
ESP_LOGD(TAG, "Received Bedjet::control");
if (this->node_state != espbt::ClientState::ESTABLISHED) {
ESP_LOGW(TAG, "Not connected, cannot handle control call yet.");
return;
}
if (call.get_mode().has_value()) {
ClimateMode mode = *call.get_mode();
BedjetPacket *pkt;
switch (mode) {
case climate::CLIMATE_MODE_OFF:
pkt = this->codec_->get_button_request(BTN_OFF);
break;
case climate::CLIMATE_MODE_HEAT:
pkt = this->codec_->get_button_request(BTN_HEAT);
break;
case climate::CLIMATE_MODE_FAN_ONLY:
pkt = this->codec_->get_button_request(BTN_COOL);
break;
case climate::CLIMATE_MODE_DRY:
pkt = this->codec_->get_button_request(BTN_DRY);
break;
default:
ESP_LOGW(TAG, "Unsupported mode: %d", mode);
return;
}
auto status = this->write_bedjet_packet_(pkt);
if (status) {
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status);
} else {
this->force_refresh_ = true;
this->mode = mode;
// We're using (custom) preset for Turbo, EXT HT, & M1-3 presets, so changing climate mode will clear those
this->custom_preset.reset();
this->preset.reset();
}
}
if (call.get_target_temperature().has_value()) {
auto target_temp = *call.get_target_temperature();
auto *pkt = this->codec_->get_set_target_temp_request(target_temp);
auto status = this->write_bedjet_packet_(pkt);
if (status) {
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status);
} else {
this->target_temperature = target_temp;
}
}
if (call.get_preset().has_value()) {
ClimatePreset preset = *call.get_preset();
BedjetPacket *pkt;
if (preset == climate::CLIMATE_PRESET_BOOST) {
pkt = this->codec_->get_button_request(BTN_TURBO);
} else {
ESP_LOGW(TAG, "Unsupported preset: %d", preset);
return;
}
auto status = this->write_bedjet_packet_(pkt);
if (status) {
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status);
} else {
// We use BOOST preset for TURBO mode, which is a short-lived/high-heat mode.
this->mode = climate::CLIMATE_MODE_HEAT;
this->preset = preset;
this->custom_preset.reset();
this->force_refresh_ = true;
}
} else if (call.get_custom_preset().has_value()) {
std::string preset = *call.get_custom_preset();
BedjetPacket *pkt;
if (preset == "M1") {
pkt = this->codec_->get_button_request(BTN_M1);
} else if (preset == "M2") {
pkt = this->codec_->get_button_request(BTN_M2);
} else if (preset == "M3") {
pkt = this->codec_->get_button_request(BTN_M3);
} else if (preset == "EXT HT") {
pkt = this->codec_->get_button_request(BTN_EXTHT);
} else {
ESP_LOGW(TAG, "Unsupported preset: %s", preset.c_str());
return;
}
auto status = this->write_bedjet_packet_(pkt);
if (status) {
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status);
} else {
this->force_refresh_ = true;
this->custom_preset = preset;
this->preset.reset();
}
}
if (call.get_fan_mode().has_value()) {
// Climate fan mode only supports low/med/high, but the BedJet supports 5-100% increments.
// We can still support a ClimateCall that requests low/med/high, and just translate it to a step increment here.
auto fan_mode = *call.get_fan_mode();
BedjetPacket *pkt;
if (fan_mode == climate::CLIMATE_FAN_LOW) {
pkt = this->codec_->get_set_fan_speed_request(3 /* = 20% */);
} else if (fan_mode == climate::CLIMATE_FAN_MEDIUM) {
pkt = this->codec_->get_set_fan_speed_request(9 /* = 50% */);
} else if (fan_mode == climate::CLIMATE_FAN_HIGH) {
pkt = this->codec_->get_set_fan_speed_request(14 /* = 75% */);
} else {
ESP_LOGW(TAG, "[%s] Unsupported fan mode: %s", this->get_name().c_str(),
LOG_STR_ARG(climate_fan_mode_to_string(fan_mode)));
return;
}
auto status = this->write_bedjet_packet_(pkt);
if (status) {
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status);
} else {
this->force_refresh_ = true;
}
} else if (call.get_custom_fan_mode().has_value()) {
auto fan_mode = *call.get_custom_fan_mode();
auto fan_step = bedjet_fan_speed_to_step(fan_mode);
if (fan_step >= 0 && fan_step <= 19) {
ESP_LOGV(TAG, "[%s] Converted fan mode %s to bedjet fan step %d", this->get_name().c_str(), fan_mode.c_str(),
fan_step);
// The index should represent the fan_step index.
BedjetPacket *pkt = this->codec_->get_set_fan_speed_request(fan_step);
auto status = this->write_bedjet_packet_(pkt);
if (status) {
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status);
} else {
this->force_refresh_ = true;
}
}
}
}
void Bedjet::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_DISCONNECT_EVT: {
ESP_LOGV(TAG, "Disconnected: reason=%d", param->disconnect.reason);
this->status_set_warning();
break;
}
case ESP_GATTC_SEARCH_CMPL_EVT: {
auto *chr = this->parent_->get_characteristic(BEDJET_SERVICE_UUID, BEDJET_COMMAND_UUID);
if (chr == nullptr) {
ESP_LOGW(TAG, "[%s] No control service found at device, not a BedJet..?", this->get_name().c_str());
break;
}
this->char_handle_cmd_ = chr->handle;
chr = this->parent_->get_characteristic(BEDJET_SERVICE_UUID, BEDJET_STATUS_UUID);
if (chr == nullptr) {
ESP_LOGW(TAG, "[%s] No status service found at device, not a BedJet..?", this->get_name().c_str());
break;
}
this->char_handle_status_ = chr->handle;
// We also need to obtain the config descriptor for this handle.
// Otherwise once we set node_state=Established, the parent will flush all handles/descriptors, and we won't be
// able to look it up.
auto *descr = this->parent_->get_config_descriptor(this->char_handle_status_);
if (descr == nullptr) {
ESP_LOGW(TAG, "No config descriptor for status handle 0x%x. Will not be able to receive status notifications",
this->char_handle_status_);
} else 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, "Config descriptor 0x%x (uuid %s) is not a client config char uuid", this->char_handle_status_,
descr->uuid.to_string().c_str());
} else {
this->config_descr_status_ = descr->handle;
}
chr = this->parent_->get_characteristic(BEDJET_SERVICE_UUID, BEDJET_NAME_UUID);
if (chr != nullptr) {
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);
if (status) {
ESP_LOGI(TAG, "[%s] Unable to read name characteristic: %d", this->get_name().c_str(), status);
}
}
ESP_LOGD(TAG, "Services complete: obtained char handles.");
this->node_state = espbt::ClientState::ESTABLISHED;
this->set_notify_(true);
#ifdef USE_TIME
if (this->time_id_.has_value()) {
this->send_local_time_();
}
#endif
break;
}
case ESP_GATTC_WRITE_DESCR_EVT: {
if (param->write.status != ESP_GATT_OK) {
// ESP_GATT_INVALID_ATTR_LEN
ESP_LOGW(TAG, "Error writing descr at handle 0x%04d, status=%d", param->write.handle, param->write.status);
break;
}
// [16:44:44][V][bedjet:279]: [JOENJET] Register for notify event success: h=0x002a s=0
// This might be the enable-notify descriptor? (or disable-notify)
ESP_LOGV(TAG, "[%s] Write to handle 0x%04x status=%d", this->get_name().c_str(), param->write.handle,
param->write.status);
break;
}
case ESP_GATTC_WRITE_CHAR_EVT: {
if (param->write.status != ESP_GATT_OK) {
ESP_LOGW(TAG, "Error writing char at handle 0x%04d, status=%d", param->write.handle, param->write.status);
break;
}
if (param->write.handle == this->char_handle_cmd_) {
if (this->force_refresh_) {
// Command write was successful. Publish the pending state, hoping that notify will kick in.
this->publish_state();
}
}
break;
}
case ESP_GATTC_READ_CHAR_EVT: {
if (param->read.conn_id != this->parent_->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);
break;
}
if (param->read.handle == this->char_handle_status_) {
// This is the additional packet that doesn't fit in the notify packet.
this->codec_->decode_extra(param->read.value, param->read.value_len);
} else if (param->read.handle == this->char_handle_name_) {
// The data should represent the name.
if (param->read.status == ESP_GATT_OK && param->read.value_len > 0) {
std::string bedjet_name(reinterpret_cast<char const *>(param->read.value), param->read.value_len);
// this->set_name(bedjet_name);
ESP_LOGV(TAG, "[%s] Got BedJet name: '%s'", this->get_name().c_str(), bedjet_name.c_str());
}
}
break;
}
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
// This event means that ESP received the request to enable notifications on the client side. But we also have to
// tell the server that we want it to send notifications. Normally BLEClient parent would handle this
// automatically, but as soon as we set our status to Established, the parent is going to purge all the
// service/char/descriptor handles, and then get_config_descriptor() won't work anymore. There's no way to disable
// the BLEClient parent behavior, so our only option is to write the handle anyway, and hope a double-write
// doesn't break anything.
if (param->reg_for_notify.handle != this->char_handle_status_) {
ESP_LOGW(TAG, "[%s] Register for notify on unexpected handle 0x%04x, expecting 0x%04x",
this->get_name().c_str(), param->reg_for_notify.handle, this->char_handle_status_);
break;
}
this->write_notify_config_descriptor_(true);
this->last_notify_ = 0;
this->force_refresh_ = true;
break;
}
case ESP_GATTC_UNREG_FOR_NOTIFY_EVT: {
// This event is not handled by the parent BLEClient, so we need to do this either way.
if (param->unreg_for_notify.handle != this->char_handle_status_) {
ESP_LOGW(TAG, "[%s] Unregister for notify on unexpected handle 0x%04x, expecting 0x%04x",
this->get_name().c_str(), param->unreg_for_notify.handle, this->char_handle_status_);
break;
}
this->write_notify_config_descriptor_(false);
this->last_notify_ = 0;
// Now we wait until the next update() poll to re-register notify...
break;
}
case ESP_GATTC_NOTIFY_EVT: {
if (param->notify.handle != this->char_handle_status_) {
ESP_LOGW(TAG, "[%s] Unexpected notify handle, wanted %04X, got %04X", this->get_name().c_str(),
this->char_handle_status_, param->notify.handle);
break;
}
// FIXME: notify events come in every ~200-300 ms, which is too fast to be helpful. So we
// throttle the updates to once every MIN_NOTIFY_THROTTLE (5 seconds).
// Another idea would be to keep notify off by default, and use update() as an opportunity to turn on
// notify to get enough data to update status, then turn off notify again.
uint32_t now = millis();
auto delta = now - this->last_notify_;
if (this->last_notify_ == 0 || delta > MIN_NOTIFY_THROTTLE || this->force_refresh_) {
bool needs_extra = this->codec_->decode_notify(param->notify.value, param->notify.value_len);
this->last_notify_ = now;
if (needs_extra) {
// this means the packet was partial, so read the status characteristic to get the second part.
auto status = esp_ble_gattc_read_char(this->parent_->gattc_if, this->parent_->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());
}
}
if (this->force_refresh_) {
// If we requested an immediate update, do that now.
this->update();
this->force_refresh_ = false;
}
}
break;
}
default:
ESP_LOGVV(TAG, "[%s] gattc unhandled event: enum=%d", this->get_name().c_str(), event);
break;
}
}
/** Reimplementation of BLEClient.gattc_event_handler() for ESP_GATTC_REG_FOR_NOTIFY_EVT.
*
* This is a copy of ble_client's automatic handling of `ESP_GATTC_REG_FOR_NOTIFY_EVT`, in order
* to undo the same on unregister. It also allows us to maintain the config descriptor separately,
* since the parent BLEClient is going to purge all descriptors once we set our connection status
* to `Established`.
*/
uint8_t Bedjet::write_notify_config_descriptor_(bool enable) {
auto handle = this->config_descr_status_;
if (handle == 0) {
ESP_LOGW(TAG, "No descriptor found for notify of handle 0x%x", this->char_handle_status_);
return -1;
}
// NOTE: BLEClient uses `uint8_t*` of length 1, but BLE spec requires 16 bits.
uint8_t notify_en[] = {0, 0};
notify_en[0] = enable;
auto status =
esp_ble_gattc_write_char_descr(this->parent_->gattc_if, this->parent_->conn_id, handle, sizeof(notify_en),
&notify_en[0], 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", this->get_name().c_str(), enable ? "true" : "false",
handle);
return ESP_GATT_OK;
}
#ifdef USE_TIME
/** Attempts to sync the local time (via `time_id`) to the BedJet device. */
void Bedjet::send_local_time_() {
if (this->node_state != espbt::ClientState::ESTABLISHED) {
ESP_LOGV(TAG, "[%s] Not connected, cannot send time.", this->get_name().c_str());
return;
}
auto *time_id = *this->time_id_;
time::ESPTime now = time_id->now();
if (now.is_valid()) {
uint8_t hour = now.hour;
uint8_t minute = now.minute;
BedjetPacket *pkt = this->codec_->get_set_time_request(hour, minute);
auto status = this->write_bedjet_packet_(pkt);
if (status) {
ESP_LOGW(TAG, "Failed setting BedJet clock: %d", status);
} else {
ESP_LOGD(TAG, "[%s] BedJet clock set to: %d:%02d", this->get_name().c_str(), hour, minute);
}
}
}
/** Initializes time sync callbacks to support syncing current time to the BedJet. */
void Bedjet::setup_time_() {
if (this->time_id_.has_value()) {
this->send_local_time_();
auto *time_id = *this->time_id_;
time_id->add_on_time_sync_callback([this] { this->send_local_time_(); });
time::ESPTime now = time_id->now();
ESP_LOGD(TAG, "Using time component to set BedJet clock: %d:%02d", now.hour, now.minute);
} else {
ESP_LOGI(TAG, "`time_id` is not configured: will not sync BedJet clock.");
}
}
#endif
/** Writes one BedjetPacket to the BLE client on the BEDJET_COMMAND_UUID. */
uint8_t Bedjet::write_bedjet_packet_(BedjetPacket *pkt) {
if (this->node_state != espbt::ClientState::ESTABLISHED) {
if (!this->parent_->enabled) {
ESP_LOGI(TAG, "[%s] Cannot write packet: Not connected, enabled=false", this->get_name().c_str());
} else {
ESP_LOGW(TAG, "[%s] Cannot write packet: Not connected", this->get_name().c_str());
}
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);
return status;
}
/** Configures the local ESP BLE client to register (`true`) or unregister (`false`) for status notifications. */
uint8_t Bedjet::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,
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,
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);
}
}
ESP_LOGV(TAG, "[%s] set_notify: enable=%d; result=%d", this->get_name().c_str(), enable, status);
return status;
}
/** Attempts to update the climate device from the last received BedjetStatusPacket.
*
* @return `true` if the status has been applied; `false` if there is nothing to apply.
*/
bool Bedjet::update_status_() {
if (!this->codec_->has_status())
return false;
BedjetStatusPacket status = *this->codec_->get_status_packet();
auto converted_temp = bedjet_temp_to_c(status.target_temp_step);
if (converted_temp > 0)
this->target_temperature = converted_temp;
converted_temp = bedjet_temp_to_c(status.ambient_temp_step);
if (converted_temp > 0)
this->current_temperature = converted_temp;
const auto *fan_mode_name = bedjet_fan_step_to_fan_mode(status.fan_step);
if (fan_mode_name != nullptr) {
this->custom_fan_mode = *fan_mode_name;
}
// TODO: Get biorhythm data to determine which preset (M1-3) is running, if any.
switch (status.mode) {
case MODE_WAIT: // Biorhythm "wait" step: device is idle
case MODE_STANDBY:
this->mode = climate::CLIMATE_MODE_OFF;
this->action = climate::CLIMATE_ACTION_IDLE;
this->fan_mode = climate::CLIMATE_FAN_OFF;
this->custom_preset.reset();
this->preset.reset();
break;
case MODE_HEAT:
case MODE_EXTHT:
this->mode = climate::CLIMATE_MODE_HEAT;
this->action = climate::CLIMATE_ACTION_HEATING;
this->custom_preset.reset();
this->preset.reset();
break;
case MODE_COOL:
this->mode = climate::CLIMATE_MODE_FAN_ONLY;
this->action = climate::CLIMATE_ACTION_COOLING;
this->custom_preset.reset();
this->preset.reset();
break;
case MODE_DRY:
this->mode = climate::CLIMATE_MODE_DRY;
this->action = climate::CLIMATE_ACTION_DRYING;
this->custom_preset.reset();
this->preset.reset();
break;
case MODE_TURBO:
this->preset = climate::CLIMATE_PRESET_BOOST;
this->custom_preset.reset();
this->mode = climate::CLIMATE_MODE_HEAT;
this->action = climate::CLIMATE_ACTION_HEATING;
break;
default:
ESP_LOGW(TAG, "[%s] Unexpected mode: 0x%02X", this->get_name().c_str(), status.mode);
break;
}
if (this->is_valid_()) {
this->publish_state();
this->codec_->clear_status();
this->status_clear_warning();
}
return true;
}
void Bedjet::update() {
ESP_LOGV(TAG, "[%s] update()", this->get_name().c_str());
if (this->node_state != espbt::ClientState::ESTABLISHED) {
if (!this->parent()->enabled) {
ESP_LOGD(TAG, "[%s] Not connected, because enabled=false", this->get_name().c_str());
} else {
// Possibly still trying to connect.
ESP_LOGD(TAG, "[%s] Not connected, enabled=true", this->get_name().c_str());
}
return;
}
auto result = this->update_status_();
if (!result) {
uint32_t now = millis();
uint32_t diff = now - this->last_notify_;
if (this->last_notify_ == 0) {
// This means we're connected and haven't received a notification, so it likely means that the BedJet is off.
// However, it could also mean that it's running, but failing to send notifications.
// We can try to unregister for notifications now, and then re-register, hoping to clear it up...
// But how do we know for sure which state we're in, and how do we actually clear out the buggy state?
ESP_LOGI(TAG, "[%s] Still waiting for first GATT notify event.", this->get_name().c_str());
this->set_notify_(false);
} else if (diff > NOTIFY_WARN_THRESHOLD) {
ESP_LOGW(TAG, "[%s] Last GATT notify was %d seconds ago.", this->get_name().c_str(), diff / 1000);
}
if (this->timeout_ > 0 && diff > this->timeout_ && this->parent()->enabled) {
ESP_LOGW(TAG, "[%s] Timed out after %d sec. Retrying...", this->get_name().c_str(), this->timeout_);
this->parent()->set_enabled(false);
this->parent()->set_enabled(true);
}
}
}
} // namespace bedjet
} // namespace esphome
#endif

View File

@@ -0,0 +1,23 @@
#pragma once
#include "bedjet_codec.h"
#include "esphome/core/helpers.h"
namespace esphome {
namespace bedjet {
// Forward declare BedJetHub
class BedJetHub;
class BedJetClient : public Parented<BedJetHub> {
public:
virtual void on_status(const BedjetStatusPacket *data) = 0;
virtual void on_bedjet_state(bool is_ready) = 0;
protected:
friend BedJetHub;
virtual std::string describe() = 0;
};
} // namespace bedjet
} // namespace esphome

View File

@@ -1,4 +1,4 @@
#include "bedjet_base.h"
#include "bedjet_codec.h"
#include <cstdio>
#include <cstring>
@@ -48,7 +48,16 @@ BedjetPacket *BedjetCodec::get_set_fan_speed_request(const uint8_t fan_step) {
/** Returns a BedjetPacket that will set the device's current time. */
BedjetPacket *BedjetCodec::get_set_time_request(const uint8_t hour, const uint8_t minute) {
this->packet_.command = CMD_SET_TIME;
this->packet_.command = CMD_SET_CLOCK;
this->packet_.data_length = 2;
this->packet_.data[0] = hour;
this->packet_.data[1] = minute;
return this->clean_packet_();
}
/** Returns a BedjetPacket that will set the device's remaining runtime. */
BedjetPacket *BedjetCodec::get_set_runtime_remaining_request(const uint8_t hour, const uint8_t minute) {
this->packet_.command = CMD_SET_RUNTIME;
this->packet_.data_length = 2;
this->packet_.data[0] = hour;
this->packet_.data[1] = minute;
@@ -57,17 +66,17 @@ BedjetPacket *BedjetCodec::get_set_time_request(const uint8_t hour, const uint8_
/** Decodes the extra bytes that were received after being notified with a partial packet. */
void BedjetCodec::decode_extra(const uint8_t *data, uint16_t length) {
ESP_LOGV(TAG, "Received extra: %d bytes: %d %d %d %d", length, data[1], data[2], data[3], data[4]);
ESP_LOGVV(TAG, "Received extra: %d bytes: %d %d %d %d", length, data[1], data[2], data[3], data[4]);
uint8_t offset = this->last_buffer_size_;
if (offset > 0 && length + offset <= sizeof(BedjetStatusPacket)) {
memcpy(((uint8_t *) (&this->buf_)) + offset, data, length);
ESP_LOGV(TAG,
"Extra bytes: skip1=0x%08x, skip2=0x%04x, skip3=0x%02x; update phase=0x%02x, "
"flags=BedjetFlags <conn=%c, leds=%c, units=%c, mute=%c, others=%02x>",
this->buf_._skip_1_, this->buf_._skip_2_, this->buf_._skip_3_, this->buf_.update_phase,
this->buf_.flags & 0x20 ? '1' : '0', this->buf_.flags & 0x10 ? '1' : '0',
this->buf_.flags & 0x04 ? '1' : '0', this->buf_.flags & 0x01 ? '1' : '0',
this->buf_.flags & ~(0x20 | 0x10 | 0x04 | 0x01));
ESP_LOGVV(TAG,
"Extra bytes: skip1=0x%08x, skip2=0x%04x, skip3=0x%02x; update phase=0x%02x, "
"flags=BedjetFlags <conn=%c, leds=%c, units=%c, mute=%c; packed=%02x>",
this->buf_.unused_1, this->buf_.unused_2, this->buf_.unused_3, this->buf_.update_phase,
this->buf_.flags.conn_test_passed ? '1' : '0', this->buf_.flags.leds_enabled ? '1' : '0',
this->buf_.flags.units_setup ? '1' : '0', this->buf_.flags.beeps_muted ? '1' : '0',
this->buf_.flags_packed);
} else {
ESP_LOGI(TAG, "Could not determine where to append to, last offset=%d, max size=%u, new size would be %d", offset,
sizeof(BedjetStatusPacket), length + offset);
@@ -82,8 +91,6 @@ bool BedjetCodec::decode_notify(const uint8_t *data, uint16_t length) {
ESP_LOGV(TAG, "Received: %d bytes: %d %d %d %d", length, data[1], data[2], data[3], data[4]);
if (data[1] == PACKET_FORMAT_V3_HOME && data[3] == PACKET_TYPE_STATUS) {
this->status_packet_.reset();
// Clear old buffer
memset(&this->buf_, 0, sizeof(BedjetStatusPacket));
// Copy new data into buffer
@@ -91,23 +98,24 @@ bool BedjetCodec::decode_notify(const uint8_t *data, uint16_t length) {
this->last_buffer_size_ = length;
// TODO: validate the packet checksum?
if (this->buf_.mode >= 0 && this->buf_.mode < 7 && this->buf_.target_temp_step >= 38 &&
this->buf_.target_temp_step <= 86 && this->buf_.actual_temp_step > 1 && this->buf_.actual_temp_step <= 100 &&
this->buf_.ambient_temp_step > 1 && this->buf_.ambient_temp_step <= 100) {
if (this->buf_.mode < 7 && this->buf_.target_temp_step >= 38 && this->buf_.target_temp_step <= 86 &&
this->buf_.actual_temp_step > 1 && this->buf_.actual_temp_step <= 100 && this->buf_.ambient_temp_step > 1 &&
this->buf_.ambient_temp_step <= 100) {
// and save it for the update() loop
this->status_packet_ = this->buf_;
return this->buf_.is_partial == 1;
this->status_packet_ = &this->buf_;
return this->buf_.is_partial;
} else {
this->status_packet_ = nullptr;
// TODO: log a warning if we detect that we connected to a non-V3 device.
ESP_LOGW(TAG, "Received potentially invalid packet (len %d):", length);
}
} else if (data[1] == PACKET_FORMAT_DEBUG || data[3] == PACKET_TYPE_DEBUG) {
// We don't actually know the packet format for this. Dump packets to log, in case a pattern presents itself.
ESP_LOGV(TAG,
"received DEBUG packet: set1=%01fF, set2=%01fF, air=%01fF; [7]=%d, [8]=%d, [9]=%d, [10]=%d, [11]=%d, "
"[12]=%d, [-1]=%d",
bedjet_temp_to_f(data[4]), bedjet_temp_to_f(data[5]), bedjet_temp_to_f(data[6]), data[7], data[8], data[9],
data[10], data[11], data[12], data[length - 1]);
ESP_LOGVV(TAG,
"received DEBUG packet: set1=%01fF, set2=%01fF, air=%01fF; [7]=%d, [8]=%d, [9]=%d, [10]=%d, [11]=%d, "
"[12]=%d, [-1]=%d",
bedjet_temp_to_f(data[4]), bedjet_temp_to_f(data[5]), bedjet_temp_to_f(data[6]), data[7], data[8],
data[9], data[10], data[11], data[12], data[length - 1]);
if (this->has_status()) {
this->status_packet_->ambient_temp_step = data[6];
@@ -119,5 +127,35 @@ bool BedjetCodec::decode_notify(const uint8_t *data, uint16_t length) {
return false;
}
/** @return `true` if the new packet is meaningfully different from the last seen packet. */
bool BedjetCodec::compare(const uint8_t *data, uint16_t length) {
if (data == nullptr) {
return false;
}
if (length < 17) {
// New packet looks small, skip it.
return false;
}
if (this->buf_.packet_format != PACKET_FORMAT_V3_HOME ||
this->buf_.packet_type != PACKET_TYPE_STATUS) { // No last seen packet, so take the new one.
return true;
}
if (data[1] != PACKET_FORMAT_V3_HOME || data[3] != PACKET_TYPE_STATUS) { // New packet is not a v3 status, skip it.
return false;
}
// Now coerce it to a status packet and compare some key fields
const BedjetStatusPacket *test = reinterpret_cast<const BedjetStatusPacket *>(data);
// These are fields that will only change due to explicit action.
// That is why we do not check ambient or actual temp here, because those are environmental.
bool explicit_fields_changed = this->buf_.mode != test->mode || this->buf_.fan_step != test->fan_step ||
this->buf_.target_temp_step != test->target_temp_step;
return explicit_fields_changed;
}
} // namespace bedjet
} // namespace esphome

View File

@@ -14,18 +14,6 @@ struct BedjetPacket {
uint8_t data[2];
};
struct BedjetFlags {
/* uint8_t */
int a_ : 1; // 0x80
int b_ : 1; // 0x40
int conn_test_passed : 1; ///< (0x20) Bit is set `1` if the last connection test passed.
int leds_enabled : 1; ///< (0x10) Bit is set `1` if the LEDs on the device are enabled.
int c_ : 1; // 0x08
int units_setup : 1; ///< (0x04) Bit is set `1` if the device's units have been configured.
int d_ : 1; // 0x02
int beeps_muted : 1; ///< (0x01) Bit is set `1` if the device's sound output is muted.
} __attribute__((packed));
enum BedjetPacketFormat : uint8_t {
PACKET_FORMAT_DEBUG = 0x05, // 5
PACKET_FORMAT_V3_HOME = 0x56, // 86
@@ -36,15 +24,25 @@ enum BedjetPacketType : uint8_t {
PACKET_TYPE_DEBUG = 0x2,
};
enum BedjetNotification : uint8_t {
NOTIFY_NONE = 0, ///< No notification pending
NOTIFY_FILTER = 1, ///< Clean Filter / Please check BedJet air filter and clean if necessary.
NOTIFY_UPDATE = 2, ///< Firmware Update / A newer version of firmware is available.
NOTIFY_UPDATE_FAIL = 3, ///< Firmware Update / Unable to connect to the firmware update server.
NOTIFY_BIO_FAIL_CLOCK_NOT_SET = 4, ///< The specified sequence cannot be run because the clock is not set
NOTIFY_BIO_FAIL_TOO_LONG = 5, ///< The specified sequence cannot be run because it contains steps that would be too
///< long running from the current time.
// Note: after handling a notification, send MAGIC_NOTIFY_ACK
};
/** The format of a BedJet V3 status packet. */
struct BedjetStatusPacket {
// [0]
uint8_t is_partial : 8; ///< `1` indicates that this is a partial packet, and more data can be read directly from the
///< characteristic.
bool is_partial : 8; ///< `1` indicates that this is a partial packet, and more data can be read directly from the
///< characteristic.
BedjetPacketFormat packet_format : 8; ///< BedjetPacketFormat::PACKET_FORMAT_V3_HOME for BedJet V3 status packet
///< format. BedjetPacketFormat::PACKET_FORMAT_DEBUG for debugging packets.
uint8_t
expecting_length : 8; ///< The expected total length of the status packet after merging the additional packet.
uint8_t expecting_length : 8; ///< The expected total length of the status packet after merging the extra packet.
BedjetPacketType packet_type : 8; ///< Typically BedjetPacketType::PACKET_TYPE_STATUS for BedJet V3 status packet.
// [4]
@@ -77,11 +75,26 @@ struct BedjetStatusPacket {
uint8_t shutdown_reason : 8; ///< The reason for the last device shutdown.
// [19-25]; the initial partial packet cuts off here after [19]
// Skip 7 bytes?
uint32_t _skip_1_ : 32; // Unknown 19-22 = 0x01810112
uint16_t _skip_2_ : 16; // Unknown 23-24 = 0x1310
uint8_t _skip_3_ : 8; // Unknown 25 = 0x00
uint8_t unused_1 : 8; // Unknown [19] = 0x01
uint8_t unused_2 : 8; // Unknown [20] = 0x81
uint8_t unused_3 : 8; // Unknown [21] = 0x01
// [22]: 0x2=is_dual_zone, ...?
struct {
int unused_1 : 1; // 0x80
int unused_2 : 1; // 0x40
int unused_3 : 1; // 0x20
int unused_4 : 1; // 0x10
int unused_5 : 1; // 0x8
int unused_6 : 1; // 0x4
bool is_dual_zone : 1; /// Is part of a Dual Zone configuration
int unused_7 : 1; // 0x1
} dual_zone_flags;
uint8_t unused_4 : 8; // Unknown 23-24 = 0x1310
uint8_t unused_5 : 8; // Unknown 23-24 = 0x1310
uint8_t unused_6 : 8; // Unknown 25 = 0x00
// [26]
// 0x18(24) = "Connection test has completed OK"
@@ -89,10 +102,27 @@ struct BedjetStatusPacket {
uint8_t update_phase : 8; ///< The current status/phase of a firmware update.
// [27]
// FIXME: cannot nest packed struct of matching length here?
/* BedjetFlags */ uint8_t flags : 8; /// See BedjetFlags for the packed byte flags.
// [28-31]; 20+11 bytes
uint32_t _skip_4_ : 32; // Unknown
union {
uint8_t flags_packed;
struct {
/* uint8_t */
int unused_1 : 1; // 0x80
int unused_2 : 1; // 0x40
bool conn_test_passed : 1; ///< (0x20) Bit is set `1` if the last connection test passed.
bool leds_enabled : 1; ///< (0x10) Bit is set `1` if the LEDs on the device are enabled.
int unused_3 : 1; // 0x08
bool units_setup : 1; ///< (0x04) Bit is set `1` if the device's units have been configured.
int unused_4 : 1; // 0x02
bool beeps_muted : 1; ///< (0x01) Bit is set `1` if the device's sound output is muted.
} __attribute__((packed)) flags;
};
// [28] = (biorhythm?) sequence step
uint8_t bio_sequence_step : 8; /// Biorhythm sequence step number
// [29] = notify_code:
BedjetNotification notify_code : 8; /// See BedjetNotification
uint16_t unused_7 : 16; // Unknown
} __attribute__((packed));
@@ -127,7 +157,7 @@ struct BedjetStatusPacket {
* - Set current time
* The BedJet needs to have its clock set properly in order to run the biorhythm programs, which might
* contain time-of-day based step rules.
* - BedjetPacket#command = BedjetCommand::CMD_SET_TIME
* - BedjetPacket#command = BedjetCommand::CMD_SET_CLOCK
* - BedjetPacket#data [0] is hours, [1] is minutes
*/
class BedjetCodec {
@@ -136,13 +166,15 @@ class BedjetCodec {
BedjetPacket *get_set_target_temp_request(float temperature);
BedjetPacket *get_set_fan_speed_request(uint8_t fan_step);
BedjetPacket *get_set_time_request(uint8_t hour, uint8_t minute);
BedjetPacket *get_set_runtime_remaining_request(uint8_t hour, uint8_t minute);
bool decode_notify(const uint8_t *data, uint16_t length);
void decode_extra(const uint8_t *data, uint16_t length);
bool compare(const uint8_t *data, uint16_t length);
inline bool has_status() { return this->status_packet_.has_value(); }
const optional<BedjetStatusPacket> &get_status_packet() const { return this->status_packet_; }
void clear_status() { this->status_packet_.reset(); }
inline bool has_status() { return this->status_packet_ != nullptr; }
const BedjetStatusPacket *get_status_packet() const { return this->status_packet_; }
void clear_status() { this->status_packet_ = nullptr; }
protected:
BedjetPacket *clean_packet_();
@@ -151,7 +183,7 @@ class BedjetCodec {
BedjetPacket packet_;
optional<BedjetStatusPacket> status_packet_;
BedjetStatusPacket *status_packet_;
BedjetStatusPacket buf_;
};

View File

@@ -7,6 +7,14 @@ namespace bedjet {
static const char *const TAG = "bedjet";
/// Converts a BedJet fan step to a speed percentage, in the range of 5% to 100%.
inline static uint8_t bedjet_fan_step_to_speed(const uint8_t fan) {
// 0 = 5%
// 19 = 100%
return 5 * fan + 5;
}
inline static uint8_t bedjet_fan_speed_to_index(const uint8_t speed) { return speed / 5 - 1; }
enum BedjetMode : uint8_t {
/// BedJet is Off
MODE_STANDBY = 0,
@@ -24,6 +32,14 @@ enum BedjetMode : uint8_t {
MODE_WAIT = 6,
};
/** Optional heating strategies to use for climate::CLIMATE_MODE_HEAT. */
enum BedjetHeatMode {
/// HVACMode.HEAT is handled using BTN_HEAT (default)
HEAT_MODE_HEAT,
/// HVACMode.HEAT is handled using BTN_EXTHT
HEAT_MODE_EXTENDED,
};
enum BedjetButton : uint8_t {
/// Turn BedJet off
BTN_OFF = 0x1,
@@ -54,14 +70,17 @@ enum BedjetButton : uint8_t {
MAGIC_CONNTEST = 0x42,
/// Request a firmware update. This will also restart the Bedjet.
MAGIC_UPDATE = 0x43,
/// Acknowledge notification handled. See BedjetNotify
MAGIC_NOTIFY_ACK = 0x52,
};
enum BedjetCommand : uint8_t {
CMD_BUTTON = 0x1,
CMD_SET_RUNTIME = 0x2,
CMD_SET_TEMP = 0x3,
CMD_STATUS = 0x6,
CMD_SET_FAN = 0x7,
CMD_SET_TIME = 0x8,
CMD_SET_CLOCK = 0x8,
};
#define BEDJET_FAN_STEP_NAMES_ \
@@ -70,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

@@ -0,0 +1,543 @@
#include "bedjet_hub.h"
#include "bedjet_child.h"
#include "bedjet_const.h"
namespace esphome {
namespace bedjet {
static const LogString *bedjet_button_to_string(BedjetButton button) {
switch (button) {
case BTN_OFF:
return LOG_STR("OFF");
case BTN_COOL:
return LOG_STR("COOL");
case BTN_HEAT:
return LOG_STR("HEAT");
case BTN_EXTHT:
return LOG_STR("EXT HT");
case BTN_TURBO:
return LOG_STR("TURBO");
case BTN_DRY:
return LOG_STR("DRY");
case BTN_M1:
return LOG_STR("M1");
case BTN_M2:
return LOG_STR("M2");
case BTN_M3:
return LOG_STR("M3");
default:
return LOG_STR("unknown");
}
}
/* Public */
void BedJetHub::upgrade_firmware() {
auto *pkt = this->codec_->get_button_request(MAGIC_UPDATE);
auto status = this->write_bedjet_packet_(pkt);
if (status) {
ESP_LOGW(TAG, "[%s] MAGIC_UPDATE button failed, status=%d", this->get_name().c_str(), status);
}
}
bool BedJetHub::button_heat() { return this->send_button(BTN_HEAT); }
bool BedJetHub::button_ext_heat() { return this->send_button(BTN_EXTHT); }
bool BedJetHub::button_turbo() { return this->send_button(BTN_TURBO); }
bool BedJetHub::button_cool() { return this->send_button(BTN_COOL); }
bool BedJetHub::button_dry() { return this->send_button(BTN_DRY); }
bool BedJetHub::button_off() { return this->send_button(BTN_OFF); }
bool BedJetHub::button_memory1() { return this->send_button(BTN_M1); }
bool BedJetHub::button_memory2() { return this->send_button(BTN_M2); }
bool BedJetHub::button_memory3() { return this->send_button(BTN_M3); }
bool BedJetHub::set_fan_index(uint8_t fan_speed_index) {
if (fan_speed_index > 19) {
ESP_LOGW(TAG, "Invalid fan speed index %d, expecting 0-19.", fan_speed_index);
return false;
}
auto *pkt = this->codec_->get_set_fan_speed_request(fan_speed_index);
auto status = this->write_bedjet_packet_(pkt);
if (status) {
ESP_LOGW(TAG, "[%s] writing fan speed failed, status=%d", this->get_name().c_str(), status);
}
return status == 0;
}
uint8_t BedJetHub::get_fan_index() {
auto *status = this->codec_->get_status_packet();
if (status != nullptr) {
return status->fan_step;
}
return 0;
}
bool BedJetHub::set_target_temp(float temp_c) {
auto *pkt = this->codec_->get_set_target_temp_request(temp_c);
auto status = this->write_bedjet_packet_(pkt);
if (status) {
ESP_LOGW(TAG, "[%s] writing target temp failed, status=%d", this->get_name().c_str(), status);
}
return status == 0;
}
bool BedJetHub::set_time_remaining(uint8_t hours, uint8_t mins) {
// FIXME: this may fail depending on current mode or other restrictions enforced by the unit.
auto *pkt = this->codec_->get_set_runtime_remaining_request(hours, mins);
auto status = this->write_bedjet_packet_(pkt);
if (status) {
ESP_LOGW(TAG, "[%s] writing remaining runtime failed, status=%d", this->get_name().c_str(), status);
}
return status == 0;
}
bool BedJetHub::send_button(BedjetButton button) {
auto *pkt = this->codec_->get_button_request(button);
auto status = this->write_bedjet_packet_(pkt);
if (status) {
ESP_LOGW(TAG, "[%s] writing button %s failed, status=%d", this->get_name().c_str(),
LOG_STR_ARG(bedjet_button_to_string(button)), status);
} else {
ESP_LOGD(TAG, "[%s] writing button %s success", this->get_name().c_str(),
LOG_STR_ARG(bedjet_button_to_string(button)));
}
return status == 0;
}
uint16_t BedJetHub::get_time_remaining() {
auto *status = this->codec_->get_status_packet();
if (status != nullptr) {
return status->time_remaining_secs + status->time_remaining_mins * 60 + status->time_remaining_hrs * 3600;
}
return 0;
}
/* Bluetooth/GATT */
uint8_t BedJetHub::write_bedjet_packet_(BedjetPacket *pkt) {
if (!this->is_connected()) {
if (!this->parent_->enabled) {
ESP_LOGI(TAG, "[%s] Cannot write packet: Not connected, enabled=false", this->get_name().c_str());
} else {
ESP_LOGW(TAG, "[%s] Cannot write packet: Not connected", this->get_name().c_str());
}
return -1;
}
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;
}
/** Configures the local ESP BLE client to register (`true`) or unregister (`false`) for status notifications. */
uint8_t BedJetHub::set_notify_(const bool enable) {
uint8_t status;
if (enable) {
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_->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);
}
}
ESP_LOGV(TAG, "[%s] set_notify: enable=%d; result=%d", this->get_name().c_str(), enable, status);
return status;
}
bool BedJetHub::discover_characteristics_() {
bool result = true;
esphome::ble_client::BLECharacteristic *chr;
if (!this->char_handle_cmd_) {
chr = this->parent_->get_characteristic(BEDJET_SERVICE_UUID, BEDJET_COMMAND_UUID);
if (chr == nullptr) {
ESP_LOGW(TAG, "[%s] No control service found at device, not a BedJet..?", this->get_name().c_str());
result = false;
} else {
this->char_handle_cmd_ = chr->handle;
}
}
if (!this->char_handle_status_) {
chr = this->parent_->get_characteristic(BEDJET_SERVICE_UUID, BEDJET_STATUS_UUID);
if (chr == nullptr) {
ESP_LOGW(TAG, "[%s] No status service found at device, not a BedJet..?", this->get_name().c_str());
result = false;
} else {
this->char_handle_status_ = chr->handle;
}
}
if (!this->config_descr_status_) {
// We also need to obtain the config descriptor for this handle.
// Otherwise once we set node_state=Established, the parent will flush all handles/descriptors, and we won't be
// able to look it up.
auto *descr = this->parent_->get_config_descriptor(this->char_handle_status_);
if (descr == nullptr) {
ESP_LOGW(TAG, "No config descriptor for status handle 0x%x. Will not be able to receive status notifications",
this->char_handle_status_);
result = false;
} else 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, "Config descriptor 0x%x (uuid %s) is not a client config char uuid", this->char_handle_status_,
descr->uuid.to_string().c_str());
result = false;
} else {
this->config_descr_status_ = descr->handle;
}
}
if (!this->char_handle_name_) {
chr = this->parent_->get_characteristic(BEDJET_SERVICE_UUID, BEDJET_NAME_UUID);
if (chr == nullptr) {
ESP_LOGW(TAG, "[%s] No name service found at device, not a BedJet..?", this->get_name().c_str());
result = false;
} else {
this->char_handle_name_ = chr->handle;
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);
}
}
}
ESP_LOGI(TAG, "[%s] Discovered service characteristics: ", this->get_name().c_str());
ESP_LOGI(TAG, " - Command char: 0x%x", this->char_handle_cmd_);
ESP_LOGI(TAG, " - Status char: 0x%x", this->char_handle_status_);
ESP_LOGI(TAG, " - config descriptor: 0x%x", this->config_descr_status_);
ESP_LOGI(TAG, " - Name char: 0x%x", this->char_handle_name_);
return result;
}
void BedJetHub::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_DISCONNECT_EVT: {
ESP_LOGV(TAG, "Disconnected: reason=%d", param->disconnect.reason);
this->status_set_warning();
this->dispatch_state_(false);
break;
}
case ESP_GATTC_SEARCH_CMPL_EVT: {
auto result = this->discover_characteristics_();
if (result) {
ESP_LOGD(TAG, "[%s] Services complete: obtained char handles.", this->get_name().c_str());
this->node_state = espbt::ClientState::ESTABLISHED;
this->set_notify_(true);
#ifdef USE_TIME
if (this->time_id_.has_value()) {
this->send_local_time();
}
#endif
this->dispatch_state_(true);
} else {
ESP_LOGW(TAG, "[%s] Failed discovering service characteristics.", this->get_name().c_str());
this->parent()->set_enabled(false);
this->status_set_warning();
this->dispatch_state_(false);
}
break;
}
case ESP_GATTC_WRITE_DESCR_EVT: {
if (param->write.status != ESP_GATT_OK) {
if (param->write.status == ESP_GATT_INVALID_ATTR_LEN) {
// This probably means that our hack for notify_en (8 bit vs 16 bit) didn't work right.
// Should we try to fall back to BLEClient's way?
ESP_LOGW(TAG, "[%s] Invalid attr length writing descr at handle 0x%04d, status=%d", this->get_name().c_str(),
param->write.handle, param->write.status);
} else {
ESP_LOGW(TAG, "[%s] Error writing descr at handle 0x%04d, status=%d", this->get_name().c_str(),
param->write.handle, param->write.status);
}
break;
}
ESP_LOGD(TAG, "[%s] Write to handle 0x%04x status=%d", this->get_name().c_str(), param->write.handle,
param->write.status);
break;
}
case ESP_GATTC_WRITE_CHAR_EVT: {
if (param->write.status != ESP_GATT_OK) {
ESP_LOGW(TAG, "Error writing char at handle 0x%04d, status=%d", param->write.handle, param->write.status);
break;
}
if (param->write.handle == this->char_handle_cmd_) {
if (this->force_refresh_) {
// Command write was successful. Publish the pending state, hoping that notify will kick in.
// FIXME: better to wait until we know the status has changed
this->dispatch_status_();
}
}
break;
}
case ESP_GATTC_READ_CHAR_EVT: {
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);
break;
}
if (param->read.handle == this->char_handle_status_) {
// This is the additional packet that doesn't fit in the notify packet.
this->codec_->decode_extra(param->read.value, param->read.value_len);
this->status_packet_ready_();
} else if (param->read.handle == this->char_handle_name_) {
// The data should represent the name.
if (param->read.status == ESP_GATT_OK && param->read.value_len > 0) {
std::string bedjet_name(reinterpret_cast<char const *>(param->read.value), param->read.value_len);
ESP_LOGV(TAG, "[%s] Got BedJet name: '%s'", this->get_name().c_str(), bedjet_name.c_str());
this->set_name_(bedjet_name);
}
}
break;
}
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
// This event means that ESP received the request to enable notifications on the client side. But we also have to
// tell the server that we want it to send notifications. Normally BLEClient parent would handle this
// automatically, but as soon as we set our status to Established, the parent is going to purge all the
// service/char/descriptor handles, and then get_config_descriptor() won't work anymore. There's no way to disable
// the BLEClient parent behavior, so our only option is to write the handle anyway, and hope a double-write
// doesn't break anything.
if (param->reg_for_notify.handle != this->char_handle_status_) {
ESP_LOGW(TAG, "[%s] Register for notify on unexpected handle 0x%04x, expecting 0x%04x",
this->get_name().c_str(), param->reg_for_notify.handle, this->char_handle_status_);
break;
}
this->write_notify_config_descriptor_(true);
this->last_notify_ = 0;
this->force_refresh_ = true;
break;
}
case ESP_GATTC_UNREG_FOR_NOTIFY_EVT: {
// This event is not handled by the parent BLEClient, so we need to do this either way.
if (param->unreg_for_notify.handle != this->char_handle_status_) {
ESP_LOGW(TAG, "[%s] Unregister for notify on unexpected handle 0x%04x, expecting 0x%04x",
this->get_name().c_str(), param->unreg_for_notify.handle, this->char_handle_status_);
break;
}
this->write_notify_config_descriptor_(false);
this->last_notify_ = 0;
// Now we wait until the next update() poll to re-register notify...
break;
}
case ESP_GATTC_NOTIFY_EVT: {
if (this->processing_)
break;
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_->get_conn_id(), param->notify.conn_id);
// FIXME: bug in BLEClient holding wrong conn_id.
}
if (param->notify.handle != this->char_handle_status_) {
ESP_LOGW(TAG, "[%s] Unexpected notify handle, wanted %04X, got %04X", this->get_name().c_str(),
this->char_handle_status_, param->notify.handle);
break;
}
// FIXME: notify events come in every ~200-300 ms, which is too fast to be helpful. So we
// throttle the updates to once every MIN_NOTIFY_THROTTLE (5 seconds).
// Another idea would be to keep notify off by default, and use update() as an opportunity to turn on
// notify to get enough data to update status, then turn off notify again.
uint32_t now = millis();
auto delta = now - this->last_notify_;
if (!this->force_refresh_ && this->codec_->compare(param->notify.value, param->notify.value_len)) {
// If the packet is meaningfully different, trigger children as well
this->force_refresh_ = true;
ESP_LOGV(TAG, "[%s] Incoming packet indicates a significant change.", this->get_name().c_str());
}
if (this->last_notify_ == 0 || delta > MIN_NOTIFY_THROTTLE || this->force_refresh_) {
// Set reentrant flag to prevent processing multiple packets.
this->processing_ = true;
ESP_LOGVV(TAG, "[%s] Decoding packet: last=%d, delta=%d, force=%s", this->get_name().c_str(),
this->last_notify_, delta, this->force_refresh_ ? "y" : "n");
bool needs_extra = this->codec_->decode_notify(param->notify.value, param->notify.value_len);
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_->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());
}
} else {
this->status_packet_ready_();
}
}
break;
}
default:
ESP_LOGVV(TAG, "[%s] gattc unhandled event: enum=%d", this->get_name().c_str(), event);
break;
}
}
inline void BedJetHub::status_packet_ready_() {
this->last_notify_ = millis();
this->processing_ = false;
if (this->force_refresh_) {
// If we requested an immediate update, do that now.
this->update();
this->force_refresh_ = false;
}
}
/** Reimplementation of BLEClient.gattc_event_handler() for ESP_GATTC_REG_FOR_NOTIFY_EVT.
*
* This is a copy of ble_client's automatic handling of `ESP_GATTC_REG_FOR_NOTIFY_EVT`, in order
* to undo the same on unregister. It also allows us to maintain the config descriptor separately,
* since the parent BLEClient is going to purge all descriptors once we set our connection status
* to `Established`.
*/
uint8_t BedJetHub::write_notify_config_descriptor_(bool enable) {
auto handle = this->config_descr_status_;
if (handle == 0) {
ESP_LOGW(TAG, "No descriptor found for notify of handle 0x%x", this->char_handle_status_);
return -1;
}
// 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_->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_->get_conn_id());
return ESP_GATT_OK;
}
/* Time Component */
#ifdef USE_TIME
void BedJetHub::send_local_time() {
if (this->time_id_.has_value()) {
auto *time_id = *this->time_id_;
time::ESPTime now = time_id->now();
if (now.is_valid()) {
this->set_clock(now.hour, now.minute);
ESP_LOGD(TAG, "Using time component to set BedJet clock: %d:%02d", now.hour, now.minute);
}
} else {
ESP_LOGI(TAG, "`time_id` is not configured: will not sync BedJet clock.");
}
}
void BedJetHub::setup_time_() {
if (this->time_id_.has_value()) {
this->send_local_time();
auto *time_id = *this->time_id_;
time_id->add_on_time_sync_callback([this] { this->send_local_time(); });
} else {
ESP_LOGI(TAG, "`time_id` is not configured: will not sync BedJet clock.");
}
}
#endif
void BedJetHub::set_clock(uint8_t hour, uint8_t minute) {
if (!this->is_connected()) {
ESP_LOGV(TAG, "[%s] Not connected, cannot send time.", this->get_name().c_str());
return;
}
BedjetPacket *pkt = this->codec_->get_set_time_request(hour, minute);
auto status = this->write_bedjet_packet_(pkt);
if (status) {
ESP_LOGW(TAG, "Failed setting BedJet clock: %d", status);
} else {
ESP_LOGD(TAG, "[%s] BedJet clock set to: %d:%02d", this->get_name().c_str(), hour, minute);
}
}
/* Internal */
void BedJetHub::loop() {}
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()->get_conn_id());
LOG_UPDATE_INTERVAL(this)
ESP_LOGCONFIG(TAG, " Child components (%d):", this->children_.size());
for (auto *child : this->children_) {
ESP_LOGCONFIG(TAG, " - %s", child->describe().c_str());
}
}
void BedJetHub::dispatch_state_(bool is_ready) {
for (auto *child : this->children_) {
child->on_bedjet_state(is_ready);
}
}
void BedJetHub::dispatch_status_() {
auto *status = this->codec_->get_status_packet();
if (!this->is_connected()) {
ESP_LOGD(TAG, "[%s] Not connected, will not send status.", this->get_name().c_str());
} else if (status != nullptr) {
ESP_LOGD(TAG, "[%s] Notifying %d children of latest status @%p.", this->get_name().c_str(), this->children_.size(),
status);
for (auto *child : this->children_) {
child->on_status(status);
}
} else {
uint32_t now = millis();
uint32_t diff = now - this->last_notify_;
if (this->last_notify_ == 0) {
// This means we're connected and haven't received a notification, so it likely means that the BedJet is off.
// However, it could also mean that it's running, but failing to send notifications.
// We can try to unregister for notifications now, and then re-register, hoping to clear it up...
// But how do we know for sure which state we're in, and how do we actually clear out the buggy state?
ESP_LOGI(TAG, "[%s] Still waiting for first GATT notify event.", this->get_name().c_str());
} else if (diff > NOTIFY_WARN_THRESHOLD) {
ESP_LOGW(TAG, "[%s] Last GATT notify was %d seconds ago.", this->get_name().c_str(), diff / 1000);
}
if (this->timeout_ > 0 && diff > this->timeout_ && this->parent()->enabled) {
ESP_LOGW(TAG, "[%s] Timed out after %d sec. Retrying...", this->get_name().c_str(), this->timeout_);
// set_enabled(false) will only close the connection if state != IDLE.
this->parent()->set_state(espbt::ClientState::CONNECTING);
this->parent()->set_enabled(false);
this->parent()->set_enabled(true);
}
}
}
void BedJetHub::register_child(BedJetClient *obj) {
this->children_.push_back(obj);
obj->set_parent(this);
}
} // namespace bedjet
} // namespace esphome

View File

@@ -0,0 +1,176 @@
#pragma once
#include "esphome/components/ble_client/ble_client.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include "esphome/core/component.h"
#include "esphome/core/defines.h"
#include "esphome/core/hal.h"
#include "bedjet_child.h"
#include "bedjet_codec.h"
#ifdef USE_TIME
#include "esphome/components/time/real_time_clock.h"
#endif
#ifdef USE_ESP32
#include <esp_gattc_api.h>
namespace esphome {
namespace bedjet {
namespace espbt = esphome::esp32_ble_tracker;
// Forward declare BedJetClient
class BedJetClient;
static const espbt::ESPBTUUID BEDJET_SERVICE_UUID = espbt::ESPBTUUID::from_raw("00001000-bed0-0080-aa55-4265644a6574");
static const espbt::ESPBTUUID BEDJET_STATUS_UUID = espbt::ESPBTUUID::from_raw("00002000-bed0-0080-aa55-4265644a6574");
static const espbt::ESPBTUUID BEDJET_COMMAND_UUID = espbt::ESPBTUUID::from_raw("00002004-bed0-0080-aa55-4265644a6574");
static const espbt::ESPBTUUID BEDJET_NAME_UUID = espbt::ESPBTUUID::from_raw("00002001-bed0-0080-aa55-4265644a6574");
/**
* Hub component connecting to the BedJet device over Bluetooth.
*/
class BedJetHub : public esphome::ble_client::BLEClientNode, public PollingComponent {
public:
/* BedJet functionality exposed to `BedJetClient` children and/or accessible from action lambdas. */
/** Attempts to check for and apply firmware updates. */
void upgrade_firmware();
/** Press the OFF button. */
bool button_off();
/** Press the HEAT button. */
bool button_heat();
/** Press the EXT HT button. */
bool button_ext_heat();
/** Press the TURBO button. */
bool button_turbo();
/** Press the COOL button. */
bool button_cool();
/** Press the DRY button. */
bool button_dry();
/** Press the M1 (memory recall) button. */
bool button_memory1();
/** Press the M2 (memory recall) button. */
bool button_memory2();
/** Press the M3 (memory recall) button. */
bool button_memory3();
/** Send the `button`. */
bool send_button(BedjetButton button);
/** Set the target temperature to `temp_c` in °C. */
bool set_target_temp(float temp_c);
/** Set the fan speed to a stepped index in the range 0-19. */
bool set_fan_index(uint8_t fan_speed_index);
/** Set the fan speed to a percent in the range 5% - 100%, at 5% increments. */
bool set_fan_speed(uint8_t fan_speed_pct) { return this->set_fan_index(bedjet_fan_speed_to_index(fan_speed_pct)); }
/** Return the fan speed index, in the range 0-19. */
uint8_t get_fan_index();
/** Return the fan speed as a percent in the range 5%-100%. */
uint8_t get_fan_speed() { return bedjet_fan_step_to_speed(this->get_fan_index()); }
/** Set the operational runtime remaining.
*
* The unit establishes and enforces runtime limits for some modes, so this call is not guaranteed to succeed.
*/
bool set_time_remaining(uint8_t hours, uint8_t mins);
/** Return the remaining runtime, in seconds. */
uint16_t get_time_remaining();
/** @return `true` if the `BLEClient::node_state` is `ClientState::ESTABLISHED`. */
bool is_connected() { return this->node_state == espbt::ClientState::ESTABLISHED; }
bool has_status() { return this->codec_->has_status(); }
const BedjetStatusPacket *get_status_packet() const { return this->codec_->get_status_packet(); }
/** Register a `BedJetClient` child component. */
void register_child(BedJetClient *obj);
/** Set the status timeout.
*
* This is the max time to wait for a status update before the connection is presumed unusable.
*/
void set_status_timeout(uint32_t timeout) { this->timeout_ = timeout; }
#ifdef USE_TIME
/** Set the `time::RealTimeClock` implementation. */
void set_time_id(time::RealTimeClock *time_id) { this->time_id_ = time_id; }
/** Attempts to sync the local time (via `time_id`) to the BedJet device. */
void send_local_time();
#endif
/** Attempt to set the BedJet device's clock to the specified time. */
void set_clock(uint8_t hour, uint8_t minute);
/* Component overrides */
void loop() override;
void update() override;
void dump_config() override;
void setup() override { this->codec_ = make_unique<BedjetCodec>(); }
float get_setup_priority() const override { return setup_priority::AFTER_WIFI; }
/** @return The BedJet's configured name, or the MAC address if not discovered yet. */
std::string get_name() {
if (this->name_.empty()) {
return this->parent_->address_str();
} else {
return this->name_;
}
}
/* BLEClient overrides */
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) override;
protected:
std::vector<BedJetClient *> children_;
void dispatch_status_();
void dispatch_state_(bool is_ready);
#ifdef USE_TIME
/** Initializes time sync callbacks to support syncing current time to the BedJet. */
void setup_time_();
optional<time::RealTimeClock *> time_id_{};
#endif
uint32_t timeout_{DEFAULT_STATUS_TIMEOUT};
static const uint32_t MIN_NOTIFY_THROTTLE = 15000;
static const uint32_t NOTIFY_WARN_THRESHOLD = 300000;
static const uint32_t DEFAULT_STATUS_TIMEOUT = 900000;
uint8_t set_notify_(bool enable);
/** Send the `BedjetPacket` to the device. */
uint8_t write_bedjet_packet_(BedjetPacket *pkt);
void set_name_(const std::string &name) { this->name_ = name; }
std::string name_;
uint32_t last_notify_ = 0;
inline void status_packet_ready_();
bool force_refresh_ = false;
bool processing_ = false;
std::unique_ptr<BedjetCodec> codec_;
bool discover_characteristics_();
uint16_t char_handle_cmd_;
uint16_t char_handle_name_;
uint16_t char_handle_status_;
uint16_t config_descr_status_;
uint8_t write_notify_config_descriptor_(bool enable);
};
} // namespace bedjet
} // namespace esphome
#endif

View File

@@ -1,42 +0,0 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import climate, ble_client, time
from esphome.const import (
CONF_ID,
CONF_RECEIVE_TIMEOUT,
CONF_TIME_ID,
)
CODEOWNERS = ["@jhansche"]
DEPENDENCIES = ["ble_client"]
bedjet_ns = cg.esphome_ns.namespace("bedjet")
Bedjet = bedjet_ns.class_(
"Bedjet", climate.Climate, ble_client.BLEClientNode, cg.PollingComponent
)
CONFIG_SCHEMA = (
climate.CLIMATE_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(Bedjet),
cv.Optional(CONF_TIME_ID): cv.use_id(time.RealTimeClock),
cv.Optional(
CONF_RECEIVE_TIMEOUT, default="0s"
): cv.positive_time_period_milliseconds,
}
)
.extend(ble_client.BLE_CLIENT_SCHEMA)
.extend(cv.polling_component_schema("30s"))
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await climate.register_climate(var, config)
await ble_client.register_ble_node(var, config)
if CONF_TIME_ID in config:
time_ = await cg.get_variable(config[CONF_TIME_ID])
cg.add(var.set_time_id(time_))
if CONF_RECEIVE_TIMEOUT in config:
cg.add(var.set_status_timeout(config[CONF_RECEIVE_TIMEOUT]))

View File

@@ -0,0 +1,65 @@
import logging
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import climate, ble_client
from esphome.const import (
CONF_HEAT_MODE,
CONF_ID,
CONF_RECEIVE_TIMEOUT,
CONF_TIME_ID,
)
from .. import (
BEDJET_CLIENT_SCHEMA,
bedjet_ns,
register_bedjet_child,
)
_LOGGER = logging.getLogger(__name__)
CODEOWNERS = ["@jhansche"]
DEPENDENCIES = ["bedjet"]
BedJetClimate = bedjet_ns.class_("BedJetClimate", climate.Climate, cg.PollingComponent)
BedjetHeatMode = bedjet_ns.enum("BedjetHeatMode")
BEDJET_HEAT_MODES = {
"heat": BedjetHeatMode.HEAT_MODE_HEAT,
"extended": BedjetHeatMode.HEAT_MODE_EXTENDED,
}
CONFIG_SCHEMA = (
climate.CLIMATE_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(BedJetClimate),
cv.Optional(CONF_HEAT_MODE, default="heat"): cv.enum(
BEDJET_HEAT_MODES, lower=True
),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(
# TODO: remove compat layer.
{
cv.Optional(ble_client.CONF_BLE_CLIENT_ID): cv.invalid(
"The 'ble_client_id' option has been removed. Please migrate "
"to the new `bedjet_id` option in the `bedjet` component.\n"
"See https://esphome.io/components/climate/bedjet.html"
),
cv.Optional(CONF_TIME_ID): cv.invalid(
"The 'time_id' option has been moved to the `bedjet` component."
),
cv.Optional(CONF_RECEIVE_TIMEOUT): cv.invalid(
"The 'receive_timeout' option has been moved to the `bedjet` component."
),
}
)
.extend(BEDJET_CLIENT_SCHEMA)
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await climate.register_climate(var, config)
await register_bedjet_child(var, config)
cg.add(var.set_heating_mode(config[CONF_HEAT_MODE]))

View File

@@ -0,0 +1,354 @@
#include "bedjet_climate.h"
#include "esphome/core/log.h"
#ifdef USE_ESP32
namespace esphome {
namespace bedjet {
using namespace esphome::climate;
/// Converts a BedJet temp step into degrees Celsius.
float bedjet_temp_to_c(const uint8_t temp) {
// BedJet temp is "C*2"; to get C, divide by 2.
return temp / 2.0f;
}
static const std::string *bedjet_fan_step_to_fan_mode(const uint8_t fan_step) {
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 < BEDJET_FAN_SPEED_COUNT; i++) {
if (fan_step_percent == BEDJET_FAN_STEP_NAME_STRINGS[i]) {
return i;
}
}
return -1;
}
static inline BedjetButton heat_button(BedjetHeatMode mode) {
return mode == HEAT_MODE_EXTENDED ? BTN_EXTHT : BTN_HEAT;
}
std::string BedJetClimate::describe() { return "BedJet Climate"; }
void BedJetClimate::dump_config() {
LOG_CLIMATE("", "BedJet Climate", this);
auto traits = this->get_traits();
ESP_LOGCONFIG(TAG, " Supported modes:");
for (auto mode : traits.get_supported_modes()) {
ESP_LOGCONFIG(TAG, " - %s", LOG_STR_ARG(climate_mode_to_string(mode)));
}
if (this->heating_mode_ == HEAT_MODE_EXTENDED) {
ESP_LOGCONFIG(TAG, " - BedJet heating mode: EXT HT");
} else {
ESP_LOGCONFIG(TAG, " - BedJet heating mode: HEAT");
}
ESP_LOGCONFIG(TAG, " Supported fan modes:");
for (const auto &mode : traits.get_supported_fan_modes()) {
ESP_LOGCONFIG(TAG, " - %s", LOG_STR_ARG(climate_fan_mode_to_string(mode)));
}
for (const auto &mode : traits.get_supported_custom_fan_modes()) {
ESP_LOGCONFIG(TAG, " - %s (c)", mode.c_str());
}
ESP_LOGCONFIG(TAG, " Supported presets:");
for (auto preset : traits.get_supported_presets()) {
ESP_LOGCONFIG(TAG, " - %s", LOG_STR_ARG(climate_preset_to_string(preset)));
}
for (const auto &preset : traits.get_supported_custom_presets()) {
ESP_LOGCONFIG(TAG, " - %s (c)", preset.c_str());
}
}
void BedJetClimate::setup() {
// restore set points
auto restore = this->restore_state_();
if (restore.has_value()) {
ESP_LOGI(TAG, "Restored previous saved state.");
restore->apply(this);
} else {
// Initial status is unknown until we connect
this->reset_state_();
}
}
/** Resets states to defaults. */
void BedJetClimate::reset_state_() {
this->mode = CLIMATE_MODE_OFF;
this->action = CLIMATE_ACTION_IDLE;
this->target_temperature = NAN;
this->current_temperature = NAN;
this->preset.reset();
this->custom_preset.reset();
this->publish_state();
}
void BedJetClimate::loop() {}
void BedJetClimate::control(const ClimateCall &call) {
ESP_LOGD(TAG, "Received BedJetClimate::control");
if (!this->parent_->is_connected()) {
ESP_LOGW(TAG, "Not connected, cannot handle control call yet.");
return;
}
if (call.get_mode().has_value()) {
ClimateMode mode = *call.get_mode();
bool button_result;
switch (mode) {
case CLIMATE_MODE_OFF:
button_result = this->parent_->button_off();
break;
case CLIMATE_MODE_HEAT:
button_result = this->parent_->send_button(heat_button(this->heating_mode_));
break;
case CLIMATE_MODE_FAN_ONLY:
button_result = this->parent_->button_cool();
break;
case CLIMATE_MODE_DRY:
button_result = this->parent_->button_dry();
break;
default:
ESP_LOGW(TAG, "Unsupported mode: %d", mode);
return;
}
if (button_result) {
this->mode = mode;
// We're using (custom) preset for Turbo, EXT HT, & M1-3 presets, so changing climate mode will clear those
this->custom_preset.reset();
this->preset.reset();
}
}
if (call.get_target_temperature().has_value()) {
auto target_temp = *call.get_target_temperature();
auto result = this->parent_->set_target_temp(target_temp);
if (result) {
this->target_temperature = target_temp;
}
}
if (call.get_preset().has_value()) {
ClimatePreset preset = *call.get_preset();
bool result;
if (preset == CLIMATE_PRESET_BOOST) {
// We use BOOST preset for TURBO mode, which is a short-lived/high-heat mode.
result = this->parent_->button_turbo();
if (result) {
this->mode = CLIMATE_MODE_HEAT;
this->preset = CLIMATE_PRESET_BOOST;
this->custom_preset.reset();
}
} else if (preset == CLIMATE_PRESET_NONE && this->preset.has_value()) {
if (this->mode == CLIMATE_MODE_HEAT && this->preset == CLIMATE_PRESET_BOOST) {
// We were in heat mode with Boost preset, and now preset is set to None, so revert to normal heat.
result = this->parent_->send_button(heat_button(this->heating_mode_));
if (result) {
this->preset.reset();
this->custom_preset.reset();
}
} else {
ESP_LOGD(TAG, "Ignoring preset '%s' call; with current mode '%s' and preset '%s'",
LOG_STR_ARG(climate_preset_to_string(preset)), LOG_STR_ARG(climate_mode_to_string(this->mode)),
LOG_STR_ARG(climate_preset_to_string(this->preset.value_or(CLIMATE_PRESET_NONE))));
}
} else {
ESP_LOGW(TAG, "Unsupported preset: %d", preset);
return;
}
} else if (call.get_custom_preset().has_value()) {
std::string preset = *call.get_custom_preset();
bool result;
if (preset == "M1") {
result = this->parent_->button_memory1();
} else if (preset == "M2") {
result = this->parent_->button_memory2();
} else if (preset == "M3") {
result = this->parent_->button_memory3();
} else if (preset == "LTD HT") {
result = this->parent_->button_heat();
} else if (preset == "EXT HT") {
result = this->parent_->button_ext_heat();
} else {
ESP_LOGW(TAG, "Unsupported preset: %s", preset.c_str());
return;
}
if (result) {
this->custom_preset = preset;
this->preset.reset();
}
}
if (call.get_fan_mode().has_value()) {
// Climate fan mode only supports low/med/high, but the BedJet supports 5-100% increments.
// We can still support a ClimateCall that requests low/med/high, and just translate it to a step increment here.
auto fan_mode = *call.get_fan_mode();
bool result;
if (fan_mode == CLIMATE_FAN_LOW) {
result = this->parent_->set_fan_speed(20);
} else if (fan_mode == CLIMATE_FAN_MEDIUM) {
result = this->parent_->set_fan_speed(50);
} else if (fan_mode == CLIMATE_FAN_HIGH) {
result = this->parent_->set_fan_speed(75);
} else {
ESP_LOGW(TAG, "[%s] Unsupported fan mode: %s", this->get_name().c_str(),
LOG_STR_ARG(climate_fan_mode_to_string(fan_mode)));
return;
}
if (result) {
this->fan_mode = fan_mode;
this->custom_fan_mode.reset();
}
} else if (call.get_custom_fan_mode().has_value()) {
auto fan_mode = *call.get_custom_fan_mode();
auto fan_index = bedjet_fan_speed_to_step(fan_mode);
if (fan_index <= 19) {
ESP_LOGV(TAG, "[%s] Converted fan mode %s to bedjet fan step %d", this->get_name().c_str(), fan_mode.c_str(),
fan_index);
bool result = this->parent_->set_fan_index(fan_index);
if (result) {
this->custom_fan_mode = fan_mode;
this->fan_mode.reset();
}
}
}
}
void BedJetClimate::on_bedjet_state(bool is_ready) {}
void BedJetClimate::on_status(const BedjetStatusPacket *data) {
ESP_LOGV(TAG, "[%s] Handling on_status with data=%p", this->get_name().c_str(), (void *) data);
auto converted_temp = bedjet_temp_to_c(data->target_temp_step);
if (converted_temp > 0)
this->target_temperature = converted_temp;
converted_temp = bedjet_temp_to_c(data->ambient_temp_step);
if (converted_temp > 0)
this->current_temperature = converted_temp;
const auto *fan_mode_name = bedjet_fan_step_to_fan_mode(data->fan_step);
if (fan_mode_name != nullptr) {
this->custom_fan_mode = *fan_mode_name;
}
// TODO: Get biorhythm data to determine which preset (M1-3) is running, if any.
switch (data->mode) {
case MODE_WAIT: // Biorhythm "wait" step: device is idle
case MODE_STANDBY:
this->mode = CLIMATE_MODE_OFF;
this->action = CLIMATE_ACTION_IDLE;
this->fan_mode = CLIMATE_FAN_OFF;
this->custom_preset.reset();
this->preset.reset();
break;
case MODE_HEAT:
this->mode = CLIMATE_MODE_HEAT;
this->action = CLIMATE_ACTION_HEATING;
this->preset.reset();
if (this->heating_mode_ == HEAT_MODE_EXTENDED) {
this->set_custom_preset_("LTD HT");
} else {
this->custom_preset.reset();
}
break;
case MODE_EXTHT:
this->mode = CLIMATE_MODE_HEAT;
this->action = CLIMATE_ACTION_HEATING;
this->preset.reset();
if (this->heating_mode_ == HEAT_MODE_EXTENDED) {
this->custom_preset.reset();
} else {
this->set_custom_preset_("EXT HT");
}
break;
case MODE_COOL:
this->mode = CLIMATE_MODE_FAN_ONLY;
this->action = CLIMATE_ACTION_COOLING;
this->custom_preset.reset();
this->preset.reset();
break;
case MODE_DRY:
this->mode = CLIMATE_MODE_DRY;
this->action = CLIMATE_ACTION_DRYING;
this->custom_preset.reset();
this->preset.reset();
break;
case MODE_TURBO:
this->preset = CLIMATE_PRESET_BOOST;
this->custom_preset.reset();
this->mode = CLIMATE_MODE_HEAT;
this->action = CLIMATE_ACTION_HEATING;
break;
default:
ESP_LOGW(TAG, "[%s] Unexpected mode: 0x%02X", this->get_name().c_str(), data->mode);
break;
}
ESP_LOGV(TAG, "[%s] After on_status, new mode=%s", this->get_name().c_str(),
LOG_STR_ARG(climate_mode_to_string(this->mode)));
// FIXME: compare new state to previous state.
this->publish_state();
}
/** Attempts to update the climate 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 BedJetClimate::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);
if (this->is_valid_()) {
// TODO: only if state changed?
this->publish_state();
this->status_clear_warning();
return true;
}
return false;
}
void BedJetClimate::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");
}
} // namespace bedjet
} // namespace esphome
#endif

View File

@@ -1,48 +1,33 @@
#pragma once
#include "esphome/components/ble_client/ble_client.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include "esphome/components/climate/climate.h"
#include "esphome/core/component.h"
#include "esphome/core/defines.h"
#include "esphome/core/hal.h"
#include "bedjet_base.h"
#ifdef USE_TIME
#include "esphome/components/time/real_time_clock.h"
#endif
#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
#include <esp_gattc_api.h>
namespace esphome {
namespace bedjet {
namespace espbt = esphome::esp32_ble_tracker;
static const espbt::ESPBTUUID BEDJET_SERVICE_UUID = espbt::ESPBTUUID::from_raw("00001000-bed0-0080-aa55-4265644a6574");
static const espbt::ESPBTUUID BEDJET_STATUS_UUID = espbt::ESPBTUUID::from_raw("00002000-bed0-0080-aa55-4265644a6574");
static const espbt::ESPBTUUID BEDJET_COMMAND_UUID = espbt::ESPBTUUID::from_raw("00002004-bed0-0080-aa55-4265644a6574");
static const espbt::ESPBTUUID BEDJET_NAME_UUID = espbt::ESPBTUUID::from_raw("00002001-bed0-0080-aa55-4265644a6574");
class Bedjet : public climate::Climate, public esphome::ble_client::BLEClientNode, public PollingComponent {
class BedJetClimate : public climate::Climate, public BedJetClient, public PollingComponent {
public:
void setup() override;
void loop() override;
void update() 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 dump_config() override;
float get_setup_priority() const override { return setup_priority::AFTER_WIFI; }
#ifdef USE_TIME
void set_time_id(time::RealTimeClock *time_id) { this->time_id_ = time_id; }
#endif
void set_status_timeout(uint32_t timeout) { this->timeout_ = timeout; }
/* BedJetClient status update */
void on_status(const BedjetStatusPacket *data) override;
void on_bedjet_state(bool is_ready) override;
std::string describe() override;
/** Attempts to check for and apply firmware updates. */
void upgrade_firmware();
/** Sets the default strategy to use for climate::CLIMATE_MODE_HEAT. */
void set_heating_mode(BedjetHeatMode mode) { this->heating_mode_ = mode; }
climate::ClimateTraits traits() override {
auto traits = climate::ClimateTraits();
@@ -74,6 +59,11 @@ class Bedjet : public climate::Climate, public esphome::ble_client::BLEClientNod
"M2",
"M3",
});
if (this->heating_mode_ == HEAT_MODE_EXTENDED) {
traits.add_supported_custom_preset("LTD HT");
} else {
traits.add_supported_custom_preset("EXT HT");
}
traits.set_visual_min_temperature(19.0);
traits.set_visual_max_temperature(43.0);
traits.set_visual_temperature_step(1.0);
@@ -83,20 +73,8 @@ class Bedjet : public climate::Climate, public esphome::ble_client::BLEClientNod
protected:
void control(const climate::ClimateCall &call) override;
#ifdef USE_TIME
void setup_time_();
void send_local_time_();
optional<time::RealTimeClock *> time_id_{};
#endif
BedjetHeatMode heating_mode_ = HEAT_MODE_HEAT;
uint32_t timeout_{DEFAULT_STATUS_TIMEOUT};
static const uint32_t MIN_NOTIFY_THROTTLE = 5000;
static const uint32_t NOTIFY_WARN_THRESHOLD = 300000;
static const uint32_t DEFAULT_STATUS_TIMEOUT = 900000;
uint8_t set_notify_(bool enable);
uint8_t write_bedjet_packet_(BedjetPacket *pkt);
void reset_state_();
bool update_status_();
@@ -105,17 +83,6 @@ class Bedjet : public climate::Climate, public esphome::ble_client::BLEClientNod
return !std::isnan(this->current_temperature) && !std::isnan(this->target_temperature) &&
this->current_temperature > 1 && this->target_temperature > 1;
}
uint32_t last_notify_ = 0;
bool force_refresh_ = false;
std::unique_ptr<BedjetCodec> codec_;
uint16_t char_handle_cmd_;
uint16_t char_handle_name_;
uint16_t char_handle_status_;
uint16_t config_descr_status_;
uint8_t write_notify_config_descriptor_(bool enable);
};
} // namespace bedjet

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

@@ -22,6 +22,7 @@ from esphome.const import (
CONF_ON_PRESS,
CONF_ON_RELEASE,
CONF_ON_STATE,
CONF_PUBLISH_INITIAL_STATE,
CONF_STATE,
CONF_TIMING,
CONF_TRIGGER_ID,
@@ -29,6 +30,7 @@ from esphome.const import (
DEVICE_CLASS_EMPTY,
DEVICE_CLASS_BATTERY,
DEVICE_CLASS_BATTERY_CHARGING,
DEVICE_CLASS_CARBON_MONOXIDE,
DEVICE_CLASS_COLD,
DEVICE_CLASS_CONNECTIVITY,
DEVICE_CLASS_DOOR,
@@ -63,6 +65,7 @@ DEVICE_CLASSES = [
DEVICE_CLASS_EMPTY,
DEVICE_CLASS_BATTERY,
DEVICE_CLASS_BATTERY_CHARGING,
DEVICE_CLASS_CARBON_MONOXIDE,
DEVICE_CLASS_COLD,
DEVICE_CLASS_CONNECTIVITY,
DEVICE_CLASS_DOOR,
@@ -326,6 +329,7 @@ BINARY_SENSOR_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).ex
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(
mqtt.MQTTBinarySensorComponent
),
cv.Optional(CONF_PUBLISH_INITIAL_STATE): cv.boolean,
cv.Optional(CONF_DEVICE_CLASS): validate_device_class,
cv.Optional(CONF_FILTERS): validate_filters,
cv.Optional(CONF_ON_PRESS): automation.validate_automation(
@@ -418,6 +422,8 @@ async def setup_binary_sensor_core_(var, config):
if CONF_DEVICE_CLASS in config:
cg.add(var.set_device_class(config[CONF_DEVICE_CLASS]))
if CONF_PUBLISH_INITIAL_STATE in config:
cg.add(var.set_publish_initial_state(config[CONF_PUBLISH_INITIAL_STATE]))
if CONF_INVERTED in config:
cg.add(var.set_inverted(config[CONF_INVERTED]))
if CONF_FILTERS in config:
@@ -477,8 +483,8 @@ async def register_binary_sensor(var, config):
await setup_binary_sensor_core_(var, config)
async def new_binary_sensor(config):
var = cg.new_Pvariable(config[CONF_ID])
async def new_binary_sensor(config, *args):
var = cg.new_Pvariable(config[CONF_ID], *args)
await register_binary_sensor(var, config)
return var

View File

@@ -37,7 +37,7 @@ void BinarySensor::send_state_internal(bool state, bool is_initial) {
}
this->has_state_ = true;
this->state = state;
if (!is_initial) {
if (!is_initial || this->publish_initial_state_) {
this->state_callback_.call(state);
}
}
@@ -69,7 +69,6 @@ void BinarySensor::add_filters(const std::vector<Filter *> &filters) {
}
}
bool BinarySensor::has_state() const { return this->has_state_; }
uint32_t BinarySensor::hash_base() { return 1210250844UL; }
bool BinarySensor::is_status_binary_sensor() const { return false; }
} // namespace binary_sensor

View File

@@ -58,6 +58,8 @@ class BinarySensor : public EntityBase {
void add_filter(Filter *filter);
void add_filters(const std::vector<Filter *> &filters);
void set_publish_initial_state(bool publish_initial_state) { this->publish_initial_state_ = publish_initial_state; }
// ========== INTERNAL METHODS ==========
// (In most use cases you won't need these)
void send_state_internal(bool state, bool is_initial);
@@ -76,12 +78,11 @@ class BinarySensor : public EntityBase {
virtual std::string device_class();
protected:
uint32_t hash_base() override;
CallbackManager<void(bool)> state_callback_{};
optional<std::string> device_class_{}; ///< Stores the override of the device class
Filter *filter_list_{nullptr};
bool has_state_{false};
bool publish_initial_state_{false};
Deduplicator<bool> publish_dedup_;
};

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

@@ -7,7 +7,7 @@ namespace bl0939 {
static const char *const TAG = "bl0939";
// https://www.belling.com.cn/media/file_object/bel_product/BL0939/datasheet/BL0939_V1.2_cn.pdf
// (unfortunatelly chinese, but the protocol can be understood with some translation tool)
// (unfortunately chinese, but the protocol can be understood with some translation tool)
static const uint8_t BL0939_READ_COMMAND = 0x55; // 0x5{A4,A3,A2,A1}
static const uint8_t BL0939_FULL_PACKET = 0xAA;
static const uint8_t BL0939_PACKET_HEADER = 0x55;

View File

@@ -8,7 +8,7 @@ namespace esphome {
namespace bl0939 {
// https://datasheet.lcsc.com/lcsc/2108071830_BL-Shanghai-Belling-BL0939_C2841044.pdf
// (unfortunatelly chinese, but the formulas can be easily understood)
// (unfortunately chinese, but the formulas can be easily understood)
// Sonoff Dual R3 V2 has the exact same resistor values for the current shunts (RL=1miliOhm)
// and for the voltage divider (R1=0.51kOhm, R2=5*390kOhm)
// as in the manufacturer's reference circuit, so the same formulas were used here (Vref=1.218V)
@@ -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,23 +1,25 @@
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,
CONF_MAC_ADDRESS,
CONF_NAME,
CONF_ON_CONNECT,
CONF_ON_DISCONNECT,
CONF_SERVICE_UUID,
CONF_TRIGGER_ID,
CONF_VALUE,
)
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
@@ -27,6 +29,8 @@ BLEClientConnectTrigger = ble_client_ns.class_(
BLEClientDisconnectTrigger = ble_client_ns.class_(
"BLEClientDisconnectTrigger", automation.Trigger.template(BLEClientNodeConstRef)
)
# Actions
BLEWriteAction = ble_client_ns.class_("BLEClientWriteAction", automation.Action)
# Espressif platformio framework is built with MAX_BLE_CONN to 3, so
# enforce this in yaml checks.
@@ -72,6 +76,39 @@ async def register_ble_node(var, config):
cg.add(parent.register_ble_node(var))
BLE_WRITE_ACTION_SCHEMA = cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(BLEClient),
cv.Required(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid,
cv.Required(CONF_CHARACTERISTIC_UUID): esp32_ble_tracker.bt_uuid,
cv.Required(CONF_VALUE): cv.templatable(cv.ensure_list(cv.hex_uint8_t)),
}
)
@automation.register_action(
"ble_client.ble_write", BLEWriteAction, BLE_WRITE_ACTION_SCHEMA
)
async def ble_write_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, paren)
value = config[CONF_VALUE]
if cg.is_template(value):
templ = await cg.templatable(value, args, cg.std_vector.template(cg.uint8))
cg.add(var.set_value_template(templ))
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))
return var
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)

View File

@@ -0,0 +1,75 @@
#include "automation.h"
#include <esp_bt_defs.h>
#include <esp_gap_ble_api.h>
#include <esp_gattc_api.h>
#include "esphome/core/log.h"
namespace esphome {
namespace ble_client {
static const char *const TAG = "ble_client.automation";
void BLEWriterClientNode::write(const std::vector<uint8_t> &value) {
if (this->node_state != espbt::ClientState::ESTABLISHED) {
ESP_LOGW(TAG, "Cannot write to BLE characteristic - not connected");
return;
} else if (this->ble_char_handle_ == 0) {
ESP_LOGW(TAG, "Cannot write to BLE characteristic - characteristic not found");
return;
}
esp_gatt_write_type_t write_type;
if (this->char_props_ & ESP_GATT_CHAR_PROP_BIT_WRITE) {
write_type = ESP_GATT_WRITE_TYPE_RSP;
ESP_LOGD(TAG, "Write type: ESP_GATT_WRITE_TYPE_RSP");
} else if (this->char_props_ & ESP_GATT_CHAR_PROP_BIT_WRITE_NR) {
write_type = ESP_GATT_WRITE_TYPE_NO_RSP;
ESP_LOGD(TAG, "Write type: ESP_GATT_WRITE_TYPE_NO_RSP");
} else {
ESP_LOGE(TAG, "Characteristic %s does not allow writing", this->char_uuid_.to_string().c_str());
return;
}
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()->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));
}
}
void BLEWriterClientNode::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_REG_EVT:
break;
case ESP_GATTC_OPEN_EVT:
this->node_state = espbt::ClientState::ESTABLISHED;
ESP_LOGD(TAG, "Connection established with %s", ble_client_->address_str().c_str());
break;
case ESP_GATTC_SEARCH_CMPL_EVT: {
auto *chr = this->parent()->get_characteristic(this->service_uuid_, this->char_uuid_);
if (chr == nullptr) {
ESP_LOGW("ble_write_action", "Characteristic %s was not found in service %s",
this->char_uuid_.to_string().c_str(), this->service_uuid_.to_string().c_str());
break;
}
this->ble_char_handle_ = chr->handle;
this->char_props_ = chr->properties;
this->node_state = espbt::ClientState::ESTABLISHED;
ESP_LOGD(TAG, "Found characteristic %s on device %s", this->char_uuid_.to_string().c_str(),
ble_client_->address_str().c_str());
break;
}
case ESP_GATTC_DISCONNECT_EVT:
this->node_state = espbt::ClientState::IDLE;
this->ble_char_handle_ = 0;
ESP_LOGD(TAG, "Disconnected from %s", ble_client_->address_str().c_str());
break;
default:
break;
}
}
} // namespace ble_client
} // namespace esphome

View File

@@ -1,5 +1,7 @@
#pragma once
#include <utility>
#include "esphome/core/automation.h"
#include "esphome/components/ble_client/ble_client.h"
@@ -13,10 +15,10 @@ class BLEClientConnectTrigger : 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_OPEN_EVT && param->open.status == ESP_GATT_OK)
this->trigger();
if (event == ESP_GATTC_SEARCH_CMPL_EVT)
if (event == ESP_GATTC_SEARCH_CMPL_EVT) {
this->node_state = espbt::ClientState::ESTABLISHED;
this->trigger();
}
}
};
@@ -26,13 +28,67 @@ 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;
}
};
class BLEWriterClientNode : public BLEClientNode {
public:
BLEWriterClientNode(BLEClient *ble_client) {
ble_client->register_ble_node(this);
ble_client_ = ble_client;
}
// 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_uuid128(uint8_t *uuid) { this->service_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;
private:
BLEClient *ble_client_;
int ble_char_handle_ = 0;
esp_gatt_char_prop_t char_props_;
espbt::ESPBTUUID service_uuid_;
espbt::ESPBTUUID char_uuid_;
};
template<typename... Ts> class BLEClientWriteAction : public Action<Ts...>, public BLEWriterClientNode {
public:
BLEClientWriteAction(BLEClient *ble_client) : BLEWriterClientNode(ble_client) {}
void play(Ts... x) override {
if (has_simple_value_) {
return write(this->value_simple_);
} else {
return write(this->value_template_(x...));
}
}
void set_value_template(std::function<std::vector<uint8_t>(Ts...)> func) {
this->value_template_ = std::move(func);
has_simple_value_ = false;
}
void set_value_simple(const std::vector<uint8_t> &value) {
this->value_simple_ = value;
has_simple_value_ = true;
}
private:
bool has_simple_value_ = true;
std::vector<uint8_t> value_simple_;
std::function<std::vector<uint8_t>(Ts...)> value_template_{};
};
} // namespace ble_client
} // namespace esphome

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,33 +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;
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) {
@@ -73,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);
}
@@ -81,120 +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, BLE_ADDR_TYPE_PUBLIC, 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());
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());
this->conn_id = param->connect.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;
}
uint8_t notify_en = 1;
auto status = esp_ble_gattc_write_char_descr(this->gattc_if, this->conn_id, descr->handle, sizeof(notify_en),
&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);
@@ -206,237 +64,20 @@ 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;
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,9 +19,9 @@ namespace ble_client {
namespace espbt = esphome::esp32_ble_tracker;
using namespace esp32_ble_client;
class BLEClient;
class BLEService;
class BLECharacteristic;
class BLEClientNode {
public:
@@ -42,57 +43,15 @@ 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,42 +61,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;
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

@@ -1,13 +1,12 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import ble_client, esp32_ble_tracker, output
from esphome.const import CONF_ID, CONF_SERVICE_UUID
from esphome.const import CONF_CHARACTERISTIC_UUID, CONF_ID, CONF_SERVICE_UUID
from .. import ble_client_ns
DEPENDENCIES = ["ble_client"]
CONF_CHARACTERISTIC_UUID = "characteristic_uuid"
CONF_REQUIRE_RESPONSE = "require_response"
BLEBinaryOutput = ble_client_ns.class_(

View File

@@ -2,6 +2,7 @@ import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor, ble_client, esp32_ble_tracker
from esphome.const import (
CONF_CHARACTERISTIC_UUID,
CONF_LAMBDA,
CONF_TRIGGER_ID,
CONF_SERVICE_UUID,
@@ -11,7 +12,6 @@ from .. import ble_client_ns
DEPENDENCIES = ["ble_client"]
CONF_CHARACTERISTIC_UUID = "characteristic_uuid"
CONF_DESCRIPTOR_UUID = "descriptor_uuid"
CONF_NOTIFY = "notify"

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

@@ -11,8 +11,6 @@ namespace ble_client {
static const char *const TAG = "ble_sensor";
uint32_t BLESensor::hash_base() { return 343459825UL; }
void BLESensor::loop() {}
void BLESensor::dump_config() {
@@ -65,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);
}
@@ -76,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);
@@ -89,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]);
@@ -124,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

@@ -37,7 +37,6 @@ class BLESensor : public sensor::Sensor, public PollingComponent, public BLEClie
uint16_t handle;
protected:
uint32_t hash_base() override;
float parse_data_(uint8_t *value, uint16_t value_len);
optional<data_to_value_t> data_to_value_func_{};
bool notify_;

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

@@ -2,6 +2,7 @@ import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import text_sensor, ble_client, esp32_ble_tracker
from esphome.const import (
CONF_CHARACTERISTIC_UUID,
CONF_ID,
CONF_TRIGGER_ID,
CONF_SERVICE_UUID,
@@ -11,7 +12,6 @@ from .. import ble_client_ns
DEPENDENCIES = ["ble_client"]
CONF_CHARACTERISTIC_UUID = "characteristic_uuid"
CONF_DESCRIPTOR_UUID = "descriptor_uuid"
CONF_NOTIFY = "notify"

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

@@ -14,8 +14,6 @@ static const char *const TAG = "ble_text_sensor";
static const std::string EMPTY = "";
uint32_t BLETextSensor::hash_base() { return 193967603UL; }
void BLETextSensor::loop() {}
void BLETextSensor::dump_config() {
@@ -68,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);
}
@@ -79,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);
@@ -92,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]);
@@ -123,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

@@ -35,7 +35,6 @@ class BLETextSensor : public text_sensor::TextSensor, public PollingComponent, p
uint16_t handle;
protected:
uint32_t hash_base() override;
bool notify_;
espbt::ESPBTUUID service_uuid_;
espbt::ESPBTUUID char_uuid_;

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,6 +2,9 @@ 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,
@@ -16,6 +19,15 @@ 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,
@@ -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 = ["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,361 @@
#include "bluetooth_proxy.h"
#include "esphome/core/log.h"
#ifdef USE_ESP32
#ifdef USE_API
#include "esphome/components/api/api_server.h"
#endif
namespace esphome {
namespace bluetooth_proxy {
static const char *const TAG = "bluetooth_proxy";
BluetoothProxy::BluetoothProxy() {
global_bluetooth_proxy = this;
this->address_ = 0;
}
bool BluetoothProxy::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
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);
this->address_type_map_[device.address_uint64()] = device.get_address_type();
if (this->address_ == 0)
return true;
if (this->state_ == espbt::ClientState::DISCOVERED) {
ESP_LOGV(TAG, "Connecting to address %s", this->address_str().c_str());
return true;
}
BLEClientBase::parse_device(device);
return true;
}
void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device) {
#ifndef USE_API
return;
#else
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);
#endif
}
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: {
#ifdef USE_API
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());
#endif
this->address_ = 0;
}
case ESP_GATTC_OPEN_EVT: {
if (param->open.status != ESP_GATT_OK && param->open.status != ESP_GATT_ALREADY_OPEN) {
#ifdef USE_API
api::global_api_server->send_bluetooth_device_connection(this->address_, false, this->mtu_, param->open.status);
#endif
break;
}
break;
}
case ESP_GATTC_SEARCH_CMPL_EVT: {
#ifdef USE_API
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());
#endif
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 %d, status=%d", param->read.handle, param->read.status);
break;
}
#ifdef USE_API
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);
#endif
break;
}
case ESP_GATTC_NOTIFY_EVT: {
if (param->notify.conn_id != this->conn_id_)
break;
ESP_LOGV(TAG, "ESP_GATTC_NOTIFY_EVT: handle=0x%x", param->notify.handle);
#ifdef USE_API
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);
#endif
break;
}
default:
break;
}
}
void BluetoothProxy::dump_config() { ESP_LOGCONFIG(TAG, "Bluetooth Proxy:"); }
void BluetoothProxy::loop() {
BLEClientBase::loop();
#ifdef USE_API
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_++;
}
#endif
}
#ifdef USE_API
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;
if (this->address_type_map_.find(this->address_) != this->address_type_map_.end()) {
// Utilise the address type cache
this->remote_addr_type_ = this->address_type_map_[this->address_];
} else {
this->remote_addr_type_ = BLE_ADDR_TYPE_PUBLIC;
}
this->remote_bda_[0] = (this->address_ >> 40) & 0xFF;
this->remote_bda_[1] = (this->address_ >> 32) & 0xFF;
this->remote_bda_[2] = (this->address_ >> 24) & 0xFF;
this->remote_bda_[3] = (this->address_ >> 16) & 0xFF;
this->remote_bda_[4] = (this->address_ >> 8) & 0xFF;
this->remote_bda_[5] = (this->address_ >> 0) & 0xFF;
this->set_state(espbt::ClientState::DISCOVERED);
esp_ble_gap_stop_scanning();
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.");
return;
}
if (this->address_ != msg.address) {
ESP_LOGW(TAG, "Address mismatch for read GATT characteristic request");
return;
}
auto *characteristic = this->get_characteristic(msg.handle);
if (characteristic == nullptr) {
ESP_LOGW(TAG, "Cannot read GATT characteristic, not found.");
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);
}
}
void BluetoothProxy::bluetooth_gatt_write(const api::BluetoothGATTWriteRequest &msg) {
if (this->state_ != espbt::ClientState::ESTABLISHED) {
ESP_LOGW(TAG, "Cannot write GATT characteristic, not connected.");
return;
}
if (this->address_ != msg.address) {
ESP_LOGW(TAG, "Address mismatch for write GATT characteristic request");
return;
}
auto *characteristic = this->get_characteristic(msg.handle);
if (characteristic == nullptr) {
ESP_LOGW(TAG, "Cannot write GATT characteristic, not found.");
return;
}
ESP_LOGV(TAG, "Writing GATT characteristic %s", characteristic->uuid.to_string().c_str());
characteristic->write_value((uint8_t *) msg.data.data(), msg.data.size(),
msg.response ? ESP_GATT_WRITE_TYPE_RSP : ESP_GATT_WRITE_TYPE_NO_RSP);
}
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.");
return;
}
if (this->address_ != msg.address) {
ESP_LOGW(TAG, "Address mismatch for read GATT characteristic descriptor request");
return;
}
auto *descriptor = this->get_descriptor(msg.handle);
if (descriptor == nullptr) {
ESP_LOGW(TAG, "Cannot read GATT characteristic descriptor, not found.");
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);
}
}
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.");
return;
}
if (this->address_ != msg.address) {
ESP_LOGW(TAG, "Address mismatch for write GATT characteristic descriptor request");
return;
}
auto *descriptor = this->get_descriptor(msg.handle);
if (descriptor == nullptr) {
ESP_LOGW(TAG, "Cannot write GATT characteristic descriptor, not found.");
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);
}
}
void BluetoothProxy::bluetooth_gatt_send_services(const api::BluetoothGATTGetServicesRequest &msg) {
if (this->address_ != msg.address) {
ESP_LOGW(TAG, "Address mismatch for service list request");
return;
}
this->send_service_ = 0;
}
void BluetoothProxy::bluetooth_gatt_notify(const api::BluetoothGATTNotifyRequest &msg) {
if (this->address_ != msg.address) {
ESP_LOGW(TAG, "Address mismatch for notify");
return;
}
auto *characteristic = this->get_characteristic(msg.handle);
if (characteristic == nullptr) {
ESP_LOGW(TAG, "Cannot notify GATT characteristic, not found.");
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);
}
} 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);
}
}
}
#endif
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,63 @@
#pragma once
#ifdef USE_ESP32
#include <map>
#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>
#ifdef USE_API
#include "esphome/components/api/api_pb2.h"
#endif // USE_API
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;
#ifdef USE_API
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);
#endif
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);
std::map<uint64_t, esp_ble_addr_type_t> address_type_map_;
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

@@ -1,4 +1,5 @@
#include "bme280.h"
#include "esphome/core/hal.h"
#include "esphome/core/log.h"
namespace esphome {
@@ -28,6 +29,7 @@ static const uint8_t BME280_REGISTER_DIG_H5 = 0xE5;
static const uint8_t BME280_REGISTER_DIG_H6 = 0xE7;
static const uint8_t BME280_REGISTER_CHIPID = 0xD0;
static const uint8_t BME280_REGISTER_RESET = 0xE0;
static const uint8_t BME280_REGISTER_CONTROLHUMID = 0xF2;
static const uint8_t BME280_REGISTER_STATUS = 0xF3;
@@ -39,6 +41,8 @@ static const uint8_t BME280_REGISTER_TEMPDATA = 0xFA;
static const uint8_t BME280_REGISTER_HUMIDDATA = 0xFD;
static const uint8_t BME280_MODE_FORCED = 0b01;
static const uint8_t BME280_SOFT_RESET = 0xB6;
static const uint8_t BME280_STATUS_IM_UPDATE = 0b01;
inline uint16_t combine_bytes(uint8_t msb, uint8_t lsb) { return ((msb & 0xFF) << 8) | (lsb & 0xFF); }
@@ -97,6 +101,28 @@ void BME280Component::setup() {
return;
}
// Send a soft reset.
if (!this->write_byte(BME280_REGISTER_RESET, BME280_SOFT_RESET)) {
this->mark_failed();
return;
}
// Wait until the NVM data has finished loading.
uint8_t status;
uint8_t retry = 5;
do {
delay(2);
if (!this->read_byte(BME280_REGISTER_STATUS, &status)) {
ESP_LOGW(TAG, "Error reading status register.");
this->mark_failed();
return;
}
} while ((status & BME280_STATUS_IM_UPDATE) && (--retry));
if (status & BME280_STATUS_IM_UPDATE) {
ESP_LOGW(TAG, "Timeout loading NVM.");
this->mark_failed();
return;
}
// Read calibration
this->calibration_.t1 = read_u16_le_(BME280_REGISTER_DIG_T1);
this->calibration_.t2 = read_s16_le_(BME280_REGISTER_DIG_T2);
@@ -137,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

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