Compare commits

...

1220 Commits

Author SHA1 Message Date
Jesse Hills
7f0dc7b07b Merge branch 'dev' into host-target 2022-03-08 08:21:39 +13:00
Jesse Hills
dc6eff83ea Only get free memory size from internal (#3259) 2022-03-02 16:44:35 +13:00
Jesse Hills
38ff66debd Remove stray define (#3260) 2022-03-02 16:32:45 +13:00
Sean Brogan
1d2e0f74ea Add Mopeka BLE and Mopeka Pro Check BLE Sensor (#2618)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-03-01 11:30:33 +13:00
Jesse Hills
bf60e40d0b Add optional display page for touchscreen binary sensors (#3247) 2022-02-23 11:05:53 +13:00
RubyBailey
c9094ca537 Add sensor support: Honeywell ABP (SPI version) (#3164)
Co-authored-by: RubyBailey <ruby_bailey11@hotmail.com>
2022-02-22 11:22:30 +01:00
Jesse Hills
68b3fd6b8f Store platform as uppercase (#3251) 2022-02-22 13:54:23 +13:00
Nicholas Peters
9323b3a248 Fix regression caused by TSL2591 auto gain (#3249) 2022-02-22 13:53:24 +13:00
Jesse Hills
b55e9329d9 Fix template button after abstract press_action (#3250) 2022-02-22 13:47:16 +13:00
Micha Nordmann
a5b4105971 support for waveshare 7.50in-hd-b (#3239) 2022-02-22 07:35:04 +13:00
Arturo Casal
d1feaa935d Add device support: MCP4728 (#3174)
* Added MCP4728 output component.

* Added tests to test1.yaml

* Added codeowners

* Lint fixes

* Implemented code review changes

* Lint fixes

* Added i2c communication check on setup()

* Fixed tests

* Lint fix

* Update esphome/components/mcp4728/mcp4728_output.cpp

Changed log function

Co-authored-by: Otto Winter <otto@otto-winter.com>

Co-authored-by: Otto Winter <otto@otto-winter.com>
2022-02-21 12:47:03 +01:00
Martin Weinelt
6919930aaa Respect ESPHOME_USE_SUBPROCESS in esp32 post_build script (#3246) 2022-02-21 14:05:13 +13:00
EdJoPaTo
69633826bb Implement send_first_at for exponential_moving_average (#3240) 2022-02-21 13:13:06 +13:00
Niorix
771162bfb1 light: add RESTORE_AND_OFF/RESTORE_AND_ON LightRestoreMode (#3238) 2022-02-21 12:52:14 +13:00
Fabian Affolter
ba785e29e9 Add support for MPU-6886 (#3183) 2022-02-21 12:23:26 +13:00
Jesse Hills
2c7b104f4a Fix fatal erroring in addon startup script (#3244) 2022-02-21 10:01:00 +13:00
Jesse Hills
78951c197a Fix lilygo touchscreen rotation (#3221) 2022-02-21 09:58:53 +13:00
cstaahl
07c1cf7137 Pulse meter internal filter mode (#3082)
Co-authored-by: Paul Daumlechner <paul.daumlechner@live.de>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
Co-authored-by: Otto Winter <otto@otto-winter.com>
2022-02-21 09:32:35 +13:00
Otto Winter
d26141151a Button code cleanup (#3242) 2022-02-21 09:17:51 +13:00
Otto Winter
f59dbe4a88 Add copy integration (#3241) 2022-02-21 09:13:37 +13:00
Otto Winter
8dae7f8225 Bump esphome-dashboard from 20220209.0 to 20220219.0 (#3231) 2022-02-19 15:57:52 +01:00
Otto Winter
5811389891 BH1750 dynamically calculate options (#3214)
* BH1750 dynamically calculate options

* Fix tests

* Fix NAN

* Convert setup to new-style

* Add myself as codeowner
2022-02-19 15:49:20 +01:00
Otto Winter
debcaf6fb7 Add ESP32C3 and ESP32S2 support to dashboard (#3152)
* Add ESP32C3 and ESP32S2 support to dashboard

* Format

* Fix tests
2022-02-19 15:47:50 +01:00
Tyler Bules
b8d10a62c2 ESP32-C3 deep sleep fix (#3066) 2022-02-19 15:13:48 +01:00
Otto Winter
d2b209234f Improve ESP8266 iram usage (#3223) 2022-02-19 15:09:17 +01:00
Otto Winter
34c9d8be50 Lint trailing whitespace (#3230) 2022-02-19 14:46:27 +01:00
Otto Winter
ae57ad0c81 Fix warning in test1.yaml (#3228) 2022-02-19 14:42:54 +01:00
Otto Winter
0c1520dd9c Fix ESP8266 climate memaccess warning (#3226) 2022-02-19 14:11:45 +01:00
Otto Winter
d594f43ebd Publish NAN when dallas conversion failed (#3227) 2022-02-19 14:11:01 +01:00
Oxan van Leeuwen
125c693e3f Add ESP32 variant config validator function (#3088)
* Add esp32_variant config validator function

* Drop unused is_esp32c3 function

Co-authored-by: Otto Winter <otto@otto-winter.com>
2022-02-19 11:41:34 +01:00
mknjc
ad2f857e15 [miscale] Add flag to clear last impedance reading if the newly received reading only contains weight (#3132) 2022-02-19 10:59:53 +01:00
Peter Valkov
e445d6aada Fix for api disconnect detection. (#2909)
Co-authored-by: Otto Winter <otto@otto-winter.com>
2022-02-19 10:36:19 +01:00
Arturo Casal
88fbb0ffbb Add sensor support: MAX44009 (#3125)
Co-authored-by: Otto Winter <otto@otto-winter.com>
2022-02-19 09:49:45 +01:00
Borys Pierov
231908fe9f Implement text_sensor based on ble_client (#3079)
Co-authored-by: Otto Winter <otto@otto-winter.com>
2022-02-19 09:45:32 +01:00
ImSorryButWho
f137cc10f4 Remote magiquest protocol (#2963)
Co-authored-by: Aaron Hertz <aaron@rockforest.org>
Co-authored-by: Otto Winter <otto@otto-winter.com>
2022-02-18 22:22:41 +01:00
Michael Labuschke
1a8f8adc2a Read all cell voltages from DalyBMS (#3203)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-02-18 11:00:03 +13:00
Jesse Hills
7a242bb4ed Binary Sensor codegen tidyup (#3217) 2022-02-18 10:39:59 +13:00
Jesse Hills
3b8bb09ae3 Add class as first positional arg to sensor_schema (#3216) 2022-02-18 10:27:20 +13:00
Jesse Hills
140db85d21 Add LONG LONG flag for arduinojson (#3212) 2022-02-18 10:11:22 +13:00
mipa87
ccce4b19e8 Fix pm1006 polling component definition (#3210) 2022-02-17 21:47:31 +01:00
Adrián Panella
8cb9be7560 Analog threshold (#3190)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-02-18 09:14:10 +13:00
Otto Winter
953f0569fb Docker ha-addon switch to nginx-light (#3218) 2022-02-17 12:07:36 +01:00
Otto Winter
34c229fd33 Fix platformio docker version mismstch (#3215) 2022-02-17 11:56:14 +01:00
Roi Tagar
958ad0d750 HttpRequestComponent::get_string - avoid copy (#2988) 2022-02-17 17:03:54 +13:00
wilberforce
36ddd9dd69 Simplify captive portal to compressed single page (#2872) 2022-02-17 17:02:10 +13:00
Felix Storm
38259c96c9 CAN bus: support bit mask for on_frame can_id (#3196) 2022-02-17 17:00:31 +13:00
Felix Storm
c054fb8a2c CAN bus: read all queued messages (#3194) 2022-02-17 17:00:14 +13:00
Otto Winter
5a0b8328d8 ESP8266 early init for pins (#3144) 2022-02-17 16:59:46 +13:00
Jesse Hills
ffa19426d7 Remove redundant name from binary_sensor constructor (#3213) 2022-02-17 16:56:44 +13:00
Stewart
c123804294 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-17 13:53:26 +13:00
Otto Winter
4e24551b90 Docker move deps install into base (#3207) 2022-02-16 22:25:04 +01:00
Stewart
51cb5da7f0 Fix missed ARDUINO_VERSION_CODE to USE_ARDUINO_VERSION_CODE changes (#3206)
Co-authored-by: Stewart Morgan <stewart@arnos-vale.net>
2022-02-16 16:50:10 +01:00
Jesse Hills
41f84447cc Update HA addon token (#3200) 2022-02-16 09:11:46 +13:00
Maurice Makaay
ce073a704b Fix strlcpy() uses to make long SSIDs and passwords work (#3199)
Co-authored-by: Maurice Makaay <mmakaay1@xs4all.net>
2022-02-16 08:54:21 +13:00
Guillermo Ruffino
113232ebb6 add sim800l diagnostics (#3136) 2022-02-15 17:01:50 +13:00
Jesse Hills
a13a1225b7 Allow framework version validator to be maximum version (#3197) 2022-02-15 11:57:47 +13:00
dependabot[bot]
0ec84be5da Bump pytest from 7.0.0 to 7.0.1 (#3189) 2022-02-11 22:23:06 +01:00
dependabot[bot]
b1cefb7e3e Bump pytest-asyncio from 0.18.0 to 0.18.1 (#3187)
Bumps [pytest-asyncio](https://github.com/pytest-dev/pytest-asyncio) from 0.18.0 to 0.18.1.
- [Release notes](https://github.com/pytest-dev/pytest-asyncio/releases)
- [Commits](https://github.com/pytest-dev/pytest-asyncio/compare/v0.18.0...v0.18.1)

---
updated-dependencies:
- dependency-name: pytest-asyncio
  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-02-11 13:58:10 +01:00
dependabot[bot]
cc0c1c08b9 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-11 13:57:46 +01:00
Otto Winter
3a67884451 Improve dallas timing (#3181)
* Improve dallas timing

* Format
2022-02-11 09:06:06 +01:00
Otto Winter
72e716cdf1 Make generating combined binary output verbose (#3127) 2022-02-11 21:06:00 +13:00
Jesse Hills
40e06c9819 Raise minimum python version to 3.8 (#3176) 2022-02-10 09:55:11 +01:00
Jesse Hills
ad6c5ff11d Clamp rotary_encoder restored value to min and max (#3184) 2022-02-09 23:12:05 +01:00
dependabot[bot]
335512e232 Bump aioesphomeapi from 10.8.1 to 10.8.2 (#3182) 2022-02-09 20:13:02 +01:00
Otto Winter
2622e59b0b Remove spurious Zeroconf instance from api client (#3143) 2022-02-09 14:57:00 +01:00
Otto Winter
35e6a13cd1 Remove unused obj attribute from AssignmentExpression (#3145) 2022-02-09 14:56:42 +01:00
Jesse Hills
b48490badc Bump version to 2022.3.0-dev 2022-02-09 23:47:36 +13:00
Jesse Hills
5c22065135 Change most references from hassio to ha-addon (#3178) 2022-02-09 23:46:20 +13:00
Borys Pierov
e7dd6c52ac Allow to set manufacturer data for BLEAdvertising (#3179) 2022-02-09 23:29:32 +13:00
dependabot[bot]
3bf042dce9 Bump pytest-asyncio from 0.17.2 to 0.18.0 (#3168)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-02-09 12:57:12 +13:00
dependabot[bot]
64f798d4b2 Bump pytest from 6.2.5 to 7.0.0 (#3163)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-02-09 11:42:44 +13:00
Jesse Hills
f43e04e15a Try fix canbus config validation (#3173) 2022-02-09 11:42:00 +13:00
Jesse Hills
88d72f8c9a Fix files CI after merging (#3175) 2022-02-09 08:04:44 +13:00
Andrej Komelj
9826726a72 Implement MQTT discovery object_id generator (#3114) 2022-02-08 22:58:38 +13:00
Jesse Hills
c66d0550e8 Inkplate 6 PLUS (#3013) 2022-02-08 22:56:56 +13:00
mckaymatthew
4aeacfd16e Add max9611 High Side Current Shunt ADC (#2705)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-02-08 22:56:40 +13:00
stegm
58fa63ad88 Add Select for modbus (#3032)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2022-02-08 22:27:22 +13:00
Jesse Hills
94f944dc9c Add Lilygo t5 4.7 Touchscreen (#3084) 2022-02-08 21:50:25 +13:00
Ashton Kemerling
116ddbdd01 Add require response option for BLE binary output (#3091) 2022-02-08 21:30:31 +13:00
Jesse Hills
1c0697b5d4 Dont warn on nonnull comparisons (#3123) 2022-02-08 21:28:12 +13:00
Otto Winter
434ca47ea0 Enable mDNS during OTA safe mode (#3146) 2022-02-08 21:21:52 +13:00
functionpointer
397ef72b16 MLX90393 three-axis magnetometer (#2770)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-02-08 20:42:11 +13:00
Jonas Bergler
7ca9245735 wifi_info, reduce polling interval (#3165)
Co-authored-by: Jonas Bergler <jbergler@meraki.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-02-08 20:27:39 +13:00
Jesse Hills
69856286e8 Text sensor schema generator similar to sensor (#3172) 2022-02-08 17:23:45 +13:00
Jeff Eberl
ad43d6a5bc Added RadonEye RD200 Component (#3119)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-02-08 16:32:37 +13:00
mknjc
1e5004f495 [debug] Refactor debug sensors to use the normal sensor model. (#3162) 2022-02-08 12:45:27 +13:00
Otto Winter
253161d3d0 Fix copy_file_if_changed src permissions copied too (#3161) 2022-02-07 21:26:16 +01:00
Jesse Hills
ab47e201c7 Bump improv library to 1.2.1 (#3160) 2022-02-04 19:15:00 +13:00
Samuel Sieb
42984fa72a Handle Tuya multi-datapoint messages (#3159)
Co-authored-by: Samuel Sieb <samuel@sieb.net>
2022-02-04 15:50:42 +13:00
Franck Nijhof
e7864a28a1 Add device class support to Switch (#3012)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2022-02-04 09:04:48 +13:00
Keilin Bickar
21803607e7 Add new Lock core component (#2958)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-02-04 07:24:31 +13:00
Otto Winter
62b366a5ec Fix ESP32C3 toolchain requires stdarg import in helpers (#3151) 2022-02-01 13:05:59 +01:00
dependabot[bot]
2b39988707 Bump black from 21.12b0 to 22.1.0 (#3147)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Otto winter <otto@otto-winter.com>
2022-02-01 10:26:37 +01:00
Otto Winter
f9e7291050 Bump pre-commit flake8 from 3.8.4 to 4.0.1 (#3149) 2022-02-01 10:22:43 +01:00
Otto Winter
4de642ff28 Bump esp-idf framework version from 4.3.0 to 4.3.2 (#3120) 2022-01-31 07:59:56 +01:00
Otto Winter
0384efcfc2 Disable platformio ldf for build (#3130) 2022-01-31 14:27:10 +13:00
Jesse Hills
bf91443f38 Improv_serial scan and send wifi networks list (#3116) 2022-01-31 11:08:20 +13:00
Jesse Hills
4a5970b4af Fix backwards string case helpers (#3126) 2022-01-31 10:58:27 +13:00
dependabot[bot]
e3fd68c849 Bump pytest-mock from 3.6.1 to 3.7.0 (#3128) 2022-01-29 13:27:41 +01:00
Otto Winter
df0de2fc2d Bump docker dependencies (#3131) 2022-01-29 13:04:15 +01:00
Matt Hamilton
0c3568fad5 Add support for Waveshare 7.5in-bv2 (#3121) 2022-01-29 00:37:47 +13:00
Oxan van Leeuwen
976f5d91ed Logically group and document helper functions (#3112) 2022-01-27 20:35:42 +13:00
drug123
0f3d4d9a47 Add Xiaomi MHOC303 sensor e-ink clock (#3115) 2022-01-27 12:54:29 +13:00
Otto Winter
ad1f4429c9 Fix lint for TSL2591 (#3118) 2022-01-26 13:50:43 +01:00
Martin
7590d5eacb set adc width to 13 bits for esp32-s2 (#3117) 2022-01-26 13:33:59 +01:00
Nicholas Peters
c5974b8833 TSL2591 automatic gain control (#3071) 2022-01-26 22:48:51 +13:00
Otto Winter
511c8de6f3 ESP8266 Set recommended framework to 3.0.2 (#2606) 2022-01-26 22:41:57 +13:00
Wouter van der Wal
a718ac7ee0 Add qr code support for displays (#2952)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2022-01-26 22:20:45 +13:00
Jesse Hills
ef832becf1 Create base touchscreen component and refactor ektf2232 (#3083)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2022-01-26 16:26:46 +13:00
micronen
3a62455948 Add Heap Sensors - free/max block/fragmentation (#1578)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
Co-authored-by: Otto winter <otto@otto-winter.com>
2022-01-25 19:18:41 +01:00
Zebble
297824e2d7 Add support for additional colors on GROW R503 (#3087) 2022-01-25 17:18:36 +01:00
Jimmy Hedman
d92f297bc0 Add IPv6 support for ESP-IDF framework (#2953)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2022-01-25 09:55:33 +01:00
guillempages
7a0827e3d0 Configurable HTTP redirect following (#3100)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2022-01-25 09:53:22 +01:00
Joshua Spence
ef256a64b8 Fix config merging with null (#3113) 2022-01-25 09:24:59 +01:00
Dav-id
1de941e837 Esp32cam full control (#3090) 2022-01-25 11:53:47 +13:00
Joshua Spence
28b65cb810 Perform merges when substituting dict keys (#3062) 2022-01-25 11:46:42 +13:00
Martin
6ff3942e8b [TCS34725] remove duplicated endian conversion (#3037)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2022-01-25 11:41:14 +13:00
Rebbe Pod
ef5d959788 Add increment_day function to ESPTime (#2955) 2022-01-24 21:54:46 +01:00
Oxan van Leeuwen
6a2c58fcc0 Implement output button (#3109) 2022-01-25 09:30:48 +13:00
Oxan van Leeuwen
4e6bdb31ac Make CallbackManager invocable (#3089) 2022-01-25 08:57:26 +13:00
Oxan van Leeuwen
80d03a631e Force braces around multi-line statements (#3094)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-01-25 08:56:36 +13:00
Martin
6b27f2d2cf Remove unused polling_component_schema from modbus number (#3108) 2022-01-25 08:44:20 +13:00
dependabot[bot]
7cb6729fa7 Bump aioesphomeapi from 10.8.0 to 10.8.1 (#3110)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-01-25 08:37:35 +13:00
Oxan van Leeuwen
2f46267994 Add cv.require_esphome_version helper (#3103) 2022-01-24 20:10:27 +01:00
Oxan van Leeuwen
cdda648360 Generate ARDUINO_VERSION_CODE in Python code (#3101)
Co-authored-by: Otto winter <otto@otto-winter.com>
2022-01-24 10:34:34 +01:00
Oxan van Leeuwen
f2d677d51a Fix path to extra_scripts in platformio.ini (#3093) 2022-01-24 16:03:34 +13:00
Oxan van Leeuwen
c2ee0f0864 Rename WEBSERVER_PORT define to USE_WEBSERVER_PORT (#3102) 2022-01-24 00:34:38 +01:00
Oxan van Leeuwen
2a84db7f85 Refactor fan platform to resemble climate/cover platforms (#2848)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
Co-authored-by: rob-deutsch <robzyb+altgithub@gmail.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-01-23 22:21:54 +13:00
VitaliyKurokhtin
8187a4bce9 Command retain option for MQTT component (#3078) 2022-01-23 21:05:37 +13:00
Oxan van Leeuwen
97681d142e Enable readability-redundant-access-specifiers check (#3096)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-01-23 20:47:22 +13:00
Oxan van Leeuwen
b2430097f2 Enable readability-named-parameter check (#3098)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-01-23 20:39:07 +13:00
Oxan van Leeuwen
7da12a878f Enable readability-redundant-member-init check (#3097) 2022-01-23 20:34:43 +13:00
Oxan van Leeuwen
a31700e16f Enable readability-qualified-auto check (#3095) 2022-01-23 20:29:58 +13:00
Oxan van Leeuwen
7854522792 Enable readability-const-return-type check (#3099) 2022-01-23 20:28:00 +13:00
Pavel Skuratovich
a6a9ebfde2 slow_pwm: allow to restart a cycle on state change (#3004)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2022-01-22 23:08:55 +01:00
Plácido Revilla
c6cbe2748e Set the wrapped single light in light partition to internal (#3092) 2022-01-22 21:04:36 +01:00
Joshua Spence
f9a7f00843 Add restore_mode to fan component (#3051) 2022-01-22 20:41:58 +01:00
William Charlton
f0b183a552 Wake-on-LAN button (#3030)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
Co-authored-by: William Charlton <will.charlton1@icloud.com>
2022-01-23 00:13:46 +13:00
Jesse Hills
338ada5c9f Allow multiple configs for cd74hc4067 (#3085) 2022-01-22 19:33:15 +13:00
Jimmy Hedman
ef88f9923f Implement IPv6 sockets for lwIP (#3015)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2022-01-21 10:08:54 +01:00
Jesse Hills
6f8c7d9ec4 Add ektf2232 touchscreen support (#3027) 2022-01-21 15:45:49 +13:00
dependabot[bot]
ec769ccf72 Bump aioesphomeapi from 10.6.0 to 10.8.0 (#3081)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-01-21 00:09:12 +01:00
Jesse Hills
045952939e Support simple transparent pngs for display (#3035) 2022-01-21 11:16:18 +13:00
Jesse Hills
1c51cac5ba Add initial_run to regular lambda light effect (#3059) 2022-01-21 11:09:07 +13:00
buxtronix
ea11462e1e AM43: autoload "sensor" to avoid compile errors (#3077) 2022-01-20 13:33:42 +01:00
Otto Winter
62f9736b1d API: Expect a name for connections (#2533) 2022-01-20 12:03:32 +01:00
Jesse Hills
1f8a1f0046 Bump improv library version (#3072) 2022-01-20 15:21:44 +13:00
cwitting
172507acb5 Fix calibration parameter for bme680 humidity calculation (#3069) 2022-01-20 10:53:52 +13:00
Martin
434ab65c16 [modbus_controller] fix incorrect start address for number write (#3073) 2022-01-20 09:19:24 +13:00
Jesse Hills
cb5f793ede Add *.py.script files to distributions (#3074) 2022-01-20 08:33:13 +13:00
Oxan van Leeuwen
737188ae50 Fail hard if no random bytes available for encryption (#3067) 2022-01-18 14:29:57 +13:00
dependabot[bot]
db21731b14 Bump pytest-asyncio from 0.17.0 to 0.17.2 (#3064)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-01-18 00:19:08 +01:00
Martin
cdb4fa2487 [modbus_controller] add missing skip_updates (#3063) 2022-01-18 09:05:13 +13:00
Paulus Schoutsen
514204f0d4 bump dashboard to 20220116.0 (#3061) 2022-01-18 08:44:18 +13:00
Jesse Hills
45ac577c4d Add number setting to web_server/rest_api (#3055) 2022-01-17 12:31:44 +13:00
Oxan van Leeuwen
09402fdb22 Fix argument order in gitpod config file (#3058) 2022-01-16 23:40:27 +01:00
Oxan van Leeuwen
89e7448007 Remove deprecated attribute from virtual entity methods (#3056) 2022-01-16 23:40:15 +01:00
dependabot[bot]
1ea6f957bc Bump pytest-asyncio from 0.16.0 to 0.17.0 (#3047)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-01-16 23:19:21 +01:00
Oxan van Leeuwen
f44fca0a4b Rename post_build scripts to fix codeowners script (#3057) 2022-01-17 11:15:11 +13:00
Ohad Lutzky
52d2f62a57 Disable caching for binary download (#3054) 2022-01-17 11:14:45 +13:00
Paulus Schoutsen
2a8668ea60 Bump dashboard to 20220113.2 (#3041) 2022-01-12 23:22:19 -08:00
Paulus Schoutsen
cc0d433621 Add factory to download name (#3040) 2022-01-13 19:35:30 +13:00
Jesse Hills
1fe89fb364 Bump version to 2022.2.0-dev 2022-01-13 11:02:08 +13:00
Jesse Hills
ee58ad1ac0 Bump esphome-dashboard to 20220113.1 (#3038) 2022-01-13 10:52:57 +13:00
Jesse Hills
c0ff899812 Generate basic config for esphome-web devices (#3036) 2022-01-12 19:37:56 +13:00
Oxan van Leeuwen
d9c938de33 Introduce big- and little-endian integer types (#2997) 2022-01-12 16:50:03 +13:00
Martin
56547b3d50 [Modbus_controller] Fix duplicate cmd check (#3031) 2022-01-12 16:38:13 +13:00
Sympatron GmbH
5026bc7a78 Native ESP32 CAN support (#1629)
Co-authored-by: Guillermo Ruffino <glm.net@gmail.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-01-12 08:54:35 +13:00
Andreas Soehlke
27364ee72c Add cd74hc4067 multiplexer (#2431)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
Co-authored-by: asoehlke <git@soehlke.de>
2022-01-11 16:59:57 +13:00
Jesse Hills
ece71a0228 Run post scripts for factory binaries for flashing (#3003)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2022-01-11 15:24:26 +13:00
Oxan van Leeuwen
073828235f Deprecate virtual methods to set entity properties (#3021) 2022-01-10 13:32:39 +01:00
Stefan Grufman
41bcc8c0f4 Nexa 433MHz RF protocol (#2037)
Co-authored-by: Stefan Grufman <stefan.grufman@gmail.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-01-10 23:35:39 +13:00
Chris Nussbaum
a0ea2aae6e Add an action for pzemac to reset the total energy (#2480) 2022-01-10 23:13:39 +13:00
Jeffrey Borg
f34b46a621 Fix heatpumpir codegen min/max temperatures (#3025) 2022-01-10 16:48:05 +13:00
Lubos Horacek
7217a4f7a4 Fix display picture for nextion display (#3018) 2022-01-10 14:08:38 +13:00
Oxan van Leeuwen
6383eca54a Clean-up random helper functions (#3022) 2022-01-10 13:50:26 +13:00
Martin
e55bd1e559 [Modbus_controller] Fix binary sensor lambda (#3020) 2022-01-10 12:29:29 +13:00
MiKuBB
9e8b701dea Adding sdm_meter ability to report total power (#2959) 2022-01-10 12:23:01 +13:00
rsumner
a4431abea8 MCP3204 4-channel 12-bit ADC component (#2895)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-01-10 12:04:48 +13:00
Kamil Trzciński
5844c1767b Extend esp32_camera with requester to improve performance (#2813) 2022-01-10 11:58:49 +13:00
Sergey Dudanov
9a70bfa471 New Midea IR component, improvements and fixes (#2847)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-01-10 11:47:19 +13:00
Valentin Ochs
b406c6403c Create new kalman_combinator component (#2965) 2022-01-09 23:44:36 +01:00
Oxan van Leeuwen
499625f266 Convert is_callable to a backport of std::is_invocable (#3023) 2022-01-10 11:07:37 +13:00
Martin
6b773553fc Add turn_on/off trigger to slow_pwm (#2921)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-01-09 19:49:57 +01:00
Joshua Spence
15fe049a99 Add restore_mode to output switch (#3016)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2022-01-09 19:47:00 +01:00
stegm
e4555f6997 Fix register ranges in modbus controller (#2981) 2022-01-09 16:24:23 +01:00
Otto Winter
470071e0b0 Bump docker dependencies (#3019) 2022-01-08 14:15:05 +01:00
Jesse Hills
ea1be8e7bf Add MCP47A1 DAC output (#3014) 2022-01-08 21:35:55 +13:00
stegm
84a830195f Fix offset bug in modbus text sensor. (#3006) 2022-01-06 16:40:22 +01:00
Oxan van Leeuwen
e62c3e00c1 Bump PlatformIO to 5.2.4 and zeroconf to 0.37.0 (#3007) 2022-01-06 16:36:23 +01:00
Oxan van Leeuwen
07e790f900 Drop uint{32,64}_to_string() helper functions (#3009) 2022-01-06 16:36:11 +01:00
Oxan van Leeuwen
640142fc0c Introduce str_lower_case() and str_upper_case() helpers (#3008) 2022-01-06 16:35:59 +01:00
Oxan van Leeuwen
5c339d4597 Convert clamp() helper to backport of std::clamp() (#3010) 2022-01-07 00:56:10 +13:00
Oxan van Leeuwen
a4931f5d78 Clean-up reverse_bits helpers (#3011) 2022-01-07 00:54:58 +13:00
Martin
5e1e543b06 Add support for BMP388 / BMP 390 pressure and temperature sensor (#2716) 2022-01-06 15:01:50 +13:00
Pavel Skuratovich
df929f9445 Fix SlowPWM output switch at the end of period (#2984) 2022-01-05 21:31:11 +01:00
Oxan van Leeuwen
d8e719d1c4 Support clang-tidy for ESP32 variants (#3001) 2022-01-05 21:30:15 +01:00
mknjc
3067e482fc atc mithermometer: Add possibility to report signal strength (#3000) 2022-01-05 16:43:37 +13:00
Martin
ed5930e934 SGP40 - Reduce delay in measurement (#2996) 2022-01-05 10:05:19 +13:00
Oxan van Leeuwen
ffea3597f4 Set correct include_dir in platformio.ini (#2999) 2022-01-04 21:59:34 +01:00
Oxan van Leeuwen
193d3e0206 Fix clang-tidy with multiple ESP32 toolchains installed (#2998) 2022-01-05 08:34:17 +13:00
Gonzalo Paniagua Javier
c8f4fbb7dd Honor user set values for col/row start for INITR_MINI_160X80. (#2976)
If the caller sets a value for colstart and/or rowstart when using the INITR_MINI_160X80 model, use those values instead of the default 24 and 0.

After this patch devices with a 160x80 TFT like the m5stick C can set row/col start (26, 1 for m5stick) and avoid garbage lines showing in the display.
2022-01-04 11:02:53 +01:00
Snōwball
c855bc31b4 Add bl0940 component used by e.g. tuya devices (#1904)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2022-01-04 10:38:58 +01:00
Martin
b924b179ab Modbus: add binary output (#2931)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2022-01-04 10:19:18 +01:00
Jesse Hills
3df0fee3de Dont validate baud_rate for sim800l platform (#2945) 2022-01-04 10:16:40 +01:00
Jesse Hills
b601560e81 Apply --no-use-pep517 for docker images (#2985) 2022-01-04 10:16:02 +01:00
Oxan van Leeuwen
e5775cf812 Introduce bit_cast() backport (#2991) 2022-01-04 10:14:57 +01:00
Igor Scheller
26dd1f8532 Set UTF-8 encoding and version for prometheus /metrics (#2993) 2022-01-04 10:14:38 +01:00
Oxan van Leeuwen
5143a5b5c5 Use to_string() from STL when available (#2992) 2022-01-03 23:30:03 +01:00
Stefan Agner
15ce27992e Support ISR based pulse counter on ESP32-C3 (#2983) 2022-01-04 11:06:43 +13:00
Oxan van Leeuwen
dbc2812022 Improve PSRAM support (#2884) 2022-01-04 10:35:15 +13:00
Martin
dce3713f12 Fix HTTPRequestComponent::get_string return value (#2987)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2022-01-03 19:40:05 +01:00
Christopher Masto
f849d45bb6 Add logging for some Nextion errors that didn't have any (#2957) 2022-01-03 19:09:25 +01:00
arunderwood
8ad06fb9ea Add SH1107_128x64 to the ssd1306 component (#2967) 2022-01-03 19:08:16 +01:00
David Buezas
9124d9d6e6 Change unset ESPHOME_LOG_LEVEL fallback to NONE (#2982)
Co-authored-by: David Buezas <david.buezas@klarna.com>
2022-01-03 18:58:35 +01:00
Martin
45ebe51e4f Modbus: fix response parsing error for coil write (#2986) 2022-01-03 18:28:28 +01:00
Martin
407661d56b Fix compile error for idf projects with ArduinoJson 6 (#2979)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2022-01-03 18:19:01 +01:00
Paulus Schoutsen
998d4229af Use template path (#2961) 2022-01-03 08:57:09 -08:00
Stefan Agner
a02d2e2e11 Explicitly use overloaded begin() for I2C master initialization (#2978)
Arduino 2.0.1 and newer support slave and master mode. The two modes
have a begin() method with different signature:

```
// Slave Begin
bool TwoWire::begin(uint8_t addr, int sdaPin, int sclPin, uint32_t frequency)

// Master Begin
bool TwoWire::begin(int sdaPin, int sclPin, uint32_t frequency)
```

Use type casting to make sure that overloaded method for master mode
is used.
2022-01-03 16:37:21 +01:00
Stefan Agner
72fa68849f Don't use pyproject.toml for esphome build (#2980) 2022-01-03 22:11:28 +13:00
Jesse Hills
33f17f75a0 Upgrade ArduinoJson to 6.18.5 and migrate code (#2844) 2022-01-01 22:31:43 +13:00
MrEditor97
23edb18d7e INA260 Current and Power Sensor support (#2788) 2021-12-31 22:08:49 +13:00
arunderwood
07ff3a853f Add pin aliases for featheresp32-s2 (#2970) 2021-12-31 20:11:28 +13:00
Sebastian Raff
2cf36bdb46 Fix switch log state if inverted (#2960) 2021-12-30 16:05:31 +13:00
Jesse Hills
f859b346a6 Remove -e for hassio images (#2964) 2021-12-30 10:42:22 +13:00
marsjan155
cb0677cafe ST7920 ESP32 fix (#2962)
Co-authored-by: Marcin Depa <m.depa91@gmail.com>
2021-12-30 10:34:30 +13:00
Daniel Hyles
c6956527d1 Remove Content-Length header from camera snapshot response (#2860)
* Update camera_web_server.cpp

Removed the duplicated CONTENT_LENGTH header

* Update camera_web_server.cpp

* Update camera_web_server.cpp
2021-12-28 09:32:17 +13:00
Jesse Hills
72c6bfaa50 Revert "Disable nightly dev build" (#2944) 2021-12-23 09:38:43 +13:00
Jesse Hills
7927b5f624 Workaround installing as editable package not working (#2936) 2021-12-23 08:43:17 +13:00
Jesse Hills
b7aad39daf Only allow internal pins for dht sensor (#2940) 2021-12-23 08:31:56 +13:00
Jesse Hills
f48de6dd43 Disable nightly dev build (#2943) 2021-12-22 23:02:58 +13:00
Jesse Hills
79d73d8f8b Add option to load docker image when building (#2938) 2021-12-22 20:49:04 +13:00
Jesse Hills
cc5947467f Require arduino in webserver for better validation (#2941) 2021-12-22 20:08:54 +13:00
George
e152f128c8 Change HDC1080 init instruction failure from error to warning (#2927)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2021-12-22 15:35:01 +13:00
Jesse Hills
99bd808ebe Update curl package version in docker (#2939) 2021-12-22 15:27:34 +13:00
Jan Čermák
beb5f3dc9d bang_bang: respect set cool- and heat-only modes (#2926) 2021-12-22 15:27:16 +13:00
Jesse Hills
f5c3b3446f Support inkplate10 (#2937) 2021-12-22 12:56:52 +13:00
jsuanet
f431c7402f Add shutdown and safe_mode button (#2918)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
Co-authored-by: Jos Suanet <jos@suanet.net>
2021-12-20 22:25:36 +01:00
Oxan van Leeuwen
4907e6f6d7 Fix MQTT button press action (#2917) 2021-12-21 08:19:20 +13:00
Jonas De Kegel
1ccee86705 Fix tm1637 bootloop (#2929) 2021-12-20 18:06:04 +01:00
Jonas De Kegel
542fb2175b Support inverted tm1637 display (#2878)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2021-12-20 09:30:35 +01:00
Frank Langtind
6ec9cfb044 Add Tuya Number support (#2765) 2021-12-20 14:35:10 +13:00
Benny de Leeuw
66e0ff8392 Add growatt modbus sensor (#2922)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-12-20 14:30:23 +13:00
Martin
1fb0a7109d Modbus: use multiply for publishing number (#2916) 2021-12-15 22:38:23 +13:00
sveip
192eb49589 ESP32 CAM add Automatic Exposure Control option (#2892)
Co-authored-by: Peter <psv@tsat.net>
Co-authored-by: Carlos Garcia Saura <CarlosGS@users.noreply.github.com>
2021-12-15 07:46:43 +13:00
Petr Vraník
5d70ff702b quantile filter support (#2900)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
Co-authored-by: pvranik <petr.vranik@mgm-tp.com>
2021-12-15 07:43:42 +13:00
jddonovan
a7b05db2a1 Adding Pascal unit to constants (#2914) 2021-12-15 07:39:50 +13:00
wilberforce
45e346cf1b Allow button POST on press from web server (#2913) 2021-12-14 15:08:01 +13:00
dependabot[bot]
80e2bfada3 Bump black from 21.11b1 to 21.12b0 (#2879)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2021-12-13 19:15:49 +01:00
Martin
16e7bd0388 fix multi-line comment warning/error (#2891) 2021-12-13 19:15:22 +01:00
Oxan van Leeuwen
b3fb35783e Set text sensor state property to filter output (#2893) 2021-12-13 15:21:09 +13:00
myhomeiot
a79c6aa9e0 Added access to ble_scan_result_evt_param as get_scan_result (#2854) 2021-12-13 13:08:18 +13:00
Martin
4bb58b2de9 Add gpio 12 to strapping pin list (#2902) 2021-12-13 11:03:08 +13:00
Jesse Hills
4e10881331 Log the actual value in modbus number (#2901) 2021-12-13 10:28:19 +13:00
Ben Owen
cec4a81e14 Add reset_duration option for waveshare epaper HAT rev 2.1 (#1481)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-12-13 10:27:11 +13:00
Carlos Garcia Saura
da45923d05 Turn verbose a debug statement in bme280 (#2906) 2021-12-13 09:32:37 +13:00
Carlos Garcia Saura
31a61b598b Reduce timing noise in duty_cycle (#2881) 2021-12-13 09:30:47 +13:00
tony
9c0506592b Add light.on_state trigger (#2868) 2021-12-13 09:19:57 +13:00
Oxan van Leeuwen
beeb0c7c5a Introduce hex parsing & formatting helper functions (#2882) 2021-12-13 09:15:23 +13:00
Martin
b2f05faee0 Move i2c scan to setup (#2869)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2021-12-13 09:12:50 +13:00
Jesse Hills
8375e1d64d Bump esphome-dashboard to 20211211.0 (#2904) 2021-12-11 21:03:41 +13:00
Keith Burzinski
cf5193d3e5 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 20:03:22 +13:00
Guillermo Ruffino
c490388e80 Modbus number/output use write single (#2896)
Co-authored-by: Martin <25747549+martgras@users.noreply.github.com>
2021-12-10 09:44:43 +13:00
Jesse Hills
24ec5a6e9d Fix published state for modbus number (#2894) 2021-12-10 09:32:34 +13:00
Oxan van Leeuwen
6df1d5222d Drop unused xSemaphoreWait define (#2888) 2021-12-08 12:46:36 +13:00
Jesse Hills
58fb7a02f6 Bump esphome-dashboard to 20211208.0 (#2887) 2021-12-08 12:42:50 +13:00
Jesse Hills
3d51ac8df0 Use new platform component config blocks for wizard (#2885) 2021-12-08 09:22:03 +13:00
Oxan van Leeuwen
6fe4ff7f85 Drop len parameter from parse_number() (#2883) 2021-12-08 08:46:25 +13:00
Yuval Brik
2253d4bc16 Support different run duration for non-timer wakeup (#2861) 2021-12-06 23:30:27 +01:00
Carlos Garcia Saura
e5cc19de43 Feed watchdog while setting up OTA (#2876) 2021-12-06 23:26:06 +01:00
Jesse Hills
5404617d43 Bump esphome-dashboard to 20211207.0 (#2877) 2021-12-07 07:41:40 +13:00
Oxan van Leeuwen
12467a18e6 Feed watchdog when no component loops (#2857) 2021-12-07 07:24:20 +13:00
Jesse Hills
1db7043a4d Allow wizard to specify secrets (#2875) 2021-12-06 20:58:51 +13:00
Jesse Hills
49932747b3 Adopt using wifi secrets that should exist at this point (#2874) 2021-12-06 20:57:56 +13:00
Jesse Hills
55db190875 Add endpoint to fetch secrets keys (#2873) 2021-12-06 20:15:34 +13:00
Massimiliano Ravelli
71fe2f7ed3 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-06 20:01:50 +13:00
Oxan van Leeuwen
ffc112c9d0 Don't disable idle task WDT when it's not enabled (#2856) 2021-12-06 20:01:14 +13:00
Oxan van Leeuwen
d3e48e296f Fix MCP23x17 not disabling pullup after config change (#2855) 2021-12-06 19:59:50 +13:00
Martin
14f6ae75ea SPS30 : fix i2c read size (#2866) 2021-12-06 19:58:26 +13:00
Carlos Garcia Saura
c84efe64d3 ADC: Turn verbose the debugging "got voltage" (#2863) 2021-12-06 19:56:53 +13:00
Martin
10e89a7dbb tlc59208f : fix compilation error (#2867) 2021-12-06 19:54:46 +13:00
Jesse Hills
ef44acbf10 Bump esphome-dashboard to 20211206.0 (#2870) 2021-12-06 19:42:49 +13:00
dependabot[bot]
06da540ab0 Bump pylint from 2.12.1 to 2.12.2 (#2858)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-05 13:29:08 +01:00
Oxan van Leeuwen
40c017fd54 Update ota_component.cpp (#2852) 2021-12-03 07:52:56 +13:00
Jesse Hills
f0bcf81a98 Add a simple helper to remap values (#2850) 2021-12-02 09:23:11 +01:00
Jesse Hills
6a0b343289 Bump version to 2022.1.0-dev 2021-12-02 19:38:49 +13:00
Martin
9ca4e8f32a modbus_controller: bugfix: enable overriding calculated register size (#2845)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-12-02 15:45:11 +13:00
Alexandre-Jacques St-Jacques
1b88b7a166 Fix wifi not working with manual_ip using esp-idf (#2849) 2021-12-02 15:33:48 +13:00
Paul Nicholls
caf352ff06 Tuya Cover improvements (#2637) 2021-12-02 15:26:56 +13:00
Oxan van Leeuwen
54106179a1 Set ESP32 watchdog to loop task (#2846) 2021-12-02 09:05:42 +13:00
Oxan van Leeuwen
607601b3a4 Enable a bunch of clang-tidy checks (#2149) 2021-12-02 09:03:51 +13:00
Oxan van Leeuwen
f58828cb82 Support setting manual_ip under networks option (#2839) 2021-12-02 08:55:27 +13:00
Leon Loopik
11330af05f Expand uart invert feature to ESP8266 (#1727) 2021-12-01 20:31:04 +01:00
Oxan van Leeuwen
fbe1bca1b9 Fix compilation using subprocesses (#2834) 2021-12-01 17:37:24 +01:00
Mark Dietzer
24a5325db3 Declare arch_get_cpu_cycle_count for esp8266 as IRAM (#2843) 2021-12-01 10:01:15 +01:00
Yuval Brik
1ec3140759 ESP32 Deep Sleep: correct level value (#2812)
Upon registering for ESP32 deep sleep, DeepSleepComponent::begin_sleep
calculates the level value to wake up on.
As part of PR #2303, the level was changed to be based on `inverted`
instead of `!inverted`:
Before:
1e8e471dec/esphome/components/deep_sleep/deep_sleep_component.cpp (L76)
After:
2b04152482/esphome/components/deep_sleep/deep_sleep_component.cpp (L80)

The level argument to `esp_sleep_enable_ext0_wakeup(pin, level)` [0]
should be 0 when the inverted property is true (low triggers wakeup),
and 1 when inverted property is false (high triggers wakeup).

Also revert the changes of #2644.

[0]
https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/sleep_modes.html#_CPPv428esp_sleep_enable_ext0_wakeup10gpio_num_ti
2021-12-01 09:38:58 +01:00
Oxan van Leeuwen
ca8db7696e Don't enable namespace comment clang-tidy check twice (#2830) 2021-12-01 17:21:19 +13:00
Oxan van Leeuwen
c9190574a9 Fix CI check for Windows line endings (#2831) 2021-12-01 17:14:25 +13:00
Oxan van Leeuwen
bfeb0b3639 Add problem matcher for Python formatting errors (#2833) 2021-12-01 17:12:14 +13:00
Oxan van Leeuwen
cbc1334b8d Fix compile warning in Tuya automations (#2837) 2021-12-01 17:11:21 +13:00
mechanarchy
08cbb97ec9 Allow Git credentials to be loaded from secrets (#2825) 2021-12-01 17:10:25 +13:00
Jesse Hills
5719cc1a24 Bump esphome-dashboard to 20211201.0 (#2842) 2021-12-01 16:54:30 +13:00
Jesse Hills
d9513e5ff2 Number mode (#2838) 2021-12-01 08:11:38 +13:00
puuu
b5a0e8b2c0 Implement unit_of_measurement for number component (#2804)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-11-30 16:20:59 +01:00
Jesse Hills
b32b918936 Button device class (#2835) 2021-11-30 16:18:21 +01:00
dependabot[bot]
0f47ffd908 Bump aioesphomeapi from 10.2.0 to 10.6.0 (#2840) 2021-11-30 16:17:48 +01:00
Carlos Garcia Saura
cd018ad3a5 Burst read for BME280, to reduce spurious spikes (#2809) 2021-11-30 16:12:52 +01:00
Adrián Panella
24dfecb6f0 cse7766: add energy sensor (#2822) 2021-11-30 16:08:00 +01:00
Oxan van Leeuwen
ab027a6ae2 Fix too-broad matcher for custom CI script (#2829) 2021-11-30 09:35:52 +01:00
Keith Burzinski
556d071e7f Fix 8266 SPI Clock Polarity Setting (#2836) 2021-11-30 19:30:45 +13:00
dentra
939fb313df Tuya text_sensor and raw data usage (#1812) 2021-11-30 08:08:52 +13:00
Jesse Hills
b5639a6472 Add support for button entities (#2824) 2021-11-30 08:00:51 +13:00
definitio
f50e40e0b8 Fix custom mode_state_topic (#2827) 2021-11-29 18:09:09 +01:00
mechanarchy
6f07421911 Optionally show internal components on the web server (#2627)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2021-11-29 16:52:20 +01:00
Maurice Makaay
adf48246a9 Improve DSMR read timeout handling (#2699) 2021-11-29 16:40:53 +01:00
anatoly-savchenkov
cae283dc86 Fixed data type inside fast_random_8() routine (#2818) 2021-11-29 08:31:15 +13:00
Conclusio
7afcb0fb04 Add delay to improve stability (#2793) 2021-11-29 08:13:42 +13:00
Dave T
10f830c3ef Correct bitmask for third color (blue) scaling. (#2817) 2021-11-29 08:12:40 +13:00
Carlos Garcia Saura
7a5c3aa7ed Fix compilation error for WPA enterprise in ESP-IDF (#2815) 2021-11-29 08:06:53 +13:00
Oxan van Leeuwen
2b50406856 Fix parsing of multiple values in EZO sensor (#2814)
Co-authored-by: Lydia Sevelt <LydiaSevelt@gmail.com>
2021-11-29 08:02:10 +13:00
Oxan van Leeuwen
10a2a7e0fc Fix parsing numbers in Anova (#2816) 2021-11-29 08:00:29 +13:00
Oxan van Leeuwen
7a564b222d Make clang-tidy suggest stdint.h int types (#2820) 2021-11-29 07:59:30 +13:00
Maurice Makaay
671d68bc2c Add missing nvs_flash_init() to ESP32 preferences code (#2805)
Co-authored-by: Maurice Makaay <mmakaay1@xs4all.net>
2021-11-26 21:25:58 +01:00
Oxan van Leeuwen
5946c37925 Fix usage of deprecated climate method in anova (#2801) 2021-11-26 09:16:39 +01:00
Martin
17a37b1de9 Modbus_controller: Add custom command. (#2680) 2021-11-26 12:48:52 +13:00
Adrián Panella
e7827a6997 total_daily_energy: allow to disable restore mode (#2795) 2021-11-25 22:35:36 +01:00
Jesse Hills
2347e043a9 Cancel previous workflows for PRs and branches (#2800) 2021-11-25 22:02:39 +01:00
Oxan van Leeuwen
00965fe19e Consistently format errors in CI scripts (#2762) 2021-11-26 09:54:11 +13:00
Oxan van Leeuwen
9681dfb458 Correct constant for dynamic I2S bus in NeoPixelBus (#2797) 2021-11-26 09:37:27 +13:00
Oxan van Leeuwen
5e631bc6ba Only match GCC warnings from ESPHome source files in CI (#2756) 2021-11-26 09:36:42 +13:00
Oxan van Leeuwen
b5f660398c Add map filter for text sensors (#2761) 2021-11-26 09:35:33 +13:00
Oxan van Leeuwen
d50bdf619f Cache virtualenv instead of pip cache between CI runs (#2759) 2021-11-26 09:29:10 +13:00
Oxan van Leeuwen
4e448b21ff Drop obsolete comment from CI workflow file (#2758) 2021-11-26 09:27:53 +13:00
Oxan van Leeuwen
2a78c2970d Fix CI cache key for test3.yaml compile (#2757) 2021-11-26 09:27:34 +13:00
Oxan van Leeuwen
3637be251e Fix parsing numbers from null-terminated buffers (#2755) 2021-11-26 09:00:49 +13:00
dependabot[bot]
2aea27d272 Bump pylint from 2.11.1 to 2.12.1 (#2798) 2021-11-25 20:34:11 +01:00
Maurice Makaay
ceb9b1d1ff Allow empty UART debug: option, logging in hex format by default (#2771)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
Co-authored-by: Maurice Makaay <account-github@makaay.nl>
2021-11-25 11:51:56 +13:00
Martin
ccfa1e23f0 Add support for sdp8xx (#2779) 2021-11-25 11:28:19 +13:00
rsumner
290da8df2d Fix LEDC resolution calculation on ESP32-C3/S2/S3 (#2794)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-11-25 11:22:51 +13:00
Martin
4b1d73791d remove LEDC_HIGH_SPEED_MODE for C3, S2, S3 (#2791) 2021-11-25 08:06:08 +13:00
Jesse Hills
7e8012c1a0 Allow specifying the dashboard bind address (#2787) 2021-11-25 07:59:32 +13:00
Maurice Makaay
15cd602e8b Add support for P1 Data Request pin control (#2676) 2021-11-23 09:34:10 +01:00
krunkel
598f5b241f Remove unnecessary write in AHT10 update (#2675) 2021-11-23 09:26:16 +01:00
dependabot[bot]
335e69e6cd Bump black from 21.10b0 to 21.11b1 (#2760) 2021-11-23 09:24:28 +01:00
Paul Monigatti
05fe5db030 Relax the icon validator to allow non-mdi icons (#2764) 2021-11-23 09:21:14 +01:00
Andreas Hergert
710096b1c6 Fixed wrong setup of tc9548a (#2766) 2021-11-23 09:20:55 +01:00
Dave T
07b882c801 Fix distorted gif frames when resizing (#2774) 2021-11-23 09:20:36 +01:00
cvwillegen
3e5331a263 Prettier date time display after time sync (#2778) 2021-11-23 09:20:20 +01:00
Oxan van Leeuwen
897277992b Introduce str_snprintf helper function (#2780) 2021-11-23 20:30:49 +13:00
Samuel Sieb
1424091ee5 Remove floating point ops from the ISR (#2751)
Co-authored-by: Samuel Sieb <samuel@sieb.net>
2021-11-22 12:11:36 +13:00
Kamil Trzciński
61ec16cdfc esp32_camera_web_server: Improve support for MotionEye (#2777) 2021-11-22 12:09:11 +13:00
Dave T
e5cb5756aa Fix frame scaling for animated gifs (#2750) 2021-11-18 23:20:32 +01:00
Maurice Makaay
9e1c3e8f01 Allow UART debug configuration with no after: definition (#2753) 2021-11-18 22:41:26 +01:00
Martin
448e1690aa Add retry handler (#2721)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2021-11-18 11:59:40 +13:00
Martin
8267f01ccd Remove arduino dependency from hm3301 (#2745) 2021-11-18 08:03:46 +13:00
Sergey V. DUDANOV
6f9439e1bc Fix byte order in NEC protocol implementation (#2534) 2021-11-17 18:35:50 +01:00
spattinson
06994c0dfc Change LUT for ttgo t5 2.13inch to improve partial refresh (#2475) 2021-11-17 18:28:36 +01:00
Maurice Makaay
dee5d639e2 Add max_telegram_length option to dsmr (#2674)
Co-authored-by: Maurice Makaay <mmakaay1@xs4all.net>
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2021-11-17 18:24:02 +01:00
Jesse Hills
df6730be55 Move to use improv lib from platformio (#2741) 2021-11-17 18:23:17 +01:00
Franck Nijhof
6c1ef398bb Re-instate device class update for binary sensors (#2743) 2021-11-17 23:28:31 +13:00
Evgeny
0469e19f54 Fix HM3301 AQI index calculator (#2739) 2021-11-17 09:52:40 +01:00
Jesse Hills
dbcfa7b599 Remove duplicated const data in esp8266 boards (#2740) 2021-11-17 16:22:38 +13:00
rotarykite
df68403b6d 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 07:57:03 +13:00
Ryan Hoffman
57bdc2b885 Add ble_client binary_output (#2200)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-11-17 07:30:42 +13:00
Ryan Hoffman
f565ff5def 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-16 18:53:36 +01:00
H. Árkosi Róbert
8ece639987 Change log level from DEBUG to INFO for sniffing services (#2736)
Sniffing for codes only happens if the user deliberately asked for it with the related service through HA - to find out the codes present in the air. The resulted data shouldn't be printed out only in debug mode, as this is information required to be known on demand for later use, not actually a debug info. Changing log level from DEBUG to INFO for sniffing services has two benefits:
- no need to run firmware with DEBUG enabled for occasional sniffing with devices in production (no need to flash back and forth with different log levels set just for this reason)
- if the user still wants DEBUG enabled, sniffed data appears in different color, it's easier to find between the lines.
2021-11-16 23:28:12 +13:00
Jan Harkes
b35f509784 Allow for subsecond sampling of hmc5883l (#2735) 2021-11-16 09:16:43 +01:00
Jesse Hills
f1954df573 Fix zeroconf time comparisons (#2733)
Co-authored-by: J. Nick Koston <nick@koston.org>
2021-11-16 12:47:06 +13:00
Jesse Hills
9e4fa5dcf1 Improv serial/checksum changes (#2731)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2021-11-16 11:02:45 +13:00
Jesse Hills
0809673ba9 Add zeroconf as a direct dependency and lock the version (#2729) 2021-11-16 09:53:52 +13:00
cvwillegen
b386284180 Ignore secrets.yaml on command line (#2715) 2021-11-15 20:06:55 +01:00
Krzysztof Białek
515519bc87 Provide an option to select MQTT unique_id generator (#2701)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2021-11-15 15:49:18 +01:00
Oxan van Leeuwen
5404163be0 Clean-up MAC address helpers (#2713) 2021-11-15 15:48:16 +01:00
Alexandre-Jacques St-Jacques
0b193eee43 Remove unnecessary duplicate touch_pad_filter_start (#2724) 2021-11-15 11:58:22 +13:00
Jesse Hills
7333123ba4 Fix indentation of write_lambda for modbus_controller number (#2722) 2021-11-15 10:59:48 +13:00
Sergey V. DUDANOV
d99c5ed890 RemoteTransmitter fix. Bug from version 2021.10. Some changes. (#2706) 2021-11-15 10:40:35 +13:00
Oxan van Leeuwen
04740fbcbb Install test requirements in lint Docker image (#2719) 2021-11-15 10:04:43 +13:00
Oxan van Leeuwen
6a7440f7d3 Feed WDT between doing ESP32 touchpad measurements (#2720) 2021-11-15 09:45:25 +13:00
Oxan van Leeuwen
14299bb2cc Drop unused constants from const.py (#2718) 2021-11-15 08:07:58 +13:00
Oxan van Leeuwen
66cebfc992 Restore InterruptLock on wifi-less ESP8266 (#2712) 2021-11-15 08:05:11 +13:00
Maurice Makaay
108b8e6705 Fix rom/rtc.h deprecation compile warning for debug component (#2520) 2021-11-14 16:17:13 +01:00
Clifford Roche
4eaa6afa4d Add greeyac protocol to IR Climate / HeatpumpIR (#2694) 2021-11-14 16:11:21 +01:00
Krzysztof Białek
f643a46bbf Allow setting custom command_topic for Select and Number components (#2714) 2021-11-14 14:59:34 +01:00
Sergey V. DUDANOV
aae63a7ff3 Add climate on_state trigger (#2707) 2021-11-13 15:42:15 +01:00
NeoAcheron
582567696e pmsx003: add support for PMS5003S device (#2710) 2021-11-13 15:14:23 +01:00
Jesse Hills
2e0c89409d Bump ESPAsyncWebServer to 2.1.0 (#2686) 2021-11-13 21:22:32 +13:00
lcavalli
7bb7456a8b Update device classes for binary sensors (#2703) 2021-11-12 13:17:10 +13:00
Jesse Hills
0372e12b81 Defines tidy (#2696)
* Move webserver defines inside arduino block

* Move esp8266 flash define

* Move prometheus define
2021-11-11 10:56:54 +01:00
Jesse Hills
a6873c1520 Only allow prometheus when using arduino (#2697) 2021-11-11 10:56:35 +01:00
Jesse Hills
f11220da3a Remove my.ha links from improv (#2695) 2021-11-11 15:15:37 +13:00
Oxan van Leeuwen
bb9793d5b7 Enable addressable light power supply based on raw values (#2690) 2021-11-11 11:53:25 +13:00
Maurice Makaay
e99af991ec 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 11:34:17 +13:00
Carlos Garcia Saura
abf3708cc2 [remote_transmitter] accurate pulse timing for ESP8266 (#2476) 2021-11-11 11:28:45 +13:00
Jesse Hills
4395d6156d Fix template number initial value being NaN (#2692) 2021-11-10 23:24:48 +01:00
Jesse Hills
04ba53c870 Bump version to 2021.12.0-dev 2021-11-11 10:10:05 +13:00
anatoly-savchenkov
f310cacd41 [ms5611] Re-implement conversion from ADC readings to sensor values (#2665) 2021-11-10 22:01:47 +01:00
Jesse Hills
5ff7c8418c Implement Improv via Serial component (#2423)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2021-11-11 08:55:45 +13:00
Laszlo Gazdag
0bdb48bcac Make OTA function switchable in web_server component (#2685)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-11-11 08:31:22 +13:00
Oxan van Leeuwen
99c775d8cb Introduce encode_value/decode_value() template functions (#2662) 2021-11-10 19:44:01 +01:00
Oxan van Leeuwen
4d43396835 Clean-up string sanitation helpers (#2660) 2021-11-10 19:42:41 +01:00
TVDLoewe
92321e219a Max7219digit multiline (#1622)
Co-authored-by: Otto winter <otto@otto-winter.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-11-11 07:41:04 +13:00
Oxan van Leeuwen
c422b2fb0b Introduce byteswap helpers (#2661)
* Backport std::byteswap() in helpers.h

* Introduce convert_big_endian() function

* Use convert_big_endian() in i2c byte swap functions
2021-11-10 19:40:18 +01:00
Otto Winter
8aa72f4c1e Neopixelbus redo method definitions (#2616) 2021-11-11 07:35:31 +13:00
Oxan van Leeuwen
d8e33c5a69 Add repeat action for automations (#2538)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-11-11 07:30:07 +13:00
Oxan van Leeuwen
15f9677d33 Introduce parse_number() helper function (#2659) 2021-11-11 07:15:06 +13:00
Carlos Garcia Saura
219b225ac0 [ESP32 ADC] Add option for raw uncalibrated output (#2663) 2021-11-10 19:12:57 +01:00
Oxan van Leeuwen
2ac232e634 Add missing hal.h include in esp32_camera_web_server (#2689) 2021-11-10 19:09:10 +01:00
Sam Hughes
710866ff4e CAP1188 Capacitive Touch Sensor Support (#2653) 2021-11-10 18:52:49 +01:00
Martin
662773b075 modbus_controller: remove hard coded register size (#2654) 2021-11-10 16:24:44 +13:00
Carlos Garcia Saura
875b803483 Remove "delay_microseconds_accurate()" and improve systemwide delayMicroseconds() (#2497) 2021-11-10 16:22:00 +13:00
Guillermo Ruffino
6e5cfac927 fix rc switch protocol 6 (#2672) 2021-11-10 16:15:15 +13:00
Duncan Findlay
97eaf3d4a1 Set up output_switch at priority DATA instead of HARDWARE. (#2648) 2021-11-10 16:12:20 +13:00
cvwillegen
366552a969 Remote base add pronto protocol (#2619) 2021-11-10 16:11:35 +13:00
Guillermo Ruffino
57b07441a1 fix esp32 rmt receiver item array length (#2671) 2021-11-10 13:15:02 +13:00
Kamil Trzciński
fb57ab0add Add esp32_camera_web_server: to expose mjpg/jpg images (#2237)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-11-10 13:10:07 +13:00
Jesse Hills
d6717c0032 Fix dashboard imports for adoption (#2684) 2021-11-10 08:38:20 +13:00
ychieux
f72389147d SSD1306_base: Add support for 64x32 size and fix flip functions (#2682)
* Add support for SSD1306 OLED display 0.42inch 64x32 and fix a typo in __init__.py preventing flip functions to operate as intended

* convert tab to spaces

* fix typo on filename for __init__.py
2021-11-09 18:47:19 +01:00
Jesse Hills
a509f6ccd2 Fix when package url has no branch/ref (#2683) 2021-11-09 17:14:08 +13:00
Martin
add484a2ea Fix gpio validation for esp32 variants (#2609)
Co-authored-by: Otto Winter <otto@otto-winter.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-11-09 07:29:28 +13:00
Paul Monigatti
a17a6d5346 Add HA Entity Category support to MQTT (#2678) 2021-11-08 10:03:30 +01:00
Maurice Makaay
be9439f10d Fix for encrypted DSMR regression (#2679)
Co-authored-by: Maurice Makaay <account-github@makaay.nl>
2021-11-07 20:39:16 -03:00
Maurice Makaay
96a50f5c6b Add SPI lib for ESP8266 and only add lib for ESP32 when using Arduino (#2677)
* Add SPI lib for ESP8266 and only add lib for ESP32 when using Arduino

* Make inclusion of the SPI library unconditional

As suggested by @Oxan. Because the component requires Arduino anyway, there is no need to make the inclusion conditional.

Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>

* Fix Python lint issue

Co-authored-by: Maurice Makaay <account-github@makaay.nl>
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2021-11-07 19:31:41 +01:00
Jesse Hills
3c0414c420 Add Entity categories for Home Assistant (#2636) 2021-11-08 07:24:52 +13:00
Maurice Makaay
b450d4c734 Fix CRC error during DSMR chunked message reading (#2622)
* DSMR chunk size from 50 to 500

* Still a few CRC errors with 500, upping to 1024.

* Adding timers to measure how long processing DSMR takes

* Handle chunked output from smart meter.

* Cleaning up and commenting the new chunk handling code

* Remove debug code.

* Fixing clang-tidy issues.

* Implementing chunked reading support for encrypted telegrams.

* Remove redundant extra delay for encrypted reader

* Beware not to flush crypted telegram headers

* Use insane data timeout for testing

* Improve logging

* Make clang-tidy happy

Co-authored-by: Maurice Makaay <mmakaay1@xs4all.net>
Co-authored-by: Maurice Makaay <account-github@makaay.nl>
2021-11-06 18:52:04 -03:00
Guillermo Ruffino
d536509a63 Allow esp8266 to compile with no wifi (#2664) 2021-11-05 10:52:38 +13:00
Tim Niemueller
11f1e28139 Make per-loop display clearing optional (#2626)
Currently, in each loop during DisplayBuffer::update_() the display is
cleared by calling DisplayBuffer::clear().

This prevents more efficient display usages that do not render the
screen in each loop, but only if necessary. This can be helpful, for
example, if images are rendered. This would cause the loop time to be
exceeded frequently.

This change adds a new optional flag "auto_clear" that can be used to
control the clearing behavior. If unset, the DisplayBuffer defaults to
enabled auto clearing, the current behavior and thus backward compatible.

This flag applies to displays that use DisplayBuffer.

Example excerpt:
globals:
  - id: state
    type: bool
    restore_value: no
    initial_value: "false"
  - id: state_processed
    type: bool
    restore_value: no
    initial_value: "false"

switch:
  - platform: template
    name: "State"
    id: state_switch
    lambda: |-
      return id(state);
    turn_on_action:
      - globals.set:
          id: state
          value: "true"
      - globals.set:
          id: state_processed
          value: "false"
    turn_off_action:
      - globals.set:
          id: state
          value: "false"
      - globals.set:
          id: state_processed
          value: "false"

display:
  - platform: ili9341
    # ...
    auto_clear_enabled: false
    lambda: |-
      if (!id(state_processed)) {
        it.fill(COLOR_WHITE);
        if (id(state)) {
          it.image(80, 20, id(image1));
        } else {
          it.image(80, 20, id(image2));
        }
        id(state_processed) = true;
      }

Co-authored-by: Tim Niemueller <timdn@google.com>
2021-11-03 17:56:09 +01:00
niklasweber
379c3e98f5 Add restore_mode to rotary_encoder (#2643) 2021-11-03 07:32:24 +13:00
dependabot[bot]
5ea77894b7 Bump black from 21.9b0 to 21.10b0 (#2650)
Bumps [black](https://github.com/psf/black) from 21.9b0 to 21.10b0.
- [Release notes](https://github.com/psf/black/releases)
- [Changelog](https://github.com/psf/black/blob/main/CHANGES.md)
- [Commits](https://github.com/psf/black/commits)

---
updated-dependencies:
- dependency-name: black
  dependency-type: direct:production
...

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-01 21:03:23 +01:00
Carlos Garcia Saura
d54b4e7c44 Fix for noise in pulse_counter and duty_cycle components (#2646) 2021-11-02 08:27:57 +13:00
Jesse Hills
d8b3af3815 Expose webserver_port to the native API (#2640) 2021-11-01 09:33:04 +13:00
Otto Winter
2b04152482 Fix deep sleep invert_wakeup mode (#2644) 2021-10-31 16:07:06 +01:00
Paul Monigatti
331a3ac387 Add option to use MQTT abbreviations (#2641) 2021-10-31 15:34:08 +13:00
Geoffrey Van Landeghem
7eee3cdc7f convert SCD30 into Component, polls dataready register (#2308)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-10-31 15:29:22 +13:00
dependabot[bot]
b12c7432e0 Bump tzlocal from 4.0.2 to 4.1 (#2645) 2021-10-30 20:12:57 +02:00
Ed
696643d037 BH1750: Fix a too high default H-res2 mode value (#2536) 2021-10-29 11:51:57 +13:00
dependabot[bot]
7e54f97003 Bump aioesphomeapi from 10.1.0 to 10.2.0 (#2642)
Bumps [aioesphomeapi](https://github.com/esphome/aioesphomeapi) from 10.1.0 to 10.2.0.
- [Release notes](https://github.com/esphome/aioesphomeapi/releases)
- [Commits](https://github.com/esphome/aioesphomeapi/compare/v10.1.0...v10.2.0)

---
updated-dependencies:
- dependency-name: aioesphomeapi
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-28 21:24:48 +02:00
Arturo Casal
77dbf84e55 Add support for CSE7761 sensor (#2546)
* Add CSE7761 sensor support

* CSE7761: Added test at test3.yaml

* CSE7761: changed string style

* CSE7761: fixed cpp lint

* CSE7761: Added codeowners

* Lots of code cleanup

* Revert incorrect setup_priority suggestion

* Added error log in read with retries.

Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>

* Improved log messages

Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2021-10-28 20:58:48 +02:00
Martin
2350c5054c use update_interval for sntp synchronization (#2563)
* use update_interval for sntp synchronization

* revert  override of default interval
2021-10-28 20:57:39 +02:00
Jesse Hills
73accf747f Allow cloning/fetching Github PR branches in external_components (#2639) 2021-10-29 07:12:05 +13:00
Alex Iribarren
0d3e6b2c4c Expose web_server port via the API (#2467) 2021-10-28 11:46:55 +13:00
dependabot[bot]
b3d7cc637b Bump aioesphomeapi from 10.0.3 to 10.1.0 (#2638)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-28 09:17:30 +13:00
Sean Brogan
2147bcbc29 Remove autoload of xiaomi_ble and ruuvi_ble (#2617) 2021-10-28 09:16:12 +13:00
niklasweber
980c2d4cae Add publish_initial_value option to rotary encoder (#2503) 2021-10-28 08:09:37 +13:00
dependabot[bot]
68316cbcf9 Bump esptool from 3.1 to 3.2 (#2632) 2021-10-26 22:14:44 +02:00
dependabot[bot]
2f4b9263c3 Bump tzlocal from 4.0.1 to 4.0.2 (#2631) 2021-10-26 22:12:37 +02:00
Jesse Hills
c2623a08e3 Fix select.set using lambda (#2633) 2021-10-27 08:27:51 +13:00
Jan Čermák
9f625ee7d1 Fix pin number validation for sn74hc595 (#2621) 2021-10-26 18:10:45 +02:00
Martin
2f85c27a05 fix modbus output (#2630) 2021-10-26 11:30:25 +02:00
Otto Winter
c612a3bf60 Constrain GH Actions workflows permissions (#2625) 2021-10-26 10:55:27 +02:00
Jesse Hills
a01f5f5cf1 Remove power and energy from sensors that are not true power (#2628) 2021-10-26 10:55:20 +02:00
Oxan van Leeuwen
87328686a0 Allow setting URL as platform_version (#2598) 2021-10-26 10:55:09 +02:00
Martin
81c11ba1f7 relax max entities checking (#2629) 2021-10-26 08:53:47 +13:00
Otto Winter
49b17c5a2d Switch issue-close-app to GH Actions and workflow cleanup (#2624) 2021-10-23 21:58:04 +02:00
Otto Winter
de06a781ff ESP8266 disable PIO LDF (#2608) 2021-10-23 19:44:55 +02:00
Otto Winter
8e77e3c685 Fix glue code missing micros() (#2623) 2021-10-23 19:25:53 +02:00
0hax
a687b083ae 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-23 19:01:23 +02:00
Stefan Agner
b9e5c7eb35 Autodetect flash size (#2615) 2021-10-23 13:25:46 +02:00
Otto Winter
1a6a063e04 Move default build path to .esphome directory (#2586) 2021-10-23 12:38:57 +02:00
Otto Winter
d85b7a6bd0 Bump platform-espressif8266 from 2.6.2 to 2.6.3 (#2620) 2021-10-23 12:37:50 +02:00
Andreas Hergert
1c4700f447 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-22 18:52:47 +02:00
Andreas Hergert
83400d0417 Bugfix tca9548a and idf refactor anh (#2612)
Co-authored-by: Andreas Hergert <andreas.hergert@otrs.com>
2021-10-22 18:20:57 +02:00
Otto Winter
77a6461c9d Fix ESP8266 OTA compression only starting framework v2.7.0 (#2610) 2021-10-22 17:23:31 +02:00
Otto Winter
6db9d1122f Fix compiler warnings and update platformio line filter (#2607) 2021-10-22 16:52:43 +02:00
Otto Winter
83bef85415 Add owner to all libraries used (#2604) 2021-10-22 14:14:14 +02:00
Otto Winter
b5b3914bbf Re-raise keyboardinterrupt (#2603) 2021-10-22 14:14:07 +02:00
Otto Winter
0d90ef94ae Add OTA upload compression for ESP8266 (#2601) 2021-10-22 13:02:55 +02:00
Otto Winter
c08b21b7cd Bump noise-c from 0.1.3 to 0.1.4 (#2602) 2021-10-22 12:12:07 +02:00
Paul Monigatti
be3cb9ef00 Add EntityBase properties to ESP32 Camera (#2600) 2021-10-22 12:10:29 +02:00
Oxan van Leeuwen
f7b3f52731 Limit hostnames to 31 characters (#2531) 2021-10-22 12:09:47 +02:00
Otto Winter
9220d9fc52 Fix socket connection closed not detected (#2587) 2021-10-22 10:46:44 +02:00
Otto Winter
68c8547067 Add IDF support to dallas (#2578) 2021-10-21 22:48:28 +02:00
dependabot[bot]
1468acfced Bump tzlocal from 3.0 to 4.0.1 (#2553)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2021-10-21 22:46:05 +02:00
dependabot[bot]
b141aea4c0 Bump aioesphomeapi from 10.0.0 to 10.0.3 (#2595)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-21 20:54:12 +02:00
Jesse Hills
a88c022406 Logging a proper url allows terminals to make it clickable (#2554) 2021-10-21 20:53:06 +02:00
dependabot[bot]
5389382798 Bump paho-mqtt from 1.6.0 to 1.6.1 (#2596)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-21 20:09:29 +02:00
Otto Winter
4765173778 Update docker base images (#2583) 2021-10-21 20:07:44 +02:00
Oxan van Leeuwen
07a9cb910f Fix validation of addressable light IDs (#2588) 2021-10-21 20:07:37 +02:00
dependabot[bot]
f408f074c4 Bump platformio from 5.2.1 to 5.2.2 (#2569)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2021-10-21 20:01:27 +02:00
dependabot[bot]
f1f2640d0e Bump esphome-dashboard from 20211021.0 to 20211021.1 (#2594)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-21 20:01:03 +02:00
Oxan van Leeuwen
27d7d7ca69 Fix old-style arduino_version on ESP8266 and with magic values (#2591) 2021-10-21 19:56:47 +02:00
Oxan van Leeuwen
c0fc5b48ae Fix pin/component switchup in SX1509 pin configuration (#2593) 2021-10-21 19:55:19 +02:00
Oxan van Leeuwen
8735d3b83e Fix PlatformIO version for latest Arduino framework (#2590) 2021-10-21 18:59:49 +02:00
Otto Winter
ca59dd1302 Fix HeatpumpIR pin (#2585) 2021-10-21 18:57:03 +02:00
Otto Winter
eccdef8211 Fix mDNS ESP8266 log not included (#2589) 2021-10-21 18:53:08 +02:00
Maurice Makaay
f2ebfe7aef Add mDNS config dump (#2576) 2021-10-21 16:02:28 +02:00
Otto Winter
cac5b356db ESP32 ADC use factory calibration data (#2574) 2021-10-21 16:01:33 +02:00
Otto Winter
156104d5f5 Fix platformio_install_deps no longer installing all lib_deps (#2584) 2021-10-21 15:29:32 +02:00
Otto Winter
c248ba4043 Fix platformio version in Dockerfile doesn't match requirements (#2582) 2021-10-21 14:53:08 +02:00
Otto Winter
c615dc573a Fix ESP8266 dallas GPIO16 INPUT_PULLUP (#2581) 2021-10-21 14:39:36 +02:00
Otto Winter
1caabb6419 Fix ESP8266 OTA adds unnecessary Update library (#2579) 2021-10-21 14:20:57 +02:00
Otto Winter
f41f7994a3 Arduino global delay/millis/... symbols workaround (#2575) 2021-10-21 14:20:23 +02:00
Otto Winter
e39f314e7a Fix wifi ble coexistence check (#2573) 2021-10-21 12:24:01 +02:00
Otto Winter
7f34561e53 Fix ESP8266 GPIO0 Pullup Validation (#2572) 2021-10-21 12:23:37 +02:00
Maurice Makaay
34606b0f1f 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 12:23:21 +02:00
dependabot[bot]
c51b509501 Bump paho-mqtt from 1.5.1 to 1.6.0 (#2568)
Bumps [paho-mqtt](https://github.com/eclipse/paho.mqtt.python) from 1.5.1 to 1.6.0.
- [Release notes](https://github.com/eclipse/paho.mqtt.python/releases)
- [Changelog](https://github.com/eclipse/paho.mqtt.python/blob/master/ChangeLog.txt)
- [Commits](https://github.com/eclipse/paho.mqtt.python/commits)

---
updated-dependencies:
- dependency-name: paho-mqtt
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-20 22:59:46 +02:00
Otto Winter
15b5968418 Revert nextion clang-tidy changes (#2566) 2021-10-21 07:31:13 +13:00
Otto Winter
64a45dc6a6 Move running process log line to debug level (#2565) 2021-10-20 20:15:09 +02:00
Jesse Hills
7cfede5b83 Bump esphome-dashboard to 20211021.0 (#2564) 2021-10-21 07:08:34 +13:00
Jesse Hills
e4d17e0b15 A few esp32_ble_server/improv fixes (#2562) 2021-10-21 06:45:10 +13:00
Carlos Garcia Saura
e79f7ce290 [ESP32] ADC auto-range setting (#2541) 2021-10-20 19:44:51 +02:00
Jesse Hills
bcc77c73e1 Bump esphome-dashboard to 20211020.1 (#2559) 2021-10-20 16:17:00 +13:00
Jesse Hills
8b11e5aeb1 Fix HA addon so it does not have logout button (#2558) 2021-10-20 13:25:00 +13:00
Jesse Hills
cb48394e8a Bump dashboard to 20211020.0 (#2556) 2021-10-20 10:53:49 +13:00
Martin
f5441a87e3 ignore exception when not waiting for a response (#2552) 2021-10-20 10:10:24 +13:00
dependabot[bot]
e2a812fa4b Bump pytest-asyncio from 0.15.1 to 0.16.0 (#2547)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-19 20:28:48 +02:00
Oxan van Leeuwen
5b5ead872b Fix ADC pin validation on ESP32-C3 (#2551) 2021-10-19 12:56:49 +02:00
Jesse Hills
03cfd78c59 Bump dashboard to 20211019.0 (#2549) 2021-10-19 15:16:39 +13:00
Oxan van Leeuwen
ced11bc707 Autodetect ESP32 variant (#2530)
Co-authored-by: Otto winter <otto@otto-winter.com>
2021-10-18 13:36:18 +13:00
Jesse Hills
6b9c084162 Fix Bluetooth setup_priorities (#2458)
Co-authored-by: Otto Winter <otto@otto-winter.com>
2021-10-18 09:56:31 +13:00
Jesse Hills
644ce2a26c Fix const used for IDF recommended version (#2542) 2021-10-18 09:55:35 +13:00
Otto Winter
5425e45851 Only show timestamp for dashboard access logs (#2540) 2021-10-18 08:01:51 +13:00
Paulus Schoutsen
12fce7a08d Bump dashboard to 20211015.0 (#2525)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-10-17 20:26:59 +13:00
Jesse Hills
0991ab3543 Allow downloading all bin files from backend in dashboard (#2514)
Co-authored-by: Otto Winter <otto@otto-winter.com>
2021-10-17 19:54:09 +13:00
Oxan van Leeuwen
65d2b37496 Fix bitshift on read in ADE7953 (#2537) 2021-10-17 19:53:49 +13:00
Oxan van Leeuwen
94d518a418 Replace framework version_hint with source option (#2529) 2021-10-15 22:07:05 +02:00
Carlos Garcia Saura
7cca673902 [esp-idf fix] increase FreeRTOS ticker loop from 100Hz to 1kHz (#2527)
Co-authored-by: Otto Winter <otto@otto-winter.com>
2021-10-15 22:06:49 +02:00
Maurice Makaay
384f8d97d8 OTA firmware MD5 check + password support for esp-idf (#2507)
Co-authored-by: Maurice Makaay <account-github@makaay.nl>
2021-10-15 22:06:32 +02:00
Oxan van Leeuwen
c82d5d63e3 Move TemplatableValue helper class to automation.h (#2511) 2021-10-15 22:05:11 +02:00
dependabot[bot]
653a3d5d11 Bump aioesphomeapi from 9.1.5 to 10.0.0 (#2508)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2021-10-15 22:05:04 +02:00
Tom Matheussen
884b7201de Continue ethernet setup if hostname fails (#2430) 2021-10-15 20:46:58 +02:00
Carlos Garcia Saura
85d2f24447 Clarify statement at the cmd wizard tool, for new users (#2519)
* Clarify next steps for the install wizard

* Update wizard.py

* Link to relevant section of guide

* Formatting

Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2021-10-15 19:51:42 +02:00
dependabot[bot]
935992bcb3 Bump pyyaml from 5.4.1 to 6.0 (#2521)
Bumps [pyyaml](https://github.com/yaml/pyyaml) from 5.4.1 to 6.0.
- [Release notes](https://github.com/yaml/pyyaml/releases)
- [Changelog](https://github.com/yaml/pyyaml/blob/master/CHANGES)
- [Commits](https://github.com/yaml/pyyaml/compare/5.4.1...6.0)

---
updated-dependencies:
- dependency-name: pyyaml
  dependency-type: direct:production
  update-type: version-update:semver-major
...

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-15 19:38:09 +02:00
Dmitriy Lopatko
dc15d1c8ec use no hold master mode for si7021/htu21d (#2528) 2021-10-15 16:52:03 +02:00
Martin
6beb9e568a Fix bug in register name definition (#2526) 2021-10-15 20:27:56 +13:00
Keith Burzinski
7178f10bda Fix Nextion HTTPClient error for ESP32 (#2524) 2021-10-15 20:26:26 +13:00
Paul Monigatti
1308236429 Revert "Added test for bme680_bsec" (#2518)
This reverts commit 7f6a50d291b14935b17802b4dce52135fad1e49e due to BSEC library license restrictions.
2021-10-14 12:31:52 +02:00
Oxan van Leeuwen
63d6b610b8 Don't define UART_SELECTION_UART2 when UART2 is unavailable (#2512) 2021-10-14 11:25:10 +02:00
Martin
8823024509 Add pressure compensation during runtime (#2493)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2021-10-14 11:24:57 +02:00
Paul Monigatti
4896f870f0 Fix: Color modes not being correctly used in light partitions (#2513) 2021-10-14 21:04:50 +13:00
Dmitriy Lopatko
7e482901d9 add missing include in sgp30 (#2517) 2021-10-14 21:00:53 +13:00
Paul Monigatti
07b309e65d Fix BME680_BSEC compilation issue with ESP32 (#2516) 2021-10-14 20:58:35 +13:00
Oxan van Leeuwen
6bbb5e9b56 Disallow using UART2 for logger on ESP-32 variants that lack it (#2510) 2021-10-14 09:21:43 +13:00
Paul Monigatti
867fecd157 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-13 21:59:52 +02:00
Oxan van Leeuwen
05388d2dfc Fix light state remaining on after turn off with transition (#2509) 2021-10-14 08:53:00 +13:00
Maurice Makaay
e06b6d7140 Add ESP32 IDF as a test env for PRs (#2494)
Co-authored-by: Maurice Makaay <account-github@makaay.nl>
2021-10-14 08:22:57 +13:00
Carlos Garcia Saura
859e508392 change millis() to micros() in feed_wdt for 3ms check (#2492) 2021-10-13 18:50:27 +02:00
razorback16
534ce11d54 TCS34725 BugFix and GA factor (#2445)
- Fixed endianness bug on tcs34725 data read
- Fixed lux adjustments based on gain, integration time and GA factor
- Added glass attenuation factor to allow using this sensor behind
  semi transparent glass

Co-authored-by: Razorback16 <razorback16@users.noreply.github.com>
2021-10-13 18:45:41 +02:00
Jesse Hills
bb86db869a Fix bad merge 2021-10-13 22:09:38 +13:00
Jesse Hills
4c4dd23e15 Merge branch 'release' into dev 2021-10-13 22:05:18 +13:00
Jesse Hills
fe5a6847b5 Bump version to 2021.11.0-dev 2021-10-13 16:40:46 +13:00
Sergio Mayoral Martínez
3dee057826 Add throttle_average sensor filter (#2485) 2021-10-13 11:35:30 +13:00
Jesse Hills
4406a08fa7 Allow multiple pn532_spi entries (#2489) 2021-10-13 09:06:52 +13:00
Rafael Treviño
c33077bc61 Improves ct_clamp component accuracy (#2283)
Co-authored-by: Rafa Treviño <rafael.trevino@bbva.com>
2021-10-13 08:42:51 +13:00
Jesse Hills
34db9d9ef2 Add optional timeout for wait_until action (#2282) 2021-10-13 08:23:24 +13:00
Oxan van Leeuwen
1184bbc976 Reduce IRAM usage in test3 (#2499) 2021-10-13 08:22:38 +13:00
Rob Deutsch
a3eb2a7ee0 Added heatpumpir support (#1343)
Co-authored-by: Otto winter <otto@otto-winter.com>
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2021-10-13 07:38:19 +13:00
Maurice Makaay
d13134135b Fix LoadProhibited crash for logger baud_rate 0 (#2498)
Co-authored-by: Maurice Makaay <mmakaay1@xs4all.net>
2021-10-12 13:51:41 +02:00
Chris Nussbaum
b4f57972fb Add on_open and on_closed triggers to cover (#2488) 2021-10-12 15:39:21 +13:00
Jesse Hills
1ab4928959 Merge branch 'dev' into host-target 2021-10-12 12:07:45 +13:00
Jesse Hills
6a5eb43454 Update Airthings BLE (#2453) 2021-10-12 11:56:47 +13:00
Dave T
04ec1c8b56 Consolidate CONF_RAW_DATA_ID to const.py (#2491) 2021-10-12 00:14:04 +02:00
niklasweber
d7ad155885 Fix reset on http_request without network connection (#2474)
* Fix reset problem when http_request is sent without network connection (#2501)

* Fix format
2021-10-12 00:11:04 +02:00
Jan Čermák
85461a752a Fix color temperature persistence on CWWW lights (#2486) 2021-10-11 23:56:35 +02:00
Dave T
039fbc677d Replace deprecated COLOR_BLACK constant (#2487) 2021-10-11 23:44:05 +02:00
dependabot[bot]
ea56a39e11 Bump esphome-dashboard from 20211006.0 to 20211011.1 (#2484)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-11 17:21:04 +02:00
dependabot[bot]
55e9560e74 Bump platformio from 5.2.0 to 5.2.1 (#2482)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-11 16:58:51 +02:00
dependabot[bot]
3cb4b4ca03 Bump flake8 from 3.9.2 to 4.0.1 (#2483)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-11 16:52:42 +02:00
dependabot[bot]
11d2866755 Bump click from 8.0.1 to 8.0.3 (#2481)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-11 16:45:27 +02:00
Jesse Hills
2c517e3e8c Use arduino btStart for arduino framework (#2457) 2021-10-11 10:38:45 +13:00
definitio
42739f0b22 Add configuration for climate topics (#2473) 2021-10-10 17:55:22 +02:00
definitio
a1f9b0d7f2 Add configuration for cover topics (#2472) 2021-10-10 17:54:07 +02:00
Chris Nussbaum
c3b8c84131 Fix below freezing temperature for Inkbird sensors (#2466) 2021-10-10 10:53:58 +02:00
Paul Monigatti
471b82f727 EntityBase Refactor (#2418)
* Renamed Nameable to EntityBase (cpp)

* Renamed NAMEABLE_SCHEMA to ENTITY_BASE_SCHEMA (Python)

* Renamed cg.Nameable to cg.EntityBase (Python)

* Remove redundant use of CONF_NAME from esp32_touch

* Remove redundant use of CONF_NAME from mcp3008

* Updated test

* Moved EntityBase from Component.h and Component.cpp

* Added icon property to EntityBase

* Added CONF_ICON to ENTITY_BASE_SCHEMA and added setup_entity function to cpp_helpers

* Added MQTT component getters for icon and disabled_by_default

* Lint

* Removed icon field from MQTT components

* Code generation now uses setup_entity to setENTITY_BASE_SCHEMA fields

* Removed unused import

* Added cstdint include

* Optimisation: don't set icon if it is empty

* Remove icon from NumberTraits and SelectTraits

* Removed unused import

* Integration and Total Daily Energy sensors now inherit icons from their parents during code generation

* Minor comment correction

* Removed redundant icon-handling code from sensor, switch, and text_sensor

* Update esphome/components/tsl2591/tsl2591.h

Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>

* Added icon property to binary sensor, climate, cover, and fan component tests

* Added icons for Binary Sensor, Climate, Cover, Fan, and Light  to API

* Consolidated EntityBase fields in MQTT components

Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2021-10-10 10:37:05 +02:00
Nate Lust
92b85f98e8 Sgp40 fix (#2462)
* Sample from SGP40 sensor at the appropriate interval

The spg40 sensor must be sampled at 1Hz for the VOC index algorithm
to work correctly. This commit introduces a on device timer to
sample correctly seperately from updating the public state of the
component.

* Add missing configuration values for SGP40

The SGP40 component was not printing all of it's configuration in
dump_config, add in the missing store_baseline value.

* Address review comments

* Format according to clang-tidy

* Attempt 2 at clang tidy
2021-10-10 10:33:04 +02:00
definitio
c092d92d45 Fix cover state (#2468) 2021-10-10 10:31:15 +02:00
Ryan Mounce
e514a1fcd4 Use enum for Tuya fan direction datapoint (#2471)
Fix regression from PR2059. Tested with Arlec DCF5242HA.
2021-10-10 10:28:37 +02:00
davidmonro
a1b28cb36e atm90e32: make the total_increasing class sensors actually be increasing totals. (#2459)
Co-authored-by: David Monro <david.monro@anu.edu.au>
2021-10-09 17:44:16 +02:00
Maurice Makaay
3f2d9abfe6 Correct I2C read() return val check in bh1750 component. (#2465)
Co-authored-by: Maurice Makaay <account-github@makaay.nl>
2021-10-09 10:30:21 +02:00
Otto Winter
f3ec4b514d Merge pull request #2461 from esphome/bump-2021.9.3
2021.9.3
2021-10-08 10:37:52 +02:00
Otto winter
fc5798fa71 Bump version to 2021.9.3 2021-10-07 22:05:30 +02:00
Otto Winter
95d7ad543f API encryption switch to libsodium backend (#2456) 2021-10-07 22:05:26 +02:00
Jesse Hills
d9b2903d78 Add log line to show if API encryption is being used (#2450) 2021-10-07 22:04:55 +02:00
dependabot[bot]
32a664eedc Bump aioesphomeapi from 9.1.2 to 9.1.4 (#2443) 2021-10-07 22:04:16 +02:00
dependabot[bot]
e7477890cf Bump aioesphomeapi from 9.1.1 to 9.1.2 (#2426)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-07 22:03:52 +02:00
Otto Winter
9bf72ff05f Re-enable TCP nodelay for ESP32 (#2390) 2021-10-07 22:03:05 +02:00
Martin
5461f87ff0 I2c fix (#2460) 2021-10-07 21:18:00 +02:00
Otto Winter
1c58b17235 API encryption switch to libsodium backend (#2456) 2021-10-06 22:36:12 +02:00
Alex Iribarren
d34a1c3ed6 Add timestamp to ESPHome dashboard/cli logs (#2455) 2021-10-07 08:56:07 +13:00
Jesse Hills
22e3bc7cfe Add id() for restoring global (#2454) 2021-10-06 22:35:11 +13:00
Paul Monigatti
955c96731e Add Safe Mode Restart Switch (#2437) 2021-10-06 20:44:48 +13:00
Otto Winter
54a173dbf1 I2C re-introduce very verbose logging (#2446) 2021-10-06 11:57:23 +13:00
Jesse Hills
9ff8240802 Bump esphome-dashboard to 20211006.0 (#2451) 2021-10-06 11:45:01 +13:00
Alex Iribarren
7bbb5213f3 Only ping once every two seconds (#2448) 2021-10-06 11:44:48 +13:00
dependabot[bot]
b8b30599ee Bump aioesphomeapi from 9.1.4 to 9.1.5 (#2449)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-06 11:29:30 +13:00
Jesse Hills
e083d7f4d0 Add log line to show if API encryption is being used (#2450) 2021-10-06 11:26:18 +13:00
Martin
a57580b5ab Fix compilation error (#2447) 2021-10-05 17:56:32 +02:00
dependabot[bot]
e22f1fc044 Bump pytest-cov from 2.12.1 to 3.0.0 (#2444) 2021-10-05 14:50:18 +02:00
dependabot[bot]
e09ee8f23d Bump aioesphomeapi from 9.1.2 to 9.1.4 (#2443) 2021-10-05 14:49:55 +02:00
Paul Monigatti
6ec546a6a4 Improved validation for Addressable Light Partition Segments (#2439)
Co-authored-by: Otto Winter <otto@otto-winter.com>
2021-10-05 12:00:23 +13:00
NMC
877367677b Add support for Airthing Wave Mini (#2440) 2021-10-05 11:56:34 +13:00
Otto Winter
8be4086224 Always upload using esptool (#2433) 2021-10-04 16:59:15 +02:00
Otto Winter
871d3b66fb Fix restoring globals (#2442) 2021-10-04 16:15:25 +02:00
Otto Winter
87358e8843 Fix esp32 no longer has Hash internal lib (#2441) 2021-10-04 16:14:51 +02:00
Maurice Makaay
5c06cd8eb3 Fix I2C recovery ESP32 esp-idf (#2438)
Co-authored-by: Maurice Makaay <mmakaay1@xs4all.net>
2021-10-04 12:33:25 +02:00
Stefan Agner
46b4c970d1 Fix socket abstraction for ESP-IDF v4 (#2434) 2021-10-03 22:21:45 +02:00
Stefan Agner
49f46a7cdd Use size_t to fix comparision using RISC-V toolchain (#2436) 2021-10-03 21:55:19 +02:00
Stefan Agner
1627dff166 Disable dependency finder on ESP32 (#2435) 2021-10-03 21:53:40 +02:00
Maurice Makaay
cee08debff Hotfix for ESP8266 OTA issue: ERROR Error binary size (#2432)
Co-authored-by: Maurice Makaay <mmakaay1@xs4all.net>
2021-10-03 16:15:01 +02:00
Otto Winter
912793eddf Convert time to use tzdata (#2425) 2021-10-03 14:10:53 +02:00
Keith Burzinski
eaa5200a35 Thermostat publish state fix (#2427) 2021-10-03 14:10:43 +02:00
cvwillegen
a7687c3e17 Add local MAC address to WiFi info (#2428) 2021-10-03 13:27:59 +02:00
Maurice Makaay
932e0469f7 Fix ESP32 esp-idf OTA updates (#2424)
* WIP on separating out the OTA backends.

* Split off the individual OTA backends.

* Cleanup the three backends, split into .h and .cpp.

* After successfull flashing, activate the new boot partition.

* Fix linting issues.

* Minor cleanup

Co-authored-by: Maurice Makaay <mmakaay1@xs4all.net>
Co-authored-by: Otto winter <otto@otto-winter.com>
2021-10-02 16:02:01 +02:00
dependabot[bot]
15ab8918af Bump aioesphomeapi from 9.1.1 to 9.1.2 (#2426)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-02 15:20:12 +02:00
Maurice Makaay
d0dfc94a61 Fix I2C recovery on Arduino (#2412)
Co-authored-by: Maurice Makaay <mmakaay1@xs4all.net>
2021-10-01 12:53:37 +02:00
Maurice Makaay
5a2984d03a Fix attach_interrupt(...) for esp-idf framework (#2416)
Co-authored-by: Maurice Makaay <mmakaay1@xs4all.net>
2021-10-01 10:11:07 +02:00
Maurice Makaay
c89018a431 Option to ignore CRC for EFuse MAC address (#2399)
* Accept changes as proposed by black.

* Added test and implemented optional correctly.

* Disable PHY RF full calibration (because it calls the breaking MAC retrieval function).

* Disable CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE instead of enable, dummy!

* Rename CONF_IGNORE_EFUSE_MAC_CRC to CONF_ESP32_IGNORE_EFUSE_MAC_CRC.

* Removed unused import.

* Fix ordering of constants.

* Moved all MAC address logic to core helpers.

* Use pretty MAC address for the log.

* Use standard MAC formatter function for debug component.

* Fix clang-formatting.

* Fix clang-formatting.

* Brought wording of comments in line with other function-describing comments.

* Processed code review by @OttoWinter

* Add USE_ESP32_IGNORE_EFUSE_MAC_CRC to defines.h

Co-authored-by: Maurice Makaay <mmakaay1@xs4all.net>
2021-09-30 18:08:15 +02:00
Oxan van Leeuwen
1031ea4313 Fix line endings normalization (#2407)
* Strip CRLF line endings from modbus controller files

* Normalize all line endings to LF
2021-09-30 18:07:28 +02:00
Oxan van Leeuwen
5b0fbbaada Replace std::move() with const references where possible (#2421)
* Replace std::move() with const references where possible

* Fix formatting
2021-09-30 16:25:08 +02:00
Oxan van Leeuwen
0e4f1ac40d Fix default environment for clang-tidy (#2420)
* Drop unnecessary platformio call from script/lint-cpp

* Default environment for clang-tidy to esp32-tidy
2021-09-30 16:24:02 +02:00
Andy Allsopp
946db3fd50 Add viewport meta tag to web server layout (#2419)
* Update web_server.cpp

Added viewport meta tag to web_server.cpp in order to better control layout on mobile browsers. Adds 70 characters. Vastly improves accessibility on mobile devices.

* Update web_server.cpp

split line to meet clang format requirement.

* Update web_server.cpp

Reworked line break for clangtidy
2021-09-30 13:03:30 +02:00
WeekendWarrior1
3dfc8d4291 String manipulation filters for text sensors (#2393)
* initial text sensor filter POC

* fixed verbose logging

* add append, prepend, substitute filters

* add to lower, get to upper working without dummy

* clang lint

* more linting...

* std::move append and prepend filters

* fix verbose filter::input logging

* value.c_str() in input print

* lambda filter verbose log fix

* correct log tag, neaten to upper and to lower

* add on_raw_value automation/trigger
2021-09-29 23:25:06 +02:00
Oxan van Leeuwen
4f5e4f3b86 Move #ifdef to after header include (#2417)
defines.h needs to be included first.

Fixes esphome/issues#2490.
2021-09-29 23:21:52 +02:00
Jesse Hills
505d1d78fb Remove default initializations from tuya cover (#2415) 2021-09-29 12:19:19 +13:00
Otto Winter
7af1c04493 Bump debian base to 5.1.0 / 20210902 (#2413) 2021-09-28 23:16:00 +02:00
Otto Winter
855c98d815 Fix tuya cover lint checks (#2414) 2021-09-28 23:15:52 +02:00
Marek Marczykowski-Górecki
c26ea7e4e0 Tuya: add cover component (#2279) 2021-09-29 10:02:13 +13:00
irtimaled
c39ac9edfe Support HSV-based color support on tuya light (#2400)
* fix: stop tuya light state getting reset

* fix typo

* Support for HSV color in Tuya

* Clamp formatting
2021-09-28 22:19:17 +02:00
Stephen Tierney
af04f565cf Add support for SCD4X (#2217)
* Initial commit

* clang-format fixes

* Update CODEOWNERS

* clang-format fixes

* Fix merge error

* Fix missing return

Co-authored-by: Otto winter <otto@otto-winter.com>
2021-09-28 22:10:25 +02:00
Paul Monigatti
2b9054d3b2 Initialised ESPPreferenceObject::backend_ to nullptr (#2411) 2021-09-28 16:26:46 +02:00
Jesse Hills
c83ecf764d Merge pull request #2410 from esphome/bump-2021.9.2
2021.9.2
2021-09-28 16:04:19 +13:00
Jesse Hills
a2485a18cb Bump version to 2021.9.2 2021-09-28 15:41:58 +13:00
Jesse Hills
8ef2ad17b5 Fix lint issues in web_server_base (#2409) 2021-09-28 15:41:51 +13:00
Otto Winter
4579f78bf9 Merge pull request from GHSA-48mj-p7x2-5jfm 2021-09-28 15:39:41 +13:00
Sergey V. DUDANOV
1853407645 Midea fix (#2395) 2021-09-28 15:36:23 +13:00
Otto Winter
cb5efc1c42 Bump aioesphomeapi to 9.1.1 (#2350) 2021-09-28 15:35:03 +13:00
Jesse Hills
2234f6aacf Fix lint issues in web_server_base (#2409) 2021-09-28 15:33:30 +13:00
Otto Winter
be965a60eb Merge pull request from GHSA-48mj-p7x2-5jfm
* Move web_server auth to web_server_base

* Fix

* Fix

* Add middleware system
2021-09-28 13:53:38 +13:00
Oxan van Leeuwen
5596751c2c Add str_sprintf function that returns std::string (#2408) 2021-09-28 10:24:55 +13:00
Paulus Schoutsen
6417d8132d bump dashboard to 20210927.0 (#2405) 2021-09-27 23:08:21 +02:00
0hax
5624fafb3a Fix handling of timestamps in Teleinfo component. (#2392)
* teleinfo: avoid a buffer overflow.

When reading tag or values, data is written to the buffer even if the
size if bigger than the buffer. Add a new 'max_len' argument to
get_field() to avoid this error.

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

* teleinfo: read extra timestamp field for some tags.

Some tags has an extra timestamp field that need to be read before the
actual data.
The code is inspired by Jpsy work:

29339c14f9

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

* teleinfo: increase MAX_BUF_SIZE to suffice for 3-phase Linky in Standard mode.

* teleinfo: handle DATE tag correctly.

The DATE tag is special due its format and need to be handled
separately.
Fix from DrCoolzic.

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

Co-authored-by: Jörg Wagner <jwagner@digilog.de>
2021-09-27 22:32:08 +02:00
Daniel Müller
2eb5f89d82 Add cover toggle support (#1809)
* Add cover toggle support

Step through open/stop/close/stop sequence with every toggle

* Move the cover toggle logic to perform()

* Add clang-tidy CI suggestion

* Implement cover toggle action as cover trait

* Handle toggle correctly if cover fully closed on POR

* Fix CI finding

* Add deprecated warning

* Don't add already deprecated interface

Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>

* Don't add already deprecated interface

Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>

* Don't add already deprecated interface

Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>

Co-authored-by: Mueller, Daniel <daniel.mueller@karlstorz.com>
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2021-09-27 22:31:15 +02:00
Marcos Pérez Ferro
e30f17f64f Add Current based cover (#1439)
* Adding first version of current_base cover. No Interlock yet.

* simplifying code

* Implementing malfunction protection

* Adding test

* Fixing too long lines

* Fixing test sensor names

* Adding missing id's in ade7953 tests

* Adding code owners as requested

* Fixing issue setting position when stop reached

* Fixing issue setting position when stop reached

* Black formatting

* Fixing format issues

* Fix for concurrent changes

Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2021-09-27 22:22:45 +02:00
irtimaled
1ba560dc9e fix: stop tuya light state getting reset (#2401)
* fix: stop tuya light state getting reset

* fix typo
2021-09-27 21:54:51 +02:00
Maurice Makaay
8c86a18dc6 Add missing include for defines.h. (#2403)
Co-authored-by: Maurice Makaay <mmakaay1@xs4all.net>
2021-09-27 21:53:47 +02:00
Christian Taedcke
b2d516c70a ccs811: Skip reading data if it is not available (#2404)
On bootup the ccs811 reports that no data is available. No error flag
is set in that case. The current implementation ignores this, reads
and publishes the invalid data, which is 0xFDFD for both tvoc and co2
in my case.
This commit fixes this and does not read and publish invalid data.
2021-09-27 21:53:05 +02:00
Otto Winter
45940b0514 Dashboard node import and render in browser (#2374) 2021-09-27 10:10:53 -07:00
Otto Winter
97e76d64d6 Re-enable TCP nodelay for ESP32 (#2390) 2021-09-27 11:40:28 +02:00
Jesse Hills
756c6721e9 Fix NDEF URI casing (#2397) 2021-09-27 11:11:27 +13:00
JonasEr
4c390d9f9f Extend nfc ndef records with Text (#2191)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2021-09-27 09:38:08 +13:00
Sergey V. DUDANOV
0d0954d74b Midea fix (#2395) 2021-09-27 09:32:33 +13:00
Martin
7672ba2c8d Modbus controller (#1779)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-09-27 09:27:24 +13:00
WeekendWarrior1
4d28afc153 add fan.cycle_speed action (#2329) 2021-09-27 08:32:46 +13:00
irtimaled
7246f42a8e Tuya rgb support (#2278)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-09-26 21:34:06 +13:00
irtimaled
bdcffc7ba9 fix: Setting Tuya string DP value (#2394) 2021-09-26 21:27:43 +13:00
rbaron
95a6715b2b Adds light sensor support for b-parasites (#2391) 2021-09-25 13:16:27 +02:00
Otto Winter
5342edf04a Misc fixes for esp-idf (#2386) 2021-09-25 10:05:32 +02:00
Otto Winter
d344b1ca0e Fix arduino esp32 wifi v2 (#2389) 2021-09-25 10:04:57 +02:00
Otto Winter
278863d027 Fix some issues with wifi driver after IDF refactor (#2387) 2021-09-25 09:16:32 +02:00
Otto Winter
8503e08ee6 Fix InterruptLock on ESP-IDF (#2388) 2021-09-25 09:14:07 +02:00
Otto Winter
aec02afcdc Fix clang-tidy header filter (#2385)
* Fix clang-tidy header filter

* Allow private members

* Fix clang-tidy detections

* Run clang-format

* Fix remaining detections

* Fix graph

* Run clang-format
2021-09-24 18:02:28 +02:00
Maurice Makaay
52dd79691b Read unencrypted DSMR telegrams in chunks (#2382)
Co-authored-by: Maurice Makaay <mmakaay1@xs4all.net>
2021-09-24 14:15:22 +02:00
dependabot[bot]
aea2491fa4 Bump voluptuous from 0.12.1 to 0.12.2 (#2381) 2021-09-23 23:36:19 +02:00
Christian Taedcke
963b28181f Always execute i2c bus recovery on setup (#2379) 2021-09-23 20:11:40 +02:00
Christian Taedcke
210a9a4162 Fix esp-idf pinmask bit-shift overflow (#2380) 2021-09-23 18:24:29 +02:00
Otto Winter
a27a884191 Add missing MockObj operators (#2378) 2021-09-23 11:53:10 +02:00
Paul Monigatti
17dcba8f8a Fix: Pin flags code generation returning FLAG_NONE (#2377)
Co-authored-by: Otto winter <otto@otto-winter.com>
2021-09-23 11:19:17 +02:00
Paul Monigatti
ea6a7a22ff Fix ESP8266 ADC (#2376) 2021-09-23 10:45:41 +02:00
Stijn Tintel
5ddba719c5 Fix ir_climate on ESP32-C3 (#2314)
Co-authored-by: Otto winter <otto@otto-winter.com>
2021-09-22 20:13:24 +02:00
Otto Winter
b398d826c1 Fix two i2c error code return errors (#2375) 2021-09-22 20:07:43 +02:00
Philipp Riederer
edb557f79e ledc: do not try to write_state to an uninitialized output (#1732)
Co-authored-by: Philipp Tölke <ptoelke@tecracer.de>
Co-authored-by: Otto winter <otto@otto-winter.com>
2021-09-22 19:50:19 +02:00
dependabot[bot]
f463cd98f8 Bump tzlocal from 2.1 to 3.0 (#2294)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Otto winter <otto@otto-winter.com>
2021-09-22 19:50:11 +02:00
Martin
262d69308d fix i2c scanning eror for Arduino (#2364) 2021-09-22 19:08:42 +02:00
Oxan van Leeuwen
0406e27100 Don't generate IDs with the name of loaded integrations (#2373) 2021-09-22 19:07:57 +02:00
Oxan van Leeuwen
ed3ad615d8 Fix compilation due to incompatibility between #1237 and IDF changes (#2372) 2021-09-22 14:07:39 +02:00
ZJY
66761ff340 Add SSD1305 support to SSD1306 integration along with few new options (#1902)
* Add serveral options for SSD1306 integration
* Add SSD1305 support
  (SSD1305 is similar to SSD1306, it seems SSD1305 has
  brightness and color register but does not have charge pump)
* Add some description when manipulating registers
* Add flip, offset and invert option to get more compatibility
  with various display modules
* Fix typo `setup_ssd1036' -> `setup_ssd1306'

* Add SSD1306 brightness validation tip

* Add more description, limit offset range

* Changes according to linter

* Fix test

* Raise error instead of using warning

* Fix wrong logic

* Remove logger

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>

* Remove logging import

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-09-22 13:47:41 +02:00
Gustavo Ambrozio
8bebf138ee Wifi scan results (#1605)
* adding a scan results wifi text sensor

* Code comment

* Adding scan results to test

* Removing redundant call

* linting

* Better method to update wifi info

Co-authored-by: Otto Winter <otto@otto-winter.com>

* Getting loop back

At least for now.

* Trying out suggestion again

* Applying cr suggestions

Co-authored-by: Otto Winter <otto@otto-winter.com>
2021-09-22 13:44:09 +02:00
wifwucite
fd836e982e Mqtt topics to support numeric fan speed (#1859)
* numeric speed added

* when dumping config for MQTT components log a note when skipped due to is_internal

* added new topics to paython code validation/generation

* reformatted with black

* formatting corrected

* use dump_config_ mechanism to skip internal components

* use dump_config_ mechanism to skip internal components

* style issues resolved

* do_dump_config removed

* formatting fixed

* formatting fixed

* Drop parent dump_config() calls

Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2021-09-22 13:42:58 +02:00
Trevor North
e32722db70 Allow sloppy datapoint message length (#1982)
This allows datapoint update messages to be handled even if the overall
message is longer than required (likely that it contains trailing empty
bytes).

The specific type handling will read only the expected data lengths so
we only need to hard bail if we have too little data not too much.
2021-09-22 13:29:05 +02:00
Stephen Tierney
b20760c93c Add support for LTR390 (#1505)
* Add support for ltr390

* Fix linting errors

* Fix more linting errors

* Linting fixes continued

* Linting forever

* Another one

* Fix regression and linting

* Fix narrowing conversion

* Add test and bugfix

* Add codeowners

* Update CODEOWNERS

* Update sensor defs

* Reformatted with black

* Fixed device class import

* Update CODEOWNERS

* Update CODEOWNERS

* Adding all config options

As requested https://github.com/esphome/esphome/pull/1505#discussion_r597326897

* Moving test to different config file

test1.yml runs out of memory

* Update according to comments

* Add safety clause to reading modes

* Fix clang-tidy complaint

* Revert change to i2c component

* Fix for changes in dev

* Revert "Revert change to i2c component"

This reverts commit 2810df59e9c05311df6d32149ed79a393676503b.

Co-authored-by: Otto winter <otto@otto-winter.com>
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2021-09-22 13:24:19 +02:00
Jesse Hills
654e31124e Correctly invert the float output state (#2368) 2021-09-22 22:59:03 +12:00
Stanislav Meduna
8e36e1b92e ili9341: use larger SPI transfers (#1628)
The original version uses write_byte to tranfer every byte of
the display buffer which is quite extensive as every byte needs
to be waited for in the SPI driver.

This patch prepares transfers in 64-byte chunks. The result is
a visible faster redraw of the display.

Co-authored-by: Otto winter <otto@otto-winter.com>
2021-09-22 12:43:17 +02:00
Tommy van der Vorst
9fe7b08874 Add support for Waveshare 7.5 inch (C) bichromatic display (black-and-white only for now) (#1844)
* Add support for Waveshare 7.5 inch (B) bichromatic display (black-and-white only for now)

* Use drawing commands specific to bichromatic displays

* Fix inaccurate comment

* Fix merge error

* Formatting

Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2021-09-22 12:39:41 +02:00
Robert Resch
f1364d4af4 Combine code of xiaomi_miscale and xiaomi_miscale2 (#2266)
* Combine xiaomi_miscale and xiaomi_miscale2

* check if message contains impedance

* auto detect scale version

* remove xiaomi_miscale2

* fix lint errors

* Apply suggestions from code review

Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>

* Apply suggestions from code review on old code

* Fix clang-tidy warnings

Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2021-09-22 12:12:55 +02:00
Silvio
ed593544d8 Add support for Daly Smart BMS (#2156)
* Add support for Daly Smart BMS

* Fix clang-format and python lint

* Fix const declaration

* Add code owner

* Fix malloc with std::vector

* Fix with suggestions

* Revert "Fix with suggestions"

This reverts commit bc618f20cf83e3df903fdbbca8d2529d946264b0.

* Fix last commit

* Fix Python Lint

* Fix typo

* Use std::vector instead pointer and fix loop

* Fix typo

* Add test configuration to test3.yaml

* Fix test3.yaml

* Fix uart in test3.yaml
2021-09-22 12:03:42 +02:00
Niccolò Maggioni
0929a0f8aa Discard senseair commands echoes & fix calibration result check (#2358) 2021-09-22 11:15:51 +02:00
Paul Monigatti
13b3412b45 Fix Dallas parent not being set (#2369) 2021-09-22 11:12:42 +02:00
Maurice Makaay
888e315553 Fix OTA crash during reading of new bin file. (#2366)
Co-authored-by: Maurice Makaay <mmakaay1@xs4all.net>
2021-09-22 10:37:46 +02:00
Otto Winter
11daabc9c2 Fix docker pio settings not applied (#2370) 2021-09-22 10:32:39 +02:00
WeekendWarrior1
40e0100c1e add = to default font glpyh list (#2361) 2021-09-22 16:57:16 +12:00
Paul Monigatti
c51352d04d Allow non-addressable lights in light partitions (#2256) 2021-09-22 13:59:21 +12:00
Paul Monigatti
c8a8acd46e Fix ESP8266 preference loading (#2367) 2021-09-22 13:55:49 +12:00
Otto Winter
bbac1534a3 Fix ESP8266 preferences not set up (#2362) 2021-09-21 21:59:11 +02:00
Oxan van Leeuwen
637b55bfbf Allow compilation against IDF from repository (#2355)
* Fix src_filter in platformio.ini after src_dir change

* Add -Wno-nonnull-compare to platformio.ini as well

* Create default sdkconfig for static analysis

* Add more compiler flags to clang ignore list

* Clean-up platformio.ini

* Remove unnecessary blank line

* Fix accidentally dropped library

* Don't gitignore sdkconfig.defaults

Co-authored-by: Otto winter <otto@otto-winter.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-09-21 17:12:17 +02:00
Maurice Makaay
92a24d52be Fix OTA password mismatch error. (#2363)
Co-authored-by: Maurice Makaay <mmakaay1@xs4all.net>
2021-09-21 17:11:58 +02:00
Alex
491f8cc611 Configurable Flash Write Interval (#2119)
Co-authored-by: Alex <33379584+alexyao2015@users.noreply.github.com>
Co-authored-by: Otto winter <otto@otto-winter.com>
2021-09-21 13:47:51 +02:00
Oxan van Leeuwen
71fc61117b Fix duplicate defines and restore alphabetical order (#2352) 2021-09-21 16:52:01 +12:00
Otto Winter
24f445dade Fix src_filter in platformio.ini after src_dir change (#2353) 2021-09-21 16:37:13 +12:00
Otto Winter
7c884329eb Fix MDNS not registered (#2359) 2021-09-21 16:34:56 +12:00
Martin
bac58bba4d fixes compilation error in rtttl (#2357)
Compilation error for millis() and delay() after #2303
2021-09-20 22:13:46 +02:00
Otto winter
52b86e3440 Add host target platform 2021-09-20 16:22:04 +02:00
Otto Winter
250bf3f054 CI cache only restore from direct matches (#2351) 2021-09-20 13:14:05 +02:00
Otto Winter
e65a7d887f Bump aioesphomeapi to 9.1.1 (#2350) 2021-09-20 12:02:37 +02:00
Otto Winter
ac0d921413 ESP-IDF support and generic target platforms (#2303)
* Socket refactor and SSL

* esp-idf temp

* Fixes

* Echo component and noise

* Add noise API transport support

* Updates

* ESP-IDF

* Complete

* Fixes

* Fixes

* Versions update

* New i2c APIs

* Complete i2c refactor

* SPI migration

* Revert ESP Preferences migration, too complex for now

* OTA support

* Remove echo again

* Remove ssl again

* GPIOFlags updates

* Rename esphal and ICACHE_RAM_ATTR

* Make ESP32 arduino compilable again

* Fix GPIO flags

* Complete pin registry refactor and fixes

* Fixes to make test1 compile

* Remove sdkconfig file

* Ignore sdkconfig file

* Fixes in reviewing

* Make test2 compile

* Make test4 compile

* Make test5 compile

* Run clang-format

* Fix lint errors

* Use esp-idf APIs instead of btStart

* Another round of fixes

* Start implementing ESP8266

* Make test3 compile

* Guard esp8266 code

* Lint

* Reformat

* Fixes

* Fixes v2

* more fixes

* ESP-IDF tidy target

* Convert ARDUINO_ARCH_ESPxx

* Update WiFiSignalSensor

* Update time ifdefs

* OTA needs millis from hal

* RestartSwitch needs delay from hal

* ESP-IDF Uart

* Fix OTA blank password

* Allow setting sdkconfig

* Fix idf partitions and allow setting sdkconfig from yaml

* Re-add read/write compat APIs and fix esp8266 uart

* Fix esp8266 store log strings in flash

* Fix ESP32 arduino preferences not initialized

* Update ifdefs

* Change how sdkconfig change is detected

* Add checks to ci-custom and fix them

* Run clang-format

* Add esp-idf clang-tidy target and fix errors

* Fixes from clang-tidy idf round 2

* Fixes from compiling tests with esp-idf

* Run clang-format

* Switch test5.yaml to esp-idf

* Implement ESP8266 Preferences

* Lint

* Re-do PIO package version selection a bit

* Fix arduinoespressif32 package version

* Fix unit tests

* Lint

* Lint fixes

* Fix readv/writev not defined

* Fix graphing component

* Re-add all old options from core/config.py

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-09-20 11:47:51 +02:00
Oxan van Leeuwen
1e8e471dec Introduce call_dump_config() indirection (#2325) 2021-09-20 11:16:31 +02:00
Otto Winter
2d7f8b3bdf Install python requirements after apt ones for better caching (#2349)
* Install python requirements after apt ones for better caching

* Fix buildkit caching works differently
2021-09-20 10:31:48 +02:00
Oxan van Leeuwen
7452ef23b1 Add ESPHOME_VERSION_CODE define (#2324) 2021-09-20 20:16:59 +12:00
Christian Taedcke
9ebe075f9b Add deep sleep wakeup from touch (#1238) (#2281) 2021-09-20 20:12:32 +12:00
Aljaž Srebrnič
3052c64dd7 Add invert_colors option for st7735 (#2327) 2021-09-20 20:08:08 +12:00
Otto Winter
5e345783bd Fix docker release deploy push flag (#2348) 2021-09-20 09:55:18 +02:00
poptix
81685573e1 Properly calculate negative temperatures in sm300d2 (#2335)
Co-authored-by: Matt Hallacy <github@poptix.net>
2021-09-20 19:44:18 +12:00
synco
945ed5d3bd Added graphing component (#2109)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
Co-authored-by: Synco Reynders <synco@deviceware.co.nz>
Co-authored-by: Otto winter <otto@otto-winter.com>
2021-09-20 19:29:47 +12:00
Otto Winter
fff5ba03c2 Also run docker CI when requirements change (#2347) 2021-09-20 09:29:09 +02:00
besteru
82eca13d7b Fix error reporting for DHT bit read loop (#2344) 2021-09-20 09:14:44 +02:00
synco
5f21b925da Calculating the AC only component of the samples (#1906)
Co-authored-by: Synco Reynders <synco@deviceware.co.nz>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-09-20 09:12:50 +02:00
Otto Winter
272ceadbb0 Redo docker build system with buildkit+multi-stage and cache pio packages (#2338) 2021-09-20 09:07:38 +02:00
Jesse Hills
d26c2b1a44 Merge pull request #2346 from esphome/bump-2021.9.1
2021.9.1
2021-09-20 14:56:48 +12:00
Oxan van Leeuwen
8bda8e5393 Clean-up sensor integration (#2275) 2021-09-20 14:34:59 +12:00
Jesse Hills
954b8a0cff Bump version to 2021.9.1 2021-09-20 14:16:57 +12:00
Otto Winter
7c17e72db4 Add readv and writev for more efficient API packets (#2342) 2021-09-20 14:16:56 +12:00
Oxan van Leeuwen
d180aee57f Apply color brightness to addressable light effects (#2321) 2021-09-20 14:16:56 +12:00
Oxan van Leeuwen
e3ffecefc0 Cease using deprecated Cover methods in automations (#2326) 2021-09-20 14:16:56 +12:00
Oxan van Leeuwen
4c61cf153c Light transition fixes (#2320) 2021-09-20 14:16:56 +12:00
Paul Monigatti
c78fb90e2f Fix MQTT discovery for sensor state_class (#2331) 2021-09-20 14:16:39 +12:00
Otto Winter
a990898256 Add readv and writev for more efficient API packets (#2342) 2021-09-20 10:33:10 +12:00
Luca Gugelmann
c60c618204 Fix SPIDevice::write_byte16 to actually take a 16 bit argument (#2345) 2021-09-20 09:19:20 +12:00
Stefan Rado
53bd197c44 Add eco mode to tuya climate component (#1860)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-09-20 09:17:43 +12:00
dependabot[bot]
dbb195691b Bump pylint from 2.10.2 to 2.11.1 (#2334)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Otto winter <otto@otto-winter.com>
2021-09-19 19:22:28 +02:00
Oxan van Leeuwen
50da630811 Apply color brightness to addressable light effects (#2321) 2021-09-19 18:46:26 +02:00
Kamil Trzciński
30eca885c9 Add esp8266_disable_ssl_support: config option (#2236) 2021-09-19 18:46:17 +02:00
Oxan van Leeuwen
f76685fccf Cease using deprecated Cover methods in automations (#2326) 2021-09-19 18:31:31 +02:00
Oxan van Leeuwen
68d547595e Light transition fixes (#2320) 2021-09-19 18:31:20 +02:00
Paul Monigatti
64341d1d18 Fix MQTT discovery for sensor state_class (#2331) 2021-09-19 18:30:41 +02:00
Otto Winter
2e49039c01 Reduce stale/lock gh actions interval (#2341) 2021-09-19 14:20:35 +02:00
Jesse Hills
321504cf29 Merge pull request #2328 from esphome/bump-2021.9.0
2021.9.0
2021-09-16 09:40:29 +12:00
Jesse Hills
0f4a7bf1f5 Bump version to 2021.9.0 2021-09-16 09:12:01 +12:00
Jesse Hills
711e74a12b Merge branch 'beta' into bump-2021.9.0 2021-09-16 09:12:01 +12:00
Matthew Mazzanti
8f3a739da7 Allow transforms and flashes to not update remote_values (#2313) 2021-09-16 05:59:58 +12:00
Oxan van Leeuwen
aed140d802 Fix typo 2021-09-15 19:13:30 +02:00
Oxan van Leeuwen
c69b88bb55 Fix platformio.ini parser used by container build 2021-09-15 19:10:51 +02:00
Oxan van Leeuwen
c6dc8a11e2 Add namespace to all PlatformIO library references (#2296)
* Remove unnecessary duplication in platformio.ini

* Add namespace to all platformio library references

* Add cmake-build-* to gitignore

They're generated by the CLion add-on for each PlatformIO environment.
Listing them all separately seems nonsensical.
2021-09-15 19:01:31 +02:00
dependabot[bot]
6366ff6421 Bump black from 21.8b0 to 21.9b0 (#2305) 2021-09-15 18:02:41 +02:00
dependabot[bot]
607ddaa632 Bump aioesphomeapi from 9.0.0 to 9.1.0 (#2306) 2021-09-15 18:02:28 +02:00
Guillermo Ruffino
d281e59f3a ac_dimmer increase gate time for robotdyn (#1708)
* ac_dimmer increate gate time for robotdyn

* add explanation on longer gate enable time
2021-09-15 08:40:52 -03:00
Stefan Rado
2db8c42e1d Support direct relay state feedback for tuya climate component (#1668)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-09-15 20:23:35 +12:00
Jesse Hills
aa8eb2c92a Merge pull request #2319 from esphome/bump-2021.9.0b5
2021.9.0b5
2021-09-15 19:51:55 +12:00
Jesse Hills
b422a63b2a Bump version to 2021.9.0b5 2021-09-15 19:01:54 +12:00
Jesse Hills
ad5f2cd748 Start a wifi scan after saving station details (#2315) 2021-09-15 19:01:54 +12:00
Maurice Makaay
efae363739 Fix aioesphomeapi API logger with explicit api.port in the YAML. (#2310) 2021-09-15 19:01:54 +12:00
Jesse Hills
2d79d21c50 Simple time.sleep in place of threading wait due to upgraded zeroconf (#2307) 2021-09-15 19:01:54 +12:00
jsuanet
3b9d126322 Fix unit of measurement fields for DSMR power consumed/delivered fields (#2304)
Co-authored-by: Jos Suanet <jos@suanet.net>
2021-09-15 19:01:54 +12:00
Jesse Hills
0ea77de98c Start a wifi scan after saving station details (#2315) 2021-09-15 19:00:51 +12:00
Maurice Makaay
19014331d8 Fix aioesphomeapi API logger with explicit api.port in the YAML. (#2310) 2021-09-15 08:48:27 +02:00
Jesse Hills
b276ac0588 Simple time.sleep in place of threading wait due to upgraded zeroconf (#2307) 2021-09-15 16:39:13 +12:00
Guillermo Ruffino
de33cbd7e7 Dsmr updates (#2157)
* add option to use check_crc

* ignore newline before ( in parsing

* add gas delivered text for raw sensor

* fix compile issue when not listing any sensor

* make gas_mbus_id configurable

* update dsmr lib for clang
2021-09-14 22:14:49 -03:00
JonasEr
103ba4c696 Bug fix of NFC message & records becoming inaccessible in on_tag lambdas (#2309) 2021-09-15 10:06:43 +12:00
jsuanet
5a90b83f63 Fix unit of measurement fields for DSMR power consumed/delivered fields (#2304)
Co-authored-by: Jos Suanet <jos@suanet.net>
2021-09-15 09:22:45 +12:00
Oxan van Leeuwen
716039e452 Use standard version of make_unique when available (#2292) 2021-09-14 14:27:35 +02:00
Jesse Hills
896654aaef Merge pull request #2302 from esphome/bump-2021.9.0b4
2021.9.0b4
2021-09-14 23:23:51 +12:00
Jesse Hills
5fad38f65f Bump version to 2021.9.0b4 2021-09-14 23:07:08 +12:00
Jesse Hills
89f2ea5725 Fix binary strobe (#2301) 2021-09-14 23:07:07 +12:00
Jesse Hills
a32ad33b4e Allow simple hostname for sntp servers (#2300) 2021-09-14 23:07:07 +12:00
Otto Winter
a328fff5a7 Fix api noise explicit reject (#2297) 2021-09-14 23:07:07 +12:00
Otto Winter
233783c76c API Noise logging (#2298) 2021-09-14 23:07:07 +12:00
Otto Winter
39a18fb358 Bump platformio to 5.2.0 (#2291) 2021-09-14 23:07:02 +12:00
Jas Strong
460a144ca8 t6615: tolerate sensor dropping commands (#2255)
The Amphenol T6615 has a built-in calibration system which means that
the sensor could go away for a couple of seconds to figure itself out.
While this is happening, commands are silently dropped.

This caused the previous version of this code to lock up completely,
since there was no way for the command_ state machine to tick back to
the NONE state.

Instead of just breaking the state machine, which might be harmful on
a multi-core or multi-threaded device, add a timestamp and only break
the lock if it's been more than a second since the command was issued.

The command usually doesn't take more than a few milliseconds to
complete, so this should not affect things unduly.

While we're at it, rewrite the rx side to be more robust against
bytes going missing.

Instead of reading in the data essentially inline, read into a buffer
and process it when enough has been read to make progress.

If data stops coming when we expect it to, or the data is malformed,
have a timeout that sends a new command.

Co-authored-by: jas <jas@asspa.in>
2021-09-14 23:06:13 +12:00
Oxan van Leeuwen
23ead416d5 Suppress excessive warnings about deprecated Fan interfaces (#2270) 2021-09-14 23:06:13 +12:00
Oxan van Leeuwen
1b5f11bbee Only try compat parsing after regular parsing fails (#2269) 2021-09-14 23:06:13 +12:00
Jesse Hills
4cc2817fcd Fix binary strobe (#2301) 2021-09-14 22:59:15 +12:00
Jesse Hills
d437cc915c Allow simple hostname for sntp servers (#2300) 2021-09-14 22:40:45 +12:00
Otto Winter
dd3f2f6c7e Fix api noise explicit reject (#2297) 2021-09-14 11:53:49 +02:00
Otto Winter
5f61897bec Add stale/lock bots (#2299) 2021-09-14 10:35:37 +02:00
dependabot[bot]
c5d26a5b4a Bump click from 7.1.2 to 8.0.1 (#1824)
Bumps [click](https://github.com/pallets/click) from 7.1.2 to 8.0.1.
- [Release notes](https://github.com/pallets/click/releases)
- [Changelog](https://github.com/pallets/click/blob/main/CHANGES.rst)
- [Commits](https://github.com/pallets/click/compare/7.1.2...8.0.1)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-09-14 10:26:49 +02:00
Otto Winter
855112dfc3 API Noise logging (#2298) 2021-09-14 09:53:37 +02:00
Jesse Hills
0da97289e6 Merge pull request #2295 from esphome/bump-2021.9.0b3
2021.9.0b3
2021-09-14 07:47:20 +12:00
Otto Winter
b9767bdcbc Bump platformio to 5.2.0 (#2291) 2021-09-13 21:16:13 +02:00
Jesse Hills
91f12a50cf Bump version to 2021.9.0b3 2021-09-14 07:13:00 +12:00
Otto Winter
e92a9d1d9e Fix API socket issues (#2288)
* Fix API socket issues

* Fix compile error against beta

* Format
2021-09-14 07:13:00 +12:00
Oxan van Leeuwen
4eb51ab4d6 Disable automatic usage of SNTP servers from DHCP (#2273) 2021-09-14 07:12:59 +12:00
Oxan van Leeuwen
e6b0a0ca2b Clean-up sensor integration (#2275) 2021-09-13 18:58:49 +02:00
Oxan van Leeuwen
924df1e7de Run clang-tidy against Arduino 3 (#2146)
* Add macros header with more usable Arduino version defines

* Change Arduino version checking to use our version defines

* Add missing ESP8266 check

* Rename Arduino version macro to ARDUINO_VERSION_CODE

* Upgrade clang-tidy to use Arduino 3

* Fix clang-tidy warnings

* Upgrade NeoPixelBus to upstream 2.6.7

* Use Arduino-version-appropriate API to set redirect flags

* Remove now unnecessary CLANG_TIDY ifdefs

* Add preprocessor hackery to avoid including pgmspace.h

* Bump base image to 4.1.1 and update lint

* Fix nfctag

* Fix make_unique ambiguous

* Fix ignore name

* Fix ambiguous v2

* Remove unused begin

* Cast time_t to prevent issues on platforms where time_t is 32bit

Co-authored-by: Otto winter <otto@otto-winter.com>
2021-09-13 18:55:04 +02:00
Otto Winter
ed7983af41 Fix API socket issues (#2288)
* Fix API socket issues

* Fix compile error against beta

* Format
2021-09-13 18:52:53 +02:00
Oxan van Leeuwen
40c474cd83 Run clang-tidy against ESP32 (#2147)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
Co-authored-by: Otto winter <otto@otto-winter.com>
2021-09-13 18:11:27 +02:00
Otto Winter
a2d2863c72 Revert "Bump tzlocal from 2.1 to 3.0 (#2154)" (#2289)
This reverts commit e0cff214b2.
2021-09-13 17:22:24 +02:00
Oxan van Leeuwen
133a17d6eb Add esphal.h include to inkplate6 component (#2286) 2021-09-13 16:55:01 +02:00
Oxan van Leeuwen
fe47ddc27a Convert st7735.h to use LF line endings (#2287) 2021-09-13 16:39:35 +02:00
Tercio Filho
aad03f1bf5 Fix issue #2054. PZEM004T Component doesn't set the module address. (#1784) 2021-09-13 15:36:01 +02:00
Otto Winter
a4867a00ea Activate owning-memory clang-tidy check (#1891)
* Activate owning-memory clang-tidy check

* Lint

* Lint

* Fix issue with new NfcTag constructor

* Update pointers for number and select

* Add back the NOLINT to display buffer

* Fix merge

* DSMR fixes

* Nextion fixes

* Fix pipsolar

* Fix lwip socket

* Format

* Change socket fix

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-09-13 11:31:02 +02:00
dependabot[bot]
e0cff214b2 Bump tzlocal from 2.1 to 3.0 (#2154)
Bumps [tzlocal](https://github.com/regebro/tzlocal) from 2.1 to 3.0.
- [Release notes](https://github.com/regebro/tzlocal/releases)
- [Changelog](https://github.com/regebro/tzlocal/blob/master/CHANGES.txt)
- [Commits](https://github.com/regebro/tzlocal/compare/2.1...3.0)

---
updated-dependencies:
- dependency-name: tzlocal
  dependency-type: direct:production
  update-type: version-update:semver-major
...

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-09-13 11:25:28 +02:00
James Braid
c6109024aa Fix SM300D2 sensor component routines so they correctly read the sensor output (#2159) 2021-09-13 11:23:59 +02:00
Alex
4e308f551c Fix devcontainer scripts on Windows (#2239) 2021-09-13 10:08:06 +02:00
Kamil Trzciński
8a2b1d9359 Expose select on Frontend web_server: (#2245) 2021-09-13 10:06:28 +02:00
Jas Strong
63a186bdf9 t6615: tolerate sensor dropping commands (#2255)
The Amphenol T6615 has a built-in calibration system which means that
the sensor could go away for a couple of seconds to figure itself out.
While this is happening, commands are silently dropped.

This caused the previous version of this code to lock up completely,
since there was no way for the command_ state machine to tick back to
the NONE state.

Instead of just breaking the state machine, which might be harmful on
a multi-core or multi-threaded device, add a timestamp and only break
the lock if it's been more than a second since the command was issued.

The command usually doesn't take more than a few milliseconds to
complete, so this should not affect things unduly.

While we're at it, rewrite the rx side to be more robust against
bytes going missing.

Instead of reading in the data essentially inline, read into a buffer
and process it when enough has been read to make progress.

If data stops coming when we expect it to, or the data is malformed,
have a timeout that sends a new command.

Co-authored-by: jas <jas@asspa.in>
2021-09-13 09:54:48 +02:00
Oxan van Leeuwen
d594a6fcbc Store strings only used for logging in flash (#2274)
Co-authored-by: Otto winter <otto@otto-winter.com>
2021-09-13 09:48:52 +02:00
Oxan van Leeuwen
e18dfdd656 Suppress excessive warnings about deprecated Fan interfaces (#2270) 2021-09-13 09:39:18 +02:00
Oxan van Leeuwen
3aa107142b Only try compat parsing after regular parsing fails (#2269) 2021-09-13 09:37:11 +02:00
Oxan van Leeuwen
0cd24c629a Compatibility with clang-tidy v14 (#2272) 2021-09-13 09:35:55 +02:00
Oxan van Leeuwen
f31e0532c4 Untangle core headers (part 1) (#2276) 2021-09-13 09:33:29 +02:00
irtimaled
f0b6aabc96 Support inverting color temperature on tuya lights (#2277) 2021-09-13 09:33:20 +02:00
Oxan van Leeuwen
97a18717e6 Disable automatic usage of SNTP servers from DHCP (#2273) 2021-09-13 12:44:39 +12:00
Oxan van Leeuwen
ede1de9021 Drop obsolete comments from CONTRIBUTING.md (#2271) 2021-09-12 22:04:35 +02:00
Jesse Hills
f1a8d957f8 Merge pull request #2267 from esphome/bump-2021.9.0b2
2021.9.0b2
2021-09-10 22:13:54 +12:00
Jesse Hills
9821a3442b Bump version to 2021.9.0b2 2021-09-10 21:34:38 +12:00
poptix
87842e097b sm300d2: Accept (undocumented) 0x80 checksum offset. (#2263)
Co-authored-by: Matt Hallacy <github@poptix.net>
2021-09-10 21:34:38 +12:00
Jesse Hills
7dd40e2014 Fix a few ESP32-C3 compiler issues (#2265)
* Fix using Serial when using ESP32-C3 standard pins

* Force type for std::min in pn532

* Fix variable size where size_t is different on exp32-c3
2021-09-10 21:34:38 +12:00
poptix
3d71e2e189 sm300d2: Accept (undocumented) 0x80 checksum offset. (#2263)
Co-authored-by: Matt Hallacy <github@poptix.net>
2021-09-10 21:05:25 +12:00
Jesse Hills
affaaf7d2c Fix a few ESP32-C3 compiler issues (#2265)
* Fix using Serial when using ESP32-C3 standard pins

* Force type for std::min in pn532

* Fix variable size where size_t is different on exp32-c3
2021-09-10 12:10:28 +12:00
Jesse Hills
a719998220 Merge pull request #2262 from esphome/bump-2021.9.0b1
2021.9.0b1
2021-09-09 11:40:33 +12:00
Jesse Hills
bad161a5c1 Bump version to 2021.10.0-dev 2021-09-09 10:28:34 +12:00
Jesse Hills
09a6fdf1c7 Bump version to 2021.9.0b1 2021-09-09 10:28:34 +12:00
Stephen Tierney
d2616cbdfc PMSA003i Update state_class and async (#2216)
* Update component (state_class and async)

* No need to specify empty device class

* Remove unused import

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-09-09 10:14:08 +12:00
Keith Burzinski
faf1c8bee8 SGP40 sensor start-up fix (#2178) 2021-09-09 09:42:35 +12:00
Peter van Dijk
f09aca4865 pm1006: add support for sending a measurement request (#2214) 2021-09-09 09:35:00 +12:00
Jesse Hills
cc52f37933 Revert "Dont dump legacy fields (#2241)" (#2259)
This reverts commit 97eba1eecc.
2021-09-09 09:29:08 +12:00
Otto Winter
e5051eefbc API encryption (#2254) 2021-09-09 09:22:47 +12:00
Peter van Dijk
9e5cd0da51 ccs811: publish firmware version; log bootloader and HW version; fix a bug (#2006) 2021-09-09 09:19:43 +12:00
Sergey V. DUDANOV
4e120a291e Midea support v2 (#2188) 2021-09-09 09:10:02 +12:00
Otto Winter
2790d72bff Convert API to use sockets (#2253)
* Socket component

* Lint

* Lint

* Fix esp8266 missing INADDR_ANY

* API convert to sockets and frame helper

* Fix compile error

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-09-08 12:52:57 +02:00
Otto Winter
e44f447d85 Fix socket not setting callbacks early enough (#2260) 2021-09-08 12:02:32 +02:00
Jesse Hills
4356581db0 Remove removesuffix 2021-09-08 19:51:20 +12:00
Jesse Hills
f87a701b28 Bump dashboard to 20210908.0 and fix card names for yml (#2258) 2021-09-08 17:14:12 +12:00
Jesse Hills
fa2eb46cd6 Allow .yml files to show up in the dashboard (#2257) 2021-09-08 16:11:09 +12:00
Otto Winter
f924e80f43 Socket component (#2250) 2021-09-08 15:41:42 +12:00
Chris Nussbaum
6180ee8065 Template sensors always publish on update interval (#2224)
Co-authored-by: Chris Nussbaum <chris.nussbaum@protolabs.com>
2021-09-08 15:36:49 +12:00
wifwucite
1be106c0b5 Fix fan speed restore issue on boot (#1867) 2021-09-08 15:30:17 +12:00
dgtal1
b0533db2eb Add new trigger to fan component on_speed_set (#2246) 2021-09-08 15:15:57 +12:00
Otto Winter
dba502c756 Logger prevent recursive logging (#2251) 2021-09-08 08:57:20 +12:00
Oxan van Leeuwen
d9cb64b893 Add device classes new in HA 2021.9 (#2248) 2021-09-07 10:12:26 +12:00
Kamil Trzciński
2d91e6b977 template: select: fix initial_value cannot be used with lambda (#2244) 2021-09-07 08:00:08 +12:00
Alex
4a6f1f150a Fix runtime exception due to dict typing (#2243) 2021-09-06 14:48:28 +12:00
Peter van Dijk
7f76f3726f LOG_UPDATE_INTERVAL: correctly report "never" (#2240) 2021-09-06 14:47:13 +12:00
Jesse Hills
e2d97b6f36 Light: include ON_OFF capability to BRIGHTNESS ColorMode (#2204) 2021-09-06 08:57:58 +12:00
Jesse Hills
2a653642f5 Fix encoding bug (#2242) 2021-09-06 08:57:37 +12:00
Jesse Hills
97eba1eecc Dont dump legacy fields (#2241) 2021-09-06 08:36:55 +12:00
Jesse Hills
ff6bed54c6 Remove last_reset_type and convert all those sensors to TOTAL_INCREASING (#2233) 2021-09-06 08:30:47 +12:00
Jesse Hills
f9b0666adf Allow using a git source for a package (#2193) 2021-09-06 08:23:06 +12:00
Jesse Hills
ca12b8aa56 Move to use zeroconf library instead of inline copy (#2192) 2021-09-06 08:22:15 +12:00
Christian Ferbar
77508f7e44 Fix UARTComponent hardware vs software UART0 conflict (#2229)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2021-09-04 14:49:34 +12:00
Oxan van Leeuwen
54de0ca0da Reject template select/number/switches that don't handle user input (#2230) 2021-09-04 14:46:53 +12:00
Oxan van Leeuwen
f364788c03 Expose WHITE/CWWW/RGBCT color modes over MQTT (#2231) 2021-09-04 14:32:33 +12:00
DAVe3283
00aaf84c37 Fix uptime's state_class (esphome/issues#2337) (#2205) 2021-09-03 16:58:30 +12:00
Kamil Trzciński
b01bc76dc5 mqtt_sensor: properly send state_class via MQTT (#2228) 2021-09-03 16:37:18 +12:00
dependabot[bot]
910f812737 Bump pylint from 2.9.6 to 2.10.2 (#2197)
Bumps [pylint](https://github.com/PyCQA/pylint) from 2.9.6 to 2.10.2.
- [Release notes](https://github.com/PyCQA/pylint/releases)
- [Changelog](https://github.com/PyCQA/pylint/blob/main/ChangeLog)
- [Commits](https://github.com/PyCQA/pylint/compare/v2.9.6...v2.10.2)

---
updated-dependencies:
- dependency-name: pylint
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-09-03 16:20:42 +12:00
Chris Nussbaum
a4d024f43d Add is_on and is_off conditions for the fan component (#2225)
Co-authored-by: Chris Nussbaum <chris.nussbaum@protolabs.com>
2021-09-02 12:16:11 +12:00
Alex
9937ad7fa0 Cleanup flash transitions (#2227) 2021-09-02 11:56:40 +12:00
Petko Bordjukov
edcd88123d iBeacon support for ble_presence (#1627) 2021-09-02 11:46:15 +12:00
Alex
ea1b5e19f0 Add transitions to light flash (#2201) 2021-08-31 14:18:16 +12:00
Jesse Hills
54337befc2 Fix some lint errors in pylint 2.10.2 (#2226) 2021-08-31 14:00:58 +12:00
Jérôme Laban
140ef791aa Support for the AirThings Wave Plus (#1656)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-08-31 14:00:30 +12:00
dependabot[bot]
58350b6c99 Bump pytest from 6.2.4 to 6.2.5 (#2223)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-31 11:10:52 +12:00
dependabot[bot]
f186ff8b46 Bump black from 21.7b0 to 21.8b0 (#2222)
Bumps [black](https://github.com/psf/black) from 21.7b0 to 21.8b0.
- [Release notes](https://github.com/psf/black/releases)
- [Changelog](https://github.com/psf/black/blob/main/CHANGES.md)
- [Commits](https://github.com/psf/black/commits)

---
updated-dependencies:
- dependency-name: black
  dependency-type: direct:production
...

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-31 10:40:06 +12:00
WeekendWarrior1
03190611bb Add H-Bridge fan component (#2212)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-08-31 10:10:22 +12:00
Guillermo Ruffino
37f322585e Glmnet schema 202105 (#2220) 2021-08-31 06:48:19 +12:00
Marcio Granzotto Rodrigues
9218e85bd6 Remove footer validation for fujitsu_general (#2196) 2021-08-31 06:03:30 +12:00
Jesse Hills
f923ba87c0 Bump dashboard to 20210826.0 (#2211) 2021-08-30 09:41:14 +12:00
Oxan van Leeuwen
fac49896df Update known boards (#2190) 2021-08-30 09:41:05 +12:00
Paul Monigatti
56225701f9 Fix Packages when using MQTT (#2210) 2021-08-30 09:27:38 +12:00
Alessandro Campolo
b5de43b225 cs_pin made optional for ili9341 (#2219) 2021-08-30 09:07:06 +12:00
Jesse Hills
b955527f6c Fix css/js file loading for webserver when esphome not executed form config directory (#2207) 2021-08-26 15:34:39 +12:00
marsjan155
94b28102f5 Add st7920 display, (#1440)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-08-26 14:33:03 +12:00
WeekendWarrior1
de871862a8 Optionally set direction on fan.turn_on action (#2171)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-08-26 10:25:24 +12:00
Jesse Hills
3be56fd502 Fix SDM energy units to be KILO... (#2206) 2021-08-26 09:27:00 +12:00
Jesse Hills
5086cd716f Merge pull request #2203 from esphome/bump-2021.8.2
2021.8.2
2021-08-25 19:59:13 +12:00
Jesse Hills
4937af0cd9 Bump version to 2021.8.2 2021-08-25 19:46:55 +12:00
Jesse Hills
877a5fda41 Revert "Light: include ON_OFF capability to BRIGHTNESS ColorMode (#2186)" (#2202)
This reverts commit b0fa317302.
2021-08-25 19:46:54 +12:00
Jesse Hills
39cd2838df Revert "Light: include ON_OFF capability to BRIGHTNESS ColorMode (#2186)" (#2202)
This reverts commit b0fa317302.
2021-08-25 19:38:51 +12:00
Stephan Peijnik-Steinwender
565473c90c ST7789V: Make backlight_pin optional (#2180) 2021-08-24 22:57:53 +12:00
Jesse Hills
1fac91a659 Merge pull request #2199 from esphome/bump-2021.8.1
2021.8.1
2021-08-24 14:40:24 +12:00
Jesse Hills
0a4837c1f0 Bump version to 2021.8.1 2021-08-24 14:26:28 +12:00
mtl010957
e7404183a0 Internally all temperature units are Celsius so just send it directly (#1840) 2021-08-24 14:26:28 +12:00
Samuel Sieb
44f8dcfb6e Fix template select lambda (#2198) 2021-08-24 14:26:27 +12:00
Chris Nussbaum
481e0e98f8 Tuya fan component uses enum datapoint type for speed instead of integer (#2182)
Co-authored-by: Chris Nussbaum <chris.nussbaum@protolabs.com>
2021-08-24 14:26:27 +12:00
puuu
9de40c26eb mqtt_light: remove legacy API config that is not compatible with HA 2021.8 (#2183) 2021-08-24 14:26:27 +12:00
Oxan van Leeuwen
ad953f02d1 Fix addressable light control without transitions & effects with transitions (#2187) 2021-08-24 14:26:27 +12:00
puuu
3869e56521 Light: include ON_OFF capability to BRIGHTNESS ColorMode (#2186) 2021-08-24 14:26:27 +12:00
Jesse Hills
63d87b17aa Fix pypi download url (#2177) 2021-08-24 14:26:27 +12:00
mtl010957
ed68a0e773 Internally all temperature units are Celsius so just send it directly (#1840) 2021-08-24 13:38:59 +12:00
Samuel Sieb
e2640c8368 Fix template select lambda (#2198) 2021-08-24 13:26:59 +12:00
Chris Nussbaum
eff626248f Tuya fan component uses enum datapoint type for speed instead of integer (#2182)
Co-authored-by: Chris Nussbaum <chris.nussbaum@protolabs.com>
2021-08-24 13:20:39 +12:00
puuu
ce29a3b07a mqtt_light: remove legacy API config that is not compatible with HA 2021.8 (#2183) 2021-08-24 13:18:40 +12:00
Otto Winter
1b89174558 Store source package in Component for debugging (#2070) 2021-08-24 06:49:19 +12:00
Oxan van Leeuwen
1c1ad32610 Add deprecated attribute to some deprecated types/methods (#2185) 2021-08-24 06:48:12 +12:00
Jesse Hills
71237e2f76 Fix template select log message mentioning number (#2194) 2021-08-23 21:21:30 +12:00
Oxan van Leeuwen
518c271eba Fix addressable light control without transitions & effects with transitions (#2187) 2021-08-23 20:44:24 +12:00
Oxan van Leeuwen
d71996e58d Reduce static RAM usage (#2140) 2021-08-23 20:43:54 +12:00
Oxan van Leeuwen
2f33cd2db5 Remove double scheduling from addressable lights (#1963) 2021-08-23 20:00:38 +12:00
Oxan van Leeuwen
5ec9bb0fb5 Clean-up constant definitions (#2148) 2021-08-23 19:21:30 +12:00
Oxan van Leeuwen
8cc3cbb22e Add macros header with more usable Arduino version defines (#2145) 2021-08-23 19:19:21 +12:00
puuu
b0fa317302 Light: include ON_OFF capability to BRIGHTNESS ColorMode (#2186) 2021-08-21 22:26:24 +12:00
Jesse Hills
5cb56bc677 Set SDM voltage state class to measurement (#2181) 2021-08-19 21:28:58 +12:00
Jesse Hills
21f8fd9fa5 Fix pypi download url (#2177) 2021-08-18 15:39:57 +12:00
Jesse Hills
2100ef63a9 Bump version to 2021.9.0-dev 2021-08-18 15:19:25 +12:00
Jesse Hills
29db77c9c9 Merge branch 'release' into dev 2021-08-18 15:17:33 +12:00
Jesse Hills
2e59ad90cc Fix docker release for new tags without v 2021-08-18 15:10:44 +12:00
Jesse Hills
1b8c9edcde Merge pull request #2176 from esphome/bump-2021.8.0
2021.8.0
2021-08-18 15:03:48 +12:00
Jesse Hills
d4c2a85f9c Bump version to v2021.8.0 2021-08-18 14:25:10 +12:00
Jesse Hills
8c75b87e94 Merge pull request #2175 from esphome/bump-1.21.0b3
1.21.0b3
2021-08-18 11:37:39 +12:00
Jesse Hills
409d4b9d47 Bump version to v1.21.0b3 2021-08-18 11:11:39 +12:00
Jesse Hills
4e3b95d120 Add new total_increasing state-class for Home Assistant 2021.9+ (#2166) 2021-08-18 11:11:39 +12:00
Jesse Hills
61a9c9fa33 Remove specified accuracy_decimals from total_daily_energy (#2174) 2021-08-18 11:11:39 +12:00
Jesse Hills
9c605f2d46 Send dirty states when screen wakes up (#2167) 2021-08-18 11:11:39 +12:00
Franck Nijhof
44bb5a89c8 Add Gas device class to DSMR component (#2169) 2021-08-18 11:11:39 +12:00
Daniel Hyles
cbdb96f105 Add a dummy color temp (#2161) 2021-08-18 11:11:39 +12:00
Oxan van Leeuwen
9ee3463d07 Initialize color temperature to value within range if possible (#2168) 2021-08-18 11:11:39 +12:00
Jesse Hills
f0b14055b6 Add new total_increasing state-class for Home Assistant 2021.9+ (#2166) 2021-08-18 11:04:13 +12:00
Jesse Hills
fbd9e87b51 Remove specified accuracy_decimals from total_daily_energy (#2174) 2021-08-18 10:32:19 +12:00
Jesse Hills
edb3b77916 Send dirty states when screen wakes up (#2167) 2021-08-18 10:22:00 +12:00
Chris Nussbaum
ebaa84617f Total daily energy methods (#2163)
Co-authored-by: Chris Nussbaum <chris.nussbaum@protolabs.com>
2021-08-18 07:16:02 +12:00
Franck Nijhof
8eb18995cb Add device class update to binary sensor (#2170) 2021-08-17 21:44:05 +12:00
Franck Nijhof
ebabf0e7d8 Add Gas device class to DSMR component (#2169) 2021-08-17 21:43:51 +12:00
Maurice Makaay
607e1f823d Minor code cleanup in light components (#2162)
Co-authored-by: Maurice Makaay <mmakaay1@xs4all.net>
2021-08-17 20:12:29 +12:00
Daniel Hyles
3b52a306cd Add a dummy color temp (#2161) 2021-08-17 20:05:37 +12:00
Oxan van Leeuwen
0c370d5897 Initialize color temperature to value within range if possible (#2168) 2021-08-17 14:02:38 +12:00
Jesse Hills
8bf0448f41 Merge pull request #2160 from esphome/bump-1.21.0b2
1.21.0b2
2021-08-16 14:22:28 +12:00
Jesse Hills
14e04eb231 Bump version to v1.21.0b2 2021-08-16 12:32:48 +12:00
Otto Winter
1be9bac3a9 Fix native API log level enum values (#2151) 2021-08-16 12:32:48 +12:00
Keith Burzinski
02b5a3efb8 Thermostat delayed fan mode fix (#2158) 2021-08-16 12:32:48 +12:00
puuu
bd457f64d8 let sensors announce its state_class via mqtt (#2155) 2021-08-16 12:32:48 +12:00
Oxan van Leeuwen
9efeea14f2 Always send all light state values in API (#2150) 2021-08-16 12:32:48 +12:00
Otto Winter
9b48ff5775 Fix native API log level enum values (#2151) 2021-08-16 11:57:50 +12:00
Keith Burzinski
117b58ebe6 Thermostat delayed fan mode fix (#2158) 2021-08-16 09:31:48 +12:00
puuu
303b699005 let sensors announce its state_class via mqtt (#2155) 2021-08-16 07:59:29 +12:00
Oxan van Leeuwen
9173da0416 Always send all light state values in API (#2150) 2021-08-16 07:40:34 +12:00
Jesse Hills
d2cd65f5db Merge pull request #2144 from esphome/bump-1.21.0b1
1.21.0b1
2021-08-11 22:09:43 +12:00
Jesse Hills
2735f96516 Fix bad merge again 2021-08-11 21:54:24 +12:00
Jesse Hills
6847645782 Fix bad merge conflict 2021-08-11 21:45:04 +12:00
Jesse Hills
b0bc898278 Bump version to v1.21.0b1 2021-08-11 21:27:39 +12:00
Jesse Hills
c0f6af7213 Merge branch 'dev' into bump-1.21.0b1 2021-08-11 21:26:26 +12:00
Jesse Hills
5edebaf468 Bump version to v1.22.0-dev 2021-08-11 19:48:46 +12:00
Oxan van Leeuwen
d436409153 Support multiple configuration directories for update-all subcommand (#1925) 2021-08-11 17:21:57 +12:00
Branimir Lambov
8c41fc2b1d Support for the DKE screen version of LilyGo-TTGO-T5 V2.3 (#1969)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-08-11 17:14:17 +12:00
Oxan van Leeuwen
46f17bea66 Modular light transformers (#2124) 2021-08-11 16:51:35 +12:00
Stefan Agner
11477dbc03 Fix format warning in Tuya component (#1954) 2021-08-11 16:50:05 +12:00
buxtronix
947c104eff Support for AM43 BLE blind motors (#1744)
Co-authored-by: Ben Buxton <bb@cactii.net>
Co-authored-by: Otto Winter <otto@otto-winter.com>
Co-authored-by: Stefan Agner <stefan@agner.ch>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
Co-authored-by: René Klomp <rene@klomp.ws>
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
Co-authored-by: Geoff Davis <geoff@geoffdavis.com>
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
Co-authored-by: dentra <dentra@users.noreply.github.com>
Co-authored-by: Guillermo Ruffino <glm.net@gmail.com>
Co-authored-by: Franck Nijhof <frenck@frenck.nl>
Co-authored-by: Barry Loong <loongyh@users.noreply.github.com>
Co-authored-by: Sergey V. DUDANOV <sergey.dudanov@gmail.com>
Co-authored-by: Balazs Scheidler <bazsi77@gmail.com>
2021-08-11 16:07:10 +12:00
Stefan Agner
e5366dbbe7 Add deassert_rts_dtr option to force RTS/DTR low when using miniterm (#2089) 2021-08-11 07:55:36 +12:00
Andreas Hergert
d3375193a9 Feature pipsolar anh (#1664)
Co-authored-by: Andreas Hergert <andreas.hergert@otrs.com>
Co-authored-by: Otto Winter <otto@otto-winter.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-08-11 07:48:32 +12:00
Chris Nussbaum
6144ce1fe0 Break the Tuya set_datapoint_value method into separate methods per datapoint type (#2059)
Co-authored-by: Chris Nussbaum <chris.nussbaum@protolabs.com>
Co-authored-by: Trevor North <trevor@freedisc.co.uk>
2021-08-11 07:44:31 +12:00
Dave T
1771e673d2 Warn if underscore character is used in hostname (#2079)
* Prevent underscore character being used in 'name'.

* Restrict underscores in hostnames, not all names.

* Use hostname validator for node name.

* Allow underscore in hostname but warn once.

* Add renaming instructions link to warning.

* Point underscore warning to FAQ section

Co-authored-by: Otto Winter <otto@otto-winter.com>

Co-authored-by: Otto Winter <otto@otto-winter.com>
2021-08-10 14:14:42 +02:00
Jesse Hills
d258e06fd7 Add rgbct and color_temperature light platforms (#2138)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2021-08-10 21:28:56 +12:00
Otto Winter
854f4a8896 Format dev temp idedata (#2142) 2021-08-10 11:14:04 +02:00
Stefan Agner
f94c221a9a Increase task wdt timeout for ESP32/ESP32-C3 (#2096) 2021-08-10 11:10:52 +02:00
Stephen Tierney
6a2f0f5143 Add support for PMSA003i (#1501)
Co-authored-by: Otto Winter <otto@otto-winter.com>
Co-authored-by: steve <steve@Hackintosh.local>
Co-authored-by: Otto winter <otto@otto-winter.com>
2021-08-10 11:00:16 +02:00
WJCarpenter
183e2a8471 Support component tsl2591 (#2131)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
Co-authored-by: WJCarpenter <bill@carpenter.org>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-08-10 10:48:06 +02:00
Oxan van Leeuwen
c6c2842bdb Always abort on allocation when out-of-memory (#2129)
Co-authored-by: Otto winter <otto@otto-winter.com>
2021-08-10 10:46:46 +02:00
Guillermo Ruffino
f26767b65e Dsmr component (#1881)
Co-authored-by: Otto winter <otto@otto-winter.com>
2021-08-10 10:32:16 +02:00
Keith Burzinski
98d32876b5 Thermostat enhancements 2 (#2114) 2021-08-10 10:16:44 +02:00
Trammell Hudson
e5d0f3c036 waveshare_epaper: add support for ttgo t5 b74 variant display (#1869)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-08-10 10:09:46 +02:00
Adrián Panella
cc15aaacbb RFC: status_led: allow to share single light (#1974)
Co-authored-by: Otto winter <otto@otto-winter.com>
2021-08-10 09:55:34 +02:00
Oxan van Leeuwen
553df1d57b Don't mark COLOR_* constants as static in header (#2141) 2021-08-10 09:53:48 +02:00
Tom Matheussen
b92311402a Adds CGPR1 - Qingping Motion & Ambient light sensor support (#1675)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-08-10 17:06:04 +12:00
Jesse Hills
93796491af Allow entities to be disabled by default in HA (#2113)
Co-authored-by: Otto Winter <otto@otto-winter.com>
2021-08-10 13:45:31 +12:00
Oxan van Leeuwen
c038cf27a7 Don't discard cold/warm white brightness in constant brightness mode (#2136) 2021-08-10 13:30:29 +12:00
Otto Winter
1f42d32eb5 Fix some issues with deprecated argv syntax detection (#2127) 2021-08-10 13:27:21 +12:00
Adrián Panella
06bde559da Add Dish Network protocol (#2117)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-08-10 13:26:42 +12:00
Keith Burzinski
922f7167f5 Add new Toshiba AC unit protocol (#1987) 2021-08-10 13:25:11 +12:00
Keith Burzinski
90c0d3e12f Add Toshiba AC generic IR remote protocol (#2019) 2021-08-10 13:21:10 +12:00
Oxan van Leeuwen
bf5f846fc6 Refactor clang-tidy script to use actual compiler flags and includes (#2133)
Co-authored-by: Otto winter <otto@otto-winter.com>
2021-08-09 22:43:18 +02:00
Oxan van Leeuwen
926bcc71ae Only compile protobuf dumping when very verbose logging is enabled (#2139) 2021-08-09 22:32:06 +02:00
Jesse Hills
ea4a458214 Removed unused arguments from rgbww code (#2137) 2021-08-09 16:44:52 +12:00
Jesse Hills
b3ae3e1feb Tidy HA addon (#1937) 2021-08-09 10:30:19 +12:00
buxtronix
fe7af21c91 Anova fahrenheit support (#2126)
Co-authored-by: Ben Buxton <bb@cactii.net>
2021-08-09 08:05:36 +12:00
Sourabh Jaiswal
29f72037fe Added support for Hitachi AC424 remote type (#2101) 2021-08-08 10:59:52 -03:00
Oxan van Leeuwen
1d6b4bfcef Don't stop effects if brightness goes to zero (#2134) 2021-08-07 23:24:47 +12:00
Stefan Agner
5bfac5ec09 Allow multiple unnamed libraries (#2132) 2021-08-07 23:16:34 +12:00
Oxan van Leeuwen
dfffaace26 Drop legacy esphomeyaml command wrapper code (#2130) 2021-08-07 23:15:32 +12:00
Oxan van Leeuwen
1d5f628c7a Add support for ESP8266 Arduino v3.0.1 (#2128) 2021-08-07 23:14:57 +12:00
Jesse Hills
cb8a6f66fa Add state classes to pvvx_mithermometer (#2125) 2021-08-05 12:05:36 +12:00
Oxan van Leeuwen
cb21c7c18d Fix crash when using addressable_set with out-of-range indices (#2120) 2021-08-05 11:30:32 +12:00
Oxan van Leeuwen
0d104776bc Various follow-up fixes to color mode changes (#2118) 2021-08-05 11:28:39 +12:00
Otto Winter
5f27757039 Merge pull request #2123 from esphome/bump-1.20.4
1.20.4
2021-08-04 17:54:53 +02:00
Otto winter
532907219b Bump version to v1.20.4 2021-08-04 17:46:10 +02:00
Otto Winter
eeaba74553 Fix external components not refreshing with default or high refresh time (#2122) 2021-08-04 17:46:10 +02:00
brambo123
dd637582a4 Fix time.on_time triggering if time jumped back (#1806)
Co-authored-by: Otto winter <otto@otto-winter.com>
2021-08-04 17:46:10 +02:00
Carlos Garcia Saura
b0d12aeea1 [duty_cycle] initialize two missing variables (#2088) 2021-08-04 17:46:09 +02:00
Łukasz Śliwiński
bdbd813455 Use proper schema for the analog pin shorthand (#2103)
The wrong error message is displayed like:
> GPIO17 (TOUT) is an analog-only pin on the ESP8266.
in case of the analog pin validation because the method `shorthand_analog_pin` uses wrong `GPIO_FULL_INPUT_PIN_SCHEMA` instead of `GPIO_FULL_ANALOG_PIN_SCHEMA`.
2021-08-04 17:46:09 +02:00
Paul Monigatti
a6fac2b175 Fix min/max keys in MQTT Number to match Home Assistant (#2102) 2021-08-04 17:46:09 +02:00
Guillermo Ruffino
5ce923ea90 fix diplay trigger missing base class (#2099) 2021-08-04 17:46:08 +02:00
Otto Winter
29f0508dc2 Fix PID climate breaks when restoring old modes (#2086) 2021-08-04 17:46:08 +02:00
Otto Winter
3ffa59f0cd Fix climate restore schema changed resulting in invalid restore (#2068)
Co-authored-by: Stefan Agner <stefan@agner.ch>
2021-08-04 17:46:08 +02:00
WeekendWarrior1
790d6ef94c Move configure_rmt() into setup() (#2028) 2021-08-04 17:46:08 +02:00
WeekendWarrior1
7828f48b9a Correctly invert esp32 RMT TX (#2022) 2021-08-04 17:46:07 +02:00
Otto Winter
768c71830b Fix external components not refreshing with default or high refresh time (#2122) 2021-08-04 17:33:17 +02:00
Oxan van Leeuwen
ceb0564ebf Fix mixup between ColorMode and ColorCapability (#2121) 2021-08-04 12:32:42 +12:00
Oxan van Leeuwen
20f7eb7327 Add version argument to ESPDEPRECATED macro (#2116) 2021-08-04 10:43:01 +12:00
Oxan van Leeuwen
441d5bd44d Migrate COLOR constants to Color class & disallow implicit conversions to Color (#2093)
Co-authored-by: Xo Wang <xo@geekshavefeelings.com>
2021-08-04 09:21:57 +12:00
brambo123
9fa19df2ff Fix time.on_time triggering if time jumped back (#1806)
Co-authored-by: Otto winter <otto@otto-winter.com>
2021-08-03 19:56:23 +02:00
Rob Gridley
39f64f597e Add SM16703 to supported FastLED chipsets (#1751)
Co-authored-by: Otto winter <otto@otto-winter.com>
2021-08-03 16:56:29 +02:00
Brett Profitt
160429eb24 Add support for Waveshare E-Paper 4.2" B V2 (#1610)
Co-authored-by: Otto winter <otto@otto-winter.com>
2021-08-03 16:41:53 +02:00
Andreas Hergert
6516c64e67 Add min_save_interval to total_energy/integration for memory wear (#1665)
Co-authored-by: Andreas Hergert <andreas.hergert@otrs.com>
Co-authored-by: Otto Winter <otto@otto-winter.com>
2021-08-03 16:41:34 +02:00
dependabot[bot]
4c8a703084 Bump esptool from 2.8 to 3.1 (#1839)
Bumps [esptool](https://github.com/espressif/esptool) from 2.8 to 3.1.
- [Release notes](https://github.com/espressif/esptool/releases)
- [Commits](https://github.com/espressif/esptool/compare/v2.8...v3.1)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-03 08:49:45 +02:00
Keith Burzinski
335210d788 Thermostat enhancements and code clean-up (#2073) 2021-08-02 11:08:24 +02:00
Jesse Hills
9b04e657db Fix import (#2108) 2021-08-02 20:53:34 +12:00
Jesse Hills
f7311aa025 Dont force 0 state instead of min_power unless explicit config set (#2107) 2021-08-02 20:33:00 +12:00
John "Warthog9" Hawley
fb24e55c8d pmsx003: add standard particle, particle counts (#1694) 2021-08-02 10:32:08 +02:00
Carlos Garcia Saura
b58ca46a46 [duty_cycle] initialize two missing variables (#2088) 2021-08-02 10:28:25 +02:00
Jesse Hills
76991cdcc4 Add select entities and implement template select (#2067)
Co-authored-by: Otto Winter <otto@otto-winter.com>
2021-08-02 20:00:51 +12:00
John K. Luebs
69c7cf783e Fix missing include in light_traits.h (#2105) 2021-08-02 07:13:46 +12:00
Otto Winter
f751c3828e Fix MQTT light include (#2104)
Fixes https://github.com/esphome/issues/issues/2285
2021-08-01 12:33:10 +02:00
Otto Winter
5c65f9f9ad Convert sensor_schema to use kwargs (#2094)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-08-01 12:21:32 +02:00
Eric Severance
81ae6709e4 Fix parity bit calculation for ESP8266SoftwareSerial (#1873) 2021-08-01 20:56:05 +12:00
Łukasz Śliwiński
593a3d48fb Use proper schema for the analog pin shorthand (#2103)
The wrong error message is displayed like:
> GPIO17 (TOUT) is an analog-only pin on the ESP8266.
in case of the analog pin validation because the method `shorthand_analog_pin` uses wrong `GPIO_FULL_INPUT_PIN_SCHEMA` instead of `GPIO_FULL_ANALOG_PIN_SCHEMA`.
2021-08-01 00:37:48 +12:00
Paul Monigatti
a8b90283d8 Fix min/max keys in MQTT Number to match Home Assistant (#2102) 2021-07-31 23:20:10 +12:00
Guillermo Ruffino
80076f935d fix diplay trigger missing base class (#2099) 2021-07-30 16:55:26 +12:00
Jesse Hills
9fbb3659a6 Merge pull request #2098 from esphome/bump-1.20.3
1.20.3
2021-07-30 15:46:22 +12:00
Jesse Hills
fee446c28a Bump version to v1.20.3 2021-07-30 11:00:10 +12:00
Jesse Hills
1d56f0b035 Set pulse meter total to use state class measurement and last reset type auto (#2097) 2021-07-30 11:00:10 +12:00
Otto Winter
34e8979d40 Convert more code to async-def syntax (#2095) 2021-07-30 10:54:10 +12:00
Jesse Hills
2966a62429 Set pulse meter total to use state class measurement and last reset type auto (#2097) 2021-07-30 10:53:33 +12:00
Oxan van Leeuwen
5983ccc55c Color mode implementation (#2012) 2021-07-29 19:11:56 +02:00
Kodey Converse
de382b704c Add device class support to MQTT cover (#2092) 2021-07-29 16:08:48 +02:00
Otto Winter
16dbbfabc6 Add demo integration (#2085) 2021-07-29 11:50:55 +02:00
Otto Winter
af8d04818d Pull ESP32 Wifi fixes from arduino-esp32 (#2069) 2021-07-29 11:44:19 +02:00
Mike Meessen
ee19ef1aac Add support for the HRXL MaxSonar WR series sensors (#2020) 2021-07-29 11:37:31 +02:00
Tyler Menezes
5e2d4e332a Add T6615 (#1170)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-07-29 11:24:36 +02:00
rnauber
c6c857dfff Add support for the TLC5947 24-Channel, 12-Bit PWM LED Driver (#2066)
Co-authored-by: Richard Nauber <richard@nauber.dev>
2021-07-29 11:16:04 +02:00
Nicholas Peters
8dbac20f8b Add SDP3x sensor (#2064)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
Co-authored-by: Otto Winter <otto@otto-winter.com>
2021-07-29 10:57:52 +02:00
Jesse Hills
341fddb9aa Merge pull request #2091 from esphome/bump-1.20.2
1.20.2
2021-07-29 20:42:05 +12:00
Jesse Hills
456824669f Bump version to v1.20.2 2021-07-29 19:51:17 +12:00
Jesse Hills
62f3039d82 Use sensor_schema for total_daily_energy (#2090)
Co-authored-by: Otto Winter <otto@otto-winter.com>
2021-07-29 19:51:17 +12:00
Jesse Hills
be4c718859 HLW8012 - Dump energy sensor config (#2082) 2021-07-29 19:51:17 +12:00
Jesse Hills
c2f9ed7c59 Bump esphome dashboard to 20210728.0 (#2081) 2021-07-29 19:51:17 +12:00
John K. Luebs
bfac6607d1 More Tuya MCU robustness (#2080) 2021-07-29 19:51:17 +12:00
Jesse Hills
513066ba52 Use sensor_schema for total_daily_energy (#2090)
Co-authored-by: Otto Winter <otto@otto-winter.com>
2021-07-29 19:46:15 +12:00
Jesse Hills
316777f757 HLW8012 - Dump energy sensor config (#2082) 2021-07-29 07:53:54 +12:00
Stefan Agner
246950159d Bump ESPAsyncWebServer-esphome to 1.3.0 (#2075) 2021-07-28 21:24:10 +02:00
Otto Winter
31d6a54b06 Fix PID climate breaks when restoring old modes (#2086) 2021-07-28 21:23:41 +02:00
dependabot[bot]
5c3a6164bb Bump pylint from 2.9.5 to 2.9.6 (#2087) 2021-07-28 21:23:24 +02:00
Oxan van Leeuwen
1652914d39 Make light.addressable_set color parameters behave as documented & consistent with elsewhere (#2009) 2021-07-28 19:49:43 +02:00
Otto Winter
618cfd9ec5 Add sensor monetary device_class (#2083) 2021-07-28 15:34:18 +02:00
Peter van Dijk
f97cfe9916 pm1006: add rx-only support (#2038) 2021-07-28 10:41:21 +02:00
Jesse Hills
b9259a0238 Bump esphome dashboard to 20210728.0 (#2081) 2021-07-28 15:18:31 +12:00
John K. Luebs
5abbe385c5 More Tuya MCU robustness (#2080) 2021-07-28 14:01:15 +12:00
Jesse Hills
e43dcded62 Merge pull request #2074 from esphome/bump-1.20.1
1.20.1
2021-07-27 11:00:48 +12:00
Jesse Hills
887081fd71 Bump version to v1.20.1 2021-07-27 09:43:05 +12:00
Otto Winter
71ded24fce Fix MQTT climate custom fan modes without regular ones (#2071) 2021-07-27 09:43:05 +12:00
Chris Nussbaum
1e2a9e8348 Couple more updates for the Tuya component (#2065)
Co-authored-by: Chris Nussbaum <chris.nussbaum@protolabs.com>
2021-07-27 09:43:05 +12:00
buxtronix
64a3aa7092 Log warning about lack of support for Anova nano (#2063)
Co-authored-by: Ben Buxton <bb@cactii.net>
2021-07-27 09:43:05 +12:00
carstenschroeder
fda8dd4ce3 Fixes new auto mode COOL and HEAT after #1994 (#2053) 2021-07-27 09:43:04 +12:00
Sergey V. DUDANOV
1efabd27d8 midea_ac: fix presets implementation (#2054) 2021-07-27 09:43:04 +12:00
Maurice Makaay
caa651e55b Accept change as proposed by black. (#2055) 2021-07-27 09:43:04 +12:00
Otto Winter
b0a3891498 Fix MQTT climate custom fan modes without regular ones (#2071) 2021-07-27 07:46:13 +12:00
Otto Winter
2a9e3d84fd Fix climate restore schema changed resulting in invalid restore (#2068)
Co-authored-by: Stefan Agner <stefan@agner.ch>
2021-07-26 15:19:50 +02:00
Oxan van Leeuwen
a3dcac62f9 Fix a bunch of typos (#2058)
Co-authored-by: Stefan Agner <stefan@agner.ch>
Co-authored-by: Otto Winter <otto@otto-winter.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-07-26 14:48:57 +02:00
Chris Nussbaum
6b535b11f8 Couple more updates for the Tuya component (#2065)
Co-authored-by: Chris Nussbaum <chris.nussbaum@protolabs.com>
2021-07-26 21:39:03 +12:00
Stefan Agner
d9f09a7523 Initial ESP32-C3-DevKitM-1 board support (#2062)
Co-authored-by: Stijn Tintel <stijn@linux-ipv6.be>
2021-07-26 11:10:56 +02:00
Stefan Agner
159744e09e Support library override using named library with repository (#2056) 2021-07-26 10:50:45 +02:00
Stefan Agner
c2637a76f7 Print BLE 128-bit UUIDs according to spec (#2061)
Since the iterator integer counts the bytes backwards we need to
use the complement to 15.
2021-07-26 10:49:38 +02:00
buxtronix
237edd75d1 Log warning about lack of support for Anova nano (#2063)
Co-authored-by: Ben Buxton <bb@cactii.net>
2021-07-26 10:41:54 +02:00
WeekendWarrior1
a34d5e3901 Move configure_rmt() into setup() (#2028) 2021-07-26 09:32:08 +02:00
WeekendWarrior1
1dd43a75f2 Correctly invert esp32 RMT TX (#2022) 2021-07-26 09:20:02 +02:00
Trevor North
1f5cbca509 Merge build flags from platformio_options (#1651) 2021-07-26 08:59:18 +02:00
Oxan van Leeuwen
3749c11f21 Fix clang-format script behaviour without -i + code cleanup (#2002)
Co-authored-by: Stefan Agner <stefan@agner.ch>
2021-07-26 09:54:32 +12:00
Stefan Agner
66cdb761dc Fix minor build issues with Arduino ESP32 2.0.0-rc1 (#2057) 2021-07-24 21:55:25 +12:00
Stefan Agner
f0d9ad6a4e Add TAG to all compile units (#2060)
When using static TAG is only valid in the current compile unit. For
some reason it seems that the current ESP8266/ESP32 compiler use the
instance from ble.cpp, but it seems that this causes issues with newer
compiler leading to compile time errors like this:
In file included from /root/.platformio/packages/framework-arduinoespressif32/cores/esp32/esp32-hal-log.h:164,
                 from /root/.platformio/packages/framework-arduinoespressif32/cores/esp32/esp32-hal.h:71,
                 from /root/.platformio/packages/framework-arduinoespressif32/cores/esp32/Arduino.h:36,
                 from src/esphome/core/esphal.h:3,
                 from src/esphome/core/helpers.h:10,
                 from src/esphome/components/esp32_ble/ble_uuid.h:3,
                 from src/esphome/components/esp32_ble/ble_advertising.cpp:5:
src/esphome/components/esp32_ble/ble_advertising.cpp: In member function 'void esphome::esp32_ble::BLEAdvertising::start()':
src/esphome/components/esp32_ble/ble_advertising.cpp:64:14: error: 'TAG' was not declared in this scope
     ESP_LOGE(TAG, "esp_ble_gap_config_adv_data failed (Advertising): %d", err);
              ^~~
2021-07-24 03:53:59 +12:00
carstenschroeder
03e317d052 Fixes new auto mode COOL and HEAT after #1994 (#2053) 2021-07-22 14:39:57 +02:00
Sergey V. DUDANOV
ba461e51a8 midea_ac: fix presets implementation (#2054) 2021-07-22 14:39:21 +02:00
Maurice Makaay
80949521b6 Accept change as proposed by black. (#2055) 2021-07-22 14:37:42 +02:00
Sourabh Jaiswal
acbb8e9fd0 Added support for Selec Energy Meter (#1993)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-07-22 15:31:28 +12:00
Pasi Suominen
90394a50df Added support for pvvx_mithermometer sensor (#1546)
Co-authored-by: Pasi Suominen <pasiz@pasizdesk.pasiz.net>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-07-22 15:21:08 +12:00
Jesse Hills
5379794f16 Add test5 back to CI (#2052) 2021-07-22 13:24:01 +12:00
Keith Burzinski
0a32321c85 Thermostat fixes+updates 1 (#2032)
Co-authored-by: Otto Winter <otto@otto-winter.com>
2021-07-22 09:09:31 +12:00
dependabot[bot]
c9062599df Bump pylint from 2.9.4 to 2.9.5 (#2050)
Bumps [pylint](https://github.com/PyCQA/pylint) from 2.9.4 to 2.9.5.
- [Release notes](https://github.com/PyCQA/pylint/releases)
- [Changelog](https://github.com/PyCQA/pylint/blob/main/ChangeLog)
- [Commits](https://github.com/PyCQA/pylint/compare/v2.9.4...v2.9.5)

---
updated-dependencies:
- dependency-name: pylint
  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>
2021-07-22 08:58:41 +12:00
Jesse Hills
10a6e9b4ee Merge pull request #2051 from esphome/bump-1.20.0
1.20.0
2021-07-22 08:32:30 +12:00
Jesse Hills
4b8ec44262 Bump version to v1.20.0 2021-07-22 07:55:49 +12:00
Jesse Hills
bd74ed4bc0 Merge branch 'beta' into bump-1.20.0 2021-07-22 07:55:49 +12:00
dependabot[bot]
fc42f14448 Bump pylint from 2.8.2 to 2.9.4 (#2047)
* Bump pylint from 2.8.2 to 2.9.4

Bumps [pylint](https://github.com/PyCQA/pylint) from 2.8.2 to 2.9.4.
- [Release notes](https://github.com/PyCQA/pylint/releases)
- [Changelog](https://github.com/PyCQA/pylint/blob/main/ChangeLog)
- [Commits](https://github.com/PyCQA/pylint/compare/v2.8.2...v2.9.4)

---
updated-dependencies:
- dependency-name: pylint
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

* Fix up functionality needed for latest pylint (#2049)

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Sean Vig <sean.v.775@gmail.com>
2021-07-21 14:40:09 +12:00
Jesse Hills
d01f296420 Merge pull request #2048 from esphome/bump-1.20.0b6
1.20.0b6
2021-07-21 11:35:27 +12:00
Jesse Hills
27112e2ace Bump version to v1.20.0b6 2021-07-21 10:52:48 +12:00
Sean Vig
837930234f Remove superfluous polling on ADS1115 (#2015) 2021-07-21 10:52:48 +12:00
Jesse Hills
e19aa3bbe0 Adding last_reset_type to sensors that should support it. (#2039) 2021-07-21 10:52:48 +12:00
Oxan van Leeuwen
35b5c1ed56 Fix white value transition for addressable lights (#2045) 2021-07-21 10:52:48 +12:00
Sean Vig
3e65e6c69a Remove superfluous polling on ADS1115 (#2015) 2021-07-21 09:35:45 +12:00
Jesse Hills
3b3297d269 Adding last_reset_type to sensors that should support it. (#2039) 2021-07-21 09:20:20 +12:00
Oxan van Leeuwen
fc0deb642a Fix white value transition for addressable lights (#2045) 2021-07-21 08:42:03 +12:00
Stefan Agner
9f2b2f51ff Esp32 c3 support (#2035) 2021-07-20 11:12:22 +02:00
Jesse Hills
c9d93ff685 Merge pull request #2044 from esphome/bump-1.20.0b5
1.20.0b5
2021-07-20 17:27:53 +12:00
Jesse Hills
fa72990a63 Bump version to v1.20.0b5 2021-07-20 17:09:58 +12:00
Otto Winter
e5afb1c4ea ESP32 ADC use esp-idf (#2024)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-07-20 17:09:57 +12:00
Sean Vig
73ead5f328 Correct ADS1115 handling of multiple sensors in continuous mode (#2016)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-07-20 17:09:57 +12:00
Paulus Schoutsen
5c57b51378 Bump dashboard to 20210719.0 (#2043) 2021-07-20 17:09:57 +12:00
Sergey V. DUDANOV
e25935ef21 midea_ac: Fix turbo mode. Preset BOOST. (#2029) 2021-07-20 17:09:57 +12:00
Jesse Hills
c7a52c3894 Add restore_value to template number (#2041) 2021-07-20 17:09:57 +12:00
Otto Winter
01a4b4e82f ESP32 ADC use esp-idf (#2024)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-07-20 17:05:56 +12:00
Sean Vig
766866197b Correct ADS1115 handling of multiple sensors in continuous mode (#2016)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-07-20 17:05:25 +12:00
Paulus Schoutsen
9b5a3cbcd3 Bump dashboard to 20210719.0 (#2043) 2021-07-19 21:44:39 -07:00
Sergey V. DUDANOV
d2ed3b9bec midea_ac: Fix turbo mode. Preset BOOST. (#2029) 2021-07-20 16:26:07 +12:00
Jesse Hills
99d2db42cd Add restore_value to template number (#2041) 2021-07-20 15:40:42 +12:00
Jesse Hills
7619507e6c Convert Arduino boolean to bool (#2042) 2021-07-20 15:31:54 +12:00
Jesse Hills
53a4689ed1 Merge pull request #2040 from esphome/bump-1.20.0b4
1.20.0b4
2021-07-20 12:29:15 +12:00
Jesse Hills
0a82e6e792 Bump version to v1.20.0b4 2021-07-20 10:28:23 +12:00
Jesse Hills
98855e4123 Number and Template Number updates (#2036)
Co-authored-by: Otto winter <otto@otto-winter.com>
2021-07-20 10:28:22 +12:00
Jesse Hills
71d9d64a02 Number and Template Number updates (#2036)
Co-authored-by: Otto winter <otto@otto-winter.com>
2021-07-20 08:22:49 +12:00
Jesse Hills
6a09d7c49b Merge pull request #2034 from esphome/bump-1.20.0b3
1.20.0b3
2021-07-19 08:44:04 +12:00
Jesse Hills
46e50ba53f Bump version to v1.20.0b3 2021-07-19 08:28:58 +12:00
Otto Winter
f1e3ff2ed2 Improve external components error messages (#2026) 2021-07-19 08:28:58 +12:00
Otto Winter
7787fa8f29 Dashboard disable assets caching (#2025) 2021-07-19 08:28:58 +12:00
Otto Winter
70902029f8 GH Actions CI use GHCR (#2027) 2021-07-19 08:28:58 +12:00
Otto Winter
4f9a56c884 Refactor docker build system and workflows (#2023) 2021-07-19 08:28:53 +12:00
Sean Vig
3715ba030b Fix ethernet component hostname handling (#2010)
Co-authored-by: Otto Winter <otto@otto-winter.com>
2021-07-19 08:25:08 +12:00
dependabot[bot]
2e49fd7b48 Bump black from 21.6b0 to 21.7b0 (#2031)
Bumps [black](https://github.com/psf/black) from 21.6b0 to 21.7b0.
- [Release notes](https://github.com/psf/black/releases)
- [Changelog](https://github.com/psf/black/blob/main/CHANGES.md)
- [Commits](https://github.com/psf/black/commits)

---
updated-dependencies:
- dependency-name: black
  dependency-type: direct:production
...

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-07-19 08:09:00 +12:00
Otto Winter
06912b492f Improve external components error messages (#2026) 2021-07-16 10:23:08 +02:00
Otto Winter
442e58b07a Dashboard disable assets caching (#2025) 2021-07-16 10:22:42 +02:00
Otto Winter
799f04efc0 GH Actions CI use GHCR (#2027) 2021-07-15 21:51:52 +02:00
Otto Winter
cc7dbeada6 Refactor docker build system and workflows (#2023) 2021-07-15 21:30:04 +02:00
Sean Vig
45d368e3a1 Always tick mdns in ethernet component (#2018) 2021-07-15 16:12:48 +12:00
Sean Vig
628a94bad3 Fix ethernet component hostname handling (#2010)
Co-authored-by: Otto Winter <otto@otto-winter.com>
2021-07-15 15:45:41 +12:00
Jesse Hills
0c93be97a9 Merge pull request #2017 from esphome/bump-1.20.0b2
1.20.0b2
2021-07-15 14:59:08 +12:00
Jesse Hills
54eb6070fb Bump version to v1.20.0b2 2021-07-15 13:55:58 +12:00
SenexCrenshaw
4dbf1c521e Nextion upload and sensors (#1464)
Co-authored-by: Senex Crenshaw <senexcrenshaw@gmail.com>
2021-07-15 13:55:58 +12:00
SenexCrenshaw
0651716b96 Nextion upload and sensors (#1464)
Co-authored-by: Senex Crenshaw <senexcrenshaw@gmail.com>
2021-07-15 12:51:15 +12:00
Jesse Hills
f30b8f6b3c Merge pull request #2014 from esphome/bump-1.20.0b1
1.20.0b1
2021-07-15 08:17:04 +12:00
Jesse Hills
0992609bf4 Bump version to v1.21.0-dev 2021-07-15 07:45:05 +12:00
Jesse Hills
18c08f24ad Bump version to v1.20.0b1 2021-07-15 07:45:05 +12:00
Jesse Hills
a7f53aea0e Merge branch 'dev' into bump-1.20.0b1 2021-07-15 07:45:05 +12:00
St4n
c399905675 [Teleinfo] do not stop parsing frame if there is only a CRC error (#1999)
Co-authored-by: Stephane Angot <s.angot@meetic-corp.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-07-14 21:21:39 +12:00
Stefan Agner
5cb0c11feb Introduce clamp as a template function (#1953) 2021-07-14 17:08:18 +12:00
WeekendWarrior1
08b67e7aea catch 0.0 in float set_level pre-adjustment (#2013) 2021-07-14 14:43:30 +12:00
Jesse Hills
07ae8ec553 Remove a whole bunch of deprecated/removed stuff (#1981) 2021-07-14 14:42:16 +12:00
Sourabh Jaiswal
04c3a43c17 Added support for havells_solar sensor (#1988)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-07-14 13:05:51 +12:00
dependabot[bot]
b632344596 Bump black from 21.5b1 to 21.6b0 (#2011)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-07-13 11:39:04 +02:00
Oxan van Leeuwen
fb8ec79a52 Color brightness fixes (#2008) 2021-07-13 12:28:29 +12:00
Huub Eikens
7dd16df846 Sgp30 sensor improvements (#1510)
Co-authored-by: Umberto73 <huub@eikens.com>
Co-authored-by: Guillermo Ruffino <glm.net@gmail.com>
2021-07-13 09:21:54 +12:00
Otto Winter
551e9c6111 Bang bang climate new mode meanings (#1996) 2021-07-12 22:56:55 +02:00
Mikko Tervala
cc9f0b3f47 Add support for IBS-TH1 External Sensor (#1983) 2021-07-13 08:55:53 +12:00
monkeyclass
d77c3abdc0 Fixed lolin32 lite key (#2001)
Co-authored-by: monkeyclass <oliver_reinholdt@hotmail.com>
2021-07-13 07:37:34 +12:00
Jesse Hills
dd37a4e04c Add Number entities (from Home Assistant) (#1971)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2021-07-13 07:20:12 +12:00
Oxan van Leeuwen
1f5c79bd17 Fix deprecation message for old climate swing mode methods (#2003) 2021-07-11 16:51:24 +12:00
Maurice Makaay
623570a117 Add state callback to ota component (#1816)
Co-authored-by: Maurice Makaay <mmakaay1@xs4all.net>
Co-authored-by: Guillermo Ruffino <glm.net@gmail.com>
2021-07-11 07:52:19 +12:00
carstenschroeder
cdbc146e5d Climate modes COOL and HEAT are auto modes (#1994) 2021-07-10 11:37:55 +02:00
Otto Winter
7ae611256a Improve climate mode code docs (#1995)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-07-10 11:23:04 +02:00
dependabot[bot]
b62c47fede Bump pytest-asyncio from 0.14.0 to 0.15.1 (#1793)
Bumps [pytest-asyncio](https://github.com/pytest-dev/pytest-asyncio) from 0.14.0 to 0.15.1.
- [Release notes](https://github.com/pytest-dev/pytest-asyncio/releases)
- [Commits](https://github.com/pytest-dev/pytest-asyncio/compare/v0.14.0...v0.15.1)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-07-09 23:28:37 +02:00
dependabot[bot]
99f497c3b8 Bump pytest-cov from 2.11.1 to 2.12.1 (#1855)
Bumps [pytest-cov](https://github.com/pytest-dev/pytest-cov) from 2.11.1 to 2.12.1.
- [Release notes](https://github.com/pytest-dev/pytest-cov/releases)
- [Changelog](https://github.com/pytest-dev/pytest-cov/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest-cov/compare/v2.11.1...v2.12.1)

---
updated-dependencies:
- dependency-name: pytest-cov
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-07-09 23:28:28 +02:00
dependabot[bot]
4f88c2489b Bump protobuf from 3.17.0 to 3.17.3 (#1986)
Bumps [protobuf](https://github.com/protocolbuffers/protobuf) from 3.17.0 to 3.17.3.
- [Release notes](https://github.com/protocolbuffers/protobuf/releases)
- [Changelog](https://github.com/protocolbuffers/protobuf/blob/master/generate_changelog.py)
- [Commits](https://github.com/protocolbuffers/protobuf/compare/v3.17.0...v3.17.3)

---
updated-dependencies:
- dependency-name: protobuf
  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>
2021-07-09 23:27:08 +02:00
Michael Gorven
294ba1fca7 Support custom fan modes in mqtt_climate (#1989)
Co-authored-by: Michael Gorven <michael@gorven.za.net>
2021-07-09 23:25:27 +02:00
Jesse Hills
be61b38a2c Allow WiFi AP to use device name (#1990) 2021-07-09 00:39:37 +12:00
Oxan van Leeuwen
f9797825ad Change color model to fix white channel issues (#1895) 2021-07-08 21:37:47 +12:00
Oxan van Leeuwen
fd4b7d4588 Don't try compat parsing for "esphome version" (#1966) 2021-07-06 10:17:36 +12:00
Jesse Hills
062cedc200 remote_receiver: use config parent receiver for registering dumpers (#1980) 2021-07-06 10:15:29 +12:00
WeekendWarrior1
79b9d0579d Add stepper.set_acceleration and stepper.set_deceleration to stepper component (#1977) 2021-07-05 13:22:43 +12:00
buxtronix
ab31117bf3 Anova ble component (#1752)
Co-authored-by: Ben Buxton <bb@cactii.net>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-07-05 11:59:12 +12:00
Adrián Panella
d31040f5d8 hlw8012: fix constants for BL0937 (#1973)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-07-05 11:09:09 +12:00
dependabot[bot]
52d19fa43d Bump pytest-mock from 3.5.1 to 3.6.1 (#1754)
Bumps [pytest-mock](https://github.com/pytest-dev/pytest-mock) from 3.5.1 to 3.6.1.
- [Release notes](https://github.com/pytest-dev/pytest-mock/releases)
- [Changelog](https://github.com/pytest-dev/pytest-mock/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest-mock/compare/v3.5.1...v3.6.1)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-07-05 10:27:13 +12:00
Martin Weinelt
8ca34f7098 Bump hypothesis from 5.21.0 to 5.49.0 (#1753) 2021-07-05 10:10:59 +12:00
Oxan van Leeuwen
4c4099966a Fix invalid escape sequences in regex (#1814) 2021-07-05 10:04:18 +12:00
Paul Doidge
86ac7f3a59 Time Based Cover: Fixed apparent race condition on ESP32 chips (#1984) 2021-07-05 07:39:18 +12:00
Trevor North
9e400a7857 Fix tuya fan speed send (#1978) 2021-07-04 23:47:22 +12:00
Otto Winter
d5278351da Rename master branch to release (#1976) 2021-07-02 15:42:36 +02:00
definitio
36861595f1 Add device_class support for MQTT integration (#1832) 2021-07-01 15:36:01 +02:00
Frederik Gladhorn
d604321f37 Simplify initializing glyph_data (#1970)
Make it easier to read the initialization with zeros, no loop required.
2021-06-30 20:36:48 +02:00
bazuchan
964ab65497 Climate component for Ballu air conditioners with remote model YKR-K/002E (#1939) 2021-06-28 16:26:30 -03:00
Jesse Hills
3b940b1c04 Set is_valid to true straight away when min_length is 0 (#1960) 2021-06-25 07:09:07 +12:00
Jesse Hills
a91e6a6bdf Merge pull request #1959 from esphome/bump-1.19.4
1.19.4
2021-06-24 13:31:01 +12:00
Jesse Hills
8600620305 Bump version to v1.19.4 2021-06-24 12:49:45 +12:00
Jesse Hills
96721f305f Bump dashboard to 20210623.0 (#1958) 2021-06-24 12:49:45 +12:00
Otto Winter
2bf70d7d00 Compat argv parsing improvements (#1952) 2021-06-24 12:49:45 +12:00
Otto Winter
1d8c170f48 Add climate preset NONE again (#1951) 2021-06-24 12:49:45 +12:00
Otto Winter
6009c7edb4 Disallow power_save_mode NONE if used together with BLE (#1950) 2021-06-24 12:49:44 +12:00
Otto Winter
e3f36c033e API raise minor version for climate changes (#1947) 2021-06-24 12:49:44 +12:00
Otto Winter
d4eb0f1655 Rework climate traits (#1941)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-06-24 12:49:36 +12:00
Jesse Hills
5fca480921 Bump dashboard to 20210623.0 (#1958) 2021-06-24 12:38:59 +12:00
Oxan van Leeuwen
7051f897bc Validate color temperature values for RGBWW/CWWW lights (#1957)
Check if the color temperature of the cold white channel is colder (less) than
the warm white channel.
2021-06-24 00:07:27 +02:00
Otto Winter
2cb3015a28 Compat argv parsing improvements (#1952) 2021-06-23 20:27:08 +02:00
Otto Winter
d0859a7d33 Add climate preset NONE again (#1951) 2021-06-23 20:25:19 +02:00
Jesse Hills
e20ec00071 Merge pull request #1956 from esphome/bump-1.19.3
1.19.3
2021-06-23 20:16:42 +12:00
Jesse Hills
150114d774 Bump version to v1.19.3 2021-06-23 19:39:37 +12:00
Jesse Hills
89dfa5ea82 Bump esphome-dashboard to 20210622.0 (#1955) 2021-06-23 19:39:31 +12:00
Jesse Hills
61ebc629f6 Bump esphome-dashboard to 20210622.0 (#1955) 2021-06-22 22:15:11 -07:00
Stefan Agner
32f2da77f8 More VSCode devcontainer improvements (#1934) 2021-06-22 16:37:05 +02:00
Otto Winter
bfca3f242a Disallow power_save_mode NONE if used together with BLE (#1950) 2021-06-22 10:53:10 +02:00
Stefan Agner
3dfff2930a Improve DHT read timings (#1901)
Make sure that the initial rising edge is properly detected even if
timing is somewhat off.

Set MCU start signal to 1ms for AM2302.
2021-06-22 10:07:14 +02:00
Paulus Schoutsen
027e0de48e Bump dashboard to 20210621.0 (#1946) 2021-06-22 16:17:46 +12:00
Otto Winter
c811141a4f API raise minor version for climate changes (#1947) 2021-06-21 22:02:18 +02:00
Otto Winter
871c0ee2a5 Rework climate traits (#1941)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-06-21 21:17:01 +02:00
Jesse Hills
b8a7741c61 Update generation script to add const (#1945) 2021-06-21 21:27:35 +12:00
Jesse Hills
97aa930ad2 Merge pull request #1943 from esphome/bump-1.19.2
1.19.2
2021-06-21 14:59:11 +12:00
Jesse Hills
2a5def10e7 Bump version to v1.19.2 2021-06-21 14:40:05 +12:00
Jesse Hills
969834e037 Fix bad climate control enum (#1942) 2021-06-21 14:40:05 +12:00
Jesse Hills
d73a44c504 Allow wifi setup to proceed when there is no sta or ap (#1931) 2021-06-21 14:40:05 +12:00
Sergey V. DUDANOV
8aec092ab6 Fix midea_ac query frame (#1940) 2021-06-21 14:40:05 +12:00
Chris Nussbaum
4fa959ba45 Don't send Tuya commands while currently receiving a message (#1886)
Co-authored-by: Chris Nussbaum <chris.nussbaum@protolabs.com>
2021-06-21 14:40:05 +12:00
Jesse Hills
b6011b9353 Fix bad climate control enum (#1942) 2021-06-21 14:17:38 +12:00
Jesse Hills
40a5005d94 Allow wifi setup to proceed when there is no sta or ap (#1931) 2021-06-21 09:00:16 +12:00
Sergey V. DUDANOV
c5eba21ff6 Fix midea_ac query frame (#1940) 2021-06-21 08:59:12 +12:00
Jesse Hills
4891cfef56 Add data sizes to tuya log message (#1938) 2021-06-18 15:50:56 +12:00
Chris Nussbaum
4395664547 Don't send Tuya commands while currently receiving a message (#1886)
Co-authored-by: Chris Nussbaum <chris.nussbaum@protolabs.com>
2021-06-18 14:58:39 +12:00
Keith Burzinski
04d926af39 Add variable bit width for Samsung protocol (#1927) 2021-06-18 13:54:46 +12:00
Stefan Agner
f9a31c1abb Fix error print in script/helpers.py (#1935) 2021-06-18 13:49:25 +12:00
Jesse Hills
b43712d78d Merge pull request #1936 from esphome/bump-1.19.1
1.19.1
2021-06-18 12:10:30 +12:00
Jesse Hills
01904a0f10 Bump version to v1.19.1 2021-06-18 11:52:02 +12:00
Jesse Hills
dd875e7529 Replace CLIMATE_MODE_AUTO with CLIMATE_MODE_HEAT_COOL in most cases (#1933) 2021-06-18 11:52:02 +12:00
Otto Winter
f1dcf0f0b8 Improve config final validation (#1917) 2021-06-18 11:52:02 +12:00
Sergey V. DUDANOV
a045d001bf Fix: midea_ac: fixed query status frame (#1922) 2021-06-18 11:52:01 +12:00
Paulus Schoutsen
066c1022d0 Update dashboard to 20210617.0 (#1930) 2021-06-18 11:52:01 +12:00
Jesse Hills
dca1c0f160 Replace CLIMATE_MODE_AUTO with CLIMATE_MODE_HEAT_COOL in most cases (#1933) 2021-06-18 11:48:40 +12:00
Otto Winter
2419bc3678 Improve config final validation (#1917) 2021-06-18 07:54:14 +12:00
Sergey V. DUDANOV
c19b3ecd43 Fix: midea_ac: fixed query status frame (#1922) 2021-06-18 07:39:13 +12:00
Paulus Schoutsen
ef1e91d838 Update dashboard to 20210617.0 (#1930) 2021-06-18 07:35:54 +12:00
Barry Loong
e5929225eb Fix typo in test3.yaml (#1928) 2021-06-17 21:39:59 +12:00
Jesse Hills
59c192becc Merge pull request #1923 from esphome/bump-1.19.0
1.19.0
2021-06-17 06:09:09 +12:00
Jesse Hills
a800816750 Merge branch 'beta' into bump-1.19.0 2021-06-17 05:59:02 +12:00
Jesse Hills
99d9ab4e40 Merge pull request #1926 from esphome/bump-1.19.0b7
1.19.0b7
2021-06-17 05:55:09 +12:00
Jesse Hills
f310ca1b74 Bump version to v1.19.0b7 2021-06-17 05:40:55 +12:00
Franck Nijhof
f763daa577 Fix update-all from dashboard (#1924) 2021-06-17 05:40:55 +12:00
Franck Nijhof
607c3ae651 Fix update-all from dashboard (#1924) 2021-06-17 05:39:04 +12:00
Jesse Hills
970563e07b Bump version to v1.19.0 2021-06-16 21:00:51 +12:00
Jesse Hills
e006045f59 Merge pull request #1921 from esphome/bump-1.19.0b6
1.19.0b6
2021-06-16 15:42:43 +12:00
Jesse Hills
fff3645901 Bump version to v1.19.0b6 2021-06-16 13:51:31 +12:00
Jesse Hills
a5383fd208 Shorten the ble name to prevent crash with long device names (#1920) 2021-06-16 13:51:31 +12:00
Jesse Hills
5591832b50 Shorten the ble name to prevent crash with long device names (#1920) 2021-06-16 13:49:06 +12:00
Paulus Schoutsen
25b116048c Bump dashboard to 20210615.0 (#1918) 2021-06-16 09:04:17 +12:00
Guillermo Ruffino
24ba9eba46 fixes compatibility with esphome cfg vscode (#1911) 2021-06-15 20:20:24 +12:00
Otto Winter
424c34225f Run script/setup in devcontainer instead of pip install (#1913) 2021-06-15 20:19:21 +12:00
Paulus Schoutsen
d781f3a11b Bump frontend to 20210614.0 (#1912) 2021-06-15 20:18:27 +12:00
Stefan Agner
a80f9ed336 Support ESP8266 Arduino 3.0.0 (#1897)
Co-authored-by: Otto Winter <otto@otto-winter.com>
2021-06-15 08:50:58 +02:00
Otto Winter
92bbedfa5a Fix #1908 mutating input parameter 2021-06-15 08:48:18 +02:00
Jesse Hills
86710ed483 Validate that either networks, ap, or improv is set up (#1910) 2021-06-15 13:16:43 +12:00
Jesse Hills
da7eb9ac90 Allow no networks or AP to be set. (#1908)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2021-06-15 11:05:10 +12:00
dentra
c411043681 Adds support cpp to vscode (#1828)
Co-authored-by: Stefan Agner <stefan@agner.ch>
2021-06-15 10:45:22 +12:00
Oxan van Leeuwen
93f8ee7e60 Fix non-const global (#1907) 2021-06-15 09:12:06 +12:00
Oxan van Leeuwen
0efc1f06f2 Split files in light component (#1893) 2021-06-14 18:01:56 +02:00
Paulus Schoutsen
9ad9d64ac7 Add new wizard + allow installing firmware over webserial (#1887) 2021-06-12 10:49:05 +12:00
Jesse Hills
5a2cfa2798 Move esp32_ble_server to its own component (#1898) 2021-06-12 08:31:15 +12:00
Jesse Hills
eb24da7c82 Ensure wifi is in at least station mode before starting improv (#1899) 2021-06-11 23:28:00 +12:00
Oxan van Leeuwen
f93e261d75 Convert st7735.cpp to use Unix line separators (#1894) 2021-06-11 08:33:12 +12:00
Stefan Agner
501f88ca86 Avoid non-const globals and enable clang-tidy check (#1892) 2021-06-11 08:19:44 +12:00
Otto Winter
360effcb72 Activate some clang-tidy checks (#1884) 2021-06-10 13:04:40 +02:00
Otto Winter
eb9bd69405 Configure clang-format for consistent pointer alignment (#1890) 2021-06-10 12:55:20 +02:00
Jesse Hills
11b8210e36 Update ambiguous command (#1889) 2021-06-10 21:13:18 +12:00
Geoff Davis
d23376b81e Add support for waveshare_epaper 1.54v2 (#1843)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-06-10 14:05:27 +12:00
Jesse Hills
99d90845b5 BLE loop use (#1882) 2021-06-10 14:04:39 +12:00
Oxan van Leeuwen
ea0127e42b Simplify LightCall validation (#1874) 2021-06-09 17:01:05 +02:00
1655 changed files with 82168 additions and 27571 deletions

View File

@@ -49,7 +49,7 @@ ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: true
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true

View File

@@ -2,38 +2,29 @@
Checks: >-
*,
-abseil-*,
-altera-*,
-android-*,
-boost-*,
-bugprone-branch-clone,
-bugprone-macro-parentheses,
-bugprone-narrowing-conversions,
-bugprone-reserved-identifier,
-bugprone-signed-char-misuse,
-bugprone-suspicious-include,
-bugprone-too-small-loop-variable,
-bugprone-unhandled-self-assignment,
-cert-dcl37-c,
-cert-dcl50-cpp,
-cert-dcl51-cpp,
-cert-err58-cpp,
-cert-oop54-cpp,
-cert-oop57-cpp,
-cert-str34-c,
-clang-analyzer-core.CallAndMessage,
-clang-analyzer-optin.*,
-clang-analyzer-optin.cplusplus.UninitializedObject,
-clang-analyzer-osx.*,
-clang-analyzer-security.*,
-clang-diagnostic-delete-abstract-non-virtual-dtor,
-clang-diagnostic-delete-non-abstract-non-virtual-dtor,
-clang-diagnostic-shadow-field,
-clang-diagnostic-unused-const-variable,
-clang-diagnostic-unused-parameter,
-concurrency-*,
-cppcoreguidelines-avoid-c-arrays,
-cppcoreguidelines-avoid-goto,
-cppcoreguidelines-avoid-magic-numbers,
-cppcoreguidelines-avoid-non-const-global-variables,
-cppcoreguidelines-c-copy-assignment-signature,
-cppcoreguidelines-init-variables,
-cppcoreguidelines-macro-usage,
-cppcoreguidelines-narrowing-conversions,
-cppcoreguidelines-non-private-member-variables-in-classes,
-cppcoreguidelines-owning-memory,
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
-cppcoreguidelines-pro-bounds-constant-array-index,
-cppcoreguidelines-pro-bounds-pointer-arithmetic,
@@ -45,17 +36,17 @@ Checks: >-
-cppcoreguidelines-pro-type-union-access,
-cppcoreguidelines-pro-type-vararg,
-cppcoreguidelines-special-member-functions,
-fuchsia-*,
-fuchsia-default-arguments,
-fuchsia-multiple-inheritance,
-fuchsia-overloaded-operator,
-fuchsia-statically-constructed-objects,
-fuchsia-default-arguments-declarations,
-fuchsia-default-arguments-calls,
-google-build-using-namespace,
-google-explicit-constructor,
-google-readability-braces-around-statements,
-google-readability-casting,
-google-readability-namespace-comments,
-google-readability-todo,
-google-runtime-int,
-google-runtime-references,
-hicpp-*,
-llvm-else-after-return,
@@ -65,39 +56,29 @@ Checks: >-
-llvmlibc-*,
-misc-non-private-member-variables-in-classes,
-misc-no-recursion,
-misc-unconventional-assign-operator,
-misc-unused-parameters,
-modernize-avoid-c-arrays,
-modernize-deprecated-headers,
-modernize-pass-by-value,
-modernize-pass-by-value,
-modernize-avoid-bind,
-modernize-concat-nested-namespaces,
-modernize-return-braced-init-list,
-modernize-use-auto,
-modernize-use-default-member-init,
-modernize-use-equals-default,
-modernize-use-trailing-return-type,
-modernize-use-nodiscard,
-mpi-*,
-objc-*,
-performance-unnecessary-value-param,
-readability-braces-around-statements,
-readability-const-return-type,
-readability-convert-member-functions-to-static,
-readability-else-after-return,
-readability-function-cognitive-complexity,
-readability-implicit-bool-conversion,
-readability-isolate-declaration,
-readability-magic-numbers,
-readability-make-member-function-const,
-readability-named-parameter,
-readability-qualified-auto,
-readability-redundant-access-specifiers,
-readability-redundant-member-init,
-readability-redundant-string-init,
-readability-uppercase-literal-suffix,
-readability-use-anyofallof,
-warnings-as-errors,
-zircon-*
WarningsAsErrors: '*'
HeaderFilterRegex: '^.*/src/esphome/.*'
AnalyzeTemporaryDtors: false
FormatStyle: google
CheckOptions:
@@ -105,9 +86,11 @@ CheckOptions:
value: '1'
- key: google-readability-function-size.StatementThreshold
value: '800'
- key: google-readability-namespace-comments.ShortNamespaceLines
- key: google-runtime-int.TypeSuffix
value: '_t'
- key: llvm-namespace-comment.ShortNamespaceLines
value: '10'
- key: google-readability-namespace-comments.SpacesBeforeComments
- key: llvm-namespace-comment.SpacesBeforeComments
value: '2'
- key: modernize-loop-convert.MaxCopySize
value: '16'
@@ -121,6 +104,12 @@ CheckOptions:
value: llvm
- key: modernize-use-nullptr.NullMacros
value: 'NULL'
- key: modernize-make-unique.MakeSmartPtrFunction
value: 'make_unique'
- key: modernize-make-unique.MakeSmartPtrFunctionHeader
value: 'esphome/core/helpers.h'
- key: readability-braces-around-statements.ShortStatementLines
value: 2
- key: readability-identifier-naming.LocalVariableCase
value: 'lower_case'
- key: readability-identifier-naming.ClassCase
@@ -134,15 +123,19 @@ CheckOptions:
- key: readability-identifier-naming.StaticConstantCase
value: 'UPPER_CASE'
- key: readability-identifier-naming.StaticVariableCase
value: 'UPPER_CASE'
value: 'lower_case'
- key: readability-identifier-naming.GlobalConstantCase
value: 'UPPER_CASE'
- key: readability-identifier-naming.ParameterCase
value: 'lower_case'
- key: readability-identifier-naming.PrivateMemberPrefix
value: 'NO_PRIVATE_MEMBERS_ALWAYS_USE_PROTECTED'
- key: readability-identifier-naming.PrivateMethodPrefix
value: 'NO_PRIVATE_METHODS_ALWAYS_USE_PROTECTED'
- key: readability-identifier-naming.PrivateMemberCase
value: 'lower_case'
- key: readability-identifier-naming.PrivateMemberSuffix
value: '_'
- key: readability-identifier-naming.PrivateMethodCase
value: 'lower_case'
- key: readability-identifier-naming.PrivateMethodSuffix
value: '_'
- key: readability-identifier-naming.ClassMemberCase
value: 'lower_case'
- key: readability-identifier-naming.ClassMemberCase
@@ -163,3 +156,5 @@ CheckOptions:
value: 'lower_case'
- key: readability-identifier-naming.VirtualMethodSuffix
value: ''
- key: readability-qualified-auto.AddConstToQualified
value: 0

View File

@@ -1,17 +1,29 @@
{
"name": "ESPHome Dev",
"context": "..",
"dockerFile": "../docker/Dockerfile.dev",
"postCreateCommand": "mkdir -p config && pip3 install -e .",
"runArgs": ["--privileged", "-e", "ESPHOME_DASHBOARD_USE_PING=1"],
"image": "esphome/esphome-lint:dev",
"postCreateCommand": [
"script/devcontainer-post-create"
],
"runArgs": [
"--privileged",
"-e",
"ESPHOME_DASHBOARD_USE_PING=1"
],
"appPort": 6052,
"extensions": [
// python
"ms-python.python",
"visualstudioexptteam.vscodeintellicode",
"redhat.vscode-yaml"
// yaml
"redhat.vscode-yaml",
// cpp
"ms-vscode.cpptools",
// editorconfig
"editorconfig.editorconfig",
],
"settings": {
"python.pythonPath": "/usr/local/bin/python",
"python.languageServer": "Pylance",
"python.pythonPath": "/usr/bin/python3",
"python.linting.pylintEnabled": true,
"python.linting.enabled": true,
"python.formatting.provider": "black",
@@ -19,7 +31,7 @@
"editor.formatOnSave": true,
"editor.formatOnType": true,
"files.trimTrailingWhitespace": true,
"terminal.integrated.shell.linux": "/bin/bash",
"terminal.integrated.defaultProfile.linux": "bash",
"yaml.customTags": [
"!secret scalar",
"!lambda scalar",
@@ -27,6 +39,18 @@
"!include_dir_list scalar",
"!include_dir_merge_list scalar",
"!include_dir_merge_named scalar"
]
],
"files.exclude": {
"**/.git": true,
"**/.DS_Store": true,
"**/*.pyc": {
"when": "$(basename).py"
},
"**/__pycache__": true
},
"files.associations": {
"**/.vscode/*.json": "jsonc"
},
"C_Cpp.clang_format_path": "/usr/bin/clang-format-11",
}
}

View File

@@ -103,6 +103,10 @@ venv.bak/
# mypy
.mypy_cache/
# PlatformIO
.pio/
# ESPHome
config/
examples/
Dockerfile

View File

@@ -7,7 +7,7 @@ insert_final_newline = true
charset = utf-8
# python
[*.{py}]
[*.py]
indent_style = space
indent_size = 4
@@ -25,4 +25,10 @@ indent_size = 2
[*.{yaml,yml}]
indent_style = space
indent_size = 2
quote_type = single
quote_type = single
# JSON
[*.json]
indent_style = space
indent_size = 2

2
.gitattributes vendored Normal file
View File

@@ -0,0 +1,2 @@
# Normalize line endings to LF in the repository
* text eol=lf

View File

@@ -9,4 +9,3 @@ contact_links:
- 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.

View File

@@ -1,4 +1,4 @@
# What does this implement/fix?
# What does this implement/fix?
Quick description and explanation of changes
@@ -16,6 +16,7 @@ Quick description and explanation of changes
## Test Environment
- [ ] ESP32
- [ ] ESP32 IDF
- [ ] ESP8266
## Example entry for `config.yaml`:
@@ -34,6 +35,6 @@ Quick description and explanation of changes
## Checklist:
- [ ] The code change is tested and works locally.
- [ ] Tests have been added to verify that the new code works (under `tests/` folder).
If user exposed functionality or configuration variables are added/changed:
- [ ] Documentation added/updated in [esphome-docs](https://github.com/esphome/esphome-docs).

View File

@@ -1,7 +0,0 @@
comment: >-
https://github.com/esphome/esphome/issues/430
issueConfigs:
- content:
- "OTHERWISE THE ISSUE WILL BE CLOSED AUTOMATICALLY"
caseInsensitive: false

36
.github/lock.yml vendored
View File

@@ -1,36 +0,0 @@
# Configuration for Lock Threads - https://github.com/dessant/lock-threads
# Number of days of inactivity before a closed issue or pull request is locked
daysUntilLock: 7
# Skip issues and pull requests created before a given timestamp. Timestamp must
# follow ISO 8601 (`YYYY-MM-DD`). Set to `false` to disable
skipCreatedBefore: false
# Issues and pull requests with these labels will be ignored. Set to `[]` to disable
exemptLabels:
- keep-open
# Label to add before locking, such as `outdated`. Set to `false` to disable
lockLabel: false
# Comment to post before locking. Set to `false` to disable
lockComment: false
# Assign `resolved` as the reason for locking. Set to `false` to disable
setLockReason: false
# Limit to only `issues` or `pulls`
# only: issues
# Optionally, specify configuration settings just for `issues` or `pulls`
# issues:
# exemptLabels:
# - help-wanted
# lockLabel: outdated
# pulls:
# daysUntilLock: 30
# Repository to extend settings from
# _extends: repo

59
.github/stale.yml vendored
View File

@@ -1,59 +0,0 @@
# Configuration for probot-stale - https://github.com/probot/stale
# Number of days of inactivity before an Issue or Pull Request becomes stale
daysUntilStale: 60
# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
daysUntilClose: 7
# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled)
onlyLabels: []
# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
exemptLabels:
- not-stale
# Set to true to ignore issues in a project (defaults to false)
exemptProjects: false
# Set to true to ignore issues in a milestone (defaults to false)
exemptMilestones: true
# Set to true to ignore issues with an assignee (defaults to false)
exemptAssignees: false
# Label to use when marking as stale
staleLabel: stale
# Comment to post when marking as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when removing the stale label.
# unmarkComment: >
# Your comment here.
# Comment to post when closing a stale Issue or Pull Request.
# closeComment: >
# Your comment here.
# Limit the number of actions per hour, from 1-30. Default is 30
limitPerRun: 10
# Limit to only `issues` or `pulls`
only: pulls
# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls':
# pulls:
# daysUntilStale: 30
# markComment: >
# This pull request has been automatically marked as stale because it has not had
# recent activity. It will be closed if no further activity occurs. Thank you
# for your contributions.
# issues:
# exemptLabels:
# - confirmed

View File

@@ -3,53 +3,51 @@ name: CI for docker images
# Only run when docker paths change
on:
push:
branches: [dev, beta, master]
branches: [dev, beta, release]
paths:
- 'docker/**'
- '.github/workflows/**'
- 'requirements*.txt'
- 'platformio.ini'
pull_request:
paths:
- 'docker/**'
- '.github/workflows/**'
- 'requirements*.txt'
- 'platformio.ini'
permissions:
contents: read
packages: read
jobs:
check-docker:
name: Build docker containers
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
arch: [amd64, armv7, aarch64]
build_type: ["hassio", "docker"]
build_type: ["ha-addon", "docker", "lint"]
steps:
- uses: actions/checkout@v2
- name: Set up env variables
run: |
base_version="3.4.0"
- 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
if [[ "${{ matrix.build_type }}" == "hassio" ]]; then
build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}"
build_to="esphome/esphome-hassio-${{ matrix.arch }}"
dockerfile="docker/Dockerfile.hassio"
else
build_from="esphome/esphome-base-${{ matrix.arch }}:${base_version}"
build_to="esphome/esphome-${{ matrix.arch }}"
dockerfile="docker/Dockerfile"
fi
- name: Set TAG
run: |
echo "TAG=check" >> $GITHUB_ENV
echo "BUILD_FROM=${build_from}" >> $GITHUB_ENV
echo "BUILD_TO=${build_to}" >> $GITHUB_ENV
echo "DOCKERFILE=${dockerfile}" >> $GITHUB_ENV
- name: Pull for cache
run: |
docker pull "${BUILD_TO}:dev" || true
- name: Register QEMU binfmt
run: docker run --rm --privileged multiarch/qemu-user-static:5.2.0-2 --reset -p yes
- run: |
docker build \
--build-arg "BUILD_FROM=${BUILD_FROM}" \
--build-arg "BUILD_VERSION=ci" \
--cache-from "${BUILD_TO}:dev" \
--file "${DOCKERFILE}" \
.
- name: Run build
run: |
docker/build.py \
--tag "${TAG}" \
--arch "${{ matrix.arch }}" \
--build-type "${{ matrix.build_type }}" \
build

View File

@@ -1,161 +1,166 @@
# THESE JOBS ARE COPIED IN release.yml and release-dev.yml
# PLEASE ALSO UPDATE THOSE FILES WHEN CHANGING LINES HERE
name: CI
on:
push:
# On dev branch release-dev already performs CI checks
# On other branches the `pull_request` trigger will be used
branches: [beta, master]
branches: [dev, beta, release]
pull_request:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
lint-clang-format:
ci:
name: ${{ matrix.name }}
runs-on: ubuntu-latest
# cpp lint job runs with esphome-lint docker image so that clang-format-*
# doesn't have to be installed
container: esphome/esphome-lint:1.1
steps:
- uses: actions/checkout@v2
# Set up the pio project so that the cpp checks know how files are compiled
# (build flags, libraries etc)
- name: Set up platformio environment
run: pio init --ide atom
- name: Run clang-format
run: script/clang-format -i
- name: Suggest changes
run: script/ci-suggest-changes
lint-clang-tidy:
runs-on: ubuntu-latest
# cpp lint job runs with esphome-lint docker image so that clang-format-*
# doesn't have to be installed
container: esphome/esphome-lint:1.1
# Split clang-tidy check into 4 jobs. Each one will check 1/4th of the .cpp files
strategy:
fail-fast: false
matrix:
split: [1, 2, 3, 4]
steps:
- uses: actions/checkout@v2
# Set up the pio project so that the cpp checks know how files are compiled
# (build flags, libraries etc)
- name: Set up platformio environment
run: pio init --ide atom
include:
- id: ci-custom
name: Run script/ci-custom
- id: lint-python
name: Run script/lint-python
- id: test
file: tests/test1.yaml
name: Test tests/test1.yaml
pio_cache_key: test1
- id: test
file: tests/test2.yaml
name: Test tests/test2.yaml
pio_cache_key: test2
- id: test
file: tests/test3.yaml
name: Test tests/test3.yaml
pio_cache_key: test3
- id: test
file: tests/test4.yaml
name: Test tests/test4.yaml
pio_cache_key: test4
- id: test
file: tests/test5.yaml
name: Test tests/test5.yaml
pio_cache_key: test5
- id: pytest
name: Run pytest
- id: clang-format
name: Run script/clang-format
- id: clang-tidy
name: Run script/clang-tidy for ESP8266
options: --environment esp8266-arduino-tidy --grep USE_ESP8266
pio_cache_key: tidyesp8266
- id: clang-tidy
name: Run script/clang-tidy for ESP32 Arduino 1/4
options: --environment esp32-arduino-tidy --split-num 4 --split-at 1
pio_cache_key: tidyesp32
- id: clang-tidy
name: Run script/clang-tidy for ESP32 Arduino 2/4
options: --environment esp32-arduino-tidy --split-num 4 --split-at 2
pio_cache_key: tidyesp32
- id: clang-tidy
name: Run script/clang-tidy for ESP32 Arduino 3/4
options: --environment esp32-arduino-tidy --split-num 4 --split-at 3
pio_cache_key: tidyesp32
- id: clang-tidy
name: Run script/clang-tidy for ESP32 Arduino 4/4
options: --environment esp32-arduino-tidy --split-num 4 --split-at 4
pio_cache_key: tidyesp32
- id: clang-tidy
name: Run script/clang-tidy for ESP32 IDF
options: --environment esp32-idf-tidy --grep USE_ESP_IDF
pio_cache_key: tidyesp32-idf
- name: Register problem matchers
run: |
echo "::add-matcher::.github/workflows/matchers/clang-tidy.json"
echo "::add-matcher::.github/workflows/matchers/gcc.json"
- name: Run clang-tidy
run: script/clang-tidy --all-headers --fix --split-num 4 --split-at ${{ matrix.split }}
- name: Suggest changes
run: script/ci-suggest-changes
lint-python:
# Don't use the esphome-lint docker image because it may contain outdated requirements.
# This way, all dependencies are cached via the cache action.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
id: python
with:
python-version: '3.7'
- name: Cache pip modules
uses: actions/cache@v1
python-version: '3.8'
- name: Cache virtualenv
uses: actions/cache@v2
with:
path: ~/.cache/pip
key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
path: .venv
key: venv-${{ steps.python.outputs.python-version }}-${{ hashFiles('requirements*.txt') }}
restore-keys: |
esphome-pip-3.7-
- name: Set up python environment
run: script/setup
venv-${{ steps.python.outputs.python-version }}-
- name: Set up virtualenv
run: |
python -m venv .venv
source .venv/bin/activate
pip install -U pip
pip install -r requirements.txt -r requirements_optional.txt -r requirements_test.txt
pip install -e .
echo "$GITHUB_WORKSPACE/.venv/bin" >> $GITHUB_PATH
echo "VIRTUAL_ENV=$GITHUB_WORKSPACE/.venv" >> $GITHUB_ENV
# Use per check platformio cache because checks use different parts
- name: Cache platformio
uses: actions/cache@v2
with:
path: ~/.platformio
key: platformio-${{ matrix.pio_cache_key }}-${{ hashFiles('platformio.ini') }}
if: matrix.id == 'test' || matrix.id == 'clang-tidy'
- name: Install clang tools
run: |
sudo apt-get install \
clang-format-11 \
clang-tidy-11
if: matrix.id == 'clang-tidy' || matrix.id == 'clang-format'
- name: Register problem matchers
run: |
echo "::add-matcher::.github/workflows/matchers/ci-custom.json"
echo "::add-matcher::.github/workflows/matchers/lint-python.json"
echo "::add-matcher::.github/workflows/matchers/python.json"
echo "::add-matcher::.github/workflows/matchers/pytest.json"
echo "::add-matcher::.github/workflows/matchers/gcc.json"
echo "::add-matcher::.github/workflows/matchers/clang-tidy.json"
- name: Lint Custom
run: script/ci-custom.py
run: |
script/ci-custom.py
script/build_codeowners.py --check
if: matrix.id == 'ci-custom'
- name: Lint Python
run: script/lint-python
- name: Lint CODEOWNERS
run: script/build_codeowners.py --check
if: matrix.id == 'lint-python'
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
test:
- test1
- test2
- test3
- test4
- test5
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.7'
- name: Cache pip modules
uses: actions/cache@v1
with:
path: ~/.cache/pip
key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
restore-keys: |
esphome-pip-3.7-
# Use per test platformio cache because tests have different platform versions
- name: Cache ~/.platformio
uses: actions/cache@v1
with:
path: ~/.platformio
key: test-home-platformio-${{ matrix.test }}-${{ hashFiles('esphome/core/config.py') }}
restore-keys: |
test-home-platformio-${{ matrix.test }}-
- name: Set up environment
run: script/setup
- run: esphome compile ${{ matrix.file }}
if: matrix.id == 'test'
env:
# Also cache libdeps, store them in a ~/.platformio subfolder
PLATFORMIO_LIBDEPS_DIR: ~/.platformio/libdeps
- name: Register problem matchers
run: |
echo "::add-matcher::.github/workflows/matchers/gcc.json"
echo "::add-matcher::.github/workflows/matchers/python.json"
- run: esphome compile tests/${{ matrix.test }}.yaml
pytest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.7'
- name: Cache pip modules
uses: actions/cache@v1
with:
path: ~/.cache/pip
key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
restore-keys: |
esphome-pip-3.7-
- name: Set up environment
run: script/setup
- name: Install Github Actions annotator
run: pip install pytest-github-actions-annotate-failures
- name: Register problem matchers
run: |
echo "::add-matcher::.github/workflows/matchers/python.json"
- name: Run pytest
run: |
pytest \
-qq \
--durations=10 \
-o console_output_style=count \
tests
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.
- name: Run clang-format
run: |
script/clang-format -i
git diff-index --quiet HEAD --
if: matrix.id == 'clang-format'
- name: Run clang-tidy
run: |
script/clang-tidy --all-headers --fix ${{ matrix.options }}
if: matrix.id == 'clang-tidy'
env:
# Also cache libdeps, store them in a ~/.platformio subfolder
PLATFORMIO_LIBDEPS_DIR: ~/.platformio/libdeps
- name: Suggested changes
run: script/ci-suggest-changes
if: always() && (matrix.id == 'clang-tidy' || matrix.id == 'clang-format')

View File

@@ -1,42 +0,0 @@
name: Build and publish lint docker image
# Only run when docker paths change
on:
push:
branches: [dev]
paths:
- 'docker/Dockerfile.lint'
- 'requirements.txt'
- 'requirements_optional.txt'
- 'requirements_test.txt'
- 'platformio.ini'
- '.github/workflows/docker-lint-build.yml'
jobs:
publish-docker-lint-iage:
name: Build docker containers
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set TAG
run: |
echo "TAG=1.1" >> $GITHUB_ENV
- name: Pull for cache
run: |
docker pull "esphome/esphome-lint:latest" || true
- name: Build
run: |
docker build \
--cache-from "esphome/esphome-lint:latest" \
--file "docker/Dockerfile.lint" \
--tag "esphome/esphome-lint:latest" \
--tag "esphome/esphome-lint:${TAG}" \
.
- name: Log in to docker hub
env:
DOCKER_USER: ${{ secrets.DOCKER_USER }}
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
run: docker login -u "${DOCKER_USER}" -p "${DOCKER_PASSWORD}"
- run: |
docker push "esphome/esphome-lint:${TAG}"
docker push "esphome/esphome-lint:latest"

27
.github/workflows/lock.yml vendored Normal file
View File

@@ -0,0 +1,27 @@
name: Lock
on:
schedule:
- cron: '30 0 * * *'
workflow_dispatch:
permissions:
issues: write
pull-requests: write
concurrency:
group: lock
jobs:
lock:
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@v3
with:
pr-inactive-days: "1"
pr-lock-reason: ""
exclude-any-pr-labels: keep-open
issue-inactive-days: "7"
issue-lock-reason: ""
exclude-any-issue-labels: keep-open

View File

@@ -4,7 +4,7 @@
"owner": "ci-custom",
"pattern": [
{
"regexp": "^ERROR (.*):(\\d+):(\\d+) - (.*)$",
"regexp": "^(.*):(\\d+):(\\d+):\\s+lint:\\s+(.*)$",
"file": 1,
"line": 2,
"column": 3,

View File

@@ -5,7 +5,7 @@
"severity": "error",
"pattern": [
{
"regexp": "^(.*):(\\d+):(\\d+):\\s+(?:fatal\\s+)?(warning|error):\\s+(.*)$",
"regexp": "^src/(.*):(\\d+):(\\d+):\\s+(?:fatal\\s+)?(warning|error):\\s+(.*)$",
"file": 1,
"line": 2,
"column": 3,

View File

@@ -1,11 +1,22 @@
{
"problemMatcher": [
{
"owner": "black",
"severity": "error",
"pattern": [
{
"regexp": "^(.*): (Please format this file with the black formatter)",
"file": 1,
"message": 2
}
]
},
{
"owner": "flake8",
"severity": "error",
"pattern": [
{
"regexp": "^(.*):(\\d+) - ([EFCDNW]\\d{3}.*)$",
"regexp": "^(.*):(\\d+): ([EFCDNW]\\d{3}.*)$",
"file": 1,
"line": 2,
"message": 3
@@ -17,7 +28,7 @@
"severity": "error",
"pattern": [
{
"regexp": "^(.*):(\\d+) - (\\[[EFCRW]\\d{4}\\(.*\\),.*\\].*)$",
"regexp": "^(.*):(\\d+): (\\[[EFCRW]\\d{4}\\(.*\\),.*\\].*)$",
"file": 1,
"line": 2,
"message": 3

19
.github/workflows/matchers/pytest.json vendored Normal file
View File

@@ -0,0 +1,19 @@
{
"problemMatcher": [
{
"owner": "pytest",
"fileLocation": "absolute",
"pattern": [
{
"regexp": "^\\s+File \"(.*)\", line (\\d+), in (.*)$",
"file": 1,
"line": 2
},
{
"regexp": "^\\s+(.*)$",
"message": 1
}
]
}
]
}

View File

@@ -1,247 +0,0 @@
name: Publish dev releases to docker hub
on:
push:
branches:
- dev
jobs:
# THE LINT/TEST JOBS ARE COPIED FROM ci.yaml
lint-clang-format:
runs-on: ubuntu-latest
# cpp lint job runs with esphome-lint docker image so that clang-format-*
# doesn't have to be installed
container: esphome/esphome-lint:1.1
steps:
- uses: actions/checkout@v2
# Set up the pio project so that the cpp checks know how files are compiled
# (build flags, libraries etc)
- name: Set up platformio environment
run: pio init --ide atom
- name: Run clang-format
run: script/clang-format -i
- name: Suggest changes
run: script/ci-suggest-changes
lint-clang-tidy:
runs-on: ubuntu-latest
# cpp lint job runs with esphome-lint docker image so that clang-format-*
# doesn't have to be installed
container: esphome/esphome-lint:1.1
# Split clang-tidy check into 4 jobs. Each one will check 1/4th of the .cpp files
strategy:
fail-fast: false
matrix:
split: [1, 2, 3, 4]
steps:
- uses: actions/checkout@v2
# Set up the pio project so that the cpp checks know how files are compiled
# (build flags, libraries etc)
- name: Set up platformio environment
run: pio init --ide atom
- name: Register problem matchers
run: |
echo "::add-matcher::.github/workflows/matchers/clang-tidy.json"
echo "::add-matcher::.github/workflows/matchers/gcc.json"
- name: Run clang-tidy
run: script/clang-tidy --all-headers --fix --split-num 4 --split-at ${{ matrix.split }}
- name: Suggest changes
run: script/ci-suggest-changes
lint-python:
# Don't use the esphome-lint docker image because it may contain outdated requirements.
# This way, all dependencies are cached via the cache action.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.7'
- name: Cache pip modules
uses: actions/cache@v1
with:
path: ~/.cache/pip
key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
restore-keys: |
esphome-pip-3.7-
- name: Set up python environment
run: script/setup
- name: Register problem matchers
run: |
echo "::add-matcher::.github/workflows/matchers/ci-custom.json"
echo "::add-matcher::.github/workflows/matchers/lint-python.json"
echo "::add-matcher::.github/workflows/matchers/python.json"
- name: Lint Custom
run: script/ci-custom.py
- name: Lint Python
run: script/lint-python
- name: Lint CODEOWNERS
run: script/build_codeowners.py --check
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
test:
- test1
- test2
- test3
- test4
- test5
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.7'
- name: Cache pip modules
uses: actions/cache@v1
with:
path: ~/.cache/pip
key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
restore-keys: |
esphome-pip-3.7-
# Use per test platformio cache because tests have different platform versions
- name: Cache ~/.platformio
uses: actions/cache@v1
with:
path: ~/.platformio
key: test-home-platformio-${{ matrix.test }}-${{ hashFiles('esphome/core/config.py') }}
restore-keys: |
test-home-platformio-${{ matrix.test }}-
- name: Set up environment
run: script/setup
- name: Register problem matchers
run: |
echo "::add-matcher::.github/workflows/matchers/gcc.json"
echo "::add-matcher::.github/workflows/matchers/python.json"
- run: esphome compile tests/${{ matrix.test }}.yaml
pytest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.7'
- name: Cache pip modules
uses: actions/cache@v1
with:
path: ~/.cache/pip
key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
restore-keys: |
esphome-pip-3.7-
- name: Set up environment
run: script/setup
- name: Install Github Actions annotator
run: pip install pytest-github-actions-annotate-failures
- name: Register problem matchers
run: |
echo "::add-matcher::.github/workflows/matchers/python.json"
- name: Run pytest
run: |
pytest \
-qq \
--durations=10 \
-o console_output_style=count \
tests
deploy-docker:
name: Build and publish docker containers
if: github.repository == 'esphome/esphome'
runs-on: ubuntu-latest
needs: [lint-clang-format, lint-clang-tidy, lint-python, test, pytest]
strategy:
matrix:
arch: [amd64, armv7, aarch64]
# Hassio dev image doesn't use esphome/esphome-hassio-$arch and uses base directly
build_type: ["docker"]
steps:
- uses: actions/checkout@v2
- name: Set TAG
run: |
TAG="${GITHUB_SHA:0:7}"
echo "TAG=${TAG}" >> $GITHUB_ENV
- name: Set up env variables
run: |
base_version="3.4.0"
if [[ "${{ matrix.build_type }}" == "hassio" ]]; then
build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}"
build_to="esphome/esphome-hassio-${{ matrix.arch }}"
dockerfile="docker/Dockerfile.hassio"
else
build_from="esphome/esphome-base-${{ matrix.arch }}:${base_version}"
build_to="esphome/esphome-${{ matrix.arch }}"
dockerfile="docker/Dockerfile"
fi
echo "BUILD_FROM=${build_from}" >> $GITHUB_ENV
echo "BUILD_TO=${build_to}" >> $GITHUB_ENV
echo "DOCKERFILE=${dockerfile}" >> $GITHUB_ENV
- name: Pull for cache
run: |
docker pull "${BUILD_TO}:dev" || true
- name: Register QEMU binfmt
run: docker run --rm --privileged multiarch/qemu-user-static:5.2.0-2 --reset -p yes
- run: |
docker build \
--build-arg "BUILD_FROM=${BUILD_FROM}" \
--build-arg "BUILD_VERSION=${TAG}" \
--tag "${BUILD_TO}:${TAG}" \
--tag "${BUILD_TO}:dev" \
--cache-from "${BUILD_TO}:dev" \
--file "${DOCKERFILE}" \
.
- name: Log in to docker hub
env:
DOCKER_USER: ${{ secrets.DOCKER_USER }}
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
run: docker login -u "${DOCKER_USER}" -p "${DOCKER_PASSWORD}"
- run: |
docker push "${BUILD_TO}:${TAG}"
docker push "${BUILD_TO}:dev"
deploy-docker-manifest:
if: github.repository == 'esphome/esphome'
runs-on: ubuntu-latest
needs: [deploy-docker]
steps:
- name: Enable experimental manifest support
run: |
mkdir -p ~/.docker
echo "{\"experimental\": \"enabled\"}" > ~/.docker/config.json
- name: Set TAG
run: |
TAG="${GITHUB_SHA:0:7}"
echo "TAG=${TAG}" >> $GITHUB_ENV
- name: Log in to docker hub
env:
DOCKER_USER: ${{ secrets.DOCKER_USER }}
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
run: docker login -u "${DOCKER_USER}" -p "${DOCKER_PASSWORD}"
- name: "Create the manifest"
run: |
docker manifest create esphome/esphome:${TAG} \
esphome/esphome-aarch64:${TAG} \
esphome/esphome-amd64:${TAG} \
esphome/esphome-armv7:${TAG}
docker manifest push esphome/esphome:${TAG}
docker manifest create esphome/esphome:dev \
esphome/esphome-aarch64:${TAG} \
esphome/esphome-amd64:${TAG} \
esphome/esphome-armv7:${TAG}
docker manifest push esphome/esphome:dev

View File

@@ -1,164 +1,38 @@
name: Publish Release
on:
workflow_dispatch:
release:
types: [published]
schedule:
- cron: "0 2 * * *"
permissions:
contents: read
jobs:
# THE LINT/TEST JOBS ARE COPIED FROM ci.yaml
lint-clang-format:
init:
name: Initialize build
runs-on: ubuntu-latest
# cpp lint job runs with esphome-lint docker image so that clang-format-*
# doesn't have to be installed
container: esphome/esphome-lint:1.1
outputs:
tag: ${{ steps.tag.outputs.tag }}
steps:
- uses: actions/checkout@v2
# Set up the pio project so that the cpp checks know how files are compiled
# (build flags, libraries etc)
- name: Set up platformio environment
run: pio init --ide atom
- name: Run clang-format
run: script/clang-format -i
- name: Suggest changes
run: script/ci-suggest-changes
lint-clang-tidy:
runs-on: ubuntu-latest
# cpp lint job runs with esphome-lint docker image so that clang-format-*
# doesn't have to be installed
container: esphome/esphome-lint:1.1
# Split clang-tidy check into 4 jobs. Each one will check 1/4th of the .cpp files
strategy:
fail-fast: false
matrix:
split: [1, 2, 3, 4]
steps:
- uses: actions/checkout@v2
# Set up the pio project so that the cpp checks know how files are compiled
# (build flags, libraries etc)
- name: Set up platformio environment
run: pio init --ide atom
- name: Register problem matchers
- name: Get tag
id: tag
run: |
echo "::add-matcher::.github/workflows/matchers/clang-tidy.json"
echo "::add-matcher::.github/workflows/matchers/gcc.json"
- name: Run clang-tidy
run: script/clang-tidy --all-headers --fix --split-num 4 --split-at ${{ matrix.split }}
- name: Suggest changes
run: script/ci-suggest-changes
lint-python:
# Don't use the esphome-lint docker image because it may contain outdated requirements.
# This way, all dependencies are cached via the cache action.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.7'
- name: Cache pip modules
uses: actions/cache@v1
with:
path: ~/.cache/pip
key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
restore-keys: |
esphome-pip-3.7-
- name: Set up python environment
run: script/setup
- name: Register problem matchers
run: |
echo "::add-matcher::.github/workflows/matchers/ci-custom.json"
echo "::add-matcher::.github/workflows/matchers/lint-python.json"
echo "::add-matcher::.github/workflows/matchers/python.json"
- name: Lint Custom
run: script/ci-custom.py
- name: Lint Python
run: script/lint-python
- name: Lint CODEOWNERS
run: script/build_codeowners.py --check
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
test:
- test1
- test2
- test3
- test4
- test5
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.7'
- name: Cache pip modules
uses: actions/cache@v1
with:
path: ~/.cache/pip
key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
restore-keys: |
esphome-pip-3.7-
# Use per test platformio cache because tests have different platform versions
- name: Cache ~/.platformio
uses: actions/cache@v1
with:
path: ~/.platformio
key: test-home-platformio-${{ matrix.test }}-${{ hashFiles('esphome/core/config.py') }}
restore-keys: |
test-home-platformio-${{ matrix.test }}-
- name: Set up environment
run: script/setup
- name: Register problem matchers
run: |
echo "::add-matcher::.github/workflows/matchers/gcc.json"
echo "::add-matcher::.github/workflows/matchers/python.json"
- run: esphome compile tests/${{ matrix.test }}.yaml
pytest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.7'
- name: Cache pip modules
uses: actions/cache@v1
with:
path: ~/.cache/pip
key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
restore-keys: |
esphome-pip-3.7-
- name: Set up environment
run: script/setup
- name: Install Github Actions annotator
run: pip install pytest-github-actions-annotate-failures
- name: Register problem matchers
run: |
echo "::add-matcher::.github/workflows/matchers/python.json"
- name: Run pytest
run: |
pytest \
-qq \
--durations=10 \
-o console_output_style=count \
tests
if [[ "$GITHUB_EVENT_NAME" = "release" ]]; then
TAG="${GITHUB_REF#refs/tags/}"
else
TAG=$(cat esphome/const.py | sed -n -E "s/^__version__\s+=\s+\"(.+)\"$/\1/p")
today="$(date --utc '+%Y%m%d')"
TAG="${TAG}${today}"
fi
echo "::set-output name=tag::${TAG}"
deploy-pypi:
name: Build and publish to PyPi
if: github.repository == 'esphome/esphome'
needs: [lint-clang-format, lint-clang-tidy, lint-python, test, pytest]
if: github.repository == 'esphome/esphome' && github.event_name == 'release'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
@@ -181,130 +55,100 @@ jobs:
deploy-docker:
name: Build and publish docker containers
if: github.repository == 'esphome/esphome'
permissions:
contents: read
packages: write
runs-on: ubuntu-latest
needs: [lint-clang-format, lint-clang-tidy, lint-python, test, pytest]
needs: [init]
strategy:
matrix:
arch: [amd64, armv7, aarch64]
build_type: ["hassio", "docker"]
build_type: ["ha-addon", "docker", "lint"]
steps:
- uses: actions/checkout@v2
- name: Set TAG
run: |
TAG="${GITHUB_REF#refs/tags/v}"
echo "TAG=${TAG}" >> $GITHUB_ENV
- name: Set up env variables
run: |
base_version="3.4.0"
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.9'
if [[ "${{ matrix.build_type }}" == "hassio" ]]; then
build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}"
build_to="esphome/esphome-hassio-${{ matrix.arch }}"
dockerfile="docker/Dockerfile.hassio"
else
build_from="esphome/esphome-base-${{ matrix.arch }}:${base_version}"
build_to="esphome/esphome-${{ matrix.arch }}"
dockerfile="docker/Dockerfile"
fi
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
if [[ "${{ github.event.release.prerelease }}" == "true" ]]; then
cache_tag="beta"
else
cache_tag="latest"
fi
- 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:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# Set env variables so these values don't need to be calculated again
echo "BUILD_FROM=${build_from}" >> $GITHUB_ENV
echo "BUILD_TO=${build_to}" >> $GITHUB_ENV
echo "DOCKERFILE=${dockerfile}" >> $GITHUB_ENV
echo "CACHE_TAG=${cache_tag}" >> $GITHUB_ENV
- name: Pull for cache
run: |
docker pull "${BUILD_TO}:${CACHE_TAG}" || true
- name: Register QEMU binfmt
run: docker run --rm --privileged multiarch/qemu-user-static:5.2.0-2 --reset -p yes
- run: |
docker build \
--build-arg "BUILD_FROM=${BUILD_FROM}" \
--build-arg "BUILD_VERSION=${TAG}" \
--tag "${BUILD_TO}:${TAG}" \
--cache-from "${BUILD_TO}:${CACHE_TAG}" \
--file "${DOCKERFILE}" \
.
- name: Log in to docker hub
env:
DOCKER_USER: ${{ secrets.DOCKER_USER }}
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
run: docker login -u "${DOCKER_USER}" -p "${DOCKER_PASSWORD}"
- run: docker push "${BUILD_TO}:${TAG}"
# Always publish to beta tag (also full releases)
- name: Publish docker beta tag
run: |
docker tag "${BUILD_TO}:${TAG}" "${BUILD_TO}:beta"
docker push "${BUILD_TO}:beta"
- if: ${{ !github.event.release.prerelease }}
name: Publish docker latest tag
run: |
docker tag "${BUILD_TO}:${TAG}" "${BUILD_TO}:latest"
docker push "${BUILD_TO}:latest"
- 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'
permissions:
contents: read
packages: write
runs-on: ubuntu-latest
needs: [deploy-docker]
needs: [init, deploy-docker]
strategy:
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
- name: Set TAG
run: |
TAG="${GITHUB_REF#refs/tags/v}"
echo "TAG=${TAG}" >> $GITHUB_ENV
- name: Log in to docker hub
env:
DOCKER_USER: ${{ secrets.DOCKER_USER }}
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
run: docker login -u "${DOCKER_USER}" -p "${DOCKER_PASSWORD}"
- name: "Create the manifest"
run: |
docker manifest create esphome/esphome:${TAG} \
esphome/esphome-aarch64:${TAG} \
esphome/esphome-amd64:${TAG} \
esphome/esphome-armv7:${TAG}
docker manifest push esphome/esphome:${TAG}
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:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Publish docker beta tag
- name: Run manifest
run: |
docker manifest create esphome/esphome:beta \
esphome/esphome-aarch64:${TAG} \
esphome/esphome-amd64:${TAG} \
esphome/esphome-armv7:${TAG}
docker manifest push esphome/esphome:beta
docker/build.py \
--tag "${{ needs.init.outputs.tag }}" \
--build-type "${{ matrix.build_type }}" \
manifest
- name: Publish docker latest tag
if: ${{ !github.event.release.prerelease }}
run: |
docker manifest create esphome/esphome:latest \
esphome/esphome-aarch64:${TAG} \
esphome/esphome-amd64:${TAG} \
esphome/esphome-armv7:${TAG}
docker manifest push esphome/esphome:latest
deploy-hassio-repo:
if: github.repository == 'esphome/esphome'
deploy-ha-addon-repo:
if: github.repository == 'esphome/esphome' && github.event_name == 'release'
runs-on: ubuntu-latest
needs: [deploy-docker]
steps:
- env:
TOKEN: ${{ secrets.DEPLOY_HASSIO_TOKEN }}
TOKEN: ${{ secrets.DEPLOY_HA_ADDON_REPO_TOKEN }}
run: |
TAG="${GITHUB_REF#refs/tags/v}"
TAG="${GITHUB_REF#refs/tags/}"
curl \
-u ":$TOKEN" \
-X POST \
-H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/esphome/hassio/actions/workflows/bump-version.yml/dispatches \
-d "{\"ref\":\"master\",\"inputs\":{\"version\":\"$TAG\"}}"
https://api.github.com/repos/esphome/home-assistant-addon/actions/workflows/bump-version.yml/dispatches \
-d "{\"ref\":\"main\",\"inputs\":{\"version\":\"$TAG\"}}"

48
.github/workflows/stale.yml vendored Normal file
View File

@@ -0,0 +1,48 @@
name: Stale
on:
schedule:
- cron: '30 0 * * *'
workflow_dispatch:
permissions:
issues: write
pull-requests: write
concurrency:
group: lock
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v4
with:
days-before-pr-stale: 90
days-before-pr-close: 7
days-before-issue-stale: -1
days-before-issue-close: -1
remove-stale-when-updated: true
stale-pr-label: "stale"
exempt-pr-labels: "no-stale"
stale-pr-message: >
There hasn't been any activity on this pull request recently. This
pull request has been automatically marked as stale because of that
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
close-issues:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v4
with:
days-before-pr-stale: -1
days-before-pr-close: -1
days-before-issue-stale: 1
days-before-issue-close: 1
remove-stale-when-updated: true
stale-issue-label: "stale"
exempt-issue-labels: "not-stale"
stale-issue-message: >
https://github.com/esphome/esphome/issues/430

13
.gitignore vendored
View File

@@ -13,6 +13,9 @@ __pycache__/
# Intellij Idea
.idea
# Vim
*.swp
# Hide some OS X stuff
.DS_Store
.AppleDouble
@@ -74,6 +77,7 @@ venv/
ENV/
env.bak/
venv.bak/
venv-*/
# mypy
.mypy_cache/
@@ -99,10 +103,7 @@ CMakeLists.txt
.idea/**/dynamic.xml
# CMake
cmake-build-debug/
cmake-build-livingroom8266/
cmake-build-livingroom32/
cmake-build-release/
cmake-build-*/
CMakeCache.txt
CMakeFiles
@@ -122,4 +123,8 @@ config/
tests/build/
tests/.esphome/
/.temp-clang-tidy.cpp
/.temp/
.pio/
sdkconfig.*
!sdkconfig.defaults

View File

@@ -3,4 +3,4 @@ ports:
onOpen: open-preview
tasks:
- before: pyenv local $(pyenv version | grep '^3\.' | cut -d ' ' -f 1) && script/setup
command: python -m esphome config dashboard
command: python -m esphome dashboard config

View File

@@ -2,7 +2,7 @@
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/ambv/black
rev: 20.8b1
rev: 22.1.0
hooks:
- id: black
args:
@@ -10,7 +10,7 @@ repos:
- --quiet
files: ^((esphome|script|tests)/.+)?[^/]+\.py$
- repo: https://gitlab.com/pycqa/flake8
rev: 3.8.4
rev: 4.0.1
hooks:
- id: flake8
additional_dependencies:
@@ -23,5 +23,10 @@ repos:
- id: no-commit-to-branch
args:
- --branch=dev
- --branch=master
- --branch=release
- --branch=beta
- repo: https://github.com/asottile/pyupgrade
rev: v2.31.0
hooks:
- id: pyupgrade
args: [--py38-plus]

35
.vscode/tasks.json vendored
View File

@@ -1,11 +1,32 @@
{
"version": "2.0.0",
"tasks": [
"version": "2.0.0",
"tasks": [
{
"label": "run",
"type": "shell",
"command": "python3 -m esphome dashboard config/",
"problemMatcher": []
},
{
"label": "clang-tidy",
"type": "shell",
"command": "./script/clang-tidy",
"problemMatcher": [
{
"label": "run",
"type": "shell",
"command": "python3 -m esphome dashboard config",
"problemMatcher": []
"owner": "clang-tidy",
"fileLocation": "absolute",
"pattern": [
{
"regexp": "^(.*):(\\d+):(\\d+):\\s+(error):\\s+(.*) \\[([a-z0-9,\\-]+)\\]\\s*$",
"file": 1,
"line": 2,
"column": 3,
"severity": 4,
"message": 5
}
]
}
]
]
}
]
}

View File

@@ -14,30 +14,57 @@ esphome/core/* @esphome/core
esphome/components/ac_dimmer/* @glmnet
esphome/components/adc/* @esphome/core
esphome/components/addressable_light/* @justfalter
esphome/components/airthings_ble/* @jeromelaban
esphome/components/airthings_wave_mini/* @ncareau
esphome/components/airthings_wave_plus/* @jeromelaban
esphome/components/am43/* @buxtronix
esphome/components/am43/cover/* @buxtronix
esphome/components/analog_threshold/* @ianchi
esphome/components/animation/* @syndlex
esphome/components/anova/* @buxtronix
esphome/components/api/* @OttoWinter
esphome/components/async_tcp/* @OttoWinter
esphome/components/atc_mithermometer/* @ahpohl
esphome/components/b_parasite/* @rbaron
esphome/components/ballu/* @bazuchan
esphome/components/bang_bang/* @OttoWinter
esphome/components/bh1750/* @OttoWinter
esphome/components/binary_sensor/* @esphome/core
esphome/components/bl0940/* @tobias-
esphome/components/ble_client/* @buxtronix
esphome/components/bme680_bsec/* @trvrnrth
esphome/components/bmp3xx/* @martgras
esphome/components/button/* @esphome/core
esphome/components/canbus/* @danielschramm @mvturnho
esphome/components/cap1188/* @MrEditor97
esphome/components/captive_portal/* @OttoWinter
esphome/components/ccs811/* @habbie
esphome/components/cd74hc4067/* @asoehlke
esphome/components/climate/* @esphome/core
esphome/components/climate_ir/* @glmnet
esphome/components/color_temperature/* @jesserockz
esphome/components/coolix/* @glmnet
esphome/components/copy/* @OttoWinter
esphome/components/cover/* @esphome/core
esphome/components/cs5460a/* @balrog-kun
esphome/components/cse7761/* @berfenger
esphome/components/ct_clamp/* @jesserockz
esphome/components/current_based/* @djwmarcx
esphome/components/daly_bms/* @s1lvi0
esphome/components/dashboard_import/* @esphome/core
esphome/components/debug/* @OttoWinter
esphome/components/dfplayer/* @glmnet
esphome/components/dht/* @OttoWinter
esphome/components/ds1307/* @badbadc0ffee
esphome/components/dsmr/* @glmnet @zuidwijk
esphome/components/ektf2232/* @jesserockz
esphome/components/esp32/* @esphome/core
esphome/components/esp32_ble/* @jesserockz
esphome/components/esp32_ble_server/* @jesserockz
esphome/components/esp32_camera_web_server/* @ayufan
esphome/components/esp32_can/* @Sympatron
esphome/components/esp32_improv/* @jesserockz
esphome/components/esp8266/* @esphome/core
esphome/components/exposure_notifications/* @OttoWinter
esphome/components/ezo/* @ssieb
esphome/components/fastled_base/* @OttoWinter
@@ -45,18 +72,34 @@ esphome/components/fingerprint_grow/* @OnFreund @loongyh
esphome/components/globals/* @esphome/core
esphome/components/gpio/* @esphome/core
esphome/components/gps/* @coogle
esphome/components/graph/* @synco
esphome/components/growatt_solar/* @leeuwte
esphome/components/havells_solar/* @sourabhjaiswal
esphome/components/hbridge/fan/* @WeekendWarrior
esphome/components/hbridge/light/* @DotNetDann
esphome/components/heatpumpir/* @rob-deutsch
esphome/components/hitachi_ac424/* @sourabhjaiswal
esphome/components/homeassistant/* @OttoWinter
esphome/components/honeywellabp/* @RubyBailey
esphome/components/hrxl_maxsonar_wr/* @netmikey
esphome/components/i2c/* @esphome/core
esphome/components/improv/* @jesserockz
esphome/components/improv_serial/* @esphome/core
esphome/components/ina260/* @MrEditor97
esphome/components/inkbird_ibsth1_mini/* @fkirill
esphome/components/inkplate6/* @jesserockz
esphome/components/integration/* @OttoWinter
esphome/components/interval/* @esphome/core
esphome/components/json/* @OttoWinter
esphome/components/kalman_combinator/* @Cat-Ion
esphome/components/ledc/* @OttoWinter
esphome/components/light/* @esphome/core
esphome/components/lilygo_t5_47/touchscreen/* @jesserockz
esphome/components/lock/* @esphome/core
esphome/components/logger/* @esphome/core
esphome/components/ltr390/* @sjtrny
esphome/components/max44009/* @berfenger
esphome/components/max7219digit/* @rspaargaren
esphome/components/max9611/* @mckaymatthew
esphome/components/mcp23008/* @jesserockz
esphome/components/mcp23017/* @jesserockz
esphome/components/mcp23s08/* @SenexCrenshaw @jesserockz
@@ -65,34 +108,73 @@ esphome/components/mcp23x08_base/* @jesserockz
esphome/components/mcp23x17_base/* @jesserockz
esphome/components/mcp23xxx_base/* @jesserockz
esphome/components/mcp2515/* @danielschramm @mvturnho
esphome/components/mcp3204/* @rsumner
esphome/components/mcp4728/* @berfenger
esphome/components/mcp47a1/* @jesserockz
esphome/components/mcp9808/* @k7hpn
esphome/components/midea_ac/* @dudanov
esphome/components/midea_dongle/* @dudanov
esphome/components/md5/* @esphome/core
esphome/components/mdns/* @esphome/core
esphome/components/midea/* @dudanov
esphome/components/midea_ir/* @dudanov
esphome/components/mitsubishi/* @RubyBailey
esphome/components/mlx90393/* @functionpointer
esphome/components/modbus_controller/* @martgras
esphome/components/modbus_controller/binary_sensor/* @martgras
esphome/components/modbus_controller/number/* @martgras
esphome/components/modbus_controller/output/* @martgras
esphome/components/modbus_controller/select/* @martgras @stegm
esphome/components/modbus_controller/sensor/* @martgras
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/mpu6886/* @fabaff
esphome/components/network/* @esphome/core
esphome/components/nextion/* @senexcrenshaw
esphome/components/nextion/binary_sensor/* @senexcrenshaw
esphome/components/nextion/sensor/* @senexcrenshaw
esphome/components/nextion/switch/* @senexcrenshaw
esphome/components/nextion/text_sensor/* @senexcrenshaw
esphome/components/nfc/* @jesserockz
esphome/components/number/* @esphome/core
esphome/components/ota/* @esphome/core
esphome/components/output/* @esphome/core
esphome/components/pid/* @OttoWinter
esphome/components/pipsolar/* @andreashergert1984
esphome/components/pm1006/* @habbie
esphome/components/pmsa003i/* @sjtrny
esphome/components/pn532/* @OttoWinter @jesserockz
esphome/components/pn532_i2c/* @OttoWinter @jesserockz
esphome/components/pn532_spi/* @OttoWinter @jesserockz
esphome/components/power_supply/* @esphome/core
esphome/components/pulse_meter/* @stevebaxter
esphome/components/preferences/* @esphome/core
esphome/components/psram/* @esphome/core
esphome/components/pulse_meter/* @cstaahl @stevebaxter
esphome/components/pvvx_mithermometer/* @pasiz
esphome/components/qr_code/* @wjtje
esphome/components/radon_eye_ble/* @jeffeb3
esphome/components/radon_eye_rd200/* @jeffeb3
esphome/components/rc522/* @glmnet
esphome/components/rc522_i2c/* @glmnet
esphome/components/rc522_spi/* @glmnet
esphome/components/restart/* @esphome/core
esphome/components/rf_bridge/* @jesserockz
esphome/components/rgbct/* @jesserockz
esphome/components/rtttl/* @glmnet
esphome/components/safe_mode/* @jsuanet @paulmonigatti
esphome/components/scd4x/* @sjtrny
esphome/components/script/* @esphome/core
esphome/components/sdm_meter/* @jesserockz @polyfaces
esphome/components/sdp3x/* @Azimath
esphome/components/selec_meter/* @sourabhjaiswal
esphome/components/select/* @esphome/core
esphome/components/sensor/* @esphome/core
esphome/components/sgp40/* @SenexCrenshaw
esphome/components/sht4x/* @sjtrny
esphome/components/shutdown/* @esphome/core
esphome/components/shutdown/* @esphome/core @jsuanet
esphome/components/sim800l/* @glmnet
esphome/components/sm2135/* @BoukeHaarsma23
esphome/components/socket/* @esphome/core
esphome/components/spi/* @esphome/core
esphome/components/ssd1322_base/* @kbx81
esphome/components/ssd1322_spi/* @kbx81
@@ -107,26 +189,37 @@ esphome/components/ssd1351_base/* @kbx81
esphome/components/ssd1351_spi/* @kbx81
esphome/components/st7735/* @SenexCrenshaw
esphome/components/st7789v/* @kbx81
esphome/components/st7920/* @marsjan155
esphome/components/substitutions/* @esphome/core
esphome/components/sun/* @OttoWinter
esphome/components/switch/* @esphome/core
esphome/components/t6615/* @tylermenezes
esphome/components/tca9548a/* @andreashergert1984
esphome/components/tcl112/* @glmnet
esphome/components/teleinfo/* @0hax
esphome/components/thermostat/* @kbx81
esphome/components/time/* @OttoWinter
esphome/components/tlc5947/* @rnauber
esphome/components/tm1637/* @glmnet
esphome/components/tmp102/* @timsavage
esphome/components/tmp117/* @Azimath
esphome/components/tof10120/* @wstrzalka
esphome/components/toshiba/* @kbx81
esphome/components/touchscreen/* @jesserockz
esphome/components/tsl2591/* @wjcarpenter
esphome/components/tuya/binary_sensor/* @jesserockz
esphome/components/tuya/climate/* @jesserockz
esphome/components/tuya/number/* @frankiboy1
esphome/components/tuya/sensor/* @jesserockz
esphome/components/tuya/switch/* @jesserockz
esphome/components/tuya/text_sensor/* @dentra
esphome/components/uart/* @esphome/core
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/xiaomi_lywsd03mmc/* @ahpohl
esphome/components/xiaomi_mhoc303/* @drug123
esphome/components/xiaomi_mhoc401/* @vevsvevs
esphome/components/xpt2046/* @numo68

View File

@@ -1,15 +1,11 @@
# Contributing to ESPHome
This python project is responsible for reading in YAML configuration files,
converting them to C++ code. This code is then converted to a platformio project and compiled
with [esphome-core](https://github.com/esphome/esphome-core), the C++ framework behind the project.
For a detailed guide, please see https://esphome.io/guides/contributing.html#contributing-to-esphomeyaml
For a detailed guide, please see https://esphome.io/guides/contributing.html#contributing-to-esphome
Things to note when contributing:
- Please test your changes :)
- If a new feature is added or an existing user-facing feature is changed, you should also
- If a new feature is added or an existing user-facing feature is changed, you should also
update the [docs](https://github.com/esphome/esphome-docs). See [contributing to esphome-docs](https://esphome.io/guides/contributing.html#contributing-to-esphomedocs)
for more information.
- Please also update the tests in the `tests/` folder. You can do so by just adding a line in one of the YAML files

View File

@@ -4,4 +4,5 @@ include requirements.txt
include esphome/dashboard/templates/*.html
recursive-include esphome/dashboard/static *.ico *.js *.css *.woff* LICENSE
recursive-include esphome *.cpp *.h *.tcc
recursive-include esphome *.py.script
recursive-include esphome LICENSE.txt

View File

@@ -1,4 +1,4 @@
# ESPHome [![Build Status](https://travis-ci.org/esphome/esphome.svg?branch=master)](https://travis-ci.org/esphome/esphome) [![Discord Chat](https://img.shields.io/discord/429907082951524364.svg)](https://discord.gg/KhAMKrd) [![GitHub release](https://img.shields.io/github/release/esphome/esphome.svg)](https://GitHub.com/esphome/esphome/releases/)
# ESPHome [![Discord Chat](https://img.shields.io/discord/429907082951524364.svg)](https://discord.gg/KhAMKrd) [![GitHub release](https://img.shields.io/github/release/esphome/esphome.svg)](https://GitHub.com/esphome/esphome/releases/)
[![ESPHome Logo](https://esphome.io/_images/logo-text.png)](https://esphome.io/)

View File

@@ -1,5 +1,58 @@
ARG BUILD_FROM=esphome/esphome-base-amd64:3.4.0
FROM ${BUILD_FROM}
# Build these with the build.py script
# Example:
# python3 docker/build.py --tag dev --arch amd64 --build-type docker build
# One of "docker", "hassio"
ARG BASEIMGTYPE=docker
# https://github.com/hassio-addons/addon-debian-base/releases
FROM ghcr.io/hassio-addons/debian-base/amd64:5.2.3 AS base-hassio-amd64
FROM ghcr.io/hassio-addons/debian-base/aarch64:5.2.3 AS base-hassio-arm64
FROM ghcr.io/hassio-addons/debian-base/armv7:5.2.3 AS base-hassio-armv7
# https://hub.docker.com/_/debian?tab=tags&page=1&name=bullseye
FROM debian:bullseye-20220125-slim AS base-docker-amd64
FROM debian:bullseye-20220125-slim AS base-docker-arm64
FROM debian:bullseye-20220125-slim AS base-docker-armv7
# Use TARGETARCH/TARGETVARIANT defined by docker
# https://docs.docker.com/engine/reference/builder/#automatic-platform-args-in-the-global-scope
FROM base-${BASEIMGTYPE}-${TARGETARCH}${TARGETVARIANT} AS base
RUN \
apt-get update \
# Use pinned versions so that we get updates with build caching
&& apt-get install -y --no-install-recommends \
python3=3.9.2-3 \
python3-pip=20.3.4-4 \
python3-setuptools=52.0.0-4 \
python3-pil=8.1.2+dfsg-0.3+deb11u1 \
python3-cryptography=3.3.2-1 \
iputils-ping=3:20210202-1 \
git=1:2.30.2-1 \
curl=7.74.0-1.3+deb11u1 \
&& rm -rf \
/tmp/* \
/var/{cache,log}/* \
/var/lib/apt/lists/*
ENV \
# Fix click python3 lang warning https://click.palletsprojects.com/en/7.x/python3/
LANG=C.UTF-8 LC_ALL=C.UTF-8 \
# Store globally installed pio libs in /piolibs
PLATFORMIO_GLOBALLIB_DIR=/piolibs
RUN \
# Ubuntu python3-pip is missing wheel
pip3 install --no-cache-dir \
wheel==0.37.1 \
platformio==5.2.5 \
# 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
# First install requirements to leverage caching when requirements don't change
COPY requirements.txt requirements_optional.txt docker/platformio_install_deps.py platformio.ini /
@@ -7,9 +60,13 @@ RUN \
pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt \
&& /platformio_install_deps.py /platformio.ini
# Then copy esphome and install
COPY . .
RUN pip3 install --no-cache-dir -e .
# ======================= docker-type image =======================
FROM base AS docker
# Copy esphome and install
COPY . /esphome
RUN pip3 install --no-cache-dir --no-use-pep517 -e /esphome
# Settings for dashboard
ENV USERNAME="" PASSWORD=""
@@ -17,14 +74,77 @@ ENV USERNAME="" PASSWORD=""
# Expose the dashboard to Docker
EXPOSE 6052
# Run healthcheck (heartbeat)
HEALTHCHECK --interval=30s --timeout=30s \
CMD curl --fail http://localhost:6052 || exit 1
COPY docker/docker_entrypoint.sh /entrypoint.sh
# The directory the user should mount their configuration files to
VOLUME /config
WORKDIR /config
# Set entrypoint to esphome so that the user doesn't have to type 'esphome'
# Set entrypoint to esphome (via a script) so that the user doesn't have to type 'esphome'
# in every docker command twice
ENTRYPOINT ["esphome"]
ENTRYPOINT ["/entrypoint.sh"]
# When no arguments given, start the dashboard in the workdir
CMD ["dashboard", "/config"]
# ======================= hassio-type image =======================
FROM base AS hassio
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 \
&& rm -rf \
/tmp/* \
/var/{cache,log}/* \
/var/lib/apt/lists/*
ARG BUILD_VERSION=dev
# Copy root filesystem
COPY docker/ha-addon-rootfs/ /
# Copy esphome and install
COPY . /esphome
RUN pip3 install --no-cache-dir --no-use-pep517 -e /esphome
# Labels
LABEL \
io.hass.name="ESPHome" \
io.hass.description="Manage and program ESP8266/ESP32 microcontrollers through YAML configuration files" \
io.hass.type="addon" \
io.hass.version="${BUILD_VERSION}"
# io.hass.arch is inherited from addon-debian-base
# ======================= lint-type image =======================
FROM base AS lint
ENV \
PLATFORMIO_CORE_DIR=/esphome/.temp/platformio
RUN \
apt-get update \
# Use pinned versions so that we get updates with build caching
&& apt-get install -y --no-install-recommends \
clang-format-11=1:11.0.1-2 \
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 \
build-essential=12.9 \
python3-dev=3.9.2-3 \
&& rm -rf \
/tmp/* \
/var/{cache,log}/* \
/var/lib/apt/lists/*
COPY requirements_test.txt /
RUN pip3 install --no-cache-dir -r /requirements_test.txt
VOLUME ["/esphome"]
WORKDIR /esphome

View File

@@ -1,13 +0,0 @@
FROM esphome/esphome-base-amd64:3.4.0
COPY . .
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
python3-wheel \
net-tools \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /workspaces
ENV SHELL /bin/bash

View File

@@ -1,25 +0,0 @@
ARG BUILD_FROM
FROM ${BUILD_FROM}
# First install requirements to leverage caching when requirements don't change
COPY requirements.txt requirements_optional.txt docker/platformio_install_deps.py platformio.ini /
RUN \
pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt \
&& /platformio_install_deps.py /platformio.ini
# Copy root filesystem
COPY docker/rootfs/ /
# Then copy esphome and install
COPY . /opt/esphome/
RUN pip3 install --no-cache-dir -e /opt/esphome
# Build arguments
ARG BUILD_VERSION=dev
# Labels
LABEL \
io.hass.name="ESPHome" \
io.hass.description="Manage and program ESP8266/ESP32 microcontrollers through YAML configuration files" \
io.hass.type="addon" \
io.hass.version=${BUILD_VERSION}

View File

@@ -1,9 +0,0 @@
FROM esphome/esphome-lint-base:3.4.0
COPY requirements.txt requirements_optional.txt requirements_test.txt docker/platformio_install_deps.py platformio.ini /
RUN \
pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt -r /requirements_test.txt \
&& /platformio_install_deps.py /platformio.ini
VOLUME ["/esphome"]
WORKDIR /esphome

162
docker/build.py Executable file
View File

@@ -0,0 +1,162 @@
#!/usr/bin/env python3
from dataclasses import dataclass
import subprocess
import argparse
from platform import machine
import shlex
import re
import sys
CHANNEL_DEV = 'dev'
CHANNEL_BETA = 'beta'
CHANNEL_RELEASE = 'release'
CHANNELS = [CHANNEL_DEV, CHANNEL_BETA, CHANNEL_RELEASE]
ARCH_AMD64 = 'amd64'
ARCH_ARMV7 = 'armv7'
ARCH_AARCH64 = 'aarch64'
ARCHS = [ARCH_AMD64, ARCH_ARMV7, ARCH_AARCH64]
TYPE_DOCKER = 'docker'
TYPE_HA_ADDON = 'ha-addon'
TYPE_LINT = 'lint'
TYPES = [TYPE_DOCKER, TYPE_HA_ADDON, TYPE_LINT]
parser = argparse.ArgumentParser()
parser.add_argument("--tag", type=str, required=True, help="The main docker tag to push to. If a version number also adds latest and/or beta tag")
parser.add_argument("--arch", choices=ARCHS, required=False, help="The architecture to build for")
parser.add_argument("--build-type", choices=TYPES, required=True, help="The type of build to run")
parser.add_argument("--dry-run", action="store_true", help="Don't run any commands, just print them")
subparsers = parser.add_subparsers(help="Action to perform", dest="command", required=True)
build_parser = subparsers.add_parser("build", help="Build the image")
build_parser.add_argument("--push", help="Also push the images", action="store_true")
build_parser.add_argument("--load", help="Load the docker image locally", action="store_true")
manifest_parser = subparsers.add_parser("manifest", help="Create a manifest from already pushed images")
@dataclass(frozen=True)
class DockerParams:
build_to: str
manifest_to: str
baseimgtype: str
platform: str
target: str
@classmethod
def for_type_arch(cls, build_type, arch):
prefix = {
TYPE_DOCKER: "esphome/esphome",
TYPE_HA_ADDON: "esphome/esphome-hassio",
TYPE_LINT: "esphome/esphome-lint"
}[build_type]
build_to = f"{prefix}-{arch}"
baseimgtype = {
TYPE_DOCKER: "docker",
TYPE_HA_ADDON: "hassio",
TYPE_LINT: "docker",
}[build_type]
platform = {
ARCH_AMD64: "linux/amd64",
ARCH_ARMV7: "linux/arm/v7",
ARCH_AARCH64: "linux/arm64",
}[arch]
target = {
TYPE_DOCKER: "docker",
TYPE_HA_ADDON: "hassio",
TYPE_LINT: "lint",
}[build_type]
return cls(
build_to=build_to,
manifest_to=prefix,
baseimgtype=baseimgtype,
platform=platform,
target=target,
)
def main():
args = parser.parse_args()
def run_command(*cmd, ignore_error: bool = False):
print(f"$ {shlex.join(list(cmd))}")
if not args.dry_run:
rc = subprocess.call(list(cmd))
if rc != 0 and not ignore_error:
print("Command failed")
sys.exit(1)
# detect channel from tag
match = re.match(r'^\d+\.\d+(?:\.\d+)?(b\d+)?$', args.tag)
if match is None:
channel = CHANNEL_DEV
elif match.group(1) is None:
channel = CHANNEL_RELEASE
else:
channel = CHANNEL_BETA
tags_to_push = [args.tag]
if channel == CHANNEL_DEV:
tags_to_push.append("dev")
elif channel == CHANNEL_BETA:
tags_to_push.append("beta")
elif channel == CHANNEL_RELEASE:
# Additionally push to beta
tags_to_push.append("beta")
tags_to_push.append("latest")
if args.command == "build":
# 1. pull cache image
params = DockerParams.for_type_arch(args.build_type, args.arch)
cache_tag = {
CHANNEL_DEV: "cache-dev",
CHANNEL_BETA: "cache-beta",
CHANNEL_RELEASE: "cache-latest",
}[channel]
cache_img = f"ghcr.io/{params.build_to}:{cache_tag}"
imgs = [f"{params.build_to}:{tag}" for tag in tags_to_push]
imgs += [f"ghcr.io/{params.build_to}:{tag}" for tag in tags_to_push]
# 3. build
cmd = [
"docker", "buildx", "build",
"--build-arg", f"BASEIMGTYPE={params.baseimgtype}",
"--build-arg", f"BUILD_VERSION={args.tag}",
"--cache-from", f"type=registry,ref={cache_img}",
"--file", "docker/Dockerfile",
"--platform", params.platform,
"--target", params.target,
]
for img in imgs:
cmd += ["--tag", img]
if args.push:
cmd += ["--push", "--cache-to", f"type=registry,ref={cache_img},mode=max"]
if args.load:
cmd += ["--load"]
run_command(*cmd, ".")
elif args.command == "manifest":
manifest = DockerParams.for_type_arch(args.build_type, ARCH_AMD64).manifest_to
targets = [f"{manifest}:{tag}" for tag in tags_to_push]
targets += [f"ghcr.io/{manifest}:{tag}" for tag in tags_to_push]
# 1. Create manifests
for target in targets:
cmd = ["docker", "manifest", "create", target]
for arch in ARCHS:
src = f"{DockerParams.for_type_arch(args.build_type, arch).build_to}:{args.tag}"
if target.startswith("ghcr.io"):
src = f"ghcr.io/{src}"
cmd.append(src)
run_command(*cmd)
# 2. Push manifests
for target in targets:
run_command(
"docker", "manifest", "push", target
)
if __name__ == "__main__":
main()

24
docker/docker_entrypoint.sh Executable file
View File

@@ -0,0 +1,24 @@
#!/bin/bash
# If /cache is mounted, use that as PIO's coredir
# otherwise use path in /config (so that PIO packages aren't downloaded on each compile)
if [[ -d /cache ]]; then
pio_cache_base=/cache/platformio
else
pio_cache_base=/config/.esphome/platformio
fi
if [[ ! -d "${pio_cache_base}" ]]; then
echo "Creating cache directory ${pio_cache_base}"
echo "You can change this behavior by mounting a directory to the container's /cache directory."
mkdir -p "${pio_cache_base}"
fi
# we can't set core_dir, because the settings file is stored in `core_dir/appstate.json`
# setting `core_dir` would therefore prevent pio from accessing
export PLATFORMIO_PLATFORMS_DIR="${pio_cache_base}/platforms"
export PLATFORMIO_PACKAGES_DIR="${pio_cache_base}/packages"
export PLATFORMIO_CACHE_DIR="${pio_cache_base}/cache"
exec esphome "$@"

View File

@@ -7,12 +7,12 @@
# Check SSL requirements, if enabled
if bashio::config.true 'ssl'; then
if ! bashio::config.has_value 'certfile'; then
bashio::fatal 'SSL is enabled, but no certfile was specified.'
bashio::log.fatal 'SSL is enabled, but no certfile was specified.'
bashio::exit.nok
fi
if ! bashio::config.has_value 'keyfile'; then
bashio::fatal 'SSL is enabled, but no keyfile was specified'
bashio::log.fatal 'SSL is enabled, but no keyfile was specified'
bashio::exit.nok
fi

View File

@@ -0,0 +1,9 @@
#!/usr/bin/with-contenv bashio
# ==============================================================================
# Community Hass.io Add-ons: ESPHome
# This files creates all directories used by esphome
# ==============================================================================
pio_cache_base=/data/cache/platformio
mkdir -p "${pio_cache_base}"

View File

@@ -10,7 +10,7 @@ server {
ssl_certificate_key /ssl/%%keyfile%%;
# Clear Hass.io Ingress header
proxy_set_header X-Hassio-Ingress "";
proxy_set_header X-HA-Ingress "";
# Redirect http requests to https on the same port.
# https://rageagainstshell.com/2016/11/redirect-http-to-https-on-the-same-port-in-nginx/

View File

@@ -4,7 +4,7 @@ server {
include /etc/nginx/includes/server_params.conf;
include /etc/nginx/includes/proxy_params.conf;
# Clear Hass.io Ingress header
proxy_set_header X-Hassio-Ingress "";
proxy_set_header X-HA-Ingress "";
location / {
proxy_pass http://esphome;

View File

@@ -3,8 +3,8 @@ server {
include /etc/nginx/includes/server_params.conf;
include /etc/nginx/includes/proxy_params.conf;
# Set Hass.io Ingress header
proxy_set_header X-Hassio-Ingress "YES";
# Set Home Assistant Ingress header
proxy_set_header X-HA-Ingress "YES";
location / {
# Only allow from Hass.io supervisor

View File

@@ -4,7 +4,7 @@
# Runs the ESPHome dashboard
# ==============================================================================
export ESPHOME_IS_HASSIO=true
export ESPHOME_IS_HA_ADDON=true
if bashio::config.true 'leave_front_door_open'; then
export DISABLE_HA_AUTHENTICATION=true
@@ -22,5 +22,14 @@ if bashio::config.has_value 'relative_url'; then
export ESPHOME_DASHBOARD_RELATIVE_URL=$(bashio::config 'relative_url')
fi
pio_cache_base=/data/cache/platformio
# we can't set core_dir, because the settings file is stored in `core_dir/appstate.json`
# setting `core_dir` would therefore prevent pio from accessing
export PLATFORMIO_PLATFORMS_DIR="${pio_cache_base}/platforms"
export PLATFORMIO_PACKAGES_DIR="${pio_cache_base}/packages"
export PLATFORMIO_CACHE_DIR="${pio_cache_base}/cache"
export PLATFORMIO_GLOBALLIB_DIR=/piolibs
bashio::log.info "Starting ESPHome dashboard..."
exec esphome dashboard /config/esphome --socket /var/run/esphome.sock --hassio
exec esphome dashboard /config/esphome --socket /var/run/esphome.sock --ha-addon

View File

@@ -3,18 +3,28 @@
# all platformio libraries in the global storage
import configparser
import re
import subprocess
import sys
config = configparser.ConfigParser()
config = configparser.ConfigParser(inline_comment_prefixes=(';', ))
config.read(sys.argv[1])
libs = []
for line in config['common']['lib_deps'].splitlines():
# Format: '1655@1.0.2 ; TinyGPSPlus (has name conflict)' (includes comment)
m = re.search(r'([a-zA-Z0-9-_/]+@[0-9\.]+)', line)
if m is None:
# Extract from every lib_deps key in all sections
for section in config.sections():
conf = config[section]
if "lib_deps" not in conf:
continue
libs.append(m.group(1))
for lib_dep in conf["lib_deps"].splitlines():
if not lib_dep:
# Empty line or comment
continue
if lib_dep.startswith("${"):
# Extending from another section
continue
if "@" not in lib_dep:
# No version pinned, this is an internal lib
continue
libs.append(lib_dep)
subprocess.check_call(['platformio', 'lib', '-g', 'install', *libs])

View File

@@ -1,23 +0,0 @@
#!/usr/bin/with-contenv bashio
# ==============================================================================
# Community Hass.io Add-ons: ESPHome
# This files installs the user ESPHome version if specified
# ==============================================================================
declare esphome_version
if bashio::config.has_value 'esphome_version'; then
esphome_version=$(bashio::config 'esphome_version')
if [[ $esphome_version == *":"* ]]; then
IFS=':' read -r -a array <<< "$esphome_version"
username=${array[0]}
ref=${array[1]}
else
username="esphome"
ref=$esphome_version
fi
full_url="https://github.com/${username}/esphome/archive/${ref}.zip"
bashio::log.info "Installing esphome version '${esphome_version}' (${full_url})..."
pip3 install -U --no-cache-dir "${full_url}" \
|| bashio::exit.nok "Failed installing esphome pinned version."
fi

View File

@@ -1,11 +0,0 @@
#!/usr/bin/with-contenv bashio
# ==============================================================================
# Community Hass.io Add-ons: ESPHome
# This files migrates the esphome config directory from the old path
# ==============================================================================
if [[ ! -d /config/esphome && -d /config/esphomeyaml ]]; then
echo "Moving config directory from /config/esphomeyaml to /config/esphome"
mv /config/esphomeyaml /config/esphome
mv /config/esphome/.esphomeyaml /config/esphome/.esphome
fi

View File

@@ -11,12 +11,14 @@ from esphome.config import iter_components, read_config, strip_default_ids
from esphome.const import (
CONF_BAUD_RATE,
CONF_BROKER,
CONF_DEASSERT_RTS_DTR,
CONF_LOGGER,
CONF_OTA,
CONF_PASSWORD,
CONF_PORT,
CONF_ESPHOME,
CONF_PLATFORMIO_OPTIONS,
SECRETS_FILES,
)
from esphome.core import CORE, EsphomeError, coroutine
from esphome.helpers import indent
@@ -71,7 +73,7 @@ def choose_upload_log_host(default, check_default, show_ota, show_mqtt, show_api
if default == "OTA":
return CORE.address
if show_mqtt and "mqtt" in CORE.config:
options.append(("MQTT ({})".format(CORE.config["mqtt"][CONF_BROKER]), "MQTT"))
options.append((f"MQTT ({CORE.config['mqtt'][CONF_BROKER]})", "MQTT"))
if default == "OTA":
return "MQTT"
if default is not None:
@@ -99,10 +101,21 @@ def run_miniterm(config, port):
baud_rate = config["logger"][CONF_BAUD_RATE]
if baud_rate == 0:
_LOGGER.info("UART logging is disabled (baud_rate=0). Not starting UART logs.")
return
_LOGGER.info("Starting log output from %s with baud rate %s", port, baud_rate)
backtrace_state = False
with serial.Serial(port, baudrate=baud_rate) as ser:
ser = serial.Serial()
ser.baudrate = baud_rate
ser.port = port
# We can't set to False by default since it leads to toggling and hence
# ESP32 resets on some platforms.
if config["logger"][CONF_DEASSERT_RTS_DTR]:
ser.dtr = False
ser.rts = False
with ser:
while True:
try:
raw = ser.readline()
@@ -132,6 +145,8 @@ def wrap_to_code(name, comp):
if comp.config_schema is not None:
conf_str = yaml_util.dump(conf)
conf_str = conf_str.replace("//", "")
# remove tailing \ to avoid multi-line comment warning
conf_str = conf_str.replace("\\\n", "\n")
cg.add(cg.LineComment(indent(conf_str)))
await coro(conf)
@@ -168,16 +183,37 @@ def compile_program(args, config):
from esphome import platformio_api
_LOGGER.info("Compiling app...")
return platformio_api.run_compile(config, CORE.verbose)
rc = platformio_api.run_compile(config, CORE.verbose)
if rc != 0:
return rc
idedata = platformio_api.get_idedata(config)
return 0 if idedata is not None else 1
def upload_using_esptool(config, port):
path = CORE.firmware_bin
from esphome import platformio_api
first_baudrate = config[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS].get(
"upload_speed", 460800
)
def run_esptool(baud_rate):
idedata = platformio_api.get_idedata(config)
firmware_offset = "0x10000" if CORE.is_esp32 else "0x0"
flash_images = [
platformio_api.FlashImage(
path=idedata.firmware_bin_path, offset=firmware_offset
),
*idedata.extra_flash_images,
]
mcu = "esp8266"
if CORE.is_esp32:
from esphome.components.esp32 import get_esp32_variant
mcu = get_esp32_variant().lower()
cmd = [
"esptool.py",
"--before",
@@ -186,14 +222,17 @@ def upload_using_esptool(config, port):
"hard_reset",
"--baud",
str(baud_rate),
"--chip",
"esp8266",
"--port",
port,
"--chip",
mcu,
"write_flash",
"0x0",
path,
"-z",
"--flash_size",
"detect",
]
for img in flash_images:
cmd += [img.offset, img.path]
if os.environ.get("ESPHOME_USE_SUBPROCESS") is None:
import esptool
@@ -217,11 +256,7 @@ def upload_using_esptool(config, port):
def upload_program(config, args, host):
# if upload is to a serial port use platformio, otherwise assume ota
if get_port_type(host) == "SERIAL":
from esphome import platformio_api
if CORE.is_esp8266:
return upload_using_esptool(config, host)
return platformio_api.run_upload(config, CORE.verbose, host)
return upload_using_esptool(config, host)
from esphome import espota2
@@ -233,7 +268,7 @@ def upload_program(config, args, host):
ota_conf = config[CONF_OTA]
remote_port = ota_conf[CONF_PORT]
password = ota_conf[CONF_PASSWORD]
password = ota_conf.get(CONF_PASSWORD, "")
return espota2.run_ota(host, remote_port, password, CORE.firmware_bin)
@@ -244,7 +279,7 @@ def show_logs(config, args, port):
run_miniterm(config, port)
return 0
if get_port_type(port) == "NETWORK" and "api" in config:
from esphome.api.client import run_logs
from esphome.components.api.client import run_logs
return run_logs(config, port)
if get_port_type(port) == "MQTT" and "mqtt" in config:
@@ -284,7 +319,6 @@ def command_vscode(args):
logging.disable(logging.INFO)
logging.disable(logging.WARNING)
CORE.config_path = args.configuration
vscode.read_config(args)
@@ -404,34 +438,49 @@ def command_update_all(args):
click.echo(f"{half_line}{middle_text}{half_line}")
for f in files:
print("Updating {}".format(color(Fore.CYAN, f)))
print(f"Updating {color(Fore.CYAN, f)}")
print("-" * twidth)
print()
rc = run_external_process(
"esphome", "--dashboard", "run", f, "--no-logs", "--device", "OTA"
)
if rc == 0:
print_bar("[{}] {}".format(color(Fore.BOLD_GREEN, "SUCCESS"), f))
print_bar(f"[{color(Fore.BOLD_GREEN, 'SUCCESS')}] {f}")
success[f] = True
else:
print_bar("[{}] {}".format(color(Fore.BOLD_RED, "ERROR"), f))
print_bar(f"[{color(Fore.BOLD_RED, 'ERROR')}] {f}")
success[f] = False
print()
print()
print()
print_bar("[{}]".format(color(Fore.BOLD_WHITE, "SUMMARY")))
print_bar(f"[{color(Fore.BOLD_WHITE, 'SUMMARY')}]")
failed = 0
for f in files:
if success[f]:
print(" - {}: {}".format(f, color(Fore.GREEN, "SUCCESS")))
print(f" - {f}: {color(Fore.GREEN, 'SUCCESS')}")
else:
print(" - {}: {}".format(f, color(Fore.BOLD_RED, "FAILED")))
print(f" - {f}: {color(Fore.BOLD_RED, 'FAILED')}")
failed += 1
return failed
def command_idedata(args, config):
from esphome import platformio_api
import json
logging.disable(logging.INFO)
logging.disable(logging.WARNING)
idedata = platformio_api.get_idedata(config)
if idedata is None:
return 1
print(json.dumps(idedata.raw, indent=2) + "\n")
return 0
PRE_CONFIG_ACTIONS = {
"wizard": command_wizard,
"version": command_version,
@@ -449,6 +498,7 @@ POST_CONFIG_ACTIONS = {
"clean-mqtt": command_clean_mqtt,
"mqtt-fingerprint": command_mqtt_fingerprint,
"clean": command_clean,
"idedata": command_idedata,
}
@@ -472,62 +522,9 @@ def parse_args(argv):
metavar=("key", "value"),
)
# Keep backward compatibility with the old command line format of
# esphome <config> <command>.
#
# Unfortunately this can't be done by adding another configuration argument to the
# main config parser, as argparse is greedy when parsing arguments, so in regular
# usage it'll eat the command as the configuration argument and error out out
# because it can't parse the configuration as a command.
#
# Instead, construct an ad-hoc parser for the old format that doesn't actually
# process the arguments, but parses them enough to let us figure out if the old
# format is used. In that case, swap the command and configuration in the arguments
# and continue on with the normal parser (after raising a deprecation warning).
#
# Disable argparse's built-in help option and add it manually to prevent this
# parser from printing the help messagefor the old format when invoked with -h.
compat_parser = argparse.ArgumentParser(parents=[options_parser], add_help=False)
compat_parser.add_argument("-h", "--help")
compat_parser.add_argument("configuration", nargs="*")
compat_parser.add_argument(
"command",
choices=[
"config",
"compile",
"upload",
"logs",
"run",
"clean-mqtt",
"wizard",
"mqtt-fingerprint",
"version",
"clean",
"dashboard",
"vscode",
],
)
# on Python 3.9+ we can simply set exit_on_error=False in the constructor
def _raise(x):
raise argparse.ArgumentError(None, x)
compat_parser.error = _raise
try:
result, unparsed = compat_parser.parse_known_args(argv[1:])
last_option = len(argv) - len(unparsed) - 1 - len(result.configuration)
argv = argv[0:last_option] + [result.command] + result.configuration + unparsed
deprecated_argv_suggestion = argv
except argparse.ArgumentError:
# This is not an old-style command line, so we don't have to do anything.
deprecated_argv_suggestion = None
# And continue on with regular parsing
parser = argparse.ArgumentParser(
description=f"ESPHome v{const.__version__}", parents=[options_parser]
)
parser.set_defaults(deprecated_argv_suggestion=deprecated_argv_suggestion)
mqtt_options = argparse.ArgumentParser(add_help=False)
mqtt_options.add_argument("--topic", help="Manually set the MQTT topic.")
@@ -612,10 +609,7 @@ def parse_args(argv):
"wizard",
help="A helpful setup wizard that will guide you through setting up ESPHome.",
)
parser_wizard.add_argument(
"configuration",
help="Your YAML configuration file.",
)
parser_wizard.add_argument("configuration", help="Your YAML configuration file.")
parser_fingerprint = subparsers.add_parser(
"mqtt-fingerprint", help="Get the SSL fingerprint from a MQTT broker."
@@ -637,8 +631,7 @@ def parse_args(argv):
"dashboard", help="Create a simple web server for a dashboard."
)
parser_dashboard.add_argument(
"configuration",
help="Your YAML configuration file directory.",
"configuration", help="Your YAML configuration file directory."
)
parser_dashboard.add_argument(
"--port",
@@ -646,6 +639,12 @@ def parse_args(argv):
type=int,
default=6052,
)
parser_dashboard.add_argument(
"--address",
help="The address to bind to.",
type=str,
default="0.0.0.0",
)
parser_dashboard.add_argument(
"--username",
help="The optional username to require for authentication.",
@@ -662,43 +661,127 @@ def parse_args(argv):
"--open-ui", help="Open the dashboard UI in a browser.", action="store_true"
)
parser_dashboard.add_argument(
"--hassio", help=argparse.SUPPRESS, action="store_true"
"--ha-addon", help=argparse.SUPPRESS, action="store_true"
)
parser_dashboard.add_argument(
"--socket", help="Make the dashboard serve under a unix socket", type=str
)
parser_vscode = subparsers.add_parser("vscode")
parser_vscode.add_argument(
"configuration", help="Your YAML configuration file.", nargs=1
)
parser_vscode.add_argument("configuration", help="Your YAML configuration file.")
parser_vscode.add_argument("--ace", action="store_true")
parser_update = subparsers.add_parser("update-all")
parser_update.add_argument(
"configuration", help="Your YAML configuration file directory.", nargs=1
"configuration", help="Your YAML configuration file directories.", nargs="+"
)
return parser.parse_args(argv[1:])
parser_idedata = subparsers.add_parser("idedata")
parser_idedata.add_argument(
"configuration", help="Your YAML configuration file(s).", nargs=1
)
# Keep backward compatibility with the old command line format of
# esphome <config> <command>.
#
# Unfortunately this can't be done by adding another configuration argument to the
# main config parser, as argparse is greedy when parsing arguments, so in regular
# usage it'll eat the command as the configuration argument and error out out
# because it can't parse the configuration as a command.
#
# Instead, if parsing using the current format fails, construct an ad-hoc parser
# that doesn't actually process the arguments, but parses them enough to let us
# figure out if the old format is used. In that case, swap the command and
# configuration in the arguments and retry with the normal parser (and raise
# a deprecation warning).
arguments = argv[1:]
# On Python 3.9+ we can simply set exit_on_error=False in the constructor
def _raise(x):
raise argparse.ArgumentError(None, x)
# First, try new-style parsing, but don't exit in case of failure
try:
# duplicate parser so that we can use the original one to raise errors later on
current_parser = argparse.ArgumentParser(add_help=False, parents=[parser])
current_parser.set_defaults(deprecated_argv_suggestion=None)
current_parser.error = _raise
return current_parser.parse_args(arguments)
except argparse.ArgumentError:
pass
# Second, try compat parsing and rearrange the command-line if it succeeds
# Disable argparse's built-in help option and add it manually to prevent this
# parser from printing the help messagefor the old format when invoked with -h.
compat_parser = argparse.ArgumentParser(parents=[options_parser], add_help=False)
compat_parser.add_argument("-h", "--help", action="store_true")
compat_parser.add_argument("configuration", nargs="*")
compat_parser.add_argument(
"command",
choices=[
"config",
"compile",
"upload",
"logs",
"run",
"clean-mqtt",
"wizard",
"mqtt-fingerprint",
"version",
"clean",
"dashboard",
"vscode",
"update-all",
],
)
try:
compat_parser.error = _raise
result, unparsed = compat_parser.parse_known_args(argv[1:])
last_option = len(arguments) - len(unparsed) - 1 - len(result.configuration)
unparsed = [
"--device" if arg in ("--upload-port", "--serial-port") else arg
for arg in unparsed
]
arguments = (
arguments[0:last_option]
+ [result.command]
+ result.configuration
+ unparsed
)
deprecated_argv_suggestion = arguments
except argparse.ArgumentError:
# old-style parsing failed, don't suggest any argument
deprecated_argv_suggestion = None
# Finally, run the new-style parser again with the possibly swapped arguments,
# and let it error out if the command is unparsable.
parser.set_defaults(deprecated_argv_suggestion=deprecated_argv_suggestion)
return parser.parse_args(arguments)
def run_esphome(argv):
args = parse_args(argv)
CORE.dashboard = args.dashboard
setup_log(args.verbose, args.quiet)
setup_log(
args.verbose,
args.quiet,
# Show timestamp for dashboard access logs
args.command == "dashboard",
)
if args.deprecated_argv_suggestion is not None and args.command != "vscode":
_LOGGER.warning(
"Calling ESPHome with the configuration before the command is deprecated "
"and will be removed in the future. "
)
_LOGGER.warning("Please instead use:")
_LOGGER.warning(" esphome %s", " ".join(args.deprecated_argv_suggestion[1:]))
_LOGGER.warning(" esphome %s", " ".join(args.deprecated_argv_suggestion))
if sys.version_info < (3, 7, 0):
if sys.version_info < (3, 8, 0):
_LOGGER.error(
"You're running ESPHome with Python <3.7. ESPHome is no longer compatible "
"with this Python version. Please reinstall ESPHome with Python 3.7+"
"You're running ESPHome with Python <3.8. ESPHome is no longer compatible "
"with this Python version. Please reinstall ESPHome with Python 3.8+"
)
return 1
@@ -710,12 +793,16 @@ def run_esphome(argv):
return 1
for conf_path in args.configuration:
if any(os.path.basename(conf_path) == x for x in SECRETS_FILES):
_LOGGER.warning("Skipping secrets file %s", conf_path)
continue
CORE.config_path = conf_path
CORE.dashboard = args.dashboard
config = read_config(dict(args.substitution) if args.substitution else {})
if config is None:
return 1
return 2
CORE.config = config
if args.command not in POST_CONFIG_ACTIONS:

File diff suppressed because one or more lines are too long

View File

@@ -1,518 +0,0 @@
from datetime import datetime
import functools
import logging
import socket
import threading
import time
# pylint: disable=unused-import
from typing import Optional # noqa
from google.protobuf import message # noqa
from esphome import const
import esphome.api.api_pb2 as pb
from esphome.const import CONF_PASSWORD, CONF_PORT
from esphome.core import EsphomeError
from esphome.helpers import resolve_ip_address, indent
from esphome.log import color, Fore
from esphome.util import safe_print
_LOGGER = logging.getLogger(__name__)
class APIConnectionError(EsphomeError):
pass
MESSAGE_TYPE_TO_PROTO = {
1: pb.HelloRequest,
2: pb.HelloResponse,
3: pb.ConnectRequest,
4: pb.ConnectResponse,
5: pb.DisconnectRequest,
6: pb.DisconnectResponse,
7: pb.PingRequest,
8: pb.PingResponse,
9: pb.DeviceInfoRequest,
10: pb.DeviceInfoResponse,
11: pb.ListEntitiesRequest,
12: pb.ListEntitiesBinarySensorResponse,
13: pb.ListEntitiesCoverResponse,
14: pb.ListEntitiesFanResponse,
15: pb.ListEntitiesLightResponse,
16: pb.ListEntitiesSensorResponse,
17: pb.ListEntitiesSwitchResponse,
18: pb.ListEntitiesTextSensorResponse,
19: pb.ListEntitiesDoneResponse,
20: pb.SubscribeStatesRequest,
21: pb.BinarySensorStateResponse,
22: pb.CoverStateResponse,
23: pb.FanStateResponse,
24: pb.LightStateResponse,
25: pb.SensorStateResponse,
26: pb.SwitchStateResponse,
27: pb.TextSensorStateResponse,
28: pb.SubscribeLogsRequest,
29: pb.SubscribeLogsResponse,
30: pb.CoverCommandRequest,
31: pb.FanCommandRequest,
32: pb.LightCommandRequest,
33: pb.SwitchCommandRequest,
34: pb.SubscribeServiceCallsRequest,
35: pb.ServiceCallResponse,
36: pb.GetTimeRequest,
37: pb.GetTimeResponse,
}
def _varuint_to_bytes(value):
if value <= 0x7F:
return bytes([value])
ret = bytes()
while value:
temp = value & 0x7F
value >>= 7
if value:
ret += bytes([temp | 0x80])
else:
ret += bytes([temp])
return ret
def _bytes_to_varuint(value):
result = 0
bitpos = 0
for val in value:
result |= (val & 0x7F) << bitpos
bitpos += 7
if (val & 0x80) == 0:
return result
return None
# pylint: disable=too-many-instance-attributes,not-callable
class APIClient(threading.Thread):
def __init__(self, address, port, password):
threading.Thread.__init__(self)
self._address = address # type: str
self._port = port # type: int
self._password = password # type: Optional[str]
self._socket = None # type: Optional[socket.socket]
self._socket_open_event = threading.Event()
self._socket_write_lock = threading.Lock()
self._connected = False
self._authenticated = False
self._message_handlers = []
self._keepalive = 5
self._ping_timer = None
self.on_disconnect = None
self.on_connect = None
self.on_login = None
self.auto_reconnect = False
self._running_event = threading.Event()
self._stop_event = threading.Event()
@property
def stopped(self):
return self._stop_event.is_set()
def _refresh_ping(self):
if self._ping_timer is not None:
self._ping_timer.cancel()
self._ping_timer = None
def func():
self._ping_timer = None
if self._connected:
try:
self.ping()
except APIConnectionError as err:
self._fatal_error(err)
else:
self._refresh_ping()
self._ping_timer = threading.Timer(self._keepalive, func)
self._ping_timer.start()
def _cancel_ping(self):
if self._ping_timer is not None:
self._ping_timer.cancel()
self._ping_timer = None
def _close_socket(self):
self._cancel_ping()
if self._socket is not None:
self._socket.close()
self._socket = None
self._socket_open_event.clear()
self._connected = False
self._authenticated = False
self._message_handlers = []
def stop(self, force=False):
if self.stopped:
raise ValueError
if self._connected and not force:
try:
self.disconnect()
except APIConnectionError:
pass
self._close_socket()
self._stop_event.set()
if not force:
self.join()
def connect(self):
if not self._running_event.wait(0.1):
raise APIConnectionError("You need to call start() first!")
if self._connected:
self.disconnect(on_disconnect=False)
try:
ip = resolve_ip_address(self._address)
except EsphomeError as err:
_LOGGER.warning(
"Error resolving IP address of %s. Is it connected to WiFi?",
self._address,
)
_LOGGER.warning(
"(If this error persists, please set a static IP address: "
"https://esphome.io/components/wifi.html#manual-ips)"
)
raise APIConnectionError(err) from err
_LOGGER.info("Connecting to %s:%s (%s)", self._address, self._port, ip)
self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self._socket.settimeout(10.0)
self._socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
try:
self._socket.connect((ip, self._port))
except OSError as err:
err = APIConnectionError(f"Error connecting to {ip}: {err}")
self._fatal_error(err)
raise err
self._socket.settimeout(0.1)
self._socket_open_event.set()
hello = pb.HelloRequest()
hello.client_info = f"ESPHome v{const.__version__}"
try:
resp = self._send_message_await_response(hello, pb.HelloResponse)
except APIConnectionError as err:
self._fatal_error(err)
raise err
_LOGGER.debug(
"Successfully connected to %s ('%s' API=%s.%s)",
self._address,
resp.server_info,
resp.api_version_major,
resp.api_version_minor,
)
self._connected = True
self._refresh_ping()
if self.on_connect is not None:
self.on_connect()
def _check_connected(self):
if not self._connected:
err = APIConnectionError("Must be connected!")
self._fatal_error(err)
raise err
def login(self):
self._check_connected()
if self._authenticated:
raise APIConnectionError("Already logged in!")
connect = pb.ConnectRequest()
if self._password is not None:
connect.password = self._password
resp = self._send_message_await_response(connect, pb.ConnectResponse)
if resp.invalid_password:
raise APIConnectionError("Invalid password!")
self._authenticated = True
if self.on_login is not None:
self.on_login()
def _fatal_error(self, err):
was_connected = self._connected
self._close_socket()
if was_connected and self.on_disconnect is not None:
self.on_disconnect(err)
def _write(self, data): # type: (bytes) -> None
if self._socket is None:
raise APIConnectionError("Socket closed")
# _LOGGER.debug("Write: %s", format_bytes(data))
with self._socket_write_lock:
try:
self._socket.sendall(data)
except OSError as err:
err = APIConnectionError(f"Error while writing data: {err}")
self._fatal_error(err)
raise err
def _send_message(self, msg):
# type: (message.Message) -> None
for message_type, klass in MESSAGE_TYPE_TO_PROTO.items():
if isinstance(msg, klass):
break
else:
raise ValueError
encoded = msg.SerializeToString()
_LOGGER.debug("Sending %s:\n%s", type(msg), indent(str(msg)))
req = bytes([0])
req += _varuint_to_bytes(len(encoded))
req += _varuint_to_bytes(message_type)
req += encoded
self._write(req)
def _send_message_await_response_complex(
self, send_msg, do_append, do_stop, timeout=5
):
event = threading.Event()
responses = []
def on_message(resp):
if do_append(resp):
responses.append(resp)
if do_stop(resp):
event.set()
self._message_handlers.append(on_message)
self._send_message(send_msg)
ret = event.wait(timeout)
try:
self._message_handlers.remove(on_message)
except ValueError:
pass
if not ret:
raise APIConnectionError("Timeout while waiting for message response!")
return responses
def _send_message_await_response(self, send_msg, response_type, timeout=5):
def is_response(msg):
return isinstance(msg, response_type)
return self._send_message_await_response_complex(
send_msg, is_response, is_response, timeout
)[0]
def device_info(self):
self._check_connected()
return self._send_message_await_response(
pb.DeviceInfoRequest(), pb.DeviceInfoResponse
)
def ping(self):
self._check_connected()
return self._send_message_await_response(pb.PingRequest(), pb.PingResponse)
def disconnect(self, on_disconnect=True):
self._check_connected()
try:
self._send_message_await_response(
pb.DisconnectRequest(), pb.DisconnectResponse
)
except APIConnectionError:
pass
self._close_socket()
if self.on_disconnect is not None and on_disconnect:
self.on_disconnect(None)
def _check_authenticated(self):
if not self._authenticated:
raise APIConnectionError("Must login first!")
def subscribe_logs(self, on_log, log_level=7, dump_config=False):
self._check_authenticated()
def on_msg(msg):
if isinstance(msg, pb.SubscribeLogsResponse):
on_log(msg)
self._message_handlers.append(on_msg)
req = pb.SubscribeLogsRequest(dump_config=dump_config)
req.level = log_level
self._send_message(req)
def _recv(self, amount):
ret = bytes()
if amount == 0:
return ret
while len(ret) < amount:
if self.stopped:
raise APIConnectionError("Stopped!")
if not self._socket_open_event.is_set():
raise APIConnectionError("No socket!")
try:
val = self._socket.recv(amount - len(ret))
except AttributeError as err:
raise APIConnectionError("Socket was closed") from err
except socket.timeout:
continue
except OSError as err:
raise APIConnectionError(f"Error while receiving data: {err}") from err
ret += val
return ret
def _recv_varint(self):
raw = bytes()
while not raw or raw[-1] & 0x80:
raw += self._recv(1)
return _bytes_to_varuint(raw)
def _run_once(self):
if not self._socket_open_event.wait(0.1):
return
# Preamble
if self._recv(1)[0] != 0x00:
raise APIConnectionError("Invalid preamble")
length = self._recv_varint()
msg_type = self._recv_varint()
raw_msg = self._recv(length)
if msg_type not in MESSAGE_TYPE_TO_PROTO:
_LOGGER.debug("Skipping message type %s", msg_type)
return
msg = MESSAGE_TYPE_TO_PROTO[msg_type]()
msg.ParseFromString(raw_msg)
_LOGGER.debug("Got message: %s:\n%s", type(msg), indent(str(msg)))
for msg_handler in self._message_handlers[:]:
msg_handler(msg)
self._handle_internal_messages(msg)
def run(self):
self._running_event.set()
while not self.stopped:
try:
self._run_once()
except APIConnectionError as err:
if self.stopped:
break
if self._connected:
_LOGGER.error("Error while reading incoming messages: %s", err)
self._fatal_error(err)
self._running_event.clear()
def _handle_internal_messages(self, msg):
if isinstance(msg, pb.DisconnectRequest):
self._send_message(pb.DisconnectResponse())
if self._socket is not None:
self._socket.close()
self._socket = None
self._connected = False
if self.on_disconnect is not None:
self.on_disconnect(None)
elif isinstance(msg, pb.PingRequest):
self._send_message(pb.PingResponse())
elif isinstance(msg, pb.GetTimeRequest):
resp = pb.GetTimeResponse()
resp.epoch_seconds = int(time.time())
self._send_message(resp)
def run_logs(config, address):
conf = config["api"]
port = conf[CONF_PORT]
password = conf[CONF_PASSWORD]
_LOGGER.info("Starting log output from %s using esphome API", address)
cli = APIClient(address, port, password)
stopping = False
retry_timer = []
has_connects = []
def try_connect(err, tries=0):
if stopping:
return
if err:
_LOGGER.warning("Disconnected from API: %s", err)
while retry_timer:
retry_timer.pop(0).cancel()
error = None
try:
cli.connect()
cli.login()
except APIConnectionError as err2: # noqa
error = err2
if error is None:
_LOGGER.info("Successfully connected to %s", address)
return
wait_time = int(min(1.5 ** min(tries, 100), 30))
if not has_connects:
_LOGGER.warning(
"Initial connection failed. The ESP might not be connected "
"to WiFi yet (%s). Re-Trying in %s seconds",
error,
wait_time,
)
else:
_LOGGER.warning(
"Couldn't connect to API (%s). Trying to reconnect in %s seconds",
error,
wait_time,
)
timer = threading.Timer(
wait_time, functools.partial(try_connect, None, tries + 1)
)
timer.start()
retry_timer.append(timer)
def on_log(msg):
time_ = datetime.now().time().strftime("[%H:%M:%S]")
text = msg.message
if msg.send_failed:
text = color(
Fore.WHITE,
"(Message skipped because it was too big to fit in "
"TCP buffer - This is only cosmetic)",
)
safe_print(time_ + text)
def on_login():
try:
cli.subscribe_logs(on_log, dump_config=not has_connects)
has_connects.append(True)
except APIConnectionError:
cli.disconnect()
cli.on_disconnect = try_connect
cli.on_login = on_login
cli.start()
try:
try_connect(None)
while True:
time.sleep(1)
except KeyboardInterrupt:
stopping = True
cli.stop(True)
while retry_timer:
retry_timer.pop(0).cancel()
return 0

View File

@@ -3,9 +3,11 @@ import esphome.config_validation as cv
from esphome.const import (
CONF_AUTOMATION_ID,
CONF_CONDITION,
CONF_COUNT,
CONF_ELSE,
CONF_ID,
CONF_THEN,
CONF_TIMEOUT,
CONF_TRIGGER_ID,
CONF_TYPE_ID,
CONF_TIME,
@@ -65,6 +67,7 @@ DelayAction = cg.esphome_ns.class_("DelayAction", Action, cg.Component)
LambdaAction = cg.esphome_ns.class_("LambdaAction", Action)
IfAction = cg.esphome_ns.class_("IfAction", Action)
WhileAction = cg.esphome_ns.class_("WhileAction", Action)
RepeatAction = cg.esphome_ns.class_("RepeatAction", Action)
WaitUntilAction = cg.esphome_ns.class_("WaitUntilAction", Action, cg.Component)
UpdateComponentAction = cg.esphome_ns.class_("UpdateComponentAction", Action)
Automation = cg.esphome_ns.class_("Automation")
@@ -240,10 +243,32 @@ async def while_action_to_code(config, action_id, template_arg, args):
return var
@register_action(
"repeat",
RepeatAction,
cv.Schema(
{
cv.Required(CONF_COUNT): cv.templatable(cv.positive_not_null_int),
cv.Required(CONF_THEN): validate_action_list,
}
),
)
async def repeat_action_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
count_template = await cg.templatable(config[CONF_COUNT], args, cg.uint32)
cg.add(var.set_count(count_template))
actions = await build_action_list(config[CONF_THEN], template_arg, args)
cg.add(var.add_then(actions))
return var
def validate_wait_until(value):
schema = cv.Schema(
{
cv.Required(CONF_CONDITION): validate_potentially_and_condition,
cv.Optional(CONF_TIMEOUT): cv.templatable(
cv.positive_time_period_milliseconds
),
}
)
if isinstance(value, dict) and CONF_CONDITION in value:
@@ -255,6 +280,9 @@ def validate_wait_until(value):
async def wait_until_action_to_code(config, action_id, template_arg, args):
conditions = await build_condition(config[CONF_CONDITION], template_arg, args)
var = cg.new_Pvariable(action_id, template_arg, conditions)
if CONF_TIMEOUT in config:
template_ = await cg.templatable(config[CONF_TIMEOUT], args, cg.uint32)
cg.add(var.set_timeout_value(template_))
await cg.register_component(var, {})
return var

View File

@@ -30,6 +30,7 @@ from esphome.cpp_generator import ( # noqa
add_library,
add_build_flag,
add_define,
add_platformio_option,
get_variable,
get_variable_with_full_id,
process_lambda,
@@ -60,12 +61,14 @@ from esphome.cpp_types import ( # noqa
uint8,
uint16,
uint32,
uint64,
int32,
int64,
const_char_ptr,
NAN,
esphome_ns,
App,
Nameable,
EntityBase,
Component,
ComponentPtr,
PollingComponent,
@@ -73,8 +76,11 @@ from esphome.cpp_types import ( # noqa
optional,
arduino_json_ns,
JsonObject,
JsonObjectRef,
JsonObjectConstRef,
JsonObjectConst,
Controller,
GPIOPin,
InternalGPIOPin,
gpio_Flags,
EntityCategory,
Parented,
)

View File

@@ -4,7 +4,7 @@
namespace esphome {
namespace a4988 {
static const char *TAG = "a4988.stepper";
static const char *const TAG = "a4988.stepper";
void A4988::setup() {
ESP_LOGCONFIG(TAG, "Setting up A4988...");

View File

@@ -1,7 +1,7 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/esphal.h"
#include "esphome/core/hal.h"
#include "esphome/components/stepper/stepper.h"
namespace esphome {

View File

@@ -1,28 +1,37 @@
#ifdef USE_ARDUINO
#include "ac_dimmer.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include <cmath>
#ifdef ARDUINO_ARCH_ESP8266
#ifdef USE_ESP8266
#include <core_esp8266_waveform.h>
#endif
#ifdef USE_ESP32_FRAMEWORK_ARDUINO
#include <esp32-hal-timer.h>
#endif
namespace esphome {
namespace ac_dimmer {
static const char *TAG = "ac_dimmer";
static const char *const TAG = "ac_dimmer";
// Global array to store dimmer objects
static AcDimmerDataStore *all_dimmers[32];
static AcDimmerDataStore *all_dimmers[32]; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
/// Time in microseconds the gate should be held high
/// 10µs should be long enough for most triacs
/// For reference: BT136 datasheet says 2µs nominal (page 7)
static uint32_t GATE_ENABLE_TIME = 10;
/// However other factors like gate driver propagation time
/// are also considered and a really low value is not important
/// See also: https://github.com/esphome/issues/issues/1632
static const uint32_t GATE_ENABLE_TIME = 50;
/// Function called from timer interrupt
/// Input is current time in microseconds (micros())
/// Returns when next "event" is expected in µs, or 0 if no such event known.
uint32_t ICACHE_RAM_ATTR HOT AcDimmerDataStore::timer_intr(uint32_t now) {
uint32_t IRAM_ATTR HOT AcDimmerDataStore::timer_intr(uint32_t now) {
// If no ZC signal received yet.
if (this->crossed_zero_at == 0)
return 0;
@@ -34,19 +43,19 @@ uint32_t ICACHE_RAM_ATTR HOT AcDimmerDataStore::timer_intr(uint32_t now) {
if (this->enable_time_us != 0 && time_since_zc >= this->enable_time_us) {
this->enable_time_us = 0;
this->gate_pin->digital_write(true);
this->gate_pin.digital_write(true);
// Prevent too short pulses
this->disable_time_us = max(this->disable_time_us, time_since_zc + GATE_ENABLE_TIME);
this->disable_time_us = std::max(this->disable_time_us, time_since_zc + GATE_ENABLE_TIME);
}
if (this->disable_time_us != 0 && time_since_zc >= this->disable_time_us) {
this->disable_time_us = 0;
this->gate_pin->digital_write(false);
this->gate_pin.digital_write(false);
}
if (time_since_zc < this->enable_time_us)
if (time_since_zc < this->enable_time_us) {
// Next event is enable, return time until that event
return this->enable_time_us - time_since_zc;
else if (time_since_zc < disable_time_us) {
} else if (time_since_zc < disable_time_us) {
// Next event is disable, return time until that event
return this->disable_time_us - time_since_zc;
}
@@ -60,14 +69,15 @@ uint32_t ICACHE_RAM_ATTR HOT AcDimmerDataStore::timer_intr(uint32_t now) {
}
/// Run timer interrupt code and return in how many µs the next event is expected
uint32_t ICACHE_RAM_ATTR HOT timer_interrupt() {
uint32_t IRAM_ATTR HOT timer_interrupt() {
// run at least with 1kHz
uint32_t min_dt_us = 1000;
uint32_t now = micros();
for (auto *dimmer : all_dimmers) {
if (dimmer == nullptr)
if (dimmer == nullptr) {
// no more dimmers
break;
}
uint32_t res = dimmer->timer_intr(now);
if (res != 0 && res < min_dt_us)
min_dt_us = res;
@@ -77,7 +87,7 @@ uint32_t ICACHE_RAM_ATTR HOT timer_interrupt() {
}
/// GPIO interrupt routine, called when ZC pin triggers
void ICACHE_RAM_ATTR HOT AcDimmerDataStore::gpio_intr() {
void IRAM_ATTR HOT AcDimmerDataStore::gpio_intr() {
uint32_t prev_crossed = this->crossed_zero_at;
// 50Hz mains frequency should give a half cycle of 10ms a 60Hz will give 8.33ms
@@ -94,7 +104,7 @@ void ICACHE_RAM_ATTR HOT AcDimmerDataStore::gpio_intr() {
if (this->value == 65535) {
// fully on, enable output immediately
this->gate_pin->digital_write(true);
this->gate_pin.digital_write(true);
} else if (this->init_cycle) {
// send a full cycle
this->init_cycle = false;
@@ -102,30 +112,30 @@ void ICACHE_RAM_ATTR HOT AcDimmerDataStore::gpio_intr() {
this->disable_time_us = cycle_time_us;
} else if (this->value == 0) {
// fully off, disable output immediately
this->gate_pin->digital_write(false);
this->gate_pin.digital_write(false);
} else {
if (this->method == DIM_METHOD_TRAILING) {
this->enable_time_us = 1; // cannot be 0
this->disable_time_us = max((uint32_t) 10, this->value * this->cycle_time_us / 65535);
this->disable_time_us = std::max((uint32_t) 10, this->value * this->cycle_time_us / 65535);
} else {
// 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;
this->enable_time_us = max((uint32_t) 1, ((65535 - this->value) * (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%
this->disable_time_us = max(this->enable_time_us + GATE_ENABLE_TIME, (uint32_t) cycle_time_us / 10);
this->disable_time_us = std::max(this->enable_time_us + GATE_ENABLE_TIME, (uint32_t) cycle_time_us / 10);
} else {
this->gate_pin->digital_write(false);
this->gate_pin.digital_write(false);
this->disable_time_us = this->cycle_time_us;
}
}
}
}
void ICACHE_RAM_ATTR HOT AcDimmerDataStore::s_gpio_intr(AcDimmerDataStore *store) {
// Attaching pin interrupts on the same pin will override the previous interupt
void IRAM_ATTR HOT AcDimmerDataStore::s_gpio_intr(AcDimmerDataStore *store) {
// Attaching pin interrupts on the same pin will override the previous interrupt
// However, the user expects that multiple dimmers sharing the same ZC pin will work.
// We solve this in a bit of a hacky way: On each pin interrupt, we check all dimmers
// if any of them are using the same ZC pin, and also trigger the interrupt for *them*.
@@ -138,11 +148,11 @@ void ICACHE_RAM_ATTR HOT AcDimmerDataStore::s_gpio_intr(AcDimmerDataStore *store
}
}
#ifdef ARDUINO_ARCH_ESP32
#ifdef USE_ESP32
// ESP32 implementation, uses basically the same code but needs to wrap
// timer_interrupt() function to auto-reschedule
static hw_timer_t *dimmer_timer = nullptr;
void ICACHE_RAM_ATTR HOT AcDimmerDataStore::s_timer_intr() { timer_interrupt(); }
static hw_timer_t *dimmer_timer = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
void IRAM_ATTR HOT AcDimmerDataStore::s_timer_intr() { timer_interrupt(); }
#endif
void AcDimmer::setup() {
@@ -171,15 +181,16 @@ void AcDimmer::setup() {
if (setup_zero_cross_pin) {
this->zero_cross_pin_->setup();
this->store_.zero_cross_pin = this->zero_cross_pin_->to_isr();
this->zero_cross_pin_->attach_interrupt(&AcDimmerDataStore::s_gpio_intr, &this->store_, FALLING);
this->zero_cross_pin_->attach_interrupt(&AcDimmerDataStore::s_gpio_intr, &this->store_,
gpio::INTERRUPT_FALLING_EDGE);
}
#ifdef ARDUINO_ARCH_ESP8266
#ifdef USE_ESP8266
// Uses ESP8266 waveform (soft PWM) class
// PWM and AcDimmer can even run at the same time this way
setTimer1Callback(&timer_interrupt);
#endif
#ifdef ARDUINO_ARCH_ESP32
#ifdef USE_ESP32
// 80 Divider -> 1 count=1µs
dimmer_timer = timerBegin(0, 80, true);
timerAttachInterrupt(dimmer_timer, &AcDimmerDataStore::s_timer_intr, true);
@@ -202,12 +213,13 @@ void AcDimmer::dump_config() {
LOG_PIN(" Zero-Cross Pin: ", this->zero_cross_pin_);
ESP_LOGCONFIG(TAG, " Min Power: %.1f%%", this->store_.min_power / 10.0f);
ESP_LOGCONFIG(TAG, " Init with half cycle: %s", YESNO(this->init_with_half_cycle_));
if (method_ == DIM_METHOD_LEADING_PULSE)
if (method_ == DIM_METHOD_LEADING_PULSE) {
ESP_LOGCONFIG(TAG, " Method: leading pulse");
else if (method_ == DIM_METHOD_LEADING)
} else if (method_ == DIM_METHOD_LEADING) {
ESP_LOGCONFIG(TAG, " Method: leading");
else
} else {
ESP_LOGCONFIG(TAG, " Method: trailing");
}
LOG_FLOAT_OUTPUT(this);
ESP_LOGV(TAG, " Estimated Frequency: %.3fHz", 1e6f / this->store_.cycle_time_us / 2);
@@ -215,3 +227,5 @@ void AcDimmer::dump_config() {
} // namespace ac_dimmer
} // namespace esphome
#endif // USE_ARDUINO

View File

@@ -1,7 +1,9 @@
#pragma once
#ifdef USE_ARDUINO
#include "esphome/core/component.h"
#include "esphome/core/esphal.h"
#include "esphome/core/hal.h"
#include "esphome/components/output/float_output.h"
namespace esphome {
@@ -11,11 +13,11 @@ enum DimMethod { DIM_METHOD_LEADING_PULSE = 0, DIM_METHOD_LEADING, DIM_METHOD_TR
struct AcDimmerDataStore {
/// Zero-cross pin
ISRInternalGPIOPin *zero_cross_pin;
ISRInternalGPIOPin zero_cross_pin;
/// Zero-cross pin number - used to share ZC pin across multiple dimmers
uint8_t zero_cross_pin_number;
/// Output pin to write to
ISRInternalGPIOPin *gate_pin;
ISRInternalGPIOPin gate_pin;
/// Value of the dimmer - 0 to 65535.
uint16_t value;
/// Minimum power for activation
@@ -37,7 +39,7 @@ struct AcDimmerDataStore {
void gpio_intr();
static void s_gpio_intr(AcDimmerDataStore *store);
#ifdef ARDUINO_ARCH_ESP32
#ifdef USE_ESP32
static void s_timer_intr();
#endif
};
@@ -47,16 +49,16 @@ class AcDimmer : public output::FloatOutput, public Component {
void setup() override;
void dump_config() override;
void set_gate_pin(GPIOPin *gate_pin) { gate_pin_ = gate_pin; }
void set_zero_cross_pin(GPIOPin *zero_cross_pin) { zero_cross_pin_ = zero_cross_pin; }
void set_gate_pin(InternalGPIOPin *gate_pin) { gate_pin_ = gate_pin; }
void set_zero_cross_pin(InternalGPIOPin *zero_cross_pin) { zero_cross_pin_ = zero_cross_pin; }
void set_init_with_half_cycle(bool init_with_half_cycle) { init_with_half_cycle_ = init_with_half_cycle; }
void set_method(DimMethod method) { method_ = method; }
protected:
void write_state(float state) override;
GPIOPin *gate_pin_;
GPIOPin *zero_cross_pin_;
InternalGPIOPin *gate_pin_;
InternalGPIOPin *zero_cross_pin_;
AcDimmerDataStore store_;
bool init_with_half_cycle_;
DimMethod method_;
@@ -64,3 +66,5 @@ class AcDimmer : public output::FloatOutput, public Component {
} // namespace ac_dimmer
} // namespace esphome
#endif // USE_ARDUINO

View File

@@ -19,17 +19,20 @@ DIM_METHODS = {
CONF_GATE_PIN = "gate_pin"
CONF_ZERO_CROSS_PIN = "zero_cross_pin"
CONF_INIT_WITH_HALF_CYCLE = "init_with_half_cycle"
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
{
cv.Required(CONF_ID): cv.declare_id(AcDimmer),
cv.Required(CONF_GATE_PIN): pins.internal_gpio_output_pin_schema,
cv.Required(CONF_ZERO_CROSS_PIN): pins.internal_gpio_input_pin_schema,
cv.Optional(CONF_INIT_WITH_HALF_CYCLE, default=True): cv.boolean,
cv.Optional(CONF_METHOD, default="leading pulse"): cv.enum(
DIM_METHODS, upper=True, space="_"
),
}
).extend(cv.COMPONENT_SCHEMA)
CONFIG_SCHEMA = cv.All(
output.FLOAT_OUTPUT_SCHEMA.extend(
{
cv.Required(CONF_ID): cv.declare_id(AcDimmer),
cv.Required(CONF_GATE_PIN): pins.internal_gpio_output_pin_schema,
cv.Required(CONF_ZERO_CROSS_PIN): pins.internal_gpio_input_pin_schema,
cv.Optional(CONF_INIT_WITH_HALF_CYCLE, default=True): cv.boolean,
cv.Optional(CONF_METHOD, default="leading pulse"): cv.enum(
DIM_METHODS, upper=True, space="_"
),
}
).extend(cv.COMPONENT_SCHEMA),
cv.only_with_arduino,
)
async def to_code(config):

View File

@@ -4,7 +4,7 @@
namespace esphome {
namespace adalight {
static const char *TAG = "adalight_light_effect";
static const char *const TAG = "adalight_light_effect";
static const uint32_t ADALIGHT_ACK_INTERVAL = 1000;
static const uint32_t ADALIGHT_RECEIVE_TIMEOUT = 1000;
@@ -25,7 +25,7 @@ void AdalightLightEffect::stop() {
AddressableLightEffect::stop();
}
int AdalightLightEffect::get_frame_size_(int led_count) const {
unsigned int AdalightLightEffect::get_frame_size_(int led_count) const {
// 3 bytes: Ada
// 2 bytes: LED count
// 1 byte: checksum
@@ -42,8 +42,9 @@ void AdalightLightEffect::reset_frame_(light::AddressableLight &it) {
void AdalightLightEffect::blank_all_leds_(light::AddressableLight &it) {
for (int led = it.size(); led-- > 0;) {
it[led].set(COLOR_BLACK);
it[led].set(Color::BLACK);
}
it.schedule_show();
}
void AdalightLightEffect::apply(light::AddressableLight &it, const Color &current_color) {
@@ -133,6 +134,7 @@ AdalightLightEffect::Frame AdalightLightEffect::parse_frame_(light::AddressableL
it[led].set(Color(led_data[0], led_data[1], led_data[2], white));
}
it.schedule_show();
return CONSUMED;
}

View File

@@ -13,7 +13,6 @@ class AdalightLightEffect : public light::AddressableLightEffect, public uart::U
public:
AdalightLightEffect(const std::string &name);
public:
void start() override;
void stop() override;
void apply(light::AddressableLight &it, const Color &current_color) override;
@@ -25,12 +24,11 @@ class AdalightLightEffect : public light::AddressableLightEffect, public uart::U
CONSUMED,
};
int get_frame_size_(int led_count) const;
unsigned int get_frame_size_(int led_count) const;
void reset_frame_(light::AddressableLight &it);
void blank_all_leds_(light::AddressableLight &it);
Frame parse_frame_(light::AddressableLight &it);
protected:
uint32_t last_ack_{0};
uint32_t last_byte_{0};
uint32_t last_reset_{0};

View File

@@ -1,92 +1,174 @@
#include "adc_sensor.h"
#include "esphome/core/log.h"
#include "esphome/core/helpers.h"
#ifdef USE_ESP8266
#ifdef USE_ADC_SENSOR_VCC
#include <Esp.h>
ADC_MODE(ADC_VCC)
#else
#include <Arduino.h>
#endif
#endif
namespace esphome {
namespace adc {
static const char *TAG = "adc";
#ifdef ARDUINO_ARCH_ESP32
void ADCSensor::set_attenuation(adc_attenuation_t attenuation) { this->attenuation_ = attenuation; }
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
#ifdef USE_ESP32
static const adc_bits_width_t ADC_WIDTH_MAX_SOC_BITS = static_cast<adc_bits_width_t>(ADC_WIDTH_MAX - 1);
#endif
void ADCSensor::setup() {
ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
#ifndef USE_ADC_SENSOR_VCC
GPIOPin(this->pin_, INPUT).setup();
pin_->setup();
#endif
#ifdef ARDUINO_ARCH_ESP32
analogSetPinAttenuation(this->pin_, this->attenuation_);
#ifdef USE_ESP32
adc1_config_width(ADC_WIDTH_MAX_SOC_BITS);
if (!autorange_) {
adc1_config_channel_atten(channel_, attenuation_);
}
// load characteristics for each attenuation
for (int i = 0; i < (int) ADC_ATTEN_MAX; i++) {
auto cal_value = esp_adc_cal_characterize(ADC_UNIT_1, (adc_atten_t) i, ADC_WIDTH_MAX_SOC_BITS,
1100, // default vref
&cal_characteristics_[i]);
switch (cal_value) {
case ESP_ADC_CAL_VAL_EFUSE_VREF:
ESP_LOGV(TAG, "Using eFuse Vref for calibration");
break;
case ESP_ADC_CAL_VAL_EFUSE_TP:
ESP_LOGV(TAG, "Using two-point eFuse Vref for calibration");
break;
case ESP_ADC_CAL_VAL_DEFAULT_VREF:
default:
break;
}
}
// adc_gpio_init doesn't exist on ESP32-C3 or ESP32-H2
#if !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32H2)
adc_gpio_init(ADC_UNIT_1, (adc_channel_t) channel_);
#endif
#endif // USE_ESP32
}
void ADCSensor::dump_config() {
LOG_SENSOR("", "ADC Sensor", this);
#ifdef ARDUINO_ARCH_ESP8266
#ifdef USE_ESP8266
#ifdef USE_ADC_SENSOR_VCC
ESP_LOGCONFIG(TAG, " Pin: VCC");
#else
ESP_LOGCONFIG(TAG, " Pin: %u", this->pin_);
LOG_PIN(" Pin: ", pin_);
#endif
#endif
#ifdef ARDUINO_ARCH_ESP32
ESP_LOGCONFIG(TAG, " Pin: %u", this->pin_);
switch (this->attenuation_) {
case ADC_0db:
ESP_LOGCONFIG(TAG, " Attenuation: 0db (max 1.1V)");
break;
case ADC_2_5db:
ESP_LOGCONFIG(TAG, " Attenuation: 2.5db (max 1.5V)");
break;
case ADC_6db:
ESP_LOGCONFIG(TAG, " Attenuation: 6db (max 2.2V)");
break;
case ADC_11db:
ESP_LOGCONFIG(TAG, " Attenuation: 11db (max 3.9V)");
break;
#endif // USE_ESP8266
#ifdef USE_ESP32
LOG_PIN(" Pin: ", pin_);
if (autorange_) {
ESP_LOGCONFIG(TAG, " Attenuation: auto");
} else {
switch (this->attenuation_) {
case ADC_ATTEN_DB_0:
ESP_LOGCONFIG(TAG, " Attenuation: 0db (max 1.1V)");
break;
case ADC_ATTEN_DB_2_5:
ESP_LOGCONFIG(TAG, " Attenuation: 2.5db (max 1.5V)");
break;
case ADC_ATTEN_DB_6:
ESP_LOGCONFIG(TAG, " Attenuation: 6db (max 2.2V)");
break;
case ADC_ATTEN_DB_11:
ESP_LOGCONFIG(TAG, " Attenuation: 11db (max 3.9V)");
break;
default: // This is to satisfy the unused ADC_ATTEN_MAX
break;
}
}
#endif
#endif // USE_ESP32
LOG_UPDATE_INTERVAL(this);
}
float ADCSensor::get_setup_priority() const { return setup_priority::DATA; }
void ADCSensor::update() {
float value_v = this->sample();
ESP_LOGD(TAG, "'%s': Got voltage=%.2fV", this->get_name().c_str(), value_v);
ESP_LOGV(TAG, "'%s': Got voltage=%.4fV", this->get_name().c_str(), value_v);
this->publish_state(value_v);
}
#ifdef USE_ESP8266
float ADCSensor::sample() {
#ifdef ARDUINO_ARCH_ESP32
float value_v = analogRead(this->pin_) / 4095.0f; // NOLINT
switch (this->attenuation_) {
case ADC_0db:
value_v *= 1.1;
break;
case ADC_2_5db:
value_v *= 1.5;
break;
case ADC_6db:
value_v *= 2.2;
break;
case ADC_11db:
value_v *= 3.9;
break;
#ifdef USE_ADC_SENSOR_VCC
int raw = ESP.getVcc(); // NOLINT(readability-static-accessed-through-instance)
#else
int raw = analogRead(this->pin_->get_pin()); // NOLINT
#endif
if (output_raw_) {
return raw;
}
return value_v;
return raw / 1024.0f;
}
#endif
#ifdef ARDUINO_ARCH_ESP8266
#ifdef USE_ADC_SENSOR_VCC
return ESP.getVcc() / 1024.0f;
#else
return analogRead(this->pin_) / 1024.0f; // NOLINT
#endif
#endif
#ifdef USE_ESP32
float ADCSensor::sample() {
if (!autorange_) {
int raw = adc1_get_raw(channel_);
if (raw == -1) {
return NAN;
}
if (output_raw_) {
return raw;
}
uint32_t mv = esp_adc_cal_raw_to_voltage(raw, &cal_characteristics_[(int) attenuation_]);
return mv / 1000.0f;
}
int raw11, raw6 = 4095, raw2 = 4095, raw0 = 4095;
adc1_config_channel_atten(channel_, ADC_ATTEN_DB_11);
raw11 = adc1_get_raw(channel_);
if (raw11 < 4095) {
adc1_config_channel_atten(channel_, ADC_ATTEN_DB_6);
raw6 = adc1_get_raw(channel_);
if (raw6 < 4095) {
adc1_config_channel_atten(channel_, ADC_ATTEN_DB_2_5);
raw2 = adc1_get_raw(channel_);
if (raw2 < 4095) {
adc1_config_channel_atten(channel_, ADC_ATTEN_DB_0);
raw0 = adc1_get_raw(channel_);
}
}
}
if (raw0 == -1 || raw2 == -1 || raw6 == -1 || raw11 == -1) {
return NAN;
}
uint32_t mv11 = esp_adc_cal_raw_to_voltage(raw11, &cal_characteristics_[(int) ADC_ATTEN_DB_11]);
uint32_t mv6 = esp_adc_cal_raw_to_voltage(raw6, &cal_characteristics_[(int) ADC_ATTEN_DB_6]);
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
uint32_t csum = c11 + c6 + c2 + c0;
// each mv is max 3900; so max value is 3900*2048*4, fits in unsigned
uint32_t mv_scaled = (mv11 * c11) + (mv6 * c6) + (mv2 * c2) + (mv0 * c0);
return mv_scaled / (float) (csum * 1000U);
}
#ifdef ARDUINO_ARCH_ESP8266
#endif // USE_ESP32
#ifdef USE_ESP8266
std::string ADCSensor::unique_id() { return get_mac_address() + "-adc"; }
#endif

View File

@@ -1,19 +1,26 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/esphal.h"
#include "esphome/core/hal.h"
#include "esphome/core/defines.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/voltage_sampler/voltage_sampler.h"
#ifdef USE_ESP32
#include "driver/adc.h"
#include <esp_adc_cal.h>
#endif
namespace esphome {
namespace adc {
class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage_sampler::VoltageSampler {
public:
#ifdef ARDUINO_ARCH_ESP32
#ifdef USE_ESP32
/// Set the attenuation for this pin. Only available on the ESP32.
void set_attenuation(adc_attenuation_t attenuation);
void set_attenuation(adc_atten_t attenuation) { attenuation_ = attenuation; }
void set_channel(adc1_channel_t channel) { channel_ = channel; }
void set_autorange(bool autorange) { autorange_ = autorange; }
#endif
/// Update adc values.
@@ -23,18 +30,23 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
void dump_config() override;
/// `HARDWARE_LATE` setup priority.
float get_setup_priority() const override;
void set_pin(uint8_t pin) { this->pin_ = pin; }
void set_pin(InternalGPIOPin *pin) { this->pin_ = pin; }
void set_output_raw(bool output_raw) { output_raw_ = output_raw; }
float sample() override;
#ifdef ARDUINO_ARCH_ESP8266
#ifdef USE_ESP8266
std::string unique_id() override;
#endif
protected:
uint8_t pin_;
InternalGPIOPin *pin_;
bool output_raw_{false};
#ifdef ARDUINO_ARCH_ESP32
adc_attenuation_t attenuation_{ADC_0db};
#ifdef USE_ESP32
adc_atten_t attenuation_{ADC_ATTEN_DB_0};
adc1_channel_t channel_{};
bool autorange_{false};
esp_adc_cal_characteristics_t cal_characteristics_[(int) ADC_ATTEN_MAX] = {};
#endif
};

View File

@@ -4,30 +4,126 @@ from esphome import pins
from esphome.components import sensor, voltage_sampler
from esphome.const import (
CONF_ATTENUATION,
CONF_RAW,
CONF_ID,
CONF_INPUT,
CONF_NUMBER,
CONF_PIN,
DEVICE_CLASS_VOLTAGE,
ICON_EMPTY,
STATE_CLASS_MEASUREMENT,
UNIT_VOLT,
)
from esphome.core import CORE
from esphome.components.esp32 import get_esp32_variant
from esphome.components.esp32.const import (
VARIANT_ESP32,
VARIANT_ESP32C3,
VARIANT_ESP32H2,
VARIANT_ESP32S2,
VARIANT_ESP32S3,
)
AUTO_LOAD = ["voltage_sampler"]
ATTENUATION_MODES = {
"0db": cg.global_ns.ADC_0db,
"2.5db": cg.global_ns.ADC_2_5db,
"6db": cg.global_ns.ADC_6db,
"11db": cg.global_ns.ADC_11db,
"0db": cg.global_ns.ADC_ATTEN_DB_0,
"2.5db": cg.global_ns.ADC_ATTEN_DB_2_5,
"6db": cg.global_ns.ADC_ATTEN_DB_6,
"11db": cg.global_ns.ADC_ATTEN_DB_11,
"auto": "auto",
}
adc1_channel_t = cg.global_ns.enum("adc1_channel_t")
# From https://github.com/espressif/esp-idf/blob/master/components/driver/include/driver/adc_common.h
# pin to adc1 channel mapping
ESP32_VARIANT_ADC1_PIN_TO_CHANNEL = {
VARIANT_ESP32: {
36: adc1_channel_t.ADC1_CHANNEL_0,
37: adc1_channel_t.ADC1_CHANNEL_1,
38: adc1_channel_t.ADC1_CHANNEL_2,
39: adc1_channel_t.ADC1_CHANNEL_3,
32: adc1_channel_t.ADC1_CHANNEL_4,
33: adc1_channel_t.ADC1_CHANNEL_5,
34: adc1_channel_t.ADC1_CHANNEL_6,
35: adc1_channel_t.ADC1_CHANNEL_7,
},
VARIANT_ESP32S2: {
1: adc1_channel_t.ADC1_CHANNEL_0,
2: adc1_channel_t.ADC1_CHANNEL_1,
3: adc1_channel_t.ADC1_CHANNEL_2,
4: adc1_channel_t.ADC1_CHANNEL_3,
5: adc1_channel_t.ADC1_CHANNEL_4,
6: adc1_channel_t.ADC1_CHANNEL_5,
7: adc1_channel_t.ADC1_CHANNEL_6,
8: adc1_channel_t.ADC1_CHANNEL_7,
9: adc1_channel_t.ADC1_CHANNEL_8,
10: adc1_channel_t.ADC1_CHANNEL_9,
},
VARIANT_ESP32S3: {
1: adc1_channel_t.ADC1_CHANNEL_0,
2: adc1_channel_t.ADC1_CHANNEL_1,
3: adc1_channel_t.ADC1_CHANNEL_2,
4: adc1_channel_t.ADC1_CHANNEL_3,
5: adc1_channel_t.ADC1_CHANNEL_4,
6: adc1_channel_t.ADC1_CHANNEL_5,
7: adc1_channel_t.ADC1_CHANNEL_6,
8: adc1_channel_t.ADC1_CHANNEL_7,
9: adc1_channel_t.ADC1_CHANNEL_8,
10: adc1_channel_t.ADC1_CHANNEL_9,
},
VARIANT_ESP32C3: {
0: adc1_channel_t.ADC1_CHANNEL_0,
1: adc1_channel_t.ADC1_CHANNEL_1,
2: adc1_channel_t.ADC1_CHANNEL_2,
3: adc1_channel_t.ADC1_CHANNEL_3,
4: adc1_channel_t.ADC1_CHANNEL_4,
},
VARIANT_ESP32H2: {
0: adc1_channel_t.ADC1_CHANNEL_0,
1: adc1_channel_t.ADC1_CHANNEL_1,
2: adc1_channel_t.ADC1_CHANNEL_2,
3: adc1_channel_t.ADC1_CHANNEL_3,
4: adc1_channel_t.ADC1_CHANNEL_4,
},
}
def validate_adc_pin(value):
vcc = str(value).upper()
if vcc == "VCC":
return cv.only_on_esp8266(vcc)
return pins.analog_pin(value)
if str(value).upper() == "VCC":
return cv.only_on_esp8266("VCC")
if CORE.is_esp32:
value = pins.internal_gpio_input_pin_number(value)
variant = get_esp32_variant()
if variant not in ESP32_VARIANT_ADC1_PIN_TO_CHANNEL:
raise cv.Invalid(f"This ESP32 variant ({variant}) is not supported")
if value not in ESP32_VARIANT_ADC1_PIN_TO_CHANNEL[variant]:
raise cv.Invalid(f"{variant} doesn't support ADC on this pin")
return pins.internal_gpio_input_pin_schema(value)
if CORE.is_esp8266:
from esphome.components.esp8266.gpio import CONF_ANALOG
value = pins.internal_gpio_pin_number({CONF_ANALOG: True, CONF_INPUT: True})(
value
)
if value != 17: # A0
raise cv.Invalid("ESP8266: Only pin A0 (GPIO17) supports ADC.")
return pins.gpio_pin_schema(
{CONF_ANALOG: True, CONF_INPUT: True}, internal=True
)(value)
raise NotImplementedError
def validate_config(config):
if config[CONF_RAW] and config.get(CONF_ATTENUATION, None) == "auto":
raise cv.Invalid("Automatic attenuation cannot be used when raw output is set.")
return config
adc_ns = cg.esphome_ns.namespace("adc")
@@ -35,20 +131,25 @@ ADCSensor = adc_ns.class_(
"ADCSensor", sensor.Sensor, cg.PollingComponent, voltage_sampler.VoltageSampler
)
CONFIG_SCHEMA = (
CONFIG_SCHEMA = cv.All(
sensor.sensor_schema(
UNIT_VOLT, ICON_EMPTY, 2, DEVICE_CLASS_VOLTAGE, STATE_CLASS_MEASUREMENT
ADCSensor,
unit_of_measurement=UNIT_VOLT,
accuracy_decimals=2,
device_class=DEVICE_CLASS_VOLTAGE,
state_class=STATE_CLASS_MEASUREMENT,
)
.extend(
{
cv.GenerateID(): cv.declare_id(ADCSensor),
cv.Required(CONF_PIN): validate_adc_pin,
cv.Optional(CONF_RAW, default=False): cv.boolean,
cv.SplitDefault(CONF_ATTENUATION, esp32="0db"): cv.All(
cv.only_on_esp32, cv.enum(ATTENUATION_MODES, lower=True)
),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(cv.polling_component_schema("60s")),
validate_config,
)
@@ -60,7 +161,20 @@ async def to_code(config):
if config[CONF_PIN] == "VCC":
cg.add_define("USE_ADC_SENSOR_VCC")
else:
cg.add(var.set_pin(config[CONF_PIN]))
pin = await cg.gpio_pin_expression(config[CONF_PIN])
cg.add(var.set_pin(pin))
if CONF_RAW in config:
cg.add(var.set_output_raw(config[CONF_RAW]))
if CONF_ATTENUATION in config:
cg.add(var.set_attenuation(config[CONF_ATTENUATION]))
if config[CONF_ATTENUATION] == "auto":
cg.add(var.set_autorange(cg.global_ns.true))
else:
cg.add(var.set_attenuation(config[CONF_ATTENUATION]))
if CORE.is_esp32:
variant = get_esp32_variant()
pin_num = config[CONF_PIN][CONF_NUMBER]
chan = ESP32_VARIANT_ADC1_PIN_TO_CHANNEL[variant][pin_num]
cg.add(var.set_channel(chan))

View File

@@ -4,7 +4,7 @@
namespace esphome {
namespace addressable_light {
static const char* TAG = "addressable_light.display";
static const char *const TAG = "addressable_light.display";
int AddressableLightDisplay::get_width_internal() { return this->width_; }
int AddressableLightDisplay::get_height_internal() { return this->height_; }
@@ -24,7 +24,7 @@ void AddressableLightDisplay::update() {
void AddressableLightDisplay::display() {
bool dirty = false;
uint8_t old_r, old_g, old_b, old_w;
Color* c;
Color *c;
for (uint32_t offset = 0; offset < this->addressable_light_buffer_.size(); offset++) {
c = &(this->addressable_light_buffer_[offset]);

View File

@@ -4,13 +4,11 @@
namespace esphome {
namespace ade7953 {
static const char *TAG = "ade7953";
static const char *const TAG = "ade7953";
void ADE7953::dump_config() {
ESP_LOGCONFIG(TAG, "ADE7953:");
if (this->has_irq_) {
ESP_LOGCONFIG(TAG, " IRQ Pin: GPIO%u", this->irq_pin_number_);
}
LOG_PIN(" IRQ Pin: ", irq_pin_);
LOG_I2C_DEVICE(this);
LOG_UPDATE_INTERVAL(this);
LOG_SENSOR(" ", "Voltage Sensor", this->voltage_sensor_);
@@ -20,27 +18,28 @@ void ADE7953::dump_config() {
LOG_SENSOR(" ", "Active Power B Sensor", this->active_power_b_sensor_);
}
#define ADE_PUBLISH_(name, factor) \
if (name && this->name##_sensor_) { \
float value = *name / factor; \
#define ADE_PUBLISH_(name, val, factor) \
if (err == i2c::ERROR_OK && this->name##_sensor_) { \
float value = (val) / (factor); \
this->name##_sensor_->publish_state(value); \
}
#define ADE_PUBLISH(name, factor) ADE_PUBLISH_(name, factor)
#define ADE_PUBLISH(name, val, factor) ADE_PUBLISH_(name, val, factor)
void ADE7953::update() {
if (!this->is_setup_)
return;
auto active_power_a = this->ade_read_<int32_t>(0x0312);
ADE_PUBLISH(active_power_a, 154.0f);
auto active_power_b = this->ade_read_<int32_t>(0x0313);
ADE_PUBLISH(active_power_b, 154.0f);
auto current_a = this->ade_read_<uint32_t>(0x031A);
ADE_PUBLISH(current_a, 100000.0f);
auto current_b = this->ade_read_<uint32_t>(0x031B);
ADE_PUBLISH(current_b, 100000.0f);
auto voltage = this->ade_read_<uint32_t>(0x031C);
ADE_PUBLISH(voltage, 26000.0f);
uint32_t val;
i2c::ErrorCode err = ade_read_32_(0x0312, &val);
ADE_PUBLISH(active_power_a, (int32_t) val, 154.0f);
err = ade_read_32_(0x0313, &val);
ADE_PUBLISH(active_power_b, (int32_t) val, 154.0f);
err = ade_read_32_(0x031A, &val);
ADE_PUBLISH(current_a, (uint32_t) val, 100000.0f);
err = ade_read_32_(0x031B, &val);
ADE_PUBLISH(current_b, (uint32_t) val, 100000.0f);
err = ade_read_32_(0x031C, &val);
ADE_PUBLISH(voltage, (uint32_t) val, 26000.0f);
// auto apparent_power_a = this->ade_read_<int32_t>(0x0310);
// auto apparent_power_b = this->ade_read_<int32_t>(0x0311);

View File

@@ -1,6 +1,7 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/hal.h"
#include "esphome/components/i2c/i2c.h"
#include "esphome/components/sensor/sensor.h"
@@ -9,10 +10,7 @@ namespace ade7953 {
class ADE7953 : public i2c::I2CDevice, public PollingComponent {
public:
void set_irq_pin(uint8_t irq_pin) {
has_irq_ = true;
irq_pin_number_ = irq_pin;
}
void set_irq_pin(InternalGPIOPin *irq_pin) { irq_pin_ = irq_pin; }
void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; }
void set_current_a_sensor(sensor::Sensor *current_a_sensor) { current_a_sensor_ = current_a_sensor; }
void set_current_b_sensor(sensor::Sensor *current_b_sensor) { current_b_sensor_ = current_b_sensor; }
@@ -24,15 +22,13 @@ class ADE7953 : public i2c::I2CDevice, public PollingComponent {
}
void setup() override {
if (this->has_irq_) {
auto pin = GPIOPin(this->irq_pin_number_, INPUT);
this->irq_pin_ = &pin;
if (this->irq_pin_ != nullptr) {
this->irq_pin_->setup();
}
this->set_timeout(100, [this]() {
this->ade_write_<uint8_t>(0x0010, 0x04);
this->ade_write_<uint8_t>(0x00FE, 0xAD);
this->ade_write_<uint16_t>(0x0120, 0x0030);
this->ade_write_8_(0x0010, 0x04);
this->ade_write_8_(0x00FE, 0xAD);
this->ade_write_16_(0x0120, 0x0030);
this->is_setup_ = true;
});
}
@@ -42,31 +38,51 @@ class ADE7953 : public i2c::I2CDevice, public PollingComponent {
void update() override;
protected:
template<typename T> bool ade_write_(uint16_t reg, T value) {
i2c::ErrorCode ade_write_8_(uint16_t reg, uint8_t value) {
std::vector<uint8_t> data;
data.push_back(reg >> 8);
data.push_back(reg >> 0);
for (int i = sizeof(T) - 1; i >= 0; i--)
data.push_back(value >> (i * 8));
return this->write_bytes_raw(data);
data.push_back(value);
return write(data.data(), data.size());
}
template<typename T> optional<T> ade_read_(uint16_t reg) {
uint8_t hi = reg >> 8;
uint8_t lo = reg >> 0;
if (!this->write_bytes_raw({hi, lo}))
return {};
auto ret = this->read_bytes_raw<sizeof(T)>();
if (!ret.has_value())
return {};
T result = 0;
for (int i = 0, j = sizeof(T) - 1; i < sizeof(T); i++, j--)
result |= T((*ret)[i]) << (j * 8);
return result;
i2c::ErrorCode ade_write_16_(uint16_t reg, uint16_t value) {
std::vector<uint8_t> data;
data.push_back(reg >> 8);
data.push_back(reg >> 0);
data.push_back(value >> 8);
data.push_back(value >> 0);
return write(data.data(), data.size());
}
i2c::ErrorCode ade_write_32_(uint16_t reg, uint32_t value) {
std::vector<uint8_t> data;
data.push_back(reg >> 8);
data.push_back(reg >> 0);
data.push_back(value >> 24);
data.push_back(value >> 16);
data.push_back(value >> 8);
data.push_back(value >> 0);
return write(data.data(), data.size());
}
i2c::ErrorCode ade_read_32_(uint16_t reg, uint32_t *value) {
uint8_t reg_data[2];
reg_data[0] = reg >> 8;
reg_data[1] = reg >> 0;
i2c::ErrorCode err = write(reg_data, 2);
if (err != i2c::ERROR_OK)
return err;
uint8_t recv[4];
err = read(recv, 4);
if (err != i2c::ERROR_OK)
return err;
*value = 0;
*value |= ((uint32_t) recv[0]) << 24;
*value |= ((uint32_t) recv[1]) << 16;
*value |= ((uint32_t) recv[2]) << 8;
*value |= ((uint32_t) recv[3]);
return i2c::ERROR_OK;
}
bool has_irq_ = false;
uint8_t irq_pin_number_;
GPIOPin *irq_pin_{nullptr};
InternalGPIOPin *irq_pin_ = nullptr;
bool is_setup_{false};
sensor::Sensor *voltage_sensor_{nullptr};
sensor::Sensor *current_a_sensor_{nullptr};

View File

@@ -8,7 +8,6 @@ from esphome.const import (
DEVICE_CLASS_CURRENT,
DEVICE_CLASS_POWER,
DEVICE_CLASS_VOLTAGE,
ICON_EMPTY,
STATE_CLASS_MEASUREMENT,
UNIT_VOLT,
UNIT_AMPERE,
@@ -30,29 +29,36 @@ CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(ADE7953),
cv.Optional(CONF_IRQ_PIN): pins.input_pin,
cv.Optional(CONF_IRQ_PIN): pins.internal_gpio_input_pin_schema,
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE, STATE_CLASS_MEASUREMENT
unit_of_measurement=UNIT_VOLT,
accuracy_decimals=1,
device_class=DEVICE_CLASS_VOLTAGE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_CURRENT_A): sensor.sensor_schema(
UNIT_AMPERE,
ICON_EMPTY,
2,
DEVICE_CLASS_CURRENT,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_AMPERE,
accuracy_decimals=2,
device_class=DEVICE_CLASS_CURRENT,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_CURRENT_B): sensor.sensor_schema(
UNIT_AMPERE,
ICON_EMPTY,
2,
DEVICE_CLASS_CURRENT,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_AMPERE,
accuracy_decimals=2,
device_class=DEVICE_CLASS_CURRENT,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_ACTIVE_POWER_A): sensor.sensor_schema(
UNIT_WATT, ICON_EMPTY, 1, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
unit_of_measurement=UNIT_WATT,
accuracy_decimals=1,
device_class=DEVICE_CLASS_POWER,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_ACTIVE_POWER_B): sensor.sensor_schema(
UNIT_WATT, ICON_EMPTY, 1, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
unit_of_measurement=UNIT_WATT,
accuracy_decimals=1,
device_class=DEVICE_CLASS_POWER,
state_class=STATE_CLASS_MEASUREMENT,
),
}
)
@@ -67,7 +73,8 @@ async def to_code(config):
await i2c.register_i2c_device(var, config)
if CONF_IRQ_PIN in config:
cg.add(var.set_irq_pin(config[CONF_IRQ_PIN]))
irq_pin = await cg.gpio_pin_expression(config[CONF_IRQ_PIN])
cg.add(var.set_irq_pin(irq_pin))
for key in [
CONF_VOLTAGE,

View File

@@ -1,10 +1,11 @@
#include "ads1115.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
namespace esphome {
namespace ads1115 {
static const char *TAG = "ads1115";
static const char *const TAG = "ads1115";
static const uint8_t ADS1115_REGISTER_CONVERSION = 0x00;
static const uint8_t ADS1115_REGISTER_CONFIG = 0x01;
@@ -64,11 +65,6 @@ void ADS1115Component::setup() {
return;
}
this->prev_config_ = config;
for (auto *sensor : this->sensors_) {
this->set_interval(sensor->get_name(), sensor->update_interval(),
[this, sensor] { this->request_measurement(sensor); });
}
}
void ADS1115Component::dump_config() {
ESP_LOGCONFIG(TAG, "Setting up ADS1115...");
@@ -107,17 +103,22 @@ float ADS1115Component::request_measurement(ADS1115Sensor *sensor) {
}
this->prev_config_ = config;
// about 1.6 ms with 860 samples per second
// about 1.2 ms with 860 samples per second
delay(2);
uint32_t start = millis();
while (this->read_byte_16(ADS1115_REGISTER_CONFIG, &config) && (config >> 15) == 0) {
if (millis() - start > 100) {
ESP_LOGW(TAG, "Reading ADS1115 timed out");
this->status_set_warning();
return NAN;
// in continuous mode, conversion will always be running, rely on the delay
// to ensure conversion is taking place with the correct settings
// can we use the rdy pin to trigger when a conversion is done?
if (!this->continuous_mode_) {
uint32_t start = millis();
while (this->read_byte_16(ADS1115_REGISTER_CONFIG, &config) && (config >> 15) == 0) {
if (millis() - start > 100) {
ESP_LOGW(TAG, "Reading ADS1115 timed out");
this->status_set_warning();
return NAN;
}
yield();
}
yield();
}
}
@@ -159,7 +160,7 @@ float ADS1115Component::request_measurement(ADS1115Sensor *sensor) {
float ADS1115Sensor::sample() { return this->parent_->request_measurement(this); }
void ADS1115Sensor::update() {
float v = this->parent_->request_measurement(this);
if (!isnan(v)) {
if (!std::isnan(v)) {
ESP_LOGD(TAG, "'%s': Got Voltage=%fV", this->get_name().c_str(), v);
this->publish_state(v);
}

View File

@@ -5,7 +5,6 @@ from esphome.const import (
CONF_GAIN,
CONF_MULTIPLEXER,
DEVICE_CLASS_VOLTAGE,
ICON_EMPTY,
STATE_CLASS_MEASUREMENT,
UNIT_VOLT,
CONF_ID,
@@ -53,11 +52,14 @@ ADS1115Sensor = ads1115_ns.class_(
CONF_ADS1115_ID = "ads1115_id"
CONFIG_SCHEMA = (
sensor.sensor_schema(
UNIT_VOLT, ICON_EMPTY, 3, DEVICE_CLASS_VOLTAGE, STATE_CLASS_MEASUREMENT
ADS1115Sensor,
unit_of_measurement=UNIT_VOLT,
accuracy_decimals=3,
device_class=DEVICE_CLASS_VOLTAGE,
state_class=STATE_CLASS_MEASUREMENT,
)
.extend(
{
cv.GenerateID(): cv.declare_id(ADS1115Sensor),
cv.GenerateID(CONF_ADS1115_ID): cv.use_id(ADS1115Component),
cv.Required(CONF_MULTIPLEXER): cv.enum(MUX, upper=True, space="_"),
cv.Required(CONF_GAIN): validate_gain,

View File

@@ -10,20 +10,21 @@
//
// According to the datasheet, the component is supposed to respond in more than 75ms. In fact, it can answer almost
// immediately for temperature. But for humidity, it takes >90ms to get a valid data. From experience, we have best
// results making successive requests; the current implementation make 3 attemps with a delay of 30ms each time.
// results making successive requests; the current implementation makes 3 attempts with a delay of 30ms each time.
#include "aht10.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
namespace esphome {
namespace aht10 {
static const char *TAG = "aht10";
static const char *const TAG = "aht10";
static const uint8_t AHT10_CALIBRATE_CMD[] = {0xE1};
static const uint8_t AHT10_MEASURE_CMD[] = {0xAC, 0x33, 0x00};
static const uint8_t AHT10_DEFAULT_DELAY = 5; // ms, for calibration and temperature measurement
static const uint8_t AHT10_HUMIDITY_DELAY = 30; // ms
static const uint8_t AHT10_ATTEMPS = 3; // safety margin, normally 3 attemps are enough: 3*30=90ms
static const uint8_t AHT10_ATTEMPTS = 3; // safety margin, normally 3 attempts are enough: 3*30=90ms
void AHT10Component::setup() {
ESP_LOGCONFIG(TAG, "Setting up AHT10...");
@@ -33,8 +34,19 @@ void AHT10Component::setup() {
this->mark_failed();
return;
}
uint8_t data;
if (!this->read_byte(0, &data, AHT10_DEFAULT_DELAY)) {
uint8_t data = 0;
if (this->write(&data, 1) != i2c::ERROR_OK) {
ESP_LOGD(TAG, "Communication with AHT10 failed!");
this->mark_failed();
return;
}
delay(AHT10_DEFAULT_DELAY);
if (this->read(&data, 1) != i2c::ERROR_OK) {
ESP_LOGD(TAG, "Communication with AHT10 failed!");
this->mark_failed();
return;
}
if (this->read(&data, 1) != i2c::ERROR_OK) {
ESP_LOGD(TAG, "Communication with AHT10 failed!");
this->mark_failed();
return;
@@ -55,15 +67,19 @@ void AHT10Component::update() {
return;
}
uint8_t data[6];
uint8_t delay = AHT10_DEFAULT_DELAY;
uint8_t delay_ms = AHT10_DEFAULT_DELAY;
if (this->humidity_sensor_ != nullptr)
delay = AHT10_HUMIDITY_DELAY;
for (int i = 0; i < AHT10_ATTEMPS; ++i) {
ESP_LOGVV(TAG, "Attemps %u at %6ld", i, millis());
delay_microseconds_accurate(4);
if (!this->read_bytes(0, data, 6, delay)) {
delay_ms = AHT10_HUMIDITY_DELAY;
bool success = false;
for (int i = 0; i < AHT10_ATTEMPTS; ++i) {
ESP_LOGVV(TAG, "Attempt %d at %6u", i, millis());
delay(delay_ms);
if (this->read(data, 6) != i2c::ERROR_OK) {
ESP_LOGD(TAG, "Communication with AHT10 failed, waiting...");
} else if ((data[0] & 0x80) == 0x80) { // Bit[7] = 0b1, device is busy
continue;
}
if ((data[0] & 0x80) == 0x80) { // Bit[7] = 0b1, device is busy
ESP_LOGD(TAG, "AHT10 is busy, waiting...");
} else if (data[1] == 0x0 && data[2] == 0x0 && (data[3] >> 4) == 0x0) {
// Unrealistic humidity (0x0)
@@ -80,11 +96,12 @@ void AHT10Component::update() {
}
} else {
// data is valid, we can break the loop
ESP_LOGVV(TAG, "Answer at %6ld", millis());
ESP_LOGVV(TAG, "Answer at %6u", millis());
success = true;
break;
}
}
if ((data[0] & 0x80) == 0x80) {
if (!success || (data[0] & 0x80) == 0x80) {
ESP_LOGE(TAG, "Measurements reading timed-out!");
this->status_set_warning();
return;
@@ -93,19 +110,19 @@ void AHT10Component::update() {
uint32_t raw_temperature = ((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5];
uint32_t raw_humidity = ((data[1] << 16) | (data[2] << 8) | data[3]) >> 4;
float temperature = ((200.0 * (float) raw_temperature) / 1048576.0) - 50.0;
float temperature = ((200.0f * (float) raw_temperature) / 1048576.0f) - 50.0f;
float humidity;
if (raw_humidity == 0) { // unrealistic value
humidity = NAN;
} else {
humidity = (float) raw_humidity * 100.0 / 1048576.0;
humidity = (float) raw_humidity * 100.0f / 1048576.0f;
}
if (this->temperature_sensor_ != nullptr) {
this->temperature_sensor_->publish_state(temperature);
}
if (this->humidity_sensor_ != nullptr) {
if (isnan(humidity))
if (std::isnan(humidity))
ESP_LOGW(TAG, "Invalid humidity! Sensor reported 0%% Hum");
this->humidity_sensor_->publish_state(humidity);
}

View File

@@ -7,7 +7,6 @@ from esphome.const import (
CONF_TEMPERATURE,
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_TEMPERATURE,
ICON_EMPTY,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
UNIT_PERCENT,
@@ -23,18 +22,16 @@ CONFIG_SCHEMA = (
{
cv.GenerateID(): cv.declare_id(AHT10Component),
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS,
ICON_EMPTY,
2,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=2,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
UNIT_PERCENT,
ICON_EMPTY,
2,
DEVICE_CLASS_HUMIDITY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_PERCENT,
accuracy_decimals=2,
device_class=DEVICE_CLASS_HUMIDITY,
state_class=STATE_CLASS_MEASUREMENT,
),
}
)

View File

@@ -0,0 +1,23 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import esp32_ble_tracker
from esphome.const import CONF_ID
DEPENDENCIES = ["esp32_ble_tracker"]
CODEOWNERS = ["@jeromelaban"]
airthings_ble_ns = cg.esphome_ns.namespace("airthings_ble")
AirthingsListener = airthings_ble_ns.class_(
"AirthingsListener", esp32_ble_tracker.ESPBTDeviceListener
)
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(AirthingsListener),
}
).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
yield esp32_ble_tracker.register_ble_device(var, config)

View File

@@ -0,0 +1,33 @@
#include "airthings_listener.h"
#include "esphome/core/log.h"
#ifdef USE_ESP32
namespace esphome {
namespace airthings_ble {
static const char *const TAG = "airthings_ble";
bool AirthingsListener::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
for (auto &it : device.get_manufacturer_datas()) {
if (it.uuid == esp32_ble_tracker::ESPBTUUID::from_uint32(0x0334)) {
if (it.data.size() < 4)
continue;
uint32_t sn = it.data[0];
sn |= ((uint32_t) it.data[1] << 8);
sn |= ((uint32_t) it.data[2] << 16);
sn |= ((uint32_t) it.data[3] << 24);
ESP_LOGD(TAG, "Found AirThings device Serial:%u (MAC: %s)", sn, device.address_str().c_str());
return true;
}
}
return false;
}
} // namespace airthings_ble
} // namespace esphome
#endif

View File

@@ -0,0 +1,19 @@
#pragma once
#ifdef USE_ESP32
#include "esphome/core/component.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
namespace esphome {
namespace airthings_ble {
class AirthingsListener : public esp32_ble_tracker::ESPBTDeviceListener {
public:
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
};
} // namespace airthings_ble
} // namespace esphome
#endif

View File

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

View File

@@ -0,0 +1,113 @@
#include "airthings_wave_mini.h"
#ifdef USE_ESP32
namespace esphome {
namespace airthings_wave_mini {
static const char *const TAG = "airthings_wave_mini";
void AirthingsWaveMini::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) {
switch (event) {
case ESP_GATTC_OPEN_EVT: {
if (param->open.status == ESP_GATT_OK) {
ESP_LOGI(TAG, "Connected successfully!");
}
break;
}
case ESP_GATTC_DISCONNECT_EVT: {
ESP_LOGW(TAG, "Disconnected!");
break;
}
case ESP_GATTC_SEARCH_CMPL_EVT: {
this->handle_ = 0;
auto *chr = this->parent()->get_characteristic(service_uuid_, sensors_data_characteristic_uuid_);
if (chr == nullptr) {
ESP_LOGW(TAG, "No sensor characteristic found at service %s char %s", service_uuid_.to_string().c_str(),
sensors_data_characteristic_uuid_.to_string().c_str());
break;
}
this->handle_ = chr->handle;
this->node_state = esp32_ble_tracker::ClientState::ESTABLISHED;
request_read_values_();
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->handle_) {
read_sensors_(param->read.value, param->read.value_len);
}
break;
}
default:
break;
}
}
void AirthingsWaveMini::read_sensors_(uint8_t *raw_value, uint16_t value_len) {
auto *value = (WaveMiniReadings *) raw_value;
if (sizeof(WaveMiniReadings) <= value_len) {
this->humidity_sensor_->publish_state(value->humidity / 100.0f);
this->pressure_sensor_->publish_state(value->pressure / 50.0f);
this->temperature_sensor_->publish_state(value->temperature / 100.0f - 273.15f);
if (is_valid_voc_value_(value->voc)) {
this->tvoc_sensor_->publish_state(value->voc);
}
// This instance must not stay connected
// so other clients can connect to it (e.g. the
// mobile app).
parent()->set_enabled(false);
}
}
bool AirthingsWaveMini::is_valid_voc_value_(uint16_t voc) { return 0 <= voc && voc <= 16383; }
void AirthingsWaveMini::update() {
if (this->node_state != esp32_ble_tracker::ClientState::ESTABLISHED) {
if (!parent()->enabled) {
ESP_LOGW(TAG, "Reconnecting to device");
parent()->set_enabled(true);
parent()->connect();
} else {
ESP_LOGW(TAG, "Connection in progress");
}
}
}
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);
if (status) {
ESP_LOGW(TAG, "Error sending read request for sensor, status=%d", status);
}
}
void AirthingsWaveMini::dump_config() {
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
LOG_SENSOR(" ", "Pressure", this->pressure_sensor_);
LOG_SENSOR(" ", "TVOC", this->tvoc_sensor_);
}
AirthingsWaveMini::AirthingsWaveMini()
: PollingComponent(10000),
service_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(SERVICE_UUID)),
sensors_data_characteristic_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(CHARACTERISTIC_UUID)) {}
} // namespace airthings_wave_mini
} // namespace esphome
#endif // USE_ESP32

View File

@@ -0,0 +1,65 @@
#pragma once
#ifdef USE_ESP32
#include <esp_gattc_api.h>
#include <algorithm>
#include <iterator>
#include "esphome/components/ble_client/ble_client.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/core/component.h"
#include "esphome/core/log.h"
namespace esphome {
namespace airthings_wave_mini {
static const char *const SERVICE_UUID = "b42e3882-ade7-11e4-89d3-123b93f75cba";
static const char *const CHARACTERISTIC_UUID = "b42e3b98-ade7-11e4-89d3-123b93f75cba";
class AirthingsWaveMini : public PollingComponent, public ble_client::BLEClientNode {
public:
AirthingsWaveMini();
void dump_config() 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 set_temperature(sensor::Sensor *temperature) { temperature_sensor_ = temperature; }
void set_humidity(sensor::Sensor *humidity) { humidity_sensor_ = humidity; }
void set_pressure(sensor::Sensor *pressure) { pressure_sensor_ = pressure; }
void set_tvoc(sensor::Sensor *tvoc) { tvoc_sensor_ = tvoc; }
protected:
bool is_valid_voc_value_(uint16_t voc);
void read_sensors_(uint8_t *value, uint16_t value_len);
void request_read_values_();
sensor::Sensor *temperature_sensor_{nullptr};
sensor::Sensor *humidity_sensor_{nullptr};
sensor::Sensor *pressure_sensor_{nullptr};
sensor::Sensor *tvoc_sensor_{nullptr};
uint16_t handle_;
esp32_ble_tracker::ESPBTUUID service_uuid_;
esp32_ble_tracker::ESPBTUUID sensors_data_characteristic_uuid_;
struct WaveMiniReadings {
uint16_t unused01;
uint16_t temperature;
uint16_t pressure;
uint16_t humidity;
uint16_t voc;
uint16_t unused02;
uint32_t unused03;
uint32_t unused04;
};
};
} // namespace airthings_wave_mini
} // namespace esphome
#endif // USE_ESP32

View File

@@ -0,0 +1,82 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor, ble_client
from esphome.const import (
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_PRESSURE,
STATE_CLASS_MEASUREMENT,
UNIT_PERCENT,
UNIT_CELSIUS,
UNIT_HECTOPASCAL,
CONF_ID,
CONF_HUMIDITY,
CONF_TVOC,
CONF_PRESSURE,
CONF_TEMPERATURE,
UNIT_PARTS_PER_BILLION,
ICON_RADIATOR,
)
DEPENDENCIES = ["ble_client"]
airthings_wave_mini_ns = cg.esphome_ns.namespace("airthings_wave_mini")
AirthingsWaveMini = airthings_wave_mini_ns.class_(
"AirthingsWaveMini", cg.PollingComponent, ble_client.BLEClientNode
)
CONFIG_SCHEMA = cv.All(
cv.Schema(
{
cv.GenerateID(): cv.declare_id(AirthingsWaveMini),
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
unit_of_measurement=UNIT_PERCENT,
device_class=DEVICE_CLASS_HUMIDITY,
state_class=STATE_CLASS_MEASUREMENT,
accuracy_decimals=2,
),
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=2,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
unit_of_measurement=UNIT_HECTOPASCAL,
accuracy_decimals=2,
device_class=DEVICE_CLASS_PRESSURE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_TVOC): sensor.sensor_schema(
unit_of_measurement=UNIT_PARTS_PER_BILLION,
icon=ICON_RADIATOR,
accuracy_decimals=0,
state_class=STATE_CLASS_MEASUREMENT,
),
}
)
.extend(cv.polling_component_schema("5min"))
.extend(ble_client.BLE_CLIENT_SCHEMA),
)
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_HUMIDITY in config:
sens = await sensor.new_sensor(config[CONF_HUMIDITY])
cg.add(var.set_humidity(sens))
if CONF_TEMPERATURE in config:
sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
cg.add(var.set_temperature(sens))
if CONF_PRESSURE in config:
sens = await sensor.new_sensor(config[CONF_PRESSURE])
cg.add(var.set_pressure(sens))
if CONF_TVOC in config:
sens = await sensor.new_sensor(config[CONF_TVOC])
cg.add(var.set_tvoc(sens))

View File

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

View File

@@ -0,0 +1,137 @@
#include "airthings_wave_plus.h"
#ifdef USE_ESP32
namespace esphome {
namespace airthings_wave_plus {
static const char *const TAG = "airthings_wave_plus";
void AirthingsWavePlus::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) {
switch (event) {
case ESP_GATTC_OPEN_EVT: {
if (param->open.status == ESP_GATT_OK) {
ESP_LOGI(TAG, "Connected successfully!");
}
break;
}
case ESP_GATTC_DISCONNECT_EVT: {
ESP_LOGW(TAG, "Disconnected!");
break;
}
case ESP_GATTC_SEARCH_CMPL_EVT: {
this->handle_ = 0;
auto *chr = this->parent()->get_characteristic(service_uuid_, sensors_data_characteristic_uuid_);
if (chr == nullptr) {
ESP_LOGW(TAG, "No sensor characteristic found at service %s char %s", service_uuid_.to_string().c_str(),
sensors_data_characteristic_uuid_.to_string().c_str());
break;
}
this->handle_ = chr->handle;
this->node_state = esp32_ble_tracker::ClientState::ESTABLISHED;
request_read_values_();
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->handle_) {
read_sensors_(param->read.value, param->read.value_len);
}
break;
}
default:
break;
}
}
void AirthingsWavePlus::read_sensors_(uint8_t *raw_value, uint16_t value_len) {
auto *value = (WavePlusReadings *) raw_value;
if (sizeof(WavePlusReadings) <= value_len) {
ESP_LOGD(TAG, "version = %d", value->version);
if (value->version == 1) {
ESP_LOGD(TAG, "ambient light = %d", value->ambientLight);
this->humidity_sensor_->publish_state(value->humidity / 2.0f);
if (is_valid_radon_value_(value->radon)) {
this->radon_sensor_->publish_state(value->radon);
}
if (is_valid_radon_value_(value->radon_lt)) {
this->radon_long_term_sensor_->publish_state(value->radon_lt);
}
this->temperature_sensor_->publish_state(value->temperature / 100.0f);
this->pressure_sensor_->publish_state(value->pressure / 50.0f);
if (is_valid_co2_value_(value->co2)) {
this->co2_sensor_->publish_state(value->co2);
}
if (is_valid_voc_value_(value->voc)) {
this->tvoc_sensor_->publish_state(value->voc);
}
// This instance must not stay connected
// so other clients can connect to it (e.g. the
// mobile app).
parent()->set_enabled(false);
} else {
ESP_LOGE(TAG, "Invalid payload version (%d != 1, newer version or not a Wave Plus?)", value->version);
}
}
}
bool AirthingsWavePlus::is_valid_radon_value_(uint16_t radon) { return 0 <= radon && radon <= 16383; }
bool AirthingsWavePlus::is_valid_voc_value_(uint16_t voc) { return 0 <= voc && voc <= 16383; }
bool AirthingsWavePlus::is_valid_co2_value_(uint16_t co2) { return 0 <= co2 && co2 <= 16383; }
void AirthingsWavePlus::update() {
if (this->node_state != esp32_ble_tracker::ClientState::ESTABLISHED) {
if (!parent()->enabled) {
ESP_LOGW(TAG, "Reconnecting to device");
parent()->set_enabled(true);
parent()->connect();
} else {
ESP_LOGW(TAG, "Connection in progress");
}
}
}
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);
if (status) {
ESP_LOGW(TAG, "Error sending read request for sensor, status=%d", status);
}
}
void AirthingsWavePlus::dump_config() {
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
LOG_SENSOR(" ", "Radon", this->radon_sensor_);
LOG_SENSOR(" ", "Radon Long Term", this->radon_long_term_sensor_);
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
LOG_SENSOR(" ", "Pressure", this->pressure_sensor_);
LOG_SENSOR(" ", "CO2", this->co2_sensor_);
LOG_SENSOR(" ", "TVOC", this->tvoc_sensor_);
}
AirthingsWavePlus::AirthingsWavePlus()
: PollingComponent(10000),
service_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(SERVICE_UUID)),
sensors_data_characteristic_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(CHARACTERISTIC_UUID)) {}
} // namespace airthings_wave_plus
} // namespace esphome
#endif // USE_ESP32

View File

@@ -0,0 +1,75 @@
#pragma once
#ifdef USE_ESP32
#include <esp_gattc_api.h>
#include <algorithm>
#include <iterator>
#include "esphome/components/ble_client/ble_client.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/core/component.h"
#include "esphome/core/log.h"
namespace esphome {
namespace airthings_wave_plus {
static const char *const SERVICE_UUID = "b42e1c08-ade7-11e4-89d3-123b93f75cba";
static const char *const CHARACTERISTIC_UUID = "b42e2a68-ade7-11e4-89d3-123b93f75cba";
class AirthingsWavePlus : public PollingComponent, public ble_client::BLEClientNode {
public:
AirthingsWavePlus();
void dump_config() 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 set_temperature(sensor::Sensor *temperature) { temperature_sensor_ = temperature; }
void set_radon(sensor::Sensor *radon) { radon_sensor_ = radon; }
void set_radon_long_term(sensor::Sensor *radon_long_term) { radon_long_term_sensor_ = radon_long_term; }
void set_humidity(sensor::Sensor *humidity) { humidity_sensor_ = humidity; }
void set_pressure(sensor::Sensor *pressure) { pressure_sensor_ = pressure; }
void set_co2(sensor::Sensor *co2) { co2_sensor_ = co2; }
void set_tvoc(sensor::Sensor *tvoc) { tvoc_sensor_ = tvoc; }
protected:
bool is_valid_radon_value_(uint16_t radon);
bool is_valid_voc_value_(uint16_t voc);
bool is_valid_co2_value_(uint16_t co2);
void read_sensors_(uint8_t *value, uint16_t value_len);
void request_read_values_();
sensor::Sensor *temperature_sensor_{nullptr};
sensor::Sensor *radon_sensor_{nullptr};
sensor::Sensor *radon_long_term_sensor_{nullptr};
sensor::Sensor *humidity_sensor_{nullptr};
sensor::Sensor *pressure_sensor_{nullptr};
sensor::Sensor *co2_sensor_{nullptr};
sensor::Sensor *tvoc_sensor_{nullptr};
uint16_t handle_;
esp32_ble_tracker::ESPBTUUID service_uuid_;
esp32_ble_tracker::ESPBTUUID sensors_data_characteristic_uuid_;
struct WavePlusReadings {
uint8_t version;
uint8_t humidity;
uint8_t ambientLight;
uint8_t unused01;
uint16_t radon;
uint16_t radon_lt;
uint16_t temperature;
uint16_t pressure;
uint16_t co2;
uint16_t voc;
};
};
} // namespace airthings_wave_plus
} // namespace esphome
#endif // USE_ESP32

View File

@@ -0,0 +1,116 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor, ble_client
from esphome.const import (
DEVICE_CLASS_CARBON_DIOXIDE,
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_PRESSURE,
STATE_CLASS_MEASUREMENT,
UNIT_PERCENT,
UNIT_CELSIUS,
UNIT_HECTOPASCAL,
ICON_RADIOACTIVE,
CONF_ID,
CONF_RADON,
CONF_RADON_LONG_TERM,
CONF_HUMIDITY,
CONF_TVOC,
CONF_CO2,
CONF_PRESSURE,
CONF_TEMPERATURE,
UNIT_BECQUEREL_PER_CUBIC_METER,
UNIT_PARTS_PER_MILLION,
UNIT_PARTS_PER_BILLION,
ICON_RADIATOR,
)
DEPENDENCIES = ["ble_client"]
airthings_wave_plus_ns = cg.esphome_ns.namespace("airthings_wave_plus")
AirthingsWavePlus = airthings_wave_plus_ns.class_(
"AirthingsWavePlus", cg.PollingComponent, ble_client.BLEClientNode
)
CONFIG_SCHEMA = cv.All(
cv.Schema(
{
cv.GenerateID(): cv.declare_id(AirthingsWavePlus),
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
unit_of_measurement=UNIT_PERCENT,
device_class=DEVICE_CLASS_HUMIDITY,
state_class=STATE_CLASS_MEASUREMENT,
accuracy_decimals=0,
),
cv.Optional(CONF_RADON): sensor.sensor_schema(
unit_of_measurement=UNIT_BECQUEREL_PER_CUBIC_METER,
icon=ICON_RADIOACTIVE,
accuracy_decimals=0,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_RADON_LONG_TERM): sensor.sensor_schema(
unit_of_measurement=UNIT_BECQUEREL_PER_CUBIC_METER,
icon=ICON_RADIOACTIVE,
accuracy_decimals=0,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=2,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
unit_of_measurement=UNIT_HECTOPASCAL,
accuracy_decimals=1,
device_class=DEVICE_CLASS_PRESSURE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_CO2): sensor.sensor_schema(
unit_of_measurement=UNIT_PARTS_PER_MILLION,
accuracy_decimals=0,
device_class=DEVICE_CLASS_CARBON_DIOXIDE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_TVOC): sensor.sensor_schema(
unit_of_measurement=UNIT_PARTS_PER_BILLION,
icon=ICON_RADIATOR,
accuracy_decimals=0,
state_class=STATE_CLASS_MEASUREMENT,
),
}
)
.extend(cv.polling_component_schema("5min"))
.extend(ble_client.BLE_CLIENT_SCHEMA),
)
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_HUMIDITY in config:
sens = await sensor.new_sensor(config[CONF_HUMIDITY])
cg.add(var.set_humidity(sens))
if CONF_RADON in config:
sens = await sensor.new_sensor(config[CONF_RADON])
cg.add(var.set_radon(sens))
if CONF_RADON_LONG_TERM in config:
sens = await sensor.new_sensor(config[CONF_RADON_LONG_TERM])
cg.add(var.set_radon_long_term(sens))
if CONF_TEMPERATURE in config:
sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
cg.add(var.set_temperature(sens))
if CONF_PRESSURE in config:
sens = await sensor.new_sensor(config[CONF_PRESSURE])
cg.add(var.set_pressure(sens))
if CONF_CO2 in config:
sens = await sensor.new_sensor(config[CONF_CO2])
cg.add(var.set_co2(sens))
if CONF_TVOC in config:
sens = await sensor.new_sensor(config[CONF_TVOC])
cg.add(var.set_tvoc(sens))

View File

@@ -5,11 +5,12 @@
#include "am2320.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
namespace esphome {
namespace am2320 {
static const char *TAG = "am2320";
static const char *const TAG = "am2320";
// ---=== Calc CRC16 ===---
uint16_t crc_16(uint8_t *ptr, uint8_t length) {
@@ -18,12 +19,14 @@ uint16_t crc_16(uint8_t *ptr, uint8_t length) {
//------------------------------
while (length--) {
crc ^= *ptr++;
for (i = 0; i < 8; i++)
for (i = 0; i < 8; i++) {
if ((crc & 0x01) != 0) {
crc >>= 1;
crc ^= 0xA001;
} else
} else {
crc >>= 1;
}
}
}
return crc;
}
@@ -37,9 +40,9 @@ void AM2320Component::update() {
return;
}
float temperature = (((data[4] & 0x7F) << 8) + data[5]) / 10.0;
float temperature = (((data[4] & 0x7F) << 8) + data[5]) / 10.0f;
temperature = (data[4] & 0x80) ? -temperature : temperature;
float humidity = ((data[2] << 8) + data[3]) / 10.0;
float humidity = ((data[2] << 8) + data[3]) / 10.0f;
ESP_LOGD(TAG, "Got temperature=%.1f°C humidity=%.1f%%", temperature, humidity);
if (this->temperature_sensor_ != nullptr)
@@ -77,7 +80,7 @@ bool AM2320Component::read_bytes_(uint8_t a_register, uint8_t *data, uint8_t len
if (conversion > 0)
delay(conversion);
return this->parent_->raw_receive(this->address_, data, len);
return this->read(data, len) == i2c::ERROR_OK;
}
bool AM2320Component::read_data_(uint8_t *data) {

View File

@@ -9,7 +9,6 @@ from esphome.const import (
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
ICON_EMPTY,
UNIT_PERCENT,
)
@@ -25,18 +24,16 @@ CONFIG_SCHEMA = (
{
cv.GenerateID(): cv.declare_id(AM2320Component),
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS,
ICON_EMPTY,
1,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=1,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
UNIT_PERCENT,
ICON_EMPTY,
1,
DEVICE_CLASS_HUMIDITY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_PERCENT,
accuracy_decimals=1,
device_class=DEVICE_CLASS_HUMIDITY,
state_class=STATE_CLASS_MEASUREMENT,
),
}
)

View File

@@ -0,0 +1,117 @@
#include "am43.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
#ifdef USE_ESP32
namespace esphome {
namespace am43 {
static const char *const TAG = "am43";
void Am43::dump_config() {
ESP_LOGCONFIG(TAG, "AM43");
LOG_SENSOR(" ", "Battery", this->battery_);
LOG_SENSOR(" ", "Illuminance", this->illuminance_);
}
void Am43::setup() {
this->encoder_ = make_unique<Am43Encoder>();
this->decoder_ = make_unique<Am43Decoder>();
this->logged_in_ = false;
this->last_battery_update_ = 0;
this->current_sensor_ = 0;
}
void Am43::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) {
switch (event) {
case ESP_GATTC_OPEN_EVT: {
this->logged_in_ = false;
break;
}
case ESP_GATTC_DISCONNECT_EVT: {
this->logged_in_ = false;
this->node_state = espbt::ClientState::IDLE;
if (this->battery_ != nullptr)
this->battery_->publish_state(NAN);
if (this->illuminance_ != nullptr)
this->illuminance_->publish_state(NAN);
break;
}
case ESP_GATTC_SEARCH_CMPL_EVT: {
auto *chr = this->parent_->get_characteristic(AM43_SERVICE_UUID, AM43_CHARACTERISTIC_UUID);
if (chr == nullptr) {
if (this->parent_->get_characteristic(AM43_TUYA_SERVICE_UUID, AM43_TUYA_CHARACTERISTIC_UUID) != nullptr) {
ESP_LOGE(TAG, "[%s] Detected a Tuya AM43 which is not supported, sorry.",
this->parent_->address_str().c_str());
} else {
ESP_LOGE(TAG, "[%s] No control service found at device, not an AM43..?",
this->parent_->address_str().c_str());
}
break;
}
this->char_handle_ = chr->handle;
break;
}
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
this->node_state = espbt::ClientState::ESTABLISHED;
this->update();
break;
}
case ESP_GATTC_NOTIFY_EVT: {
if (param->notify.handle != this->char_handle_)
break;
this->decoder_->decode(param->notify.value, param->notify.value_len);
if (this->battery_ != nullptr && this->decoder_->has_battery_level() &&
millis() - this->last_battery_update_ > 10000) {
this->battery_->publish_state(this->decoder_->battery_level_);
this->last_battery_update_ = millis();
}
if (this->illuminance_ != nullptr && this->decoder_->has_light_level()) {
this->illuminance_->publish_state(this->decoder_->light_level_);
}
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);
if (status) {
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(),
status);
}
}
this->current_sensor_ = 0;
}
break;
}
default:
break;
}
}
void Am43::update() {
if (this->node_state != espbt::ClientState::ESTABLISHED) {
ESP_LOGW(TAG, "[%s] Cannot poll, not connected", this->parent_->address_str().c_str());
return;
}
if (this->current_sensor_ == 0) {
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);
if (status)
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status);
}
this->current_sensor_++;
}
}
} // namespace am43
} // namespace esphome
#endif

View File

@@ -0,0 +1,45 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/ble_client/ble_client.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/am43/am43_base.h"
#ifdef USE_ESP32
#include <esp_gattc_api.h>
namespace esphome {
namespace am43 {
namespace espbt = esphome::esp32_ble_tracker;
class Am43 : public esphome::ble_client::BLEClientNode, public PollingComponent {
public:
void setup() 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::DATA; }
void set_battery(sensor::Sensor *battery) { battery_ = battery; }
void set_illuminance(sensor::Sensor *illuminance) { illuminance_ = illuminance; }
protected:
uint16_t char_handle_;
std::unique_ptr<Am43Encoder> encoder_;
std::unique_ptr<Am43Decoder> decoder_;
bool logged_in_;
sensor::Sensor *battery_{nullptr};
sensor::Sensor *illuminance_{nullptr};
uint8_t current_sensor_;
// The AM43 often gets into a state where it spams loads of battery update
// notifications. Here we will limit to no more than every 10s.
uint8_t last_battery_update_;
};
} // namespace am43
} // namespace esphome
#endif

View File

@@ -0,0 +1,144 @@
#include "am43_base.h"
#include <cstring>
#include <cstdio>
namespace esphome {
namespace am43 {
const uint8_t START_PACKET[5] = {0x00, 0xff, 0x00, 0x00, 0x9a};
std::string pkt_to_hex(const uint8_t *data, uint16_t len) {
char buf[64];
memset(buf, 0, 64);
for (int i = 0; i < len; i++)
sprintf(&buf[i * 2], "%02x", data[i]);
std::string ret = buf;
return ret;
}
Am43Packet *Am43Encoder::get_battery_level_request() {
uint8_t data = 0x1;
return this->encode_(0xA2, &data, 1);
}
Am43Packet *Am43Encoder::get_light_level_request() {
uint8_t data = 0x1;
return this->encode_(0xAA, &data, 1);
}
Am43Packet *Am43Encoder::get_position_request() {
uint8_t data = 0x1;
return this->encode_(CMD_GET_POSITION, &data, 1);
}
Am43Packet *Am43Encoder::get_send_pin_request(uint16_t pin) {
uint8_t data[2];
data[0] = (pin & 0xFF00) >> 8;
data[1] = pin & 0xFF;
return this->encode_(CMD_SEND_PIN, data, 2);
}
Am43Packet *Am43Encoder::get_open_request() {
uint8_t data = 0xDD;
return this->encode_(CMD_SET_STATE, &data, 1);
}
Am43Packet *Am43Encoder::get_close_request() {
uint8_t data = 0xEE;
return this->encode_(CMD_SET_STATE, &data, 1);
}
Am43Packet *Am43Encoder::get_stop_request() {
uint8_t data = 0xCC;
return this->encode_(CMD_SET_STATE, &data, 1);
}
Am43Packet *Am43Encoder::get_set_position_request(uint8_t position) {
return this->encode_(CMD_SET_POSITION, &position, 1);
}
void Am43Encoder::checksum_() {
uint8_t checksum = 0;
int i = 0;
for (i = 0; i < this->packet_.length; i++)
checksum = checksum ^ this->packet_.data[i];
this->packet_.data[i] = checksum ^ 0xff;
this->packet_.length++;
}
Am43Packet *Am43Encoder::encode_(uint8_t command, uint8_t *data, uint8_t length) {
memcpy(this->packet_.data, START_PACKET, 5);
this->packet_.data[5] = command;
this->packet_.data[6] = length;
memcpy(&this->packet_.data[7], data, length);
this->packet_.length = length + 7;
this->checksum_();
ESP_LOGV("am43", "ENC(%d): 0x%s", packet_.length, pkt_to_hex(packet_.data, packet_.length).c_str());
return &this->packet_;
}
#define VERIFY_MIN_LENGTH(x) \
if (length < (x)) \
return;
void Am43Decoder::decode(const uint8_t *data, uint16_t length) {
this->has_battery_level_ = false;
this->has_light_level_ = false;
this->has_set_position_response_ = false;
this->has_set_state_response_ = false;
this->has_position_ = false;
this->has_pin_response_ = false;
ESP_LOGV("am43", "DEC(%d): 0x%s", length, pkt_to_hex(data, length).c_str());
if (length < 2 || data[0] != 0x9a)
return;
switch (data[1]) {
case CMD_GET_BATTERY_LEVEL: {
VERIFY_MIN_LENGTH(8);
this->battery_level_ = data[7];
this->has_battery_level_ = true;
break;
}
case CMD_GET_LIGHT_LEVEL: {
VERIFY_MIN_LENGTH(5);
this->light_level_ = 100 * ((float) data[4] / 9);
this->has_light_level_ = true;
break;
}
case CMD_GET_POSITION: {
VERIFY_MIN_LENGTH(6);
this->position_ = data[5];
this->has_position_ = true;
break;
}
case CMD_NOTIFY_POSITION: {
VERIFY_MIN_LENGTH(5);
this->position_ = data[4];
this->has_position_ = true;
break;
}
case CMD_SEND_PIN: {
VERIFY_MIN_LENGTH(4);
this->pin_ok_ = data[3] == RESPONSE_ACK;
this->has_pin_response_ = true;
break;
}
case CMD_SET_POSITION: {
VERIFY_MIN_LENGTH(4);
this->set_position_ok_ = data[3] == RESPONSE_ACK;
this->has_set_position_response_ = true;
break;
}
case CMD_SET_STATE: {
VERIFY_MIN_LENGTH(4);
this->set_state_ok_ = data[3] == RESPONSE_ACK;
this->has_set_state_response_ = true;
break;
}
default:
break;
}
};
} // namespace am43
} // namespace esphome

View File

@@ -0,0 +1,78 @@
#pragma once
#include "esphome/core/log.h"
#include "esphome/core/helpers.h"
namespace esphome {
namespace am43 {
static const uint16_t AM43_SERVICE_UUID = 0xFE50;
static const uint16_t AM43_CHARACTERISTIC_UUID = 0xFE51;
//
// Tuya identifiers, only to detect and warn users as they are incompatible.
static const uint16_t AM43_TUYA_SERVICE_UUID = 0x1910;
static const uint16_t AM43_TUYA_CHARACTERISTIC_UUID = 0x2b11;
struct Am43Packet {
uint8_t length;
uint8_t data[24];
};
static const uint8_t CMD_GET_BATTERY_LEVEL = 0xA2;
static const uint8_t CMD_GET_LIGHT_LEVEL = 0xAA;
static const uint8_t CMD_GET_POSITION = 0xA7;
static const uint8_t CMD_SEND_PIN = 0x17;
static const uint8_t CMD_SET_STATE = 0x0A;
static const uint8_t CMD_SET_POSITION = 0x0D;
static const uint8_t CMD_NOTIFY_POSITION = 0xA1;
static const uint8_t RESPONSE_ACK = 0x5A;
static const uint8_t RESPONSE_NACK = 0xA5;
class Am43Encoder {
public:
Am43Packet *get_battery_level_request();
Am43Packet *get_light_level_request();
Am43Packet *get_position_request();
Am43Packet *get_send_pin_request(uint16_t pin);
Am43Packet *get_open_request();
Am43Packet *get_close_request();
Am43Packet *get_stop_request();
Am43Packet *get_set_position_request(uint8_t position);
protected:
void checksum_();
Am43Packet *encode_(uint8_t command, uint8_t *data, uint8_t length);
Am43Packet packet_;
};
class Am43Decoder {
public:
void decode(const uint8_t *data, uint16_t length);
bool has_battery_level() { return this->has_battery_level_; }
bool has_light_level() { return this->has_light_level_; }
bool has_set_position_response() { return this->has_set_position_response_; }
bool has_set_state_response() { return this->has_set_state_response_; }
bool has_position() { return this->has_position_; }
bool has_pin_response() { return this->has_pin_response_; }
union {
uint8_t position_;
uint8_t battery_level_;
float light_level_;
uint8_t set_position_ok_;
uint8_t set_state_ok_;
uint8_t pin_ok_;
};
protected:
bool has_battery_level_;
bool has_light_level_;
bool has_set_position_response_;
bool has_set_state_response_;
bool has_position_;
bool has_pin_response_;
};
} // namespace am43
} // namespace esphome

View File

@@ -0,0 +1,36 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import cover, ble_client
from esphome.const import CONF_ID, CONF_PIN
CODEOWNERS = ["@buxtronix"]
DEPENDENCIES = ["ble_client"]
AUTO_LOAD = ["am43", "sensor"]
CONF_INVERT_POSITION = "invert_position"
am43_ns = cg.esphome_ns.namespace("am43")
Am43Component = am43_ns.class_(
"Am43Component", cover.Cover, ble_client.BLEClientNode, cg.Component
)
CONFIG_SCHEMA = (
cover.COVER_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(Am43Component),
cv.Optional(CONF_PIN, default=8888): cv.int_range(min=0, max=0xFFFF),
cv.Optional(CONF_INVERT_POSITION, default=False): cv.boolean,
}
)
.extend(ble_client.BLE_CLIENT_SCHEMA)
.extend(cv.COMPONENT_SCHEMA)
)
def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
cg.add(var.set_pin(config[CONF_PIN]))
cg.add(var.set_invert_position(config[CONF_INVERT_POSITION]))
yield cg.register_component(var, config)
yield cover.register_cover(var, config)
yield ble_client.register_ble_node(var, config)

View File

@@ -0,0 +1,150 @@
#include "am43_cover.h"
#include "esphome/core/log.h"
#ifdef USE_ESP32
namespace esphome {
namespace am43 {
static const char *const TAG = "am43_cover";
using namespace esphome::cover;
void Am43Component::dump_config() {
LOG_COVER("", "AM43 Cover", this);
ESP_LOGCONFIG(TAG, " Device Pin: %d", this->pin_);
ESP_LOGCONFIG(TAG, " Invert Position: %d", (int) this->invert_position_);
}
void Am43Component::setup() {
this->position = COVER_OPEN;
this->encoder_ = make_unique<Am43Encoder>();
this->decoder_ = make_unique<Am43Decoder>();
this->logged_in_ = false;
}
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_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);
} else {
this->logged_in_ = true;
}
}
}
CoverTraits Am43Component::get_traits() {
auto traits = CoverTraits();
traits.set_supports_position(true);
traits.set_supports_tilt(false);
traits.set_is_assumed_state(false);
return traits;
}
void Am43Component::control(const CoverCall &call) {
if (this->node_state != espbt::ClientState::ESTABLISHED) {
ESP_LOGW(TAG, "[%s] Cannot send cover control, not connected", this->get_name().c_str());
return;
}
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);
if (status)
ESP_LOGW(TAG, "[%s] Error writing stop command to device, error = %d", this->get_name().c_str(), status);
}
if (call.get_position().has_value()) {
auto pos = *call.get_position();
if (this->invert_position_)
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);
if (status)
ESP_LOGW(TAG, "[%s] Error writing set_position command to device, error = %d", this->get_name().c_str(), status);
}
}
void Am43Component::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: {
this->logged_in_ = false;
break;
}
case ESP_GATTC_SEARCH_CMPL_EVT: {
auto *chr = this->parent_->get_characteristic(AM43_SERVICE_UUID, AM43_CHARACTERISTIC_UUID);
if (chr == nullptr) {
if (this->parent_->get_characteristic(AM43_TUYA_SERVICE_UUID, AM43_TUYA_CHARACTERISTIC_UUID) != nullptr) {
ESP_LOGE(TAG, "[%s] Detected a Tuya AM43 which is not supported, sorry.", this->get_name().c_str());
} else {
ESP_LOGE(TAG, "[%s] No control service found at device, not an AM43..?", this->get_name().c_str());
}
break;
}
this->char_handle_ = chr->handle;
auto status = esp_ble_gattc_register_for_notify(this->parent_->gattc_if, this->parent_->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);
}
break;
}
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
this->node_state = espbt::ClientState::ESTABLISHED;
break;
}
case ESP_GATTC_NOTIFY_EVT: {
if (param->notify.handle != this->char_handle_)
break;
this->decoder_->decode(param->notify.value, param->notify.value_len);
if (this->decoder_->has_position()) {
this->position = ((float) this->decoder_->position_ / 100.0);
if (!this->invert_position_)
this->position = 1 - this->position;
if (this->position > 0.97)
this->position = 1.0;
if (this->position < 0.02)
this->position = 0.0;
this->publish_state();
}
if (this->decoder_->has_pin_response()) {
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);
if (status)
ESP_LOGW(TAG, "[%s] Error writing set_position to device, error = %d", this->get_name().c_str(), status);
} else {
ESP_LOGW(TAG, "[%s] AM43 pin rejected!", this->get_name().c_str());
}
}
if (this->decoder_->has_set_position_response() && !this->decoder_->set_position_ok_)
ESP_LOGW(TAG, "[%s] Got nack after sending set_position. Bad pin?", this->get_name().c_str());
if (this->decoder_->has_set_state_response() && !this->decoder_->set_state_ok_)
ESP_LOGW(TAG, "[%s] Got nack after sending set_state. Bad pin?", this->get_name().c_str());
break;
}
default:
break;
}
}
} // namespace am43
} // namespace esphome
#endif

View File

@@ -0,0 +1,45 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/ble_client/ble_client.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include "esphome/components/cover/cover.h"
#include "esphome/components/am43/am43_base.h"
#ifdef USE_ESP32
#include <esp_gattc_api.h>
namespace esphome {
namespace am43 {
namespace espbt = esphome::esp32_ble_tracker;
class Am43Component : public cover::Cover, public esphome::ble_client::BLEClientNode, public Component {
public:
void setup() override;
void loop() override;
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) override;
void dump_config() override;
float get_setup_priority() const override { return setup_priority::DATA; }
cover::CoverTraits get_traits() override;
void set_pin(uint16_t pin) { this->pin_ = pin; }
void set_invert_position(bool invert_position) { this->invert_position_ = invert_position; }
protected:
void control(const cover::CoverCall &call) override;
uint16_t char_handle_;
uint16_t pin_;
bool invert_position_;
std::unique_ptr<Am43Encoder> encoder_;
std::unique_ptr<Am43Decoder> decoder_;
bool logged_in_;
float position_;
};
} // namespace am43
} // namespace esphome
#endif

View File

@@ -0,0 +1,52 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor, ble_client
from esphome.const import (
CONF_ID,
CONF_BATTERY_LEVEL,
DEVICE_CLASS_BATTERY,
ENTITY_CATEGORY_DIAGNOSTIC,
CONF_ILLUMINANCE,
ICON_BRIGHTNESS_5,
UNIT_PERCENT,
)
CODEOWNERS = ["@buxtronix"]
am43_ns = cg.esphome_ns.namespace("am43")
Am43 = am43_ns.class_("Am43", ble_client.BLEClientNode, cg.PollingComponent)
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(Am43),
cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(
unit_of_measurement=UNIT_PERCENT,
device_class=DEVICE_CLASS_BATTERY,
accuracy_decimals=0,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
),
cv.Optional(CONF_ILLUMINANCE): sensor.sensor_schema(
unit_of_measurement=UNIT_PERCENT,
icon=ICON_BRIGHTNESS_5,
accuracy_decimals=0,
),
}
)
.extend(ble_client.BLE_CLIENT_SCHEMA)
.extend(cv.polling_component_schema("120s"))
)
def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config)
yield ble_client.register_ble_node(var, config)
if CONF_BATTERY_LEVEL in config:
sens = yield sensor.new_sensor(config[CONF_BATTERY_LEVEL])
cg.add(var.set_battery(sens))
if CONF_ILLUMINANCE in config:
sens = yield sensor.new_sensor(config[CONF_ILLUMINANCE])
cg.add(var.set_illuminance(sens))

View File

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

View File

@@ -0,0 +1,40 @@
#include "analog_threshold_binary_sensor.h"
#include "esphome/core/log.h"
namespace esphome {
namespace analog_threshold {
static const char *const TAG = "analog_threshold.binary_sensor";
void AnalogThresholdBinarySensor::setup() {
float sensor_value = this->sensor_->get_state();
// TRUE state is defined to be when sensor is >= threshold
// so when undefined sensor value initialize to FALSE
if (std::isnan(sensor_value)) {
this->publish_initial_state(false);
} else {
this->publish_initial_state(sensor_value >= (this->lower_threshold_ + this->upper_threshold_) / 2.0f);
}
}
void AnalogThresholdBinarySensor::set_sensor(sensor::Sensor *analog_sensor) {
this->sensor_ = analog_sensor;
this->sensor_->add_on_state_callback([this](float sensor_value) {
// if there is an invalid sensor reading, ignore the change and keep the current state
if (!std::isnan(sensor_value)) {
this->publish_state(sensor_value >= (this->state ? this->lower_threshold_ : this->upper_threshold_));
}
});
}
void AnalogThresholdBinarySensor::dump_config() {
LOG_BINARY_SENSOR("", "Analog Threshold Binary Sensor", this);
LOG_SENSOR(" ", "Sensor", this->sensor_);
ESP_LOGCONFIG(TAG, " Upper threshold: %.11f", this->upper_threshold_);
ESP_LOGCONFIG(TAG, " Lower threshold: %.11f", this->lower_threshold_);
}
} // namespace analog_threshold
} // namespace esphome

View File

@@ -0,0 +1,29 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/binary_sensor/binary_sensor.h"
#include "esphome/components/sensor/sensor.h"
namespace esphome {
namespace analog_threshold {
class AnalogThresholdBinarySensor : public Component, public binary_sensor::BinarySensor {
public:
void dump_config() override;
void setup() override;
float get_setup_priority() const override { return setup_priority::DATA; }
void set_sensor(sensor::Sensor *analog_sensor);
void set_upper_threshold(float threshold) { this->upper_threshold_ = threshold; }
void set_lower_threshold(float threshold) { this->lower_threshold_ = threshold; }
protected:
sensor::Sensor *sensor_{nullptr};
float upper_threshold_;
float lower_threshold_;
};
} // namespace analog_threshold
} // namespace esphome

View File

@@ -0,0 +1,44 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import binary_sensor, sensor
from esphome.const import (
CONF_SENSOR_ID,
CONF_THRESHOLD,
)
analog_threshold_ns = cg.esphome_ns.namespace("analog_threshold")
AnalogThresholdBinarySensor = analog_threshold_ns.class_(
"AnalogThresholdBinarySensor", binary_sensor.BinarySensor, cg.Component
)
CONF_UPPER = "upper"
CONF_LOWER = "lower"
CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(AnalogThresholdBinarySensor),
cv.Required(CONF_SENSOR_ID): cv.use_id(sensor.Sensor),
cv.Required(CONF_THRESHOLD): cv.Any(
cv.float_,
cv.Schema(
{cv.Required(CONF_UPPER): cv.float_, cv.Required(CONF_LOWER): cv.float_}
),
),
}
).extend(cv.COMPONENT_SCHEMA)
async def to_code(config):
var = await binary_sensor.new_binary_sensor(config)
await cg.register_component(var, config)
sens = await cg.get_variable(config[CONF_SENSOR_ID])
cg.add(var.set_sensor(sens))
if isinstance(config[CONF_THRESHOLD], float):
cg.add(var.set_upper_threshold(config[CONF_THRESHOLD]))
cg.add(var.set_lower_threshold(config[CONF_THRESHOLD]))
else:
cg.add(var.set_upper_threshold(config[CONF_THRESHOLD][CONF_UPPER]))
cg.add(var.set_lower_threshold(config[CONF_THRESHOLD][CONF_LOWER]))

View File

@@ -5,7 +5,7 @@ from esphome.components import display, font
import esphome.components.image as espImage
import esphome.config_validation as cv
import esphome.codegen as cg
from esphome.const import CONF_FILE, CONF_ID, CONF_TYPE, CONF_RESIZE
from esphome.const import CONF_FILE, CONF_ID, CONF_RAW_DATA_ID, CONF_RESIZE, CONF_TYPE
from esphome.core import CORE, HexInt
_LOGGER = logging.getLogger(__name__)
@@ -15,8 +15,6 @@ MULTI_CONF = True
Animation_ = display.display_ns.class_("Animation")
CONF_RAW_DATA_ID = "raw_data_id"
ANIMATION_SCHEMA = cv.Schema(
{
cv.Required(CONF_ID): cv.declare_id(Animation_),
@@ -46,8 +44,9 @@ async def to_code(config):
width, height = image.size
frames = image.n_frames
if CONF_RESIZE in config:
image.thumbnail(config[CONF_RESIZE])
width, height = image.size
new_width_max, new_height_max = config[CONF_RESIZE]
ratio = min(new_width_max / width, new_height_max / height)
width, height = int(width * ratio), int(height * ratio)
else:
if width > 500 or height > 500:
_LOGGER.warning(
@@ -61,7 +60,13 @@ async def to_code(config):
for frameIndex in range(frames):
image.seek(frameIndex)
frame = image.convert("L", dither=Image.NONE)
if CONF_RESIZE in config:
frame = frame.resize([width, height])
pixels = list(frame.getdata())
if len(pixels) != height * width:
raise core.EsphomeError(
f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height*width})"
)
for pix in pixels:
data[pos] = pix
pos += 1
@@ -72,7 +77,13 @@ async def to_code(config):
for frameIndex in range(frames):
image.seek(frameIndex)
frame = image.convert("RGB")
if CONF_RESIZE in config:
frame = frame.resize([width, height])
pixels = list(frame.getdata())
if len(pixels) != height * width:
raise core.EsphomeError(
f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height*width})"
)
for pix in pixels:
data[pos] = pix[0]
pos += 1
@@ -87,6 +98,8 @@ async def to_code(config):
for frameIndex in range(frames):
image.seek(frameIndex)
frame = image.convert("1", dither=Image.NONE)
if CONF_RESIZE in config:
frame = frame.resize([width, height])
for y in range(height):
for x in range(width):
if frame.getpixel((x, y)):

View File

@@ -0,0 +1,151 @@
#include "anova.h"
#include "esphome/core/log.h"
#ifdef USE_ESP32
namespace esphome {
namespace anova {
static const char *const TAG = "anova";
using namespace esphome::climate;
void Anova::dump_config() { LOG_CLIMATE("", "Anova BLE Cooker", this); }
void Anova::setup() {
this->codec_ = make_unique<AnovaCodec>();
this->current_request_ = 0;
}
void Anova::loop() {}
void Anova::control(const ClimateCall &call) {
if (call.get_mode().has_value()) {
ClimateMode mode = *call.get_mode();
AnovaPacket *pkt;
switch (mode) {
case climate::CLIMATE_MODE_OFF:
pkt = this->codec_->get_stop_request();
break;
case climate::CLIMATE_MODE_HEAT:
pkt = this->codec_->get_start_request();
break;
default:
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);
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);
if (status)
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status);
}
}
void Anova::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: {
this->current_temperature = NAN;
this->target_temperature = NAN;
this->publish_state();
break;
}
case ESP_GATTC_SEARCH_CMPL_EVT: {
auto *chr = this->parent_->get_characteristic(ANOVA_SERVICE_UUID, ANOVA_CHARACTERISTIC_UUID);
if (chr == nullptr) {
ESP_LOGW(TAG, "[%s] No control service found at device, not an Anova..?", this->get_name().c_str());
ESP_LOGW(TAG, "[%s] Note, this component does not currently support Anova Nano.", this->get_name().c_str());
break;
}
this->char_handle_ = chr->handle;
auto status = esp_ble_gattc_register_for_notify(this->parent_->gattc_if, this->parent_->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);
}
break;
}
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
this->node_state = espbt::ClientState::ESTABLISHED;
this->current_request_ = 0;
this->update();
break;
}
case ESP_GATTC_NOTIFY_EVT: {
if (param->notify.handle != this->char_handle_)
break;
this->codec_->decode(param->notify.value, param->notify.value_len);
if (this->codec_->has_target_temp()) {
this->target_temperature = this->codec_->target_temp_;
}
if (this->codec_->has_current_temp()) {
this->current_temperature = this->codec_->current_temp_;
}
if (this->codec_->has_running()) {
this->mode = this->codec_->running_ ? climate::CLIMATE_MODE_HEAT : climate::CLIMATE_MODE_OFF;
}
if (this->codec_->has_unit()) {
this->fahrenheit_ = (this->codec_->unit_ == 'f');
ESP_LOGD(TAG, "Anova units is %s", this->fahrenheit_ ? "fahrenheit" : "celcius");
this->current_request_++;
}
this->publish_state();
if (this->current_request_ > 1) {
AnovaPacket *pkt = nullptr;
switch (this->current_request_++) {
case 2:
pkt = this->codec_->get_read_target_temp_request();
break;
case 3:
pkt = this->codec_->get_read_current_temp_request();
break;
default:
this->current_request_ = 1;
break;
}
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);
if (status) {
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(),
status);
}
}
}
break;
}
default:
break;
}
}
void Anova::set_unit_of_measurement(const char *unit) { this->fahrenheit_ = !strncmp(unit, "f", 1); }
void Anova::update() {
if (this->node_state != espbt::ClientState::ESTABLISHED)
return;
if (this->current_request_ < 2) {
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);
if (status)
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status);
this->current_request_++;
}
}
} // namespace anova
} // namespace esphome
#endif

View File

@@ -0,0 +1,52 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/ble_client/ble_client.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include "esphome/components/climate/climate.h"
#include "anova_base.h"
#ifdef USE_ESP32
#include <esp_gattc_api.h>
namespace esphome {
namespace anova {
namespace espbt = esphome::esp32_ble_tracker;
static const uint16_t ANOVA_SERVICE_UUID = 0xFFE0;
static const uint16_t ANOVA_CHARACTERISTIC_UUID = 0xFFE1;
class Anova : public climate::Climate, public esphome::ble_client::BLEClientNode, 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::DATA; }
climate::ClimateTraits traits() override {
auto traits = climate::ClimateTraits();
traits.set_supports_current_temperature(true);
traits.set_supported_modes({climate::CLIMATE_MODE_OFF, climate::ClimateMode::CLIMATE_MODE_HEAT});
traits.set_visual_min_temperature(25.0);
traits.set_visual_max_temperature(100.0);
traits.set_visual_temperature_step(0.1);
return traits;
}
void set_unit_of_measurement(const char *unit);
protected:
std::unique_ptr<AnovaCodec> codec_;
void control(const climate::ClimateCall &call) override;
uint16_t char_handle_;
uint8_t current_request_;
bool fahrenheit_;
};
} // namespace anova
} // namespace esphome
#endif

View File

@@ -0,0 +1,134 @@
#include "anova_base.h"
#include <cstdio>
#include <cstring>
namespace esphome {
namespace anova {
float ftoc(float f) { return (f - 32.0) * (5.0f / 9.0f); }
float ctof(float c) { return (c * 9.0f / 5.0f) + 32.0; }
AnovaPacket *AnovaCodec::clean_packet_() {
this->packet_.length = strlen((char *) this->packet_.data);
this->packet_.data[this->packet_.length] = '\0';
ESP_LOGV("anova", "SendPkt: %s\n", this->packet_.data);
return &this->packet_;
}
AnovaPacket *AnovaCodec::get_read_device_status_request() {
this->current_query_ = READ_DEVICE_STATUS;
sprintf((char *) this->packet_.data, "%s", CMD_READ_DEVICE_STATUS);
return this->clean_packet_();
}
AnovaPacket *AnovaCodec::get_read_target_temp_request() {
this->current_query_ = READ_TARGET_TEMPERATURE;
sprintf((char *) this->packet_.data, "%s", CMD_READ_TARGET_TEMP);
return this->clean_packet_();
}
AnovaPacket *AnovaCodec::get_read_current_temp_request() {
this->current_query_ = READ_CURRENT_TEMPERATURE;
sprintf((char *) this->packet_.data, "%s", CMD_READ_CURRENT_TEMP);
return this->clean_packet_();
}
AnovaPacket *AnovaCodec::get_read_unit_request() {
this->current_query_ = READ_UNIT;
sprintf((char *) this->packet_.data, "%s", CMD_READ_UNIT);
return this->clean_packet_();
}
AnovaPacket *AnovaCodec::get_read_data_request() {
this->current_query_ = READ_DATA;
sprintf((char *) this->packet_.data, "%s", CMD_READ_DATA);
return this->clean_packet_();
}
AnovaPacket *AnovaCodec::get_set_target_temp_request(float temperature) {
this->current_query_ = SET_TARGET_TEMPERATURE;
if (this->fahrenheit_)
temperature = ctof(temperature);
sprintf((char *) this->packet_.data, CMD_SET_TARGET_TEMP, temperature);
return this->clean_packet_();
}
AnovaPacket *AnovaCodec::get_set_unit_request(char unit) {
this->current_query_ = SET_UNIT;
sprintf((char *) this->packet_.data, CMD_SET_TEMP_UNIT, unit);
return this->clean_packet_();
}
AnovaPacket *AnovaCodec::get_start_request() {
this->current_query_ = START;
sprintf((char *) this->packet_.data, CMD_START);
return this->clean_packet_();
}
AnovaPacket *AnovaCodec::get_stop_request() {
this->current_query_ = STOP;
sprintf((char *) this->packet_.data, CMD_STOP);
return this->clean_packet_();
}
void AnovaCodec::decode(const uint8_t *data, uint16_t length) {
char buf[32];
memset(buf, 0, sizeof(buf));
strncpy(buf, (char *) data, std::min<uint16_t>(length, sizeof(buf) - 1));
this->has_target_temp_ = this->has_current_temp_ = this->has_unit_ = this->has_running_ = false;
switch (this->current_query_) {
case READ_DEVICE_STATUS: {
if (!strncmp(buf, "stopped", 7)) {
this->has_running_ = true;
this->running_ = false;
}
if (!strncmp(buf, "running", 7)) {
this->has_running_ = true;
this->running_ = true;
}
break;
}
case START: {
if (!strncmp(buf, "start", 5)) {
this->has_running_ = true;
this->running_ = true;
}
break;
}
case STOP: {
if (!strncmp(buf, "stop", 4)) {
this->has_running_ = true;
this->running_ = false;
}
break;
}
case READ_TARGET_TEMPERATURE:
case SET_TARGET_TEMPERATURE: {
this->target_temp_ = parse_number<float>(str_until(buf, '\r')).value_or(0.0f);
if (this->fahrenheit_)
this->target_temp_ = ftoc(this->target_temp_);
this->has_target_temp_ = true;
break;
}
case READ_CURRENT_TEMPERATURE: {
this->current_temp_ = parse_number<float>(str_until(buf, '\r')).value_or(0.0f);
if (this->fahrenheit_)
this->current_temp_ = ftoc(this->current_temp_);
this->has_current_temp_ = true;
break;
}
case SET_UNIT:
case READ_UNIT: {
this->unit_ = buf[0];
this->fahrenheit_ = buf[0] == 'f';
this->has_unit_ = true;
break;
}
default:
break;
}
}
} // namespace anova
} // namespace esphome

View File

@@ -0,0 +1,79 @@
#pragma once
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace anova {
enum CurrentQuery {
NONE,
READ_DEVICE_STATUS,
READ_TARGET_TEMPERATURE,
READ_CURRENT_TEMPERATURE,
READ_DATA,
READ_UNIT,
SET_TARGET_TEMPERATURE,
SET_UNIT,
START,
STOP,
};
struct AnovaPacket {
uint16_t length;
uint8_t data[24];
};
#define CMD_READ_DEVICE_STATUS "status\r"
#define CMD_READ_TARGET_TEMP "read set temp\r"
#define CMD_READ_CURRENT_TEMP "read temp\r"
#define CMD_READ_UNIT "read unit\r"
#define CMD_READ_DATA "read data\r"
#define CMD_SET_TARGET_TEMP "set temp %.1f\r"
#define CMD_SET_TEMP_UNIT "set unit %c\r"
#define CMD_START "start\r"
#define CMD_STOP "stop\r"
class AnovaCodec {
public:
AnovaPacket *get_read_device_status_request();
AnovaPacket *get_read_target_temp_request();
AnovaPacket *get_read_current_temp_request();
AnovaPacket *get_read_data_request();
AnovaPacket *get_read_unit_request();
AnovaPacket *get_set_target_temp_request(float temperature);
AnovaPacket *get_set_unit_request(char unit);
AnovaPacket *get_start_request();
AnovaPacket *get_stop_request();
void decode(const uint8_t *data, uint16_t length);
bool has_target_temp() { return this->has_target_temp_; }
bool has_current_temp() { return this->has_current_temp_; }
bool has_unit() { return this->has_unit_; }
bool has_running() { return this->has_running_; }
union {
float target_temp_;
float current_temp_;
char unit_;
bool running_;
};
protected:
AnovaPacket *clean_packet_();
AnovaPacket packet_;
bool has_target_temp_;
bool has_current_temp_;
bool has_unit_;
bool has_running_;
bool fahrenheit_;
CurrentQuery current_query_;
};
} // namespace anova
} // namespace esphome

View File

@@ -0,0 +1,36 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import climate, ble_client
from esphome.const import CONF_ID, CONF_UNIT_OF_MEASUREMENT
UNITS = {
"f": "f",
"c": "c",
}
CODEOWNERS = ["@buxtronix"]
DEPENDENCIES = ["ble_client"]
anova_ns = cg.esphome_ns.namespace("anova")
Anova = anova_ns.class_(
"Anova", climate.Climate, ble_client.BLEClientNode, cg.PollingComponent
)
CONFIG_SCHEMA = (
climate.CLIMATE_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(Anova),
cv.Required(CONF_UNIT_OF_MEASUREMENT): cv.enum(UNITS),
}
)
.extend(ble_client.BLE_CLIENT_SCHEMA)
.extend(cv.polling_component_schema("60s"))
)
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)
cg.add(var.set_unit_of_measurement(config[CONF_UNIT_OF_MEASUREMENT]))

View File

@@ -1,13 +1,14 @@
#include "apds9960.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
namespace esphome {
namespace apds9960 {
static const char *TAG = "apds9960";
static const char *const TAG = "apds9960";
#define APDS9960_ERROR_CHECK(func) \
if (!func) { \
if (!(func)) { \
this->mark_failed(); \
return; \
}
@@ -224,9 +225,10 @@ void APDS9960::read_gesture_data_() {
uint8_t fifo_level;
APDS9960_WARNING_CHECK(this->read_byte(0xAE, &fifo_level), "Reading FIFO level failed.");
if (fifo_level == 0)
if (fifo_level == 0) {
// no data to process
return;
}
APDS9960_WARNING_CHECK(fifo_level <= 32, "FIFO level has invalid value.")

View File

@@ -1,7 +1,7 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import binary_sensor
from esphome.const import CONF_DIRECTION, CONF_DEVICE_CLASS, DEVICE_CLASS_MOVING
from esphome.const import CONF_DIRECTION, DEVICE_CLASS_MOVING
from . import APDS9960, CONF_APDS9960_ID
DEPENDENCIES = ["apds9960"]
@@ -13,13 +13,12 @@ DIRECTIONS = {
"RIGHT": "set_right_direction",
}
CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend(
CONFIG_SCHEMA = binary_sensor.binary_sensor_schema(
device_class=DEVICE_CLASS_MOVING
).extend(
{
cv.Required(CONF_DIRECTION): cv.one_of(*DIRECTIONS, upper=True),
cv.GenerateID(CONF_APDS9960_ID): cv.use_id(APDS9960),
cv.Optional(
CONF_DEVICE_CLASS, default=DEVICE_CLASS_MOVING
): binary_sensor.device_class,
cv.Required(CONF_DIRECTION): cv.one_of(*DIRECTIONS, upper=True),
}
)

View File

@@ -3,7 +3,6 @@ import esphome.config_validation as cv
from esphome.components import sensor
from esphome.const import (
CONF_TYPE,
DEVICE_CLASS_EMPTY,
STATE_CLASS_MEASUREMENT,
UNIT_PERCENT,
ICON_LIGHTBULB,
@@ -21,7 +20,10 @@ TYPES = {
}
CONFIG_SCHEMA = sensor.sensor_schema(
UNIT_PERCENT, ICON_LIGHTBULB, 1, DEVICE_CLASS_EMPTY, STATE_CLASS_MEASUREMENT
unit_of_measurement=UNIT_PERCENT,
icon=ICON_LIGHTBULB,
accuracy_decimals=1,
state_class=STATE_CLASS_MEASUREMENT,
).extend(
{
cv.Required(CONF_TYPE): cv.one_of(*TYPES, upper=True),

View File

@@ -1,3 +1,5 @@
import base64
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
@@ -6,6 +8,7 @@ from esphome.const import (
CONF_DATA,
CONF_DATA_TEMPLATE,
CONF_ID,
CONF_KEY,
CONF_PASSWORD,
CONF_PORT,
CONF_REBOOT_TIMEOUT,
@@ -19,7 +22,7 @@ from esphome.const import (
from esphome.core import coroutine_with_priority
DEPENDENCIES = ["network"]
AUTO_LOAD = ["async_tcp"]
AUTO_LOAD = ["socket"]
CODEOWNERS = ["@OttoWinter"]
api_ns = cg.esphome_ns.namespace("api")
@@ -41,6 +44,22 @@ SERVICE_ARG_NATIVE_TYPES = {
"float[]": cg.std_vector.template(float),
"string[]": cg.std_vector.template(cg.std_string),
}
CONF_ENCRYPTION = "encryption"
def validate_encryption_key(value):
value = cv.string_strict(value)
try:
decoded = base64.b64decode(value, validate=True)
except ValueError as err:
raise cv.Invalid("Invalid key format, please check it's using base64") from err
if len(decoded) != 32:
raise cv.Invalid("Encryption key must be base64 and 32 bytes long")
# Return original data for roundtrip conversion
return value
CONFIG_SCHEMA = cv.Schema(
{
@@ -63,6 +82,11 @@ CONFIG_SCHEMA = cv.Schema(
),
}
),
cv.Optional(CONF_ENCRYPTION): cv.Schema(
{
cv.Required(CONF_KEY): validate_encryption_key,
}
),
}
).extend(cv.COMPONENT_SCHEMA)
@@ -92,6 +116,15 @@ async def to_code(config):
cg.add(var.register_user_service(trigger))
await automation.build_automation(trigger, func_args, conf)
if CONF_ENCRYPTION in config:
conf = config[CONF_ENCRYPTION]
decoded = base64.b64decode(conf[CONF_KEY])
cg.add(var.set_noise_psk(list(decoded)))
cg.add_define("USE_API_NOISE")
cg.add_library("esphome/noise-c", "0.1.4")
else:
cg.add_define("USE_API_PLAINTEXT")
cg.add_define("USE_API")
cg.add_global(api_ns.using)

View File

@@ -38,6 +38,10 @@ service APIConnection {
rpc switch_command (SwitchCommandRequest) returns (void) {}
rpc camera_image (CameraImageRequest) returns (void) {}
rpc climate_command (ClimateCommandRequest) returns (void) {}
rpc number_command (NumberCommandRequest) returns (void) {}
rpc select_command (SelectCommandRequest) returns (void) {}
rpc button_command (ButtonCommandRequest) returns (void) {}
rpc lock_command (LockCommandRequest) returns (void) {}
}
@@ -92,6 +96,9 @@ message HelloResponse {
// and only exists for debugging/logging purposes.
// For example "ESPHome v1.10.0 on ESP8266"
string server_info = 3;
// The name of the server (App.get_name())
string name = 4;
}
// Message sent at the beginning of each connection to authenticate the client
@@ -180,6 +187,8 @@ message DeviceInfoResponse {
// The esphome project details if set
string project_name = 8;
string project_version = 9;
uint32 webserver_port = 10;
}
message ListEntitiesRequest {
@@ -199,6 +208,14 @@ message SubscribeStatesRequest {
// Empty
}
// ==================== COMMON =====================
enum EntityCategory {
ENTITY_CATEGORY_NONE = 0;
ENTITY_CATEGORY_CONFIG = 1;
ENTITY_CATEGORY_DIAGNOSTIC = 2;
}
// ==================== BINARY SENSOR ====================
message ListEntitiesBinarySensorResponse {
option (id) = 12;
@@ -212,6 +229,9 @@ message ListEntitiesBinarySensorResponse {
string device_class = 5;
bool is_status_binary_sensor = 6;
bool disabled_by_default = 7;
string icon = 8;
EntityCategory entity_category = 9;
}
message BinarySensorStateResponse {
option (id) = 21;
@@ -241,6 +261,9 @@ message ListEntitiesCoverResponse {
bool supports_position = 6;
bool supports_tilt = 7;
string device_class = 8;
bool disabled_by_default = 9;
string icon = 10;
EntityCategory entity_category = 11;
}
enum LegacyCoverState {
@@ -308,6 +331,9 @@ message ListEntitiesFanResponse {
bool supports_speed = 6;
bool supports_direction = 7;
int32 supported_speed_count = 8;
bool disabled_by_default = 9;
string icon = 10;
EntityCategory entity_category = 11;
}
enum FanSpeed {
FAN_SPEED_LOW = 0;
@@ -351,6 +377,18 @@ message FanCommandRequest {
}
// ==================== LIGHT ====================
enum ColorMode {
COLOR_MODE_UNKNOWN = 0;
COLOR_MODE_ON_OFF = 1;
COLOR_MODE_BRIGHTNESS = 2;
COLOR_MODE_WHITE = 7;
COLOR_MODE_COLOR_TEMPERATURE = 11;
COLOR_MODE_COLD_WARM_WHITE = 19;
COLOR_MODE_RGB = 35;
COLOR_MODE_RGB_WHITE = 39;
COLOR_MODE_RGB_COLOR_TEMPERATURE = 47;
COLOR_MODE_RGB_COLD_WARM_WHITE = 51;
}
message ListEntitiesLightResponse {
option (id) = 15;
option (source) = SOURCE_SERVER;
@@ -361,13 +399,18 @@ message ListEntitiesLightResponse {
string name = 3;
string unique_id = 4;
bool supports_brightness = 5;
bool supports_rgb = 6;
bool supports_white_value = 7;
bool supports_color_temperature = 8;
repeated ColorMode supported_color_modes = 12;
// next four supports_* are for legacy clients, newer clients should use color modes
bool legacy_supports_brightness = 5 [deprecated=true];
bool legacy_supports_rgb = 6 [deprecated=true];
bool legacy_supports_white_value = 7 [deprecated=true];
bool legacy_supports_color_temperature = 8 [deprecated=true];
float min_mireds = 9;
float max_mireds = 10;
repeated string effects = 11;
bool disabled_by_default = 13;
string icon = 14;
EntityCategory entity_category = 15;
}
message LightStateResponse {
option (id) = 24;
@@ -378,11 +421,15 @@ message LightStateResponse {
fixed32 key = 1;
bool state = 2;
float brightness = 3;
ColorMode color_mode = 11;
float color_brightness = 10;
float red = 4;
float green = 5;
float blue = 6;
float white = 7;
float color_temperature = 8;
float cold_white = 12;
float warm_white = 13;
string effect = 9;
}
message LightCommandRequest {
@@ -396,6 +443,10 @@ message LightCommandRequest {
bool state = 3;
bool has_brightness = 4;
float brightness = 5;
bool has_color_mode = 22;
ColorMode color_mode = 23;
bool has_color_brightness = 20;
float color_brightness = 21;
bool has_rgb = 6;
float red = 7;
float green = 8;
@@ -404,6 +455,10 @@ message LightCommandRequest {
float white = 11;
bool has_color_temperature = 12;
float color_temperature = 13;
bool has_cold_white = 24;
float cold_white = 25;
bool has_warm_white = 26;
float warm_white = 27;
bool has_transition_length = 14;
uint32 transition_length = 15;
bool has_flash_length = 16;
@@ -416,6 +471,13 @@ message LightCommandRequest {
enum SensorStateClass {
STATE_CLASS_NONE = 0;
STATE_CLASS_MEASUREMENT = 1;
STATE_CLASS_TOTAL_INCREASING = 2;
}
enum SensorLastResetType {
LAST_RESET_NONE = 0;
LAST_RESET_NEVER = 1;
LAST_RESET_AUTO = 2;
}
message ListEntitiesSensorResponse {
@@ -434,6 +496,10 @@ message ListEntitiesSensorResponse {
bool force_update = 8;
string device_class = 9;
SensorStateClass state_class = 10;
// Last reset type removed in 2021.9.0
SensorLastResetType legacy_last_reset_type = 11;
bool disabled_by_default = 12;
EntityCategory entity_category = 13;
}
message SensorStateResponse {
option (id) = 25;
@@ -461,6 +527,9 @@ message ListEntitiesSwitchResponse {
string icon = 5;
bool assumed_state = 6;
bool disabled_by_default = 7;
EntityCategory entity_category = 8;
string device_class = 9;
}
message SwitchStateResponse {
option (id) = 26;
@@ -493,6 +562,8 @@ message ListEntitiesTextSensorResponse {
string unique_id = 4;
string icon = 5;
bool disabled_by_default = 6;
EntityCategory entity_category = 7;
}
message TextSensorStateResponse {
option (id) = 27;
@@ -513,9 +584,10 @@ enum LogLevel {
LOG_LEVEL_ERROR = 1;
LOG_LEVEL_WARN = 2;
LOG_LEVEL_INFO = 3;
LOG_LEVEL_DEBUG = 4;
LOG_LEVEL_VERBOSE = 5;
LOG_LEVEL_VERY_VERBOSE = 6;
LOG_LEVEL_CONFIG = 4;
LOG_LEVEL_DEBUG = 5;
LOG_LEVEL_VERBOSE = 6;
LOG_LEVEL_VERY_VERBOSE = 7;
}
message SubscribeLogsRequest {
option (id) = 28;
@@ -530,7 +602,6 @@ message SubscribeLogsResponse {
option (no_delay) = false;
LogLevel level = 1;
string tag = 2;
string message = 3;
bool send_failed = 4;
}
@@ -652,6 +723,9 @@ message ListEntitiesCameraResponse {
fixed32 key = 2;
string name = 3;
string unique_id = 4;
bool disabled_by_default = 5;
string icon = 6;
EntityCategory entity_category = 7;
}
message CameraImageResponse {
@@ -710,13 +784,14 @@ enum ClimateAction {
CLIMATE_ACTION_FAN = 6;
}
enum ClimatePreset {
CLIMATE_PRESET_ECO = 0;
CLIMATE_PRESET_AWAY = 1;
CLIMATE_PRESET_BOOST = 2;
CLIMATE_PRESET_COMFORT = 3;
CLIMATE_PRESET_HOME = 4;
CLIMATE_PRESET_SLEEP = 5;
CLIMATE_PRESET_ACTIVITY = 6;
CLIMATE_PRESET_NONE = 0;
CLIMATE_PRESET_HOME = 1;
CLIMATE_PRESET_AWAY = 2;
CLIMATE_PRESET_BOOST = 3;
CLIMATE_PRESET_COMFORT = 4;
CLIMATE_PRESET_ECO = 5;
CLIMATE_PRESET_SLEEP = 6;
CLIMATE_PRESET_ACTIVITY = 7;
}
message ListEntitiesClimateResponse {
option (id) = 46;
@@ -734,13 +809,18 @@ message ListEntitiesClimateResponse {
float visual_min_temperature = 8;
float visual_max_temperature = 9;
float visual_temperature_step = 10;
bool supports_away = 11;
// for older peer versions - in new system this
// is if CLIMATE_PRESET_AWAY exists is supported_presets
bool legacy_supports_away = 11;
bool supports_action = 12;
repeated ClimateFanMode supported_fan_modes = 13;
repeated ClimateSwingMode supported_swing_modes = 14;
repeated string supported_custom_fan_modes = 15;
repeated ClimatePreset supported_presets = 16;
repeated string supported_custom_presets = 17;
bool disabled_by_default = 18;
string icon = 19;
EntityCategory entity_category = 20;
}
message ClimateStateResponse {
option (id) = 47;
@@ -754,7 +834,8 @@ message ClimateStateResponse {
float target_temperature = 4;
float target_temperature_low = 5;
float target_temperature_high = 6;
bool away = 7;
// For older peers, equal to preset == CLIMATE_PRESET_AWAY
bool legacy_away = 7;
ClimateAction action = 8;
ClimateFanMode fan_mode = 9;
ClimateSwingMode swing_mode = 10;
@@ -777,8 +858,9 @@ message ClimateCommandRequest {
float target_temperature_low = 7;
bool has_target_temperature_high = 8;
float target_temperature_high = 9;
bool has_away = 10;
bool away = 11;
// legacy, for older peers, newer ones should use CLIMATE_PRESET_AWAY in preset
bool has_legacy_away = 10;
bool legacy_away = 11;
bool has_fan_mode = 12;
ClimateFanMode fan_mode = 13;
bool has_swing_mode = 14;
@@ -790,3 +872,171 @@ message ClimateCommandRequest {
bool has_custom_preset = 20;
string custom_preset = 21;
}
// ==================== NUMBER ====================
enum NumberMode {
NUMBER_MODE_AUTO = 0;
NUMBER_MODE_BOX = 1;
NUMBER_MODE_SLIDER = 2;
}
message ListEntitiesNumberResponse {
option (id) = 49;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_NUMBER";
string object_id = 1;
fixed32 key = 2;
string name = 3;
string unique_id = 4;
string icon = 5;
float min_value = 6;
float max_value = 7;
float step = 8;
bool disabled_by_default = 9;
EntityCategory entity_category = 10;
string unit_of_measurement = 11;
NumberMode mode = 12;
}
message NumberStateResponse {
option (id) = 50;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_NUMBER";
option (no_delay) = true;
fixed32 key = 1;
float state = 2;
// If the number does not have a valid state yet.
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
bool missing_state = 3;
}
message NumberCommandRequest {
option (id) = 51;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_NUMBER";
option (no_delay) = true;
fixed32 key = 1;
float state = 2;
}
// ==================== SELECT ====================
message ListEntitiesSelectResponse {
option (id) = 52;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_SELECT";
string object_id = 1;
fixed32 key = 2;
string name = 3;
string unique_id = 4;
string icon = 5;
repeated string options = 6;
bool disabled_by_default = 7;
EntityCategory entity_category = 8;
}
message SelectStateResponse {
option (id) = 53;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_SELECT";
option (no_delay) = true;
fixed32 key = 1;
string state = 2;
// If the select does not have a valid state yet.
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
bool missing_state = 3;
}
message SelectCommandRequest {
option (id) = 54;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_SELECT";
option (no_delay) = true;
fixed32 key = 1;
string state = 2;
}
// ==================== LOCK ====================
enum LockState {
LOCK_STATE_NONE = 0;
LOCK_STATE_LOCKED = 1;
LOCK_STATE_UNLOCKED = 2;
LOCK_STATE_JAMMED = 3;
LOCK_STATE_LOCKING = 4;
LOCK_STATE_UNLOCKING = 5;
}
enum LockCommand {
LOCK_UNLOCK = 0;
LOCK_LOCK = 1;
LOCK_OPEN = 2;
}
message ListEntitiesLockResponse {
option (id) = 58;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_LOCK";
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 assumed_state = 8;
bool supports_open = 9;
bool requires_code = 10;
# Not yet implemented:
string code_format = 11;
}
message LockStateResponse {
option (id) = 59;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_LOCK";
option (no_delay) = true;
fixed32 key = 1;
LockState state = 2;
}
message LockCommandRequest {
option (id) = 60;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_LOCK";
option (no_delay) = true;
fixed32 key = 1;
LockCommand command = 2;
# Not yet implemented:
bool has_code = 3;
string code = 4;
}
// ==================== BUTTON ====================
message ListEntitiesButtonResponse {
option (id) = 61;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_BUTTON";
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;
string device_class = 8;
}
message ButtonCommandRequest {
option (id) = 62;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_BUTTON";
option (no_delay) = true;
fixed32 key = 1;
}

View File

@@ -1,7 +1,10 @@
#include "api_connection.h"
#include "esphome/core/entity_base.h"
#include "esphome/core/log.h"
#include "esphome/core/util.h"
#include "esphome/components/network/util.h"
#include "esphome/core/version.h"
#include "esphome/core/hal.h"
#include <cerrno>
#ifdef USE_DEEP_SLEEP
#include "esphome/components/deep_sleep/deep_sleep_component.h"
@@ -16,145 +19,150 @@
namespace esphome {
namespace api {
static const char *TAG = "api.connection";
static const char *const TAG = "api.connection";
static const int ESP32_CAMERA_STOP_STREAM = 5000;
APIConnection::APIConnection(AsyncClient *client, APIServer *parent)
: client_(client), parent_(parent), initial_state_iterator_(parent, this), list_entities_iterator_(parent, this) {
this->client_->onError([](void *s, AsyncClient *c, int8_t error) { ((APIConnection *) s)->on_error_(error); }, this);
this->client_->onDisconnect([](void *s, AsyncClient *c) { ((APIConnection *) s)->on_disconnect_(); }, this);
this->client_->onTimeout([](void *s, AsyncClient *c, uint32_t time) { ((APIConnection *) s)->on_timeout_(time); },
this);
this->client_->onData([](void *s, AsyncClient *c, void *buf,
size_t len) { ((APIConnection *) s)->on_data_(reinterpret_cast<uint8_t *>(buf), len); },
this);
APIConnection::APIConnection(std::unique_ptr<socket::Socket> sock, APIServer *parent)
: parent_(parent), initial_state_iterator_(parent, this), list_entities_iterator_(parent, this) {
this->proto_write_buffer_.reserve(64);
this->send_buffer_.reserve(64);
this->recv_buffer_.reserve(32);
this->client_info_ = this->client_->remoteIP().toString().c_str();
#if defined(USE_API_PLAINTEXT)
helper_ = std::unique_ptr<APIFrameHelper>{new APIPlaintextFrameHelper(std::move(sock))};
#elif defined(USE_API_NOISE)
helper_ = std::unique_ptr<APIFrameHelper>{new APINoiseFrameHelper(std::move(sock), parent->get_noise_ctx())};
#else
#error "No frame helper defined"
#endif
}
void APIConnection::start() {
this->last_traffic_ = millis();
}
APIConnection::~APIConnection() { delete this->client_; }
void APIConnection::on_error_(int8_t error) { this->remove_ = true; }
void APIConnection::on_disconnect_() { this->remove_ = true; }
void APIConnection::on_timeout_(uint32_t time) { this->on_fatal_error(); }
void APIConnection::on_data_(uint8_t *buf, size_t len) {
if (len == 0 || buf == nullptr)
APIError err = helper_->init();
if (err != APIError::OK) {
on_fatal_error();
ESP_LOGW(TAG, "%s: Helper init failed: %s errno=%d", client_info_.c_str(), api_error_to_str(err), errno);
return;
this->recv_buffer_.insert(this->recv_buffer_.end(), buf, buf + len);
}
void APIConnection::parse_recv_buffer_() {
if (this->recv_buffer_.empty() || this->remove_)
return;
while (!this->recv_buffer_.empty()) {
if (this->recv_buffer_[0] != 0x00) {
ESP_LOGW(TAG, "Invalid preamble from %s", this->client_info_.c_str());
this->on_fatal_error();
return;
}
uint32_t i = 1;
const uint32_t size = this->recv_buffer_.size();
uint32_t consumed;
auto msg_size_varint = ProtoVarInt::parse(&this->recv_buffer_[i], size - i, &consumed);
if (!msg_size_varint.has_value())
// not enough data there yet
return;
i += consumed;
uint32_t msg_size = msg_size_varint->as_uint32();
auto msg_type_varint = ProtoVarInt::parse(&this->recv_buffer_[i], size - i, &consumed);
if (!msg_type_varint.has_value())
// not enough data there yet
return;
i += consumed;
uint32_t msg_type = msg_type_varint->as_uint32();
if (size - i < msg_size)
// message body not fully received
return;
uint8_t *msg = &this->recv_buffer_[i];
this->read_message(msg_size, msg_type, msg);
if (this->remove_)
return;
// pop front
uint32_t total = i + msg_size;
this->recv_buffer_.erase(this->recv_buffer_.begin(), this->recv_buffer_.begin() + total);
this->last_traffic_ = millis();
}
}
void APIConnection::disconnect_client() {
this->client_->close();
this->remove_ = true;
client_info_ = helper_->getpeername();
helper_->set_log_info(client_info_);
}
void APIConnection::loop() {
if (this->remove_)
return;
if (this->next_close_) {
this->disconnect_client();
return;
}
if (!network_is_connected()) {
if (!network::is_connected()) {
// when network is disconnected force disconnect immediately
// don't wait for timeout
this->on_fatal_error();
ESP_LOGW(TAG, "%s: Network unavailable, disconnecting", client_info_.c_str());
return;
}
if (this->client_->disconnected()) {
// failsafe for disconnect logic
this->on_disconnect_();
if (this->next_close_) {
// requested a disconnect
this->helper_->close();
this->remove_ = true;
return;
}
this->parse_recv_buffer_();
APIError err = helper_->loop();
if (err != APIError::OK) {
on_fatal_error();
ESP_LOGW(TAG, "%s: Socket operation failed: %s errno=%d", client_info_.c_str(), api_error_to_str(err), errno);
return;
}
ReadPacketBuffer buffer;
err = helper_->read_packet(&buffer);
if (err == APIError::WOULD_BLOCK) {
// pass
} else if (err != APIError::OK) {
on_fatal_error();
if (err == APIError::SOCKET_READ_FAILED && errno == ECONNRESET) {
ESP_LOGW(TAG, "%s: Connection reset", client_info_.c_str());
} else if (err == APIError::CONNECTION_CLOSED) {
ESP_LOGW(TAG, "%s: Connection closed", client_info_.c_str());
} else {
ESP_LOGW(TAG, "%s: Reading failed: %s errno=%d", client_info_.c_str(), api_error_to_str(err), errno);
}
return;
} else {
this->last_traffic_ = millis();
// read a packet
this->read_message(buffer.data_len, buffer.type, &buffer.container[buffer.data_offset]);
if (this->remove_)
return;
}
this->list_entities_iterator_.advance();
this->initial_state_iterator_.advance();
const uint32_t keepalive = 60000;
const uint32_t now = millis();
if (this->sent_ping_) {
// Disconnect if not responded within 2.5*keepalive
if (millis() - this->last_traffic_ > (keepalive * 5) / 2) {
ESP_LOGW(TAG, "'%s' didn't respond to ping request in time. Disconnecting...", this->client_info_.c_str());
this->disconnect_client();
if (now - this->last_traffic_ > (keepalive * 5) / 2) {
on_fatal_error();
ESP_LOGW(TAG, "%s didn't respond to ping request in time. Disconnecting...", this->client_info_.c_str());
}
} else if (millis() - this->last_traffic_ > keepalive) {
} else if (now - this->last_traffic_ > keepalive) {
ESP_LOGVV(TAG, "Sending keepalive PING...");
this->sent_ping_ = true;
this->send_ping_request(PingRequest());
}
#ifdef USE_ESP32_CAMERA
if (this->image_reader_.available()) {
uint32_t space = this->client_->space();
// reserve 15 bytes for metadata, and at least 64 bytes of data
if (space >= 15 + 64) {
uint32_t to_send = std::min(space - 15, this->image_reader_.available());
auto buffer = this->create_buffer();
// fixed32 key = 1;
buffer.encode_fixed32(1, esp32_camera::global_esp32_camera->get_object_id_hash());
// bytes data = 2;
buffer.encode_bytes(2, this->image_reader_.peek_data_buffer(), to_send);
// bool done = 3;
bool done = this->image_reader_.available() == to_send;
buffer.encode_bool(3, done);
bool success = this->send_buffer(buffer, 44);
if (this->image_reader_.available() && this->helper_->can_write_without_blocking()) {
uint32_t to_send = std::min((size_t) 1024, this->image_reader_.available());
auto buffer = this->create_buffer();
// fixed32 key = 1;
buffer.encode_fixed32(1, esp32_camera::global_esp32_camera->get_object_id_hash());
// bytes data = 2;
buffer.encode_bytes(2, this->image_reader_.peek_data_buffer(), to_send);
// bool done = 3;
bool done = this->image_reader_.available() == to_send;
buffer.encode_bool(3, done);
bool success = this->send_buffer(buffer, 44);
if (success) {
this->image_reader_.consume_data(to_send);
}
if (success && done) {
this->image_reader_.return_image();
}
if (success) {
this->image_reader_.consume_data(to_send);
}
if (success && done) {
this->image_reader_.return_image();
}
}
#endif
if (state_subs_at_ != -1) {
const auto &subs = this->parent_->get_state_subs();
if (state_subs_at_ >= (int) subs.size()) {
state_subs_at_ = -1;
} else {
auto &it = subs[state_subs_at_];
SubscribeHomeAssistantStateResponse resp;
resp.entity_id = it.entity_id;
resp.attribute = it.attribute.value();
if (this->send_subscribe_home_assistant_state_response(resp)) {
state_subs_at_++;
}
}
}
}
std::string get_default_unique_id(const std::string &component_type, Nameable *nameable) {
return App.get_name() + component_type + nameable->get_object_id();
std::string get_default_unique_id(const std::string &component_type, EntityBase *entity) {
return App.get_name() + component_type + entity->get_object_id();
}
DisconnectResponse APIConnection::disconnect(const DisconnectRequest &msg) {
// remote initiated disconnect_client
// don't close yet, we still need to send the disconnect response
// close will happen on next loop
ESP_LOGD(TAG, "%s requested disconnected", client_info_.c_str());
this->next_close_ = true;
DisconnectResponse resp;
return resp;
}
void APIConnection::on_disconnect_response(const DisconnectResponse &value) {
// pass
}
#ifdef USE_BINARY_SENSOR
@@ -176,6 +184,9 @@ bool APIConnection::send_binary_sensor_info(binary_sensor::BinarySensor *binary_
msg.unique_id = get_default_unique_id("binary_sensor", binary_sensor);
msg.device_class = binary_sensor->get_device_class();
msg.is_status_binary_sensor = binary_sensor->is_status_binary_sensor();
msg.disabled_by_default = binary_sensor->is_disabled_by_default();
msg.icon = binary_sensor->get_icon();
msg.entity_category = static_cast<enums::EntityCategory>(binary_sensor->get_entity_category());
return this->send_list_entities_binary_sensor_response(msg);
}
#endif
@@ -207,6 +218,9 @@ bool APIConnection::send_cover_info(cover::Cover *cover) {
msg.supports_position = traits.get_supports_position();
msg.supports_tilt = traits.get_supports_tilt();
msg.device_class = cover->get_device_class();
msg.disabled_by_default = cover->is_disabled_by_default();
msg.icon = cover->get_icon();
msg.entity_category = static_cast<enums::EntityCategory>(cover->get_entity_category());
return this->send_list_entities_cover_response(msg);
}
void APIConnection::cover_command(const CoverCommandRequest &msg) {
@@ -239,7 +253,10 @@ void APIConnection::cover_command(const CoverCommandRequest &msg) {
#endif
#ifdef USE_FAN
bool APIConnection::send_fan_state(fan::FanState *fan) {
// Shut-up about usage of deprecated speed_level_to_enum/speed_enum_to_level functions for a bit.
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
bool APIConnection::send_fan_state(fan::Fan *fan) {
if (!this->state_subscription_)
return false;
@@ -257,7 +274,7 @@ bool APIConnection::send_fan_state(fan::FanState *fan) {
resp.direction = static_cast<enums::FanDirection>(fan->direction);
return this->send_fan_state_response(resp);
}
bool APIConnection::send_fan_info(fan::FanState *fan) {
bool APIConnection::send_fan_info(fan::Fan *fan) {
auto traits = fan->get_traits();
ListEntitiesFanResponse msg;
msg.key = fan->get_object_id_hash();
@@ -268,10 +285,13 @@ bool APIConnection::send_fan_info(fan::FanState *fan) {
msg.supports_speed = traits.supports_speed();
msg.supports_direction = traits.supports_direction();
msg.supported_speed_count = traits.supported_speed_count();
msg.disabled_by_default = fan->is_disabled_by_default();
msg.icon = fan->get_icon();
msg.entity_category = static_cast<enums::EntityCategory>(fan->get_entity_category());
return this->send_list_entities_fan_response(msg);
}
void APIConnection::fan_command(const FanCommandRequest &msg) {
fan::FanState *fan = App.get_fan_by_key(msg.key);
fan::Fan *fan = App.get_fan_by_key(msg.key);
if (fan == nullptr)
return;
@@ -292,6 +312,7 @@ void APIConnection::fan_command(const FanCommandRequest &msg) {
call.set_direction(static_cast<fan::FanDirection>(msg.direction));
call.perform();
}
#pragma GCC diagnostic pop
#endif
#ifdef USE_LIGHT
@@ -301,21 +322,21 @@ bool APIConnection::send_light_state(light::LightState *light) {
auto traits = light->get_traits();
auto values = light->remote_values;
auto color_mode = values.get_color_mode();
LightStateResponse resp{};
resp.key = light->get_object_id_hash();
resp.state = values.is_on();
if (traits.get_supports_brightness())
resp.brightness = values.get_brightness();
if (traits.get_supports_rgb()) {
resp.red = values.get_red();
resp.green = values.get_green();
resp.blue = values.get_blue();
}
if (traits.get_supports_rgb_white_value())
resp.white = values.get_white();
if (traits.get_supports_color_temperature())
resp.color_temperature = values.get_color_temperature();
resp.color_mode = static_cast<enums::ColorMode>(color_mode);
resp.brightness = values.get_brightness();
resp.color_brightness = values.get_color_brightness();
resp.red = values.get_red();
resp.green = values.get_green();
resp.blue = values.get_blue();
resp.white = values.get_white();
resp.color_temperature = values.get_color_temperature();
resp.cold_white = values.get_cold_white();
resp.warm_white = values.get_warm_white();
if (light->supports_effects())
resp.effect = light->get_effect_name();
return this->send_light_state_response(resp);
@@ -327,11 +348,23 @@ bool APIConnection::send_light_info(light::LightState *light) {
msg.object_id = light->get_object_id();
msg.name = light->get_name();
msg.unique_id = get_default_unique_id("light", light);
msg.supports_brightness = traits.get_supports_brightness();
msg.supports_rgb = traits.get_supports_rgb();
msg.supports_white_value = traits.get_supports_rgb_white_value();
msg.supports_color_temperature = traits.get_supports_color_temperature();
if (msg.supports_color_temperature) {
msg.disabled_by_default = light->is_disabled_by_default();
msg.icon = light->get_icon();
msg.entity_category = static_cast<enums::EntityCategory>(light->get_entity_category());
for (auto mode : traits.get_supported_color_modes())
msg.supported_color_modes.push_back(static_cast<enums::ColorMode>(mode));
msg.legacy_supports_brightness = traits.supports_color_capability(light::ColorCapability::BRIGHTNESS);
msg.legacy_supports_rgb = traits.supports_color_capability(light::ColorCapability::RGB);
msg.legacy_supports_white_value =
msg.legacy_supports_rgb && (traits.supports_color_capability(light::ColorCapability::WHITE) ||
traits.supports_color_capability(light::ColorCapability::COLD_WARM_WHITE));
msg.legacy_supports_color_temperature = traits.supports_color_capability(light::ColorCapability::COLOR_TEMPERATURE) ||
traits.supports_color_capability(light::ColorCapability::COLD_WARM_WHITE);
if (msg.legacy_supports_color_temperature) {
msg.min_mireds = traits.get_min_mireds();
msg.max_mireds = traits.get_max_mireds();
}
@@ -352,6 +385,10 @@ void APIConnection::light_command(const LightCommandRequest &msg) {
call.set_state(msg.state);
if (msg.has_brightness)
call.set_brightness(msg.brightness);
if (msg.has_color_mode)
call.set_color_mode(static_cast<light::ColorMode>(msg.color_mode));
if (msg.has_color_brightness)
call.set_color_brightness(msg.color_brightness);
if (msg.has_rgb) {
call.set_red(msg.red);
call.set_green(msg.green);
@@ -361,6 +398,10 @@ void APIConnection::light_command(const LightCommandRequest &msg) {
call.set_white(msg.white);
if (msg.has_color_temperature)
call.set_color_temperature(msg.color_temperature);
if (msg.has_cold_white)
call.set_cold_white(msg.cold_white);
if (msg.has_warm_white)
call.set_warm_white(msg.warm_white);
if (msg.has_transition_length)
call.set_transition_length(msg.transition_length);
if (msg.has_flash_length)
@@ -395,8 +436,9 @@ bool APIConnection::send_sensor_info(sensor::Sensor *sensor) {
msg.accuracy_decimals = sensor->get_accuracy_decimals();
msg.force_update = sensor->get_force_update();
msg.device_class = sensor->get_device_class();
msg.state_class = static_cast<enums::SensorStateClass>(sensor->state_class);
msg.state_class = static_cast<enums::SensorStateClass>(sensor->get_state_class());
msg.disabled_by_default = sensor->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(sensor->get_entity_category());
return this->send_list_entities_sensor_response(msg);
}
#endif
@@ -419,6 +461,9 @@ bool APIConnection::send_switch_info(switch_::Switch *a_switch) {
msg.unique_id = get_default_unique_id("switch", a_switch);
msg.icon = a_switch->get_icon();
msg.assumed_state = a_switch->assumed_state();
msg.disabled_by_default = a_switch->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(a_switch->get_entity_category());
msg.device_class = a_switch->get_device_class();
return this->send_list_entities_switch_response(msg);
}
void APIConnection::switch_command(const SwitchCommandRequest &msg) {
@@ -426,10 +471,11 @@ void APIConnection::switch_command(const SwitchCommandRequest &msg) {
if (a_switch == nullptr)
return;
if (msg.state)
if (msg.state) {
a_switch->turn_on();
else
} else {
a_switch->turn_off();
}
}
#endif
@@ -453,6 +499,8 @@ bool APIConnection::send_text_sensor_info(text_sensor::TextSensor *text_sensor)
if (msg.unique_id.empty())
msg.unique_id = get_default_unique_id("text_sensor", text_sensor);
msg.icon = text_sensor->get_icon();
msg.disabled_by_default = text_sensor->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(text_sensor->get_entity_category());
return this->send_list_entities_text_sensor_response(msg);
}
#endif
@@ -475,14 +523,14 @@ bool APIConnection::send_climate_state(climate::Climate *climate) {
} else {
resp.target_temperature = climate->target_temperature;
}
if (traits.get_supports_away())
resp.away = climate->away;
if (traits.get_supports_fan_modes() && climate->fan_mode.has_value())
resp.fan_mode = static_cast<enums::ClimateFanMode>(climate->fan_mode.value());
if (!traits.get_supported_custom_fan_modes().empty() && climate->custom_fan_mode.has_value())
resp.custom_fan_mode = climate->custom_fan_mode.value();
if (traits.get_supports_presets() && climate->preset.has_value())
if (traits.get_supports_presets() && climate->preset.has_value()) {
resp.preset = static_cast<enums::ClimatePreset>(climate->preset.value());
resp.legacy_away = resp.preset == enums::CLIMATE_PRESET_AWAY;
}
if (!traits.get_supported_custom_presets().empty() && climate->custom_preset.has_value())
resp.custom_preset = climate->custom_preset.value();
if (traits.get_supports_swing_modes())
@@ -496,42 +544,33 @@ bool APIConnection::send_climate_info(climate::Climate *climate) {
msg.object_id = climate->get_object_id();
msg.name = climate->get_name();
msg.unique_id = get_default_unique_id("climate", climate);
msg.disabled_by_default = climate->is_disabled_by_default();
msg.icon = climate->get_icon();
msg.entity_category = static_cast<enums::EntityCategory>(climate->get_entity_category());
msg.supports_current_temperature = traits.get_supports_current_temperature();
msg.supports_two_point_target_temperature = traits.get_supports_two_point_target_temperature();
for (auto mode :
{climate::CLIMATE_MODE_AUTO, climate::CLIMATE_MODE_OFF, climate::CLIMATE_MODE_COOL, climate::CLIMATE_MODE_HEAT,
climate::CLIMATE_MODE_DRY, climate::CLIMATE_MODE_FAN_ONLY, climate::CLIMATE_MODE_HEAT_COOL}) {
if (traits.supports_mode(mode))
msg.supported_modes.push_back(static_cast<enums::ClimateMode>(mode));
}
for (auto mode : traits.get_supported_modes())
msg.supported_modes.push_back(static_cast<enums::ClimateMode>(mode));
msg.visual_min_temperature = traits.get_visual_min_temperature();
msg.visual_max_temperature = traits.get_visual_max_temperature();
msg.visual_temperature_step = traits.get_visual_temperature_step();
msg.supports_away = traits.get_supports_away();
msg.legacy_supports_away = traits.supports_preset(climate::CLIMATE_PRESET_AWAY);
msg.supports_action = traits.get_supports_action();
for (auto fan_mode : {climate::CLIMATE_FAN_ON, climate::CLIMATE_FAN_OFF, climate::CLIMATE_FAN_AUTO,
climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM, climate::CLIMATE_FAN_HIGH,
climate::CLIMATE_FAN_MIDDLE, climate::CLIMATE_FAN_FOCUS, climate::CLIMATE_FAN_DIFFUSE}) {
if (traits.supports_fan_mode(fan_mode))
msg.supported_fan_modes.push_back(static_cast<enums::ClimateFanMode>(fan_mode));
}
for (auto const &custom_fan_mode : traits.get_supported_custom_fan_modes()) {
for (auto fan_mode : traits.get_supported_fan_modes())
msg.supported_fan_modes.push_back(static_cast<enums::ClimateFanMode>(fan_mode));
for (auto const &custom_fan_mode : traits.get_supported_custom_fan_modes())
msg.supported_custom_fan_modes.push_back(custom_fan_mode);
}
for (auto preset : {climate::CLIMATE_PRESET_ECO, climate::CLIMATE_PRESET_AWAY, climate::CLIMATE_PRESET_BOOST,
climate::CLIMATE_PRESET_COMFORT, climate::CLIMATE_PRESET_HOME, climate::CLIMATE_PRESET_SLEEP,
climate::CLIMATE_PRESET_ACTIVITY}) {
if (traits.supports_preset(preset))
msg.supported_presets.push_back(static_cast<enums::ClimatePreset>(preset));
}
for (auto const &custom_preset : traits.get_supported_custom_presets()) {
for (auto preset : traits.get_supported_presets())
msg.supported_presets.push_back(static_cast<enums::ClimatePreset>(preset));
for (auto const &custom_preset : traits.get_supported_custom_presets())
msg.supported_custom_presets.push_back(custom_preset);
}
for (auto swing_mode : {climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_BOTH, climate::CLIMATE_SWING_VERTICAL,
climate::CLIMATE_SWING_HORIZONTAL}) {
if (traits.supports_swing_mode(swing_mode))
msg.supported_swing_modes.push_back(static_cast<enums::ClimateSwingMode>(swing_mode));
}
for (auto swing_mode : traits.get_supported_swing_modes())
msg.supported_swing_modes.push_back(static_cast<enums::ClimateSwingMode>(swing_mode));
return this->send_list_entities_climate_response(msg);
}
void APIConnection::climate_command(const ClimateCommandRequest &msg) {
@@ -548,8 +587,8 @@ void APIConnection::climate_command(const ClimateCommandRequest &msg) {
call.set_target_temperature_low(msg.target_temperature_low);
if (msg.has_target_temperature_high)
call.set_target_temperature_high(msg.target_temperature_high);
if (msg.has_away)
call.set_away(msg.away);
if (msg.has_legacy_away)
call.set_preset(msg.legacy_away ? climate::CLIMATE_PRESET_AWAY : climate::CLIMATE_PRESET_HOME);
if (msg.has_fan_mode)
call.set_fan_mode(static_cast<climate::ClimateFanMode>(msg.fan_mode));
if (msg.has_custom_fan_mode)
@@ -564,13 +603,157 @@ void APIConnection::climate_command(const ClimateCommandRequest &msg) {
}
#endif
#ifdef USE_NUMBER
bool APIConnection::send_number_state(number::Number *number, float state) {
if (!this->state_subscription_)
return false;
NumberStateResponse resp{};
resp.key = number->get_object_id_hash();
resp.state = state;
resp.missing_state = !number->has_state();
return this->send_number_state_response(resp);
}
bool APIConnection::send_number_info(number::Number *number) {
ListEntitiesNumberResponse msg;
msg.key = number->get_object_id_hash();
msg.object_id = number->get_object_id();
msg.name = number->get_name();
msg.unique_id = get_default_unique_id("number", number);
msg.icon = number->get_icon();
msg.disabled_by_default = number->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(number->get_entity_category());
msg.unit_of_measurement = number->traits.get_unit_of_measurement();
msg.mode = static_cast<enums::NumberMode>(number->traits.get_mode());
msg.min_value = number->traits.get_min_value();
msg.max_value = number->traits.get_max_value();
msg.step = number->traits.get_step();
return this->send_list_entities_number_response(msg);
}
void APIConnection::number_command(const NumberCommandRequest &msg) {
number::Number *number = App.get_number_by_key(msg.key);
if (number == nullptr)
return;
auto call = number->make_call();
call.set_value(msg.state);
call.perform();
}
#endif
#ifdef USE_SELECT
bool APIConnection::send_select_state(select::Select *select, std::string state) {
if (!this->state_subscription_)
return false;
SelectStateResponse resp{};
resp.key = select->get_object_id_hash();
resp.state = std::move(state);
resp.missing_state = !select->has_state();
return this->send_select_state_response(resp);
}
bool APIConnection::send_select_info(select::Select *select) {
ListEntitiesSelectResponse msg;
msg.key = select->get_object_id_hash();
msg.object_id = select->get_object_id();
msg.name = select->get_name();
msg.unique_id = get_default_unique_id("select", select);
msg.icon = select->get_icon();
msg.disabled_by_default = select->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(select->get_entity_category());
for (const auto &option : select->traits.get_options())
msg.options.push_back(option);
return this->send_list_entities_select_response(msg);
}
void APIConnection::select_command(const SelectCommandRequest &msg) {
select::Select *select = App.get_select_by_key(msg.key);
if (select == nullptr)
return;
auto call = select->make_call();
call.set_option(msg.state);
call.perform();
}
#endif
#ifdef USE_BUTTON
bool APIConnection::send_button_info(button::Button *button) {
ListEntitiesButtonResponse msg;
msg.key = button->get_object_id_hash();
msg.object_id = button->get_object_id();
msg.name = button->get_name();
msg.unique_id = get_default_unique_id("button", button);
msg.icon = button->get_icon();
msg.disabled_by_default = button->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(button->get_entity_category());
msg.device_class = button->get_device_class();
return this->send_list_entities_button_response(msg);
}
void APIConnection::button_command(const ButtonCommandRequest &msg) {
button::Button *button = App.get_button_by_key(msg.key);
if (button == nullptr)
return;
button->press();
}
#endif
#ifdef USE_LOCK
bool APIConnection::send_lock_state(lock::Lock *a_lock, lock::LockState state) {
if (!this->state_subscription_)
return false;
LockStateResponse resp{};
resp.key = a_lock->get_object_id_hash();
resp.state = static_cast<enums::LockState>(state);
return this->send_lock_state_response(resp);
}
bool APIConnection::send_lock_info(lock::Lock *a_lock) {
ListEntitiesLockResponse msg;
msg.key = a_lock->get_object_id_hash();
msg.object_id = a_lock->get_object_id();
msg.name = a_lock->get_name();
msg.unique_id = get_default_unique_id("lock", a_lock);
msg.icon = a_lock->get_icon();
msg.assumed_state = a_lock->traits.get_assumed_state();
msg.disabled_by_default = a_lock->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(a_lock->get_entity_category());
msg.supports_open = a_lock->traits.get_supports_open();
msg.requires_code = a_lock->traits.get_requires_code();
return this->send_list_entities_lock_response(msg);
}
void APIConnection::lock_command(const LockCommandRequest &msg) {
lock::Lock *a_lock = App.get_lock_by_key(msg.key);
if (a_lock == nullptr)
return;
switch (msg.command) {
case enums::LOCK_UNLOCK:
a_lock->unlock();
break;
case enums::LOCK_LOCK:
a_lock->lock();
break;
case enums::LOCK_OPEN:
a_lock->open();
break;
}
}
#endif
#ifdef USE_ESP32_CAMERA
void APIConnection::send_camera_state(std::shared_ptr<esp32_camera::CameraImage> image) {
if (!this->state_subscription_)
return;
if (this->image_reader_.available())
return;
this->image_reader_.set_image(image);
if (image->was_requested_by(esphome::esp32_camera::API_REQUESTER) ||
image->was_requested_by(esphome::esp32_camera::IDLE))
this->image_reader_.set_image(std::move(image));
}
bool APIConnection::send_camera_info(esp32_camera::ESP32Camera *camera) {
ListEntitiesCameraResponse msg;
@@ -578,6 +761,9 @@ bool APIConnection::send_camera_info(esp32_camera::ESP32Camera *camera) {
msg.object_id = camera->get_object_id();
msg.name = camera->get_name();
msg.unique_id = get_default_unique_id("camera", camera);
msg.disabled_by_default = camera->is_disabled_by_default();
msg.icon = camera->get_icon();
msg.entity_category = static_cast<enums::EntityCategory>(camera->get_entity_category());
return this->send_list_entities_camera_response(msg);
}
void APIConnection::camera_image(const CameraImageRequest &msg) {
@@ -585,9 +771,14 @@ void APIConnection::camera_image(const CameraImageRequest &msg) {
return;
if (msg.single)
esp32_camera::global_esp32_camera->request_image();
if (msg.stream)
esp32_camera::global_esp32_camera->request_stream();
esp32_camera::global_esp32_camera->request_image(esphome::esp32_camera::API_REQUESTER);
if (msg.stream) {
esp32_camera::global_esp32_camera->start_stream(esphome::esp32_camera::API_REQUESTER);
App.scheduler.set_timeout(this->parent_, "api_esp32_camera_stop_stream", ESP32_CAMERA_STOP_STREAM, []() {
esp32_camera::global_esp32_camera->stop_stream(esphome::esp32_camera::API_REQUESTER);
});
}
}
#endif
@@ -606,31 +797,23 @@ bool APIConnection::send_log_message(int level, const char *tag, const char *lin
auto buffer = this->create_buffer();
// LogLevel level = 1;
buffer.encode_uint32(1, static_cast<uint32_t>(level));
// string tag = 2;
// buffer.encode_string(2, tag, strlen(tag));
// string message = 3;
buffer.encode_string(3, line, strlen(line));
// SubscribeLogsResponse - 29
bool success = this->send_buffer(buffer, 29);
if (!success) {
buffer = this->create_buffer();
// bool send_failed = 4;
buffer.encode_bool(4, true);
return this->send_buffer(buffer, 29);
} else {
return true;
}
return this->send_buffer(buffer, 29);
}
HelloResponse APIConnection::hello(const HelloRequest &msg) {
this->client_info_ = msg.client_info + " (" + this->client_->remoteIP().toString().c_str();
this->client_info_ += ")";
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());
HelloResponse resp;
resp.api_version_major = 1;
resp.api_version_minor = 4;
resp.api_version_minor = 6;
resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")";
resp.name = App.get_name();
this->connection_state_ = ConnectionState::CONNECTED;
return resp;
}
@@ -641,7 +824,7 @@ ConnectResponse APIConnection::connect(const ConnectRequest &msg) {
// bool invalid_password = 1;
resp.invalid_password = !correct;
if (correct) {
ESP_LOGD(TAG, "Client '%s' connected successfully!", this->client_info_.c_str());
ESP_LOGD(TAG, "%s: Connected successfully", this->client_info_.c_str());
this->connection_state_ = ConnectionState::AUTHENTICATED;
#ifdef USE_HOMEASSISTANT_TIME
@@ -659,23 +842,25 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
resp.mac_address = get_mac_address_pretty();
resp.esphome_version = ESPHOME_VERSION;
resp.compilation_time = App.get_compilation_time();
#ifdef ARDUINO_BOARD
resp.model = ARDUINO_BOARD;
#endif
resp.model = ESPHOME_BOARD;
#ifdef USE_DEEP_SLEEP
resp.has_deep_sleep = deep_sleep::global_has_deep_sleep;
#endif
#ifdef ESPHOME_PROJECT_NAME
resp.project_name = ESPHOME_PROJECT_NAME;
resp.project_version = ESPHOME_PROJECT_VERSION;
#endif
#ifdef USE_WEBSERVER
resp.webserver_port = USE_WEBSERVER_PORT;
#endif
return resp;
}
void APIConnection::on_home_assistant_state_response(const HomeAssistantStateResponse &msg) {
for (auto &it : this->parent_->get_state_subs())
for (auto &it : this->parent_->get_state_subs()) {
if (it.entity_id == msg.entity_id && it.attribute.value() == msg.attribute) {
it.callback(msg.state);
}
}
}
void APIConnection::execute_service(const ExecuteServiceRequest &msg) {
bool found = false;
@@ -689,30 +874,20 @@ void APIConnection::execute_service(const ExecuteServiceRequest &msg) {
}
}
void APIConnection::subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) {
for (auto &it : this->parent_->get_state_subs()) {
SubscribeHomeAssistantStateResponse resp;
resp.entity_id = it.entity_id;
resp.attribute = it.attribute.value();
if (!this->send_subscribe_home_assistant_state_response(resp)) {
this->on_fatal_error();
return;
}
}
state_subs_at_ = 0;
}
bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) {
if (this->remove_)
return false;
std::vector<uint8_t> header;
header.push_back(0x00);
ProtoVarInt(buffer.get_buffer()->size()).encode(header);
ProtoVarInt(message_type).encode(header);
size_t needed_space = buffer.get_buffer()->size() + header.size();
if (needed_space > this->client_->space()) {
if (!this->helper_->can_write_without_blocking()) {
delay(0);
if (needed_space > this->client_->space()) {
APIError err = helper_->loop();
if (err != APIError::OK) {
on_fatal_error();
ESP_LOGW(TAG, "%s: Socket operation failed: %s errno=%d", client_info_.c_str(), api_error_to_str(err), errno);
return false;
}
if (!this->helper_->can_write_without_blocking()) {
// SubscribeLogsResponse
if (message_type != 29) {
ESP_LOGV(TAG, "Cannot send message because of TCP buffer space");
@@ -722,24 +897,31 @@ bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint32_t message_type)
}
}
this->client_->add(reinterpret_cast<char *>(header.data()), header.size(),
ASYNC_WRITE_FLAG_COPY | ASYNC_WRITE_FLAG_MORE);
this->client_->add(reinterpret_cast<char *>(buffer.get_buffer()->data()), buffer.get_buffer()->size(),
ASYNC_WRITE_FLAG_COPY);
bool ret = this->client_->send();
return ret;
APIError err = this->helper_->write_packet(message_type, buffer.get_buffer()->data(), buffer.get_buffer()->size());
if (err == APIError::WOULD_BLOCK)
return false;
if (err != APIError::OK) {
on_fatal_error();
if (err == APIError::SOCKET_WRITE_FAILED && errno == ECONNRESET) {
ESP_LOGW(TAG, "%s: Connection reset", client_info_.c_str());
} else {
ESP_LOGW(TAG, "%s: Packet write failed %s errno=%d", client_info_.c_str(), api_error_to_str(err), errno);
}
return false;
}
// Do not set last_traffic_ on send
return true;
}
void APIConnection::on_unauthenticated_access() {
ESP_LOGD(TAG, "'%s' tried to access without authentication.", this->client_info_.c_str());
this->on_fatal_error();
ESP_LOGD(TAG, "%s: tried to access without authentication.", this->client_info_.c_str());
}
void APIConnection::on_no_setup_connection() {
ESP_LOGD(TAG, "'%s' tried to access without full connection.", this->client_info_.c_str());
this->on_fatal_error();
ESP_LOGD(TAG, "%s: tried to access without full connection.", this->client_info_.c_str());
}
void APIConnection::on_fatal_error() {
ESP_LOGV(TAG, "Error: Disconnecting %s", this->client_info_.c_str());
this->client_->close();
this->helper_->close();
this->remove_ = true;
}

View File

@@ -5,16 +5,17 @@
#include "api_pb2.h"
#include "api_pb2_service.h"
#include "api_server.h"
#include "api_frame_helper.h"
namespace esphome {
namespace api {
class APIConnection : public APIServerConnection {
public:
APIConnection(AsyncClient *client, APIServer *parent);
virtual ~APIConnection();
APIConnection(std::unique_ptr<socket::Socket> socket, APIServer *parent);
virtual ~APIConnection() = default;
void disconnect_client();
void start();
void loop();
bool send_list_info_done() {
@@ -31,8 +32,8 @@ class APIConnection : public APIServerConnection {
void cover_command(const CoverCommandRequest &msg) override;
#endif
#ifdef USE_FAN
bool send_fan_state(fan::FanState *fan);
bool send_fan_info(fan::FanState *fan);
bool send_fan_state(fan::Fan *fan);
bool send_fan_info(fan::Fan *fan);
void fan_command(const FanCommandRequest &msg) override;
#endif
#ifdef USE_LIGHT
@@ -62,6 +63,25 @@ class APIConnection : public APIServerConnection {
bool send_climate_state(climate::Climate *climate);
bool send_climate_info(climate::Climate *climate);
void climate_command(const ClimateCommandRequest &msg) override;
#endif
#ifdef USE_NUMBER
bool send_number_state(number::Number *number, float state);
bool send_number_info(number::Number *number);
void number_command(const NumberCommandRequest &msg) override;
#endif
#ifdef USE_SELECT
bool send_select_state(select::Select *select, std::string state);
bool send_select_info(select::Select *select);
void select_command(const SelectCommandRequest &msg) override;
#endif
#ifdef USE_BUTTON
bool send_button_info(button::Button *button);
void button_command(const ButtonCommandRequest &msg) override;
#endif
#ifdef USE_LOCK
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
bool send_log_message(int level, const char *tag, const char *line);
void send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
@@ -76,10 +96,7 @@ class APIConnection : public APIServerConnection {
}
#endif
void on_disconnect_response(const DisconnectResponse &value) override {
// we initiated disconnect_client
this->next_close_ = true;
}
void on_disconnect_response(const DisconnectResponse &value) override;
void on_ping_response(const PingResponse &value) override {
// we initiated ping
this->sent_ping_ = false;
@@ -90,12 +107,7 @@ class APIConnection : public APIServerConnection {
#endif
HelloResponse hello(const HelloRequest &msg) override;
ConnectResponse connect(const ConnectRequest &msg) override;
DisconnectResponse disconnect(const DisconnectRequest &msg) override {
// remote initiated disconnect_client
this->next_close_ = true;
DisconnectResponse resp;
return resp;
}
DisconnectResponse disconnect(const DisconnectRequest &msg) override;
PingResponse ping(const PingRequest &msg) override { return {}; }
DeviceInfoResponse device_info(const DeviceInfoRequest &msg) override;
void list_entities(const ListEntitiesRequest &msg) override { this->list_entities_iterator_.begin(); }
@@ -125,19 +137,16 @@ class APIConnection : public APIServerConnection {
void on_unauthenticated_access() override;
void on_no_setup_connection() override;
ProtoWriteBuffer create_buffer() override {
this->send_buffer_.clear();
return {&this->send_buffer_};
// FIXME: ensure no recursive writes can happen
this->proto_write_buffer_.clear();
return {&this->proto_write_buffer_};
}
bool send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) override;
protected:
friend APIServer;
void on_error_(int8_t error);
void on_disconnect_();
void on_timeout_(uint32_t time);
void on_data_(uint8_t *buf, size_t len);
void parse_recv_buffer_();
bool send_(const void *buf, size_t len, bool force);
enum class ConnectionState {
WAITING_FOR_HELLO,
@@ -147,8 +156,10 @@ class APIConnection : public APIServerConnection {
bool remove_{false};
std::vector<uint8_t> send_buffer_;
std::vector<uint8_t> recv_buffer_;
// Buffer used to encode proto messages
// Re-use to prevent allocations
std::vector<uint8_t> proto_write_buffer_;
std::unique_ptr<APIFrameHelper> helper_;
std::string client_info_;
#ifdef USE_ESP32_CAMERA
@@ -160,12 +171,11 @@ class APIConnection : public APIServerConnection {
uint32_t last_traffic_;
bool sent_ping_{false};
bool service_call_subscription_{false};
bool current_nodelay_{false};
bool next_close_{false};
AsyncClient *client_;
bool next_close_ = false;
APIServer *parent_;
InitialStateIterator initial_state_iterator_;
ListEntitiesIterator list_entities_iterator_;
int state_subs_at_ = -1;
};
} // namespace api

View File

@@ -0,0 +1,1024 @@
#include "api_frame_helper.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "esphome/core/application.h"
#include "proto.h"
#include <cstring>
namespace esphome {
namespace api {
static const char *const TAG = "api.socket";
/// Is the given return value (from write syscalls) a wouldblock error?
bool is_would_block(ssize_t ret) {
if (ret == -1) {
return errno == EWOULDBLOCK || errno == EAGAIN;
}
return ret == 0;
}
const char *api_error_to_str(APIError err) {
// not using switch to ensure compiler doesn't try to build a big table out of it
if (err == APIError::OK) {
return "OK";
} else if (err == APIError::WOULD_BLOCK) {
return "WOULD_BLOCK";
} else if (err == APIError::BAD_HANDSHAKE_PACKET_LEN) {
return "BAD_HANDSHAKE_PACKET_LEN";
} else if (err == APIError::BAD_INDICATOR) {
return "BAD_INDICATOR";
} else if (err == APIError::BAD_DATA_PACKET) {
return "BAD_DATA_PACKET";
} else if (err == APIError::TCP_NODELAY_FAILED) {
return "TCP_NODELAY_FAILED";
} else if (err == APIError::TCP_NONBLOCKING_FAILED) {
return "TCP_NONBLOCKING_FAILED";
} else if (err == APIError::CLOSE_FAILED) {
return "CLOSE_FAILED";
} else if (err == APIError::SHUTDOWN_FAILED) {
return "SHUTDOWN_FAILED";
} else if (err == APIError::BAD_STATE) {
return "BAD_STATE";
} else if (err == APIError::BAD_ARG) {
return "BAD_ARG";
} else if (err == APIError::SOCKET_READ_FAILED) {
return "SOCKET_READ_FAILED";
} else if (err == APIError::SOCKET_WRITE_FAILED) {
return "SOCKET_WRITE_FAILED";
} else if (err == APIError::HANDSHAKESTATE_READ_FAILED) {
return "HANDSHAKESTATE_READ_FAILED";
} else if (err == APIError::HANDSHAKESTATE_WRITE_FAILED) {
return "HANDSHAKESTATE_WRITE_FAILED";
} else if (err == APIError::HANDSHAKESTATE_BAD_STATE) {
return "HANDSHAKESTATE_BAD_STATE";
} else if (err == APIError::CIPHERSTATE_DECRYPT_FAILED) {
return "CIPHERSTATE_DECRYPT_FAILED";
} else if (err == APIError::CIPHERSTATE_ENCRYPT_FAILED) {
return "CIPHERSTATE_ENCRYPT_FAILED";
} else if (err == APIError::OUT_OF_MEMORY) {
return "OUT_OF_MEMORY";
} else if (err == APIError::HANDSHAKESTATE_SETUP_FAILED) {
return "HANDSHAKESTATE_SETUP_FAILED";
} else if (err == APIError::HANDSHAKESTATE_SPLIT_FAILED) {
return "HANDSHAKESTATE_SPLIT_FAILED";
} else if (err == APIError::BAD_HANDSHAKE_ERROR_BYTE) {
return "BAD_HANDSHAKE_ERROR_BYTE";
} else if (err == APIError::CONNECTION_CLOSED) {
return "CONNECTION_CLOSED";
}
return "UNKNOWN";
}
#define HELPER_LOG(msg, ...) ESP_LOGVV(TAG, "%s: " msg, info_.c_str(), ##__VA_ARGS__)
// uncomment to log raw packets
//#define HELPER_LOG_PACKETS
#ifdef USE_API_NOISE
static const char *const PROLOGUE_INIT = "NoiseAPIInit";
/// Convert a noise error code to a readable error
std::string noise_err_to_str(int err) {
if (err == NOISE_ERROR_NO_MEMORY)
return "NO_MEMORY";
if (err == NOISE_ERROR_UNKNOWN_ID)
return "UNKNOWN_ID";
if (err == NOISE_ERROR_UNKNOWN_NAME)
return "UNKNOWN_NAME";
if (err == NOISE_ERROR_MAC_FAILURE)
return "MAC_FAILURE";
if (err == NOISE_ERROR_NOT_APPLICABLE)
return "NOT_APPLICABLE";
if (err == NOISE_ERROR_SYSTEM)
return "SYSTEM";
if (err == NOISE_ERROR_REMOTE_KEY_REQUIRED)
return "REMOTE_KEY_REQUIRED";
if (err == NOISE_ERROR_LOCAL_KEY_REQUIRED)
return "LOCAL_KEY_REQUIRED";
if (err == NOISE_ERROR_PSK_REQUIRED)
return "PSK_REQUIRED";
if (err == NOISE_ERROR_INVALID_LENGTH)
return "INVALID_LENGTH";
if (err == NOISE_ERROR_INVALID_PARAM)
return "INVALID_PARAM";
if (err == NOISE_ERROR_INVALID_STATE)
return "INVALID_STATE";
if (err == NOISE_ERROR_INVALID_NONCE)
return "INVALID_NONCE";
if (err == NOISE_ERROR_INVALID_PRIVATE_KEY)
return "INVALID_PRIVATE_KEY";
if (err == NOISE_ERROR_INVALID_PUBLIC_KEY)
return "INVALID_PUBLIC_KEY";
if (err == NOISE_ERROR_INVALID_FORMAT)
return "INVALID_FORMAT";
if (err == NOISE_ERROR_INVALID_SIGNATURE)
return "INVALID_SIGNATURE";
return to_string(err);
}
/// Initialize the frame helper, returns OK if successful.
APIError APINoiseFrameHelper::init() {
if (state_ != State::INITIALIZE || socket_ == nullptr) {
HELPER_LOG("Bad state for init %d", (int) state_);
return APIError::BAD_STATE;
}
int err = socket_->setblocking(false);
if (err != 0) {
state_ = State::FAILED;
HELPER_LOG("Setting nonblocking failed with errno %d", errno);
return APIError::TCP_NONBLOCKING_FAILED;
}
int enable = 1;
err = socket_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int));
if (err != 0) {
state_ = State::FAILED;
HELPER_LOG("Setting nodelay failed with errno %d", errno);
return APIError::TCP_NODELAY_FAILED;
}
// init prologue
prologue_.insert(prologue_.end(), PROLOGUE_INIT, PROLOGUE_INIT + strlen(PROLOGUE_INIT));
state_ = State::CLIENT_HELLO;
return APIError::OK;
}
/// Run through handshake messages (if in that phase)
APIError APINoiseFrameHelper::loop() {
APIError err = state_action_();
if (err == APIError::WOULD_BLOCK)
return APIError::OK;
if (err != APIError::OK)
return err;
if (!tx_buf_.empty()) {
err = try_send_tx_buf_();
if (err != APIError::OK) {
return err;
}
}
return APIError::OK;
}
/** Read a packet into the rx_buf_. If successful, stores frame data in the frame parameter
*
* @param frame: The struct to hold the frame information in.
* msg_start: points to the start of the payload - this pointer is only valid until the next
* try_receive_raw_ call
*
* @return 0 if a full packet is in rx_buf_
* @return -1 if error, check errno.
*
* errno EWOULDBLOCK: Packet could not be read without blocking. Try again later.
* errno ENOMEM: Not enough memory for reading packet.
* errno API_ERROR_BAD_INDICATOR: Bad indicator byte at start of frame.
* errno API_ERROR_HANDSHAKE_PACKET_LEN: Packet too big for this phase.
*/
APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
if (frame == nullptr) {
HELPER_LOG("Bad argument for try_read_frame_");
return APIError::BAD_ARG;
}
// read header
if (rx_header_buf_len_ < 3) {
// no header information yet
size_t to_read = 3 - rx_header_buf_len_;
ssize_t received = socket_->read(&rx_header_buf_[rx_header_buf_len_], to_read);
if (received == -1) {
if (errno == EWOULDBLOCK || errno == EAGAIN) {
return APIError::WOULD_BLOCK;
}
state_ = State::FAILED;
HELPER_LOG("Socket read failed with errno %d", errno);
return APIError::SOCKET_READ_FAILED;
} else if (received == 0) {
state_ = State::FAILED;
HELPER_LOG("Connection closed");
return APIError::CONNECTION_CLOSED;
}
rx_header_buf_len_ += received;
if ((size_t) received != to_read) {
// not a full read
return APIError::WOULD_BLOCK;
}
// header reading done
}
// read body
uint8_t indicator = rx_header_buf_[0];
if (indicator != 0x01) {
state_ = State::FAILED;
HELPER_LOG("Bad indicator byte %u", indicator);
return APIError::BAD_INDICATOR;
}
uint16_t msg_size = (((uint16_t) rx_header_buf_[1]) << 8) | rx_header_buf_[2];
if (state_ != State::DATA && msg_size > 128) {
// for handshake message only permit up to 128 bytes
state_ = State::FAILED;
HELPER_LOG("Bad packet len for handshake: %d", msg_size);
return APIError::BAD_HANDSHAKE_PACKET_LEN;
}
// reserve space for body
if (rx_buf_.size() != msg_size) {
rx_buf_.resize(msg_size);
}
if (rx_buf_len_ < msg_size) {
// more data to read
size_t to_read = msg_size - rx_buf_len_;
ssize_t received = socket_->read(&rx_buf_[rx_buf_len_], to_read);
if (received == -1) {
if (errno == EWOULDBLOCK || errno == EAGAIN) {
return APIError::WOULD_BLOCK;
}
state_ = State::FAILED;
HELPER_LOG("Socket read failed with errno %d", errno);
return APIError::SOCKET_READ_FAILED;
} else if (received == 0) {
state_ = State::FAILED;
HELPER_LOG("Connection closed");
return APIError::CONNECTION_CLOSED;
}
rx_buf_len_ += received;
if ((size_t) received != to_read) {
// not all read
return APIError::WOULD_BLOCK;
}
}
// uncomment for even more debugging
#ifdef HELPER_LOG_PACKETS
ESP_LOGVV(TAG, "Received frame: %s", format_hex_pretty(rx_buf_).c_str());
#endif
frame->msg = std::move(rx_buf_);
// consume msg
rx_buf_ = {};
rx_buf_len_ = 0;
rx_header_buf_len_ = 0;
return APIError::OK;
}
/** To be called from read/write methods.
*
* This method runs through the internal handshake methods, if in that state.
*
* 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
* traffic.
*/
APIError APINoiseFrameHelper::state_action_() {
int err;
APIError aerr;
if (state_ == State::INITIALIZE) {
HELPER_LOG("Bad state for method: %d", (int) state_);
return APIError::BAD_STATE;
}
if (state_ == State::CLIENT_HELLO) {
// waiting for client hello
ParsedFrame frame;
aerr = try_read_frame_(&frame);
if (aerr == APIError::BAD_INDICATOR) {
send_explicit_handshake_reject_("Bad indicator byte");
return aerr;
}
if (aerr == APIError::BAD_HANDSHAKE_PACKET_LEN) {
send_explicit_handshake_reject_("Bad handshake packet len");
return aerr;
}
if (aerr != APIError::OK)
return aerr;
// ignore contents, may be used in future for flags
prologue_.push_back((uint8_t)(frame.msg.size() >> 8));
prologue_.push_back((uint8_t) frame.msg.size());
prologue_.insert(prologue_.end(), frame.msg.begin(), frame.msg.end());
state_ = State::SERVER_HELLO;
}
if (state_ == State::SERVER_HELLO) {
// send server hello
std::vector<uint8_t> msg;
// chosen proto
msg.push_back(0x01);
// node name, terminated by null byte
const std::string &name = App.get_name();
const uint8_t *name_ptr = reinterpret_cast<const uint8_t *>(name.c_str());
msg.insert(msg.end(), name_ptr, name_ptr + name.size() + 1);
aerr = write_frame_(msg.data(), msg.size());
if (aerr != APIError::OK)
return aerr;
// start handshake
aerr = init_handshake_();
if (aerr != APIError::OK)
return aerr;
state_ = State::HANDSHAKE;
}
if (state_ == State::HANDSHAKE) {
int action = noise_handshakestate_get_action(handshake_);
if (action == NOISE_ACTION_READ_MESSAGE) {
// waiting for handshake msg
ParsedFrame frame;
aerr = try_read_frame_(&frame);
if (aerr == APIError::BAD_INDICATOR) {
send_explicit_handshake_reject_("Bad indicator byte");
return aerr;
}
if (aerr == APIError::BAD_HANDSHAKE_PACKET_LEN) {
send_explicit_handshake_reject_("Bad handshake packet len");
return aerr;
}
if (aerr != APIError::OK)
return aerr;
if (frame.msg.empty()) {
send_explicit_handshake_reject_("Empty handshake message");
return APIError::BAD_HANDSHAKE_ERROR_BYTE;
} else if (frame.msg[0] != 0x00) {
HELPER_LOG("Bad handshake error byte: %u", frame.msg[0]);
send_explicit_handshake_reject_("Bad handshake error byte");
return APIError::BAD_HANDSHAKE_ERROR_BYTE;
}
NoiseBuffer mbuf;
noise_buffer_init(mbuf);
noise_buffer_set_input(mbuf, frame.msg.data() + 1, frame.msg.size() - 1);
err = noise_handshakestate_read_message(handshake_, &mbuf, nullptr);
if (err != 0) {
state_ = State::FAILED;
HELPER_LOG("noise_handshakestate_read_message failed: %s", noise_err_to_str(err).c_str());
if (err == NOISE_ERROR_MAC_FAILURE) {
send_explicit_handshake_reject_("Handshake MAC failure");
} else {
send_explicit_handshake_reject_("Handshake error");
}
return APIError::HANDSHAKESTATE_READ_FAILED;
}
aerr = check_handshake_finished_();
if (aerr != APIError::OK)
return aerr;
} else if (action == NOISE_ACTION_WRITE_MESSAGE) {
uint8_t buffer[65];
NoiseBuffer mbuf;
noise_buffer_init(mbuf);
noise_buffer_set_output(mbuf, buffer + 1, sizeof(buffer) - 1);
err = noise_handshakestate_write_message(handshake_, &mbuf, nullptr);
if (err != 0) {
state_ = State::FAILED;
HELPER_LOG("noise_handshakestate_write_message failed: %s", noise_err_to_str(err).c_str());
return APIError::HANDSHAKESTATE_WRITE_FAILED;
}
buffer[0] = 0x00; // success
aerr = write_frame_(buffer, mbuf.size + 1);
if (aerr != APIError::OK)
return aerr;
aerr = check_handshake_finished_();
if (aerr != APIError::OK)
return aerr;
} else {
// bad state for action
state_ = State::FAILED;
HELPER_LOG("Bad action for handshake: %d", action);
return APIError::HANDSHAKESTATE_BAD_STATE;
}
}
if (state_ == State::CLOSED || state_ == State::FAILED) {
return APIError::BAD_STATE;
}
return APIError::OK;
}
void APINoiseFrameHelper::send_explicit_handshake_reject_(const std::string &reason) {
std::vector<uint8_t> data;
data.resize(reason.length() + 1);
data[0] = 0x01; // failure
for (size_t i = 0; i < reason.length(); i++) {
data[i + 1] = (uint8_t) reason[i];
}
// temporarily remove failed state
auto orig_state = state_;
state_ = State::EXPLICIT_REJECT;
write_frame_(data.data(), data.size());
state_ = orig_state;
}
APIError APINoiseFrameHelper::read_packet(ReadPacketBuffer *buffer) {
int err;
APIError aerr;
aerr = state_action_();
if (aerr != APIError::OK) {
return aerr;
}
if (state_ != State::DATA) {
return APIError::WOULD_BLOCK;
}
ParsedFrame frame;
aerr = try_read_frame_(&frame);
if (aerr != APIError::OK)
return aerr;
NoiseBuffer mbuf;
noise_buffer_init(mbuf);
noise_buffer_set_inout(mbuf, frame.msg.data(), frame.msg.size(), frame.msg.size());
err = noise_cipherstate_decrypt(recv_cipher_, &mbuf);
if (err != 0) {
state_ = State::FAILED;
HELPER_LOG("noise_cipherstate_decrypt failed: %s", noise_err_to_str(err).c_str());
return APIError::CIPHERSTATE_DECRYPT_FAILED;
}
size_t msg_size = mbuf.size;
uint8_t *msg_data = frame.msg.data();
if (msg_size < 4) {
state_ = State::FAILED;
HELPER_LOG("Bad data packet: size %d too short", msg_size);
return APIError::BAD_DATA_PACKET;
}
// uint16_t type;
// uint16_t data_len;
// uint8_t *data;
// uint8_t *padding; zero or more bytes to fill up the rest of the packet
uint16_t type = (((uint16_t) msg_data[0]) << 8) | msg_data[1];
uint16_t data_len = (((uint16_t) msg_data[2]) << 8) | msg_data[3];
if (data_len > msg_size - 4) {
state_ = State::FAILED;
HELPER_LOG("Bad data packet: data_len %u greater than msg_size %u", data_len, msg_size);
return APIError::BAD_DATA_PACKET;
}
buffer->container = std::move(frame.msg);
buffer->data_offset = 4;
buffer->data_len = data_len;
buffer->type = type;
return APIError::OK;
}
bool APINoiseFrameHelper::can_write_without_blocking() { return state_ == State::DATA && tx_buf_.empty(); }
APIError APINoiseFrameHelper::write_packet(uint16_t type, const uint8_t *payload, size_t payload_len) {
int err;
APIError aerr;
aerr = state_action_();
if (aerr != APIError::OK) {
return aerr;
}
if (state_ != State::DATA) {
return APIError::WOULD_BLOCK;
}
size_t padding = 0;
size_t msg_len = 4 + payload_len + padding;
size_t frame_len = 3 + msg_len + noise_cipherstate_get_mac_length(send_cipher_);
auto tmpbuf = std::unique_ptr<uint8_t[]>{new (std::nothrow) uint8_t[frame_len]};
if (tmpbuf == nullptr) {
HELPER_LOG("Could not allocate for writing packet");
return APIError::OUT_OF_MEMORY;
}
tmpbuf[0] = 0x01; // indicator
// tmpbuf[1], tmpbuf[2] to be set later
const uint8_t msg_offset = 3;
const uint8_t payload_offset = msg_offset + 4;
tmpbuf[msg_offset + 0] = (uint8_t)(type >> 8); // type
tmpbuf[msg_offset + 1] = (uint8_t) type;
tmpbuf[msg_offset + 2] = (uint8_t)(payload_len >> 8); // data_len
tmpbuf[msg_offset + 3] = (uint8_t) payload_len;
// copy data
std::copy(payload, payload + payload_len, &tmpbuf[payload_offset]);
// fill padding with zeros
std::fill(&tmpbuf[payload_offset + payload_len], &tmpbuf[frame_len], 0);
NoiseBuffer mbuf;
noise_buffer_init(mbuf);
noise_buffer_set_inout(mbuf, &tmpbuf[msg_offset], msg_len, frame_len - msg_offset);
err = noise_cipherstate_encrypt(send_cipher_, &mbuf);
if (err != 0) {
state_ = State::FAILED;
HELPER_LOG("noise_cipherstate_encrypt failed: %s", noise_err_to_str(err).c_str());
return APIError::CIPHERSTATE_ENCRYPT_FAILED;
}
size_t total_len = 3 + mbuf.size;
tmpbuf[1] = (uint8_t)(mbuf.size >> 8);
tmpbuf[2] = (uint8_t) mbuf.size;
struct iovec iov;
iov.iov_base = &tmpbuf[0];
iov.iov_len = total_len;
// write raw to not have two packets sent if NAGLE disabled
return write_raw_(&iov, 1);
}
APIError APINoiseFrameHelper::try_send_tx_buf_() {
// try send from tx_buf
while (state_ != State::CLOSED && !tx_buf_.empty()) {
ssize_t sent = socket_->write(tx_buf_.data(), tx_buf_.size());
if (sent == -1) {
if (errno == EWOULDBLOCK || errno == EAGAIN)
break;
state_ = State::FAILED;
HELPER_LOG("Socket write failed with errno %d", errno);
return APIError::SOCKET_WRITE_FAILED;
} else if (sent == 0) {
break;
}
// TODO: inefficient if multiple packets in txbuf
// replace with deque of buffers
tx_buf_.erase(tx_buf_.begin(), tx_buf_.begin() + sent);
}
return APIError::OK;
}
/** Write the data to the socket, or buffer it a write would block
*
* @param data The data to write
* @param len The length of data
*/
APIError APINoiseFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) {
if (iovcnt == 0)
return APIError::OK;
APIError aerr;
size_t total_write_len = 0;
for (int i = 0; i < iovcnt; i++) {
#ifdef HELPER_LOG_PACKETS
ESP_LOGVV(TAG, "Sending raw: %s",
format_hex_pretty(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len).c_str());
#endif
total_write_len += iov[i].iov_len;
}
if (!tx_buf_.empty()) {
// try to empty tx_buf_ first
aerr = try_send_tx_buf_();
if (aerr != APIError::OK && aerr != APIError::WOULD_BLOCK)
return aerr;
}
if (!tx_buf_.empty()) {
// tx buf not empty, can't write now because then stream would be inconsistent
for (int i = 0; i < iovcnt; i++) {
tx_buf_.insert(tx_buf_.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base),
reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
}
return APIError::OK;
}
ssize_t sent = socket_->writev(iov, iovcnt);
if (is_would_block(sent)) {
// operation would block, add buffer to tx_buf
for (int i = 0; i < iovcnt; i++) {
tx_buf_.insert(tx_buf_.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base),
reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
}
return APIError::OK;
} else if (sent == -1) {
// an error occured
state_ = State::FAILED;
HELPER_LOG("Socket write failed with errno %d", errno);
return APIError::SOCKET_WRITE_FAILED;
} else if ((size_t) sent != total_write_len) {
// partially sent, add end to tx_buf
size_t to_consume = sent;
for (int i = 0; i < iovcnt; i++) {
if (to_consume >= iov[i].iov_len) {
to_consume -= iov[i].iov_len;
} else {
tx_buf_.insert(tx_buf_.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base) + to_consume,
reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
to_consume = 0;
}
}
return APIError::OK;
}
// fully sent
return APIError::OK;
}
APIError APINoiseFrameHelper::write_frame_(const uint8_t *data, size_t len) {
uint8_t header[3];
header[0] = 0x01; // indicator
header[1] = (uint8_t)(len >> 8);
header[2] = (uint8_t) len;
struct iovec iov[2];
iov[0].iov_base = header;
iov[0].iov_len = 3;
iov[1].iov_base = const_cast<uint8_t *>(data);
iov[1].iov_len = len;
return write_raw_(iov, 2);
}
/** Initiate the data structures for the handshake.
*
* @return 0 on success, -1 on error (check errno)
*/
APIError APINoiseFrameHelper::init_handshake_() {
int err;
memset(&nid_, 0, sizeof(nid_));
// const char *proto = "Noise_NNpsk0_25519_ChaChaPoly_SHA256";
// err = noise_protocol_name_to_id(&nid_, proto, strlen(proto));
nid_.pattern_id = NOISE_PATTERN_NN;
nid_.cipher_id = NOISE_CIPHER_CHACHAPOLY;
nid_.dh_id = NOISE_DH_CURVE25519;
nid_.prefix_id = NOISE_PREFIX_STANDARD;
nid_.hybrid_id = NOISE_DH_NONE;
nid_.hash_id = NOISE_HASH_SHA256;
nid_.modifier_ids[0] = NOISE_MODIFIER_PSK0;
err = noise_handshakestate_new_by_id(&handshake_, &nid_, NOISE_ROLE_RESPONDER);
if (err != 0) {
state_ = State::FAILED;
HELPER_LOG("noise_handshakestate_new_by_id failed: %s", noise_err_to_str(err).c_str());
return APIError::HANDSHAKESTATE_SETUP_FAILED;
}
const auto &psk = ctx_->get_psk();
err = noise_handshakestate_set_pre_shared_key(handshake_, psk.data(), psk.size());
if (err != 0) {
state_ = State::FAILED;
HELPER_LOG("noise_handshakestate_set_pre_shared_key failed: %s", noise_err_to_str(err).c_str());
return APIError::HANDSHAKESTATE_SETUP_FAILED;
}
err = noise_handshakestate_set_prologue(handshake_, prologue_.data(), prologue_.size());
if (err != 0) {
state_ = State::FAILED;
HELPER_LOG("noise_handshakestate_set_prologue failed: %s", noise_err_to_str(err).c_str());
return APIError::HANDSHAKESTATE_SETUP_FAILED;
}
// set_prologue copies it into handshakestate, so we can get rid of it now
prologue_ = {};
err = noise_handshakestate_start(handshake_);
if (err != 0) {
state_ = State::FAILED;
HELPER_LOG("noise_handshakestate_start failed: %s", noise_err_to_str(err).c_str());
return APIError::HANDSHAKESTATE_SETUP_FAILED;
}
return APIError::OK;
}
APIError APINoiseFrameHelper::check_handshake_finished_() {
assert(state_ == State::HANDSHAKE);
int action = noise_handshakestate_get_action(handshake_);
if (action == NOISE_ACTION_READ_MESSAGE || action == NOISE_ACTION_WRITE_MESSAGE)
return APIError::OK;
if (action != NOISE_ACTION_SPLIT) {
state_ = State::FAILED;
HELPER_LOG("Bad action for handshake: %d", action);
return APIError::HANDSHAKESTATE_BAD_STATE;
}
int err = noise_handshakestate_split(handshake_, &send_cipher_, &recv_cipher_);
if (err != 0) {
state_ = State::FAILED;
HELPER_LOG("noise_handshakestate_split failed: %s", noise_err_to_str(err).c_str());
return APIError::HANDSHAKESTATE_SPLIT_FAILED;
}
HELPER_LOG("Handshake complete!");
noise_handshakestate_free(handshake_);
handshake_ = nullptr;
state_ = State::DATA;
return APIError::OK;
}
APINoiseFrameHelper::~APINoiseFrameHelper() {
if (handshake_ != nullptr) {
noise_handshakestate_free(handshake_);
handshake_ = nullptr;
}
if (send_cipher_ != nullptr) {
noise_cipherstate_free(send_cipher_);
send_cipher_ = nullptr;
}
if (recv_cipher_ != nullptr) {
noise_cipherstate_free(recv_cipher_);
recv_cipher_ = nullptr;
}
}
APIError APINoiseFrameHelper::close() {
state_ = State::CLOSED;
int err = socket_->close();
if (err == -1)
return APIError::CLOSE_FAILED;
return APIError::OK;
}
APIError APINoiseFrameHelper::shutdown(int how) {
int err = socket_->shutdown(how);
if (err == -1)
return APIError::SHUTDOWN_FAILED;
if (how == SHUT_RDWR) {
state_ = State::CLOSED;
}
return APIError::OK;
}
extern "C" {
// declare how noise generates random bytes (here with a good HWRNG based on the RF system)
void noise_rand_bytes(void *output, size_t len) {
if (!esphome::random_bytes(reinterpret_cast<uint8_t *>(output), len)) {
ESP_LOGE(TAG, "Failed to acquire random bytes, rebooting!");
arch_restart();
}
}
}
#endif // USE_API_NOISE
#ifdef USE_API_PLAINTEXT
/// Initialize the frame helper, returns OK if successful.
APIError APIPlaintextFrameHelper::init() {
if (state_ != State::INITIALIZE || socket_ == nullptr) {
HELPER_LOG("Bad state for init %d", (int) state_);
return APIError::BAD_STATE;
}
int err = socket_->setblocking(false);
if (err != 0) {
state_ = State::FAILED;
HELPER_LOG("Setting nonblocking failed with errno %d", errno);
return APIError::TCP_NONBLOCKING_FAILED;
}
int enable = 1;
err = socket_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int));
if (err != 0) {
state_ = State::FAILED;
HELPER_LOG("Setting nodelay failed with errno %d", errno);
return APIError::TCP_NODELAY_FAILED;
}
state_ = State::DATA;
return APIError::OK;
}
/// Not used for plaintext
APIError APIPlaintextFrameHelper::loop() {
if (state_ != State::DATA) {
return APIError::BAD_STATE;
}
// try send pending TX data
if (!tx_buf_.empty()) {
APIError err = try_send_tx_buf_();
if (err != APIError::OK) {
return err;
}
}
return APIError::OK;
}
/** Read a packet into the rx_buf_. If successful, stores frame data in the frame parameter
*
* @param frame: The struct to hold the frame information in.
* msg: store the parsed frame in that struct
*
* @return See APIError
*
* error API_ERROR_BAD_INDICATOR: Bad indicator byte at start of frame.
*/
APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
if (frame == nullptr) {
HELPER_LOG("Bad argument for try_read_frame_");
return APIError::BAD_ARG;
}
// read header
while (!rx_header_parsed_) {
uint8_t data;
ssize_t received = socket_->read(&data, 1);
if (received == -1) {
if (errno == EWOULDBLOCK || errno == EAGAIN) {
return APIError::WOULD_BLOCK;
}
state_ = State::FAILED;
HELPER_LOG("Socket read failed with errno %d", errno);
return APIError::SOCKET_READ_FAILED;
} else if (received == 0) {
state_ = State::FAILED;
HELPER_LOG("Connection closed");
return APIError::CONNECTION_CLOSED;
}
rx_header_buf_.push_back(data);
// try parse header
if (rx_header_buf_[0] != 0x00) {
state_ = State::FAILED;
HELPER_LOG("Bad indicator byte %u", rx_header_buf_[0]);
return APIError::BAD_INDICATOR;
}
size_t i = 1;
uint32_t consumed = 0;
auto msg_size_varint = ProtoVarInt::parse(&rx_header_buf_[i], rx_header_buf_.size() - i, &consumed);
if (!msg_size_varint.has_value()) {
// not enough data there yet
continue;
}
i += consumed;
rx_header_parsed_len_ = msg_size_varint->as_uint32();
auto msg_type_varint = ProtoVarInt::parse(&rx_header_buf_[i], rx_header_buf_.size() - i, &consumed);
if (!msg_type_varint.has_value()) {
// not enough data there yet
continue;
}
rx_header_parsed_type_ = msg_type_varint->as_uint32();
rx_header_parsed_ = true;
}
// header reading done
// reserve space for body
if (rx_buf_.size() != rx_header_parsed_len_) {
rx_buf_.resize(rx_header_parsed_len_);
}
if (rx_buf_len_ < rx_header_parsed_len_) {
// more data to read
size_t to_read = rx_header_parsed_len_ - rx_buf_len_;
ssize_t received = socket_->read(&rx_buf_[rx_buf_len_], to_read);
if (received == -1) {
if (errno == EWOULDBLOCK || errno == EAGAIN) {
return APIError::WOULD_BLOCK;
}
state_ = State::FAILED;
HELPER_LOG("Socket read failed with errno %d", errno);
return APIError::SOCKET_READ_FAILED;
} else if (received == 0) {
state_ = State::FAILED;
HELPER_LOG("Connection closed");
return APIError::CONNECTION_CLOSED;
}
rx_buf_len_ += received;
if ((size_t) received != to_read) {
// not all read
return APIError::WOULD_BLOCK;
}
}
// uncomment for even more debugging
#ifdef HELPER_LOG_PACKETS
ESP_LOGVV(TAG, "Received frame: %s", format_hex_pretty(rx_buf_).c_str());
#endif
frame->msg = std::move(rx_buf_);
// consume msg
rx_buf_ = {};
rx_buf_len_ = 0;
rx_header_buf_.clear();
rx_header_parsed_ = false;
return APIError::OK;
}
APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) {
APIError aerr;
if (state_ != State::DATA) {
return APIError::WOULD_BLOCK;
}
ParsedFrame frame;
aerr = try_read_frame_(&frame);
if (aerr != APIError::OK)
return aerr;
buffer->container = std::move(frame.msg);
buffer->data_offset = 0;
buffer->data_len = rx_header_parsed_len_;
buffer->type = rx_header_parsed_type_;
return APIError::OK;
}
bool APIPlaintextFrameHelper::can_write_without_blocking() { return state_ == State::DATA && tx_buf_.empty(); }
APIError APIPlaintextFrameHelper::write_packet(uint16_t type, const uint8_t *payload, size_t payload_len) {
if (state_ != State::DATA) {
return APIError::BAD_STATE;
}
std::vector<uint8_t> header;
header.push_back(0x00);
ProtoVarInt(payload_len).encode(header);
ProtoVarInt(type).encode(header);
struct iovec iov[2];
iov[0].iov_base = &header[0];
iov[0].iov_len = header.size();
iov[1].iov_base = const_cast<uint8_t *>(payload);
iov[1].iov_len = payload_len;
return write_raw_(iov, 2);
}
APIError APIPlaintextFrameHelper::try_send_tx_buf_() {
// try send from tx_buf
while (state_ != State::CLOSED && !tx_buf_.empty()) {
ssize_t sent = socket_->write(tx_buf_.data(), tx_buf_.size());
if (is_would_block(sent)) {
break;
} else if (sent == -1) {
state_ = State::FAILED;
HELPER_LOG("Socket write failed with errno %d", errno);
return APIError::SOCKET_WRITE_FAILED;
}
// TODO: inefficient if multiple packets in txbuf
// replace with deque of buffers
tx_buf_.erase(tx_buf_.begin(), tx_buf_.begin() + sent);
}
return APIError::OK;
}
/** Write the data to the socket, or buffer it a write would block
*
* @param data The data to write
* @param len The length of data
*/
APIError APIPlaintextFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) {
if (iovcnt == 0)
return APIError::OK;
APIError aerr;
size_t total_write_len = 0;
for (int i = 0; i < iovcnt; i++) {
#ifdef HELPER_LOG_PACKETS
ESP_LOGVV(TAG, "Sending raw: %s",
format_hex_pretty(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len).c_str());
#endif
total_write_len += iov[i].iov_len;
}
if (!tx_buf_.empty()) {
// try to empty tx_buf_ first
aerr = try_send_tx_buf_();
if (aerr != APIError::OK && aerr != APIError::WOULD_BLOCK)
return aerr;
}
if (!tx_buf_.empty()) {
// tx buf not empty, can't write now because then stream would be inconsistent
for (int i = 0; i < iovcnt; i++) {
tx_buf_.insert(tx_buf_.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base),
reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
}
return APIError::OK;
}
ssize_t sent = socket_->writev(iov, iovcnt);
if (is_would_block(sent)) {
// operation would block, add buffer to tx_buf
for (int i = 0; i < iovcnt; i++) {
tx_buf_.insert(tx_buf_.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base),
reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
}
return APIError::OK;
} else if (sent == -1) {
// an error occured
state_ = State::FAILED;
HELPER_LOG("Socket write failed with errno %d", errno);
return APIError::SOCKET_WRITE_FAILED;
} else if ((size_t) sent != total_write_len) {
// partially sent, add end to tx_buf
size_t to_consume = sent;
for (int i = 0; i < iovcnt; i++) {
if (to_consume >= iov[i].iov_len) {
to_consume -= iov[i].iov_len;
} else {
tx_buf_.insert(tx_buf_.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base) + to_consume,
reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
to_consume = 0;
}
}
return APIError::OK;
}
// fully sent
return APIError::OK;
}
APIError APIPlaintextFrameHelper::close() {
state_ = State::CLOSED;
int err = socket_->close();
if (err == -1)
return APIError::CLOSE_FAILED;
return APIError::OK;
}
APIError APIPlaintextFrameHelper::shutdown(int how) {
int err = socket_->shutdown(how);
if (err == -1)
return APIError::SHUTDOWN_FAILED;
if (how == SHUT_RDWR) {
state_ = State::CLOSED;
}
return APIError::OK;
}
#endif // USE_API_PLAINTEXT
} // namespace api
} // namespace esphome

View File

@@ -0,0 +1,185 @@
#pragma once
#include <cstdint>
#include <deque>
#include <utility>
#include <vector>
#include "esphome/core/defines.h"
#ifdef USE_API_NOISE
#include "noise/protocol.h"
#endif
#include "esphome/components/socket/socket.h"
#include "api_noise_context.h"
namespace esphome {
namespace api {
struct ReadPacketBuffer {
std::vector<uint8_t> container;
uint16_t type;
size_t data_offset;
size_t data_len;
};
struct PacketBuffer {
const std::vector<uint8_t> container;
uint16_t type;
uint8_t data_offset;
uint8_t data_len;
};
enum class APIError : int {
OK = 0,
WOULD_BLOCK = 1001,
BAD_HANDSHAKE_PACKET_LEN = 1002,
BAD_INDICATOR = 1003,
BAD_DATA_PACKET = 1004,
TCP_NODELAY_FAILED = 1005,
TCP_NONBLOCKING_FAILED = 1006,
CLOSE_FAILED = 1007,
SHUTDOWN_FAILED = 1008,
BAD_STATE = 1009,
BAD_ARG = 1010,
SOCKET_READ_FAILED = 1011,
SOCKET_WRITE_FAILED = 1012,
HANDSHAKESTATE_READ_FAILED = 1013,
HANDSHAKESTATE_WRITE_FAILED = 1014,
HANDSHAKESTATE_BAD_STATE = 1015,
CIPHERSTATE_DECRYPT_FAILED = 1016,
CIPHERSTATE_ENCRYPT_FAILED = 1017,
OUT_OF_MEMORY = 1018,
HANDSHAKESTATE_SETUP_FAILED = 1019,
HANDSHAKESTATE_SPLIT_FAILED = 1020,
BAD_HANDSHAKE_ERROR_BYTE = 1021,
CONNECTION_CLOSED = 1022,
};
const char *api_error_to_str(APIError err);
class APIFrameHelper {
public:
virtual ~APIFrameHelper() = default;
virtual APIError init() = 0;
virtual APIError loop() = 0;
virtual APIError read_packet(ReadPacketBuffer *buffer) = 0;
virtual bool can_write_without_blocking() = 0;
virtual APIError write_packet(uint16_t type, const uint8_t *data, size_t len) = 0;
virtual std::string getpeername() = 0;
virtual APIError close() = 0;
virtual APIError shutdown(int how) = 0;
// Give this helper a name for logging
virtual void set_log_info(std::string info) = 0;
};
#ifdef USE_API_NOISE
class APINoiseFrameHelper : public APIFrameHelper {
public:
APINoiseFrameHelper(std::unique_ptr<socket::Socket> socket, std::shared_ptr<APINoiseContext> ctx)
: socket_(std::move(socket)), ctx_(std::move(std::move(ctx))) {}
~APINoiseFrameHelper() override;
APIError init() override;
APIError loop() override;
APIError read_packet(ReadPacketBuffer *buffer) override;
bool can_write_without_blocking() override;
APIError write_packet(uint16_t type, const uint8_t *payload, size_t len) override;
std::string getpeername() override { return socket_->getpeername(); }
APIError close() override;
APIError shutdown(int how) override;
// Give this helper a name for logging
void set_log_info(std::string info) override { info_ = std::move(info); }
protected:
struct ParsedFrame {
std::vector<uint8_t> msg;
};
APIError state_action_();
APIError try_read_frame_(ParsedFrame *frame);
APIError try_send_tx_buf_();
APIError write_frame_(const uint8_t *data, size_t len);
APIError write_raw_(const struct iovec *iov, int iovcnt);
APIError init_handshake_();
APIError check_handshake_finished_();
void send_explicit_handshake_reject_(const std::string &reason);
std::unique_ptr<socket::Socket> socket_;
std::string info_;
uint8_t rx_header_buf_[3];
size_t rx_header_buf_len_ = 0;
std::vector<uint8_t> rx_buf_;
size_t rx_buf_len_ = 0;
std::vector<uint8_t> tx_buf_;
std::vector<uint8_t> prologue_;
std::shared_ptr<APINoiseContext> ctx_;
NoiseHandshakeState *handshake_ = nullptr;
NoiseCipherState *send_cipher_ = nullptr;
NoiseCipherState *recv_cipher_ = nullptr;
NoiseProtocolId nid_;
enum class State {
INITIALIZE = 1,
CLIENT_HELLO = 2,
SERVER_HELLO = 3,
HANDSHAKE = 4,
DATA = 5,
CLOSED = 6,
FAILED = 7,
EXPLICIT_REJECT = 8,
} state_ = State::INITIALIZE;
};
#endif // USE_API_NOISE
#ifdef USE_API_PLAINTEXT
class APIPlaintextFrameHelper : public APIFrameHelper {
public:
APIPlaintextFrameHelper(std::unique_ptr<socket::Socket> socket) : socket_(std::move(socket)) {}
~APIPlaintextFrameHelper() override = default;
APIError init() override;
APIError loop() override;
APIError read_packet(ReadPacketBuffer *buffer) override;
bool can_write_without_blocking() override;
APIError write_packet(uint16_t type, const uint8_t *payload, size_t len) override;
std::string getpeername() override { return socket_->getpeername(); }
APIError close() override;
APIError shutdown(int how) override;
// Give this helper a name for logging
void set_log_info(std::string info) override { info_ = std::move(info); }
protected:
struct ParsedFrame {
std::vector<uint8_t> msg;
};
APIError try_read_frame_(ParsedFrame *frame);
APIError try_send_tx_buf_();
APIError write_raw_(const struct iovec *iov, int iovcnt);
std::unique_ptr<socket::Socket> socket_;
std::string info_;
std::vector<uint8_t> rx_header_buf_;
bool rx_header_parsed_ = false;
uint32_t rx_header_parsed_type_ = 0;
uint32_t rx_header_parsed_len_ = 0;
std::vector<uint8_t> rx_buf_;
size_t rx_buf_len_ = 0;
std::vector<uint8_t> tx_buf_;
enum class State {
INITIALIZE = 1,
DATA = 2,
CLOSED = 3,
FAILED = 4,
} state_ = State::INITIALIZE;
};
#endif
} // namespace api
} // namespace esphome

View File

@@ -0,0 +1,23 @@
#pragma once
#include <cstdint>
#include <array>
#include "esphome/core/defines.h"
namespace esphome {
namespace api {
#ifdef USE_API_NOISE
using psk_t = std::array<uint8_t, 32>;
class APINoiseContext {
public:
void set_psk(psk_t psk) { psk_ = psk; }
const psk_t &get_psk() const { return psk_; }
protected:
psk_t psk_;
};
#endif // USE_API_NOISE
} // namespace api
} // namespace esphome

File diff suppressed because it is too large Load Diff

View File

@@ -9,6 +9,11 @@ namespace api {
namespace enums {
enum EntityCategory : uint32_t {
ENTITY_CATEGORY_NONE = 0,
ENTITY_CATEGORY_CONFIG = 1,
ENTITY_CATEGORY_DIAGNOSTIC = 2,
};
enum LegacyCoverState : uint32_t {
LEGACY_COVER_STATE_OPEN = 0,
LEGACY_COVER_STATE_CLOSED = 1,
@@ -32,18 +37,37 @@ enum FanDirection : uint32_t {
FAN_DIRECTION_FORWARD = 0,
FAN_DIRECTION_REVERSE = 1,
};
enum ColorMode : uint32_t {
COLOR_MODE_UNKNOWN = 0,
COLOR_MODE_ON_OFF = 1,
COLOR_MODE_BRIGHTNESS = 2,
COLOR_MODE_WHITE = 7,
COLOR_MODE_COLOR_TEMPERATURE = 11,
COLOR_MODE_COLD_WARM_WHITE = 19,
COLOR_MODE_RGB = 35,
COLOR_MODE_RGB_WHITE = 39,
COLOR_MODE_RGB_COLOR_TEMPERATURE = 47,
COLOR_MODE_RGB_COLD_WARM_WHITE = 51,
};
enum SensorStateClass : uint32_t {
STATE_CLASS_NONE = 0,
STATE_CLASS_MEASUREMENT = 1,
STATE_CLASS_TOTAL_INCREASING = 2,
};
enum SensorLastResetType : uint32_t {
LAST_RESET_NONE = 0,
LAST_RESET_NEVER = 1,
LAST_RESET_AUTO = 2,
};
enum LogLevel : uint32_t {
LOG_LEVEL_NONE = 0,
LOG_LEVEL_ERROR = 1,
LOG_LEVEL_WARN = 2,
LOG_LEVEL_INFO = 3,
LOG_LEVEL_DEBUG = 4,
LOG_LEVEL_VERBOSE = 5,
LOG_LEVEL_VERY_VERBOSE = 6,
LOG_LEVEL_CONFIG = 4,
LOG_LEVEL_DEBUG = 5,
LOG_LEVEL_VERBOSE = 6,
LOG_LEVEL_VERY_VERBOSE = 7,
};
enum ServiceArgType : uint32_t {
SERVICE_ARG_TYPE_BOOL = 0,
@@ -90,13 +114,32 @@ enum ClimateAction : uint32_t {
CLIMATE_ACTION_FAN = 6,
};
enum ClimatePreset : uint32_t {
CLIMATE_PRESET_ECO = 0,
CLIMATE_PRESET_AWAY = 1,
CLIMATE_PRESET_BOOST = 2,
CLIMATE_PRESET_COMFORT = 3,
CLIMATE_PRESET_HOME = 4,
CLIMATE_PRESET_SLEEP = 5,
CLIMATE_PRESET_ACTIVITY = 6,
CLIMATE_PRESET_NONE = 0,
CLIMATE_PRESET_HOME = 1,
CLIMATE_PRESET_AWAY = 2,
CLIMATE_PRESET_BOOST = 3,
CLIMATE_PRESET_COMFORT = 4,
CLIMATE_PRESET_ECO = 5,
CLIMATE_PRESET_SLEEP = 6,
CLIMATE_PRESET_ACTIVITY = 7,
};
enum NumberMode : uint32_t {
NUMBER_MODE_AUTO = 0,
NUMBER_MODE_BOX = 1,
NUMBER_MODE_SLIDER = 2,
};
enum LockState : uint32_t {
LOCK_STATE_NONE = 0,
LOCK_STATE_LOCKED = 1,
LOCK_STATE_UNLOCKED = 2,
LOCK_STATE_JAMMED = 3,
LOCK_STATE_LOCKING = 4,
LOCK_STATE_UNLOCKING = 5,
};
enum LockCommand : uint32_t {
LOCK_UNLOCK = 0,
LOCK_LOCK = 1,
LOCK_OPEN = 2,
};
} // namespace enums
@@ -105,7 +148,9 @@ class HelloRequest : public ProtoMessage {
public:
std::string client_info{};
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;
@@ -115,8 +160,11 @@ class HelloResponse : public ProtoMessage {
uint32_t api_version_major{0};
uint32_t api_version_minor{0};
std::string server_info{};
std::string name{};
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;
@@ -126,7 +174,9 @@ class ConnectRequest : public ProtoMessage {
public:
std::string password{};
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;
@@ -135,7 +185,9 @@ class ConnectResponse : public ProtoMessage {
public:
bool invalid_password{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;
@@ -143,35 +195,45 @@ class ConnectResponse : public ProtoMessage {
class DisconnectRequest : 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 DisconnectResponse : 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 PingRequest : 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 PingResponse : 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 DeviceInfoRequest : public ProtoMessage {
public:
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
};
@@ -186,8 +248,11 @@ class DeviceInfoResponse : public ProtoMessage {
bool has_deep_sleep{false};
std::string project_name{};
std::string project_version{};
uint32_t webserver_port{0};
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;
@@ -196,21 +261,27 @@ class DeviceInfoResponse : public ProtoMessage {
class ListEntitiesRequest : 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 ListEntitiesDoneResponse : 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 SubscribeStatesRequest : public ProtoMessage {
public:
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
};
@@ -222,8 +293,13 @@ class ListEntitiesBinarySensorResponse : public ProtoMessage {
std::string unique_id{};
std::string device_class{};
bool is_status_binary_sensor{false};
bool disabled_by_default{false};
std::string icon{};
enums::EntityCategory entity_category{};
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;
@@ -236,7 +312,9 @@ class BinarySensorStateResponse : public ProtoMessage {
bool state{false};
bool missing_state{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;
@@ -252,8 +330,13 @@ class ListEntitiesCoverResponse : public ProtoMessage {
bool supports_position{false};
bool supports_tilt{false};
std::string device_class{};
bool disabled_by_default{false};
std::string icon{};
enums::EntityCategory entity_category{};
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;
@@ -268,7 +351,9 @@ class CoverStateResponse : public ProtoMessage {
float tilt{0.0f};
enums::CoverOperation current_operation{};
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;
@@ -285,7 +370,9 @@ class CoverCommandRequest : public ProtoMessage {
float tilt{0.0f};
bool stop{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;
@@ -301,8 +388,13 @@ class ListEntitiesFanResponse : public ProtoMessage {
bool supports_speed{false};
bool supports_direction{false};
int32_t supported_speed_count{0};
bool disabled_by_default{false};
std::string icon{};
enums::EntityCategory entity_category{};
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;
@@ -318,7 +410,9 @@ class FanStateResponse : public ProtoMessage {
enums::FanDirection direction{};
int32_t speed_level{0};
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;
@@ -338,7 +432,9 @@ class FanCommandRequest : public ProtoMessage {
bool has_speed_level{false};
int32_t speed_level{0};
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;
@@ -350,15 +446,21 @@ class ListEntitiesLightResponse : public ProtoMessage {
uint32_t key{0};
std::string name{};
std::string unique_id{};
bool supports_brightness{false};
bool supports_rgb{false};
bool supports_white_value{false};
bool supports_color_temperature{false};
std::vector<enums::ColorMode> supported_color_modes{};
bool legacy_supports_brightness{false};
bool legacy_supports_rgb{false};
bool legacy_supports_white_value{false};
bool legacy_supports_color_temperature{false};
float min_mireds{0.0f};
float max_mireds{0.0f};
std::vector<std::string> effects{};
bool disabled_by_default{false};
std::string icon{};
enums::EntityCategory entity_category{};
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;
@@ -370,14 +472,20 @@ class LightStateResponse : public ProtoMessage {
uint32_t key{0};
bool state{false};
float brightness{0.0f};
enums::ColorMode color_mode{};
float color_brightness{0.0f};
float red{0.0f};
float green{0.0f};
float blue{0.0f};
float white{0.0f};
float color_temperature{0.0f};
float cold_white{0.0f};
float warm_white{0.0f};
std::string effect{};
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;
@@ -391,6 +499,10 @@ class LightCommandRequest : public ProtoMessage {
bool state{false};
bool has_brightness{false};
float brightness{0.0f};
bool has_color_mode{false};
enums::ColorMode color_mode{};
bool has_color_brightness{false};
float color_brightness{0.0f};
bool has_rgb{false};
float red{0.0f};
float green{0.0f};
@@ -399,6 +511,10 @@ class LightCommandRequest : public ProtoMessage {
float white{0.0f};
bool has_color_temperature{false};
float color_temperature{0.0f};
bool has_cold_white{false};
float cold_white{0.0f};
bool has_warm_white{false};
float warm_white{0.0f};
bool has_transition_length{false};
uint32_t transition_length{0};
bool has_flash_length{false};
@@ -406,7 +522,9 @@ class LightCommandRequest : public ProtoMessage {
bool has_effect{false};
std::string effect{};
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;
@@ -425,8 +543,13 @@ class ListEntitiesSensorResponse : public ProtoMessage {
bool force_update{false};
std::string device_class{};
enums::SensorStateClass state_class{};
enums::SensorLastResetType legacy_last_reset_type{};
bool disabled_by_default{false};
enums::EntityCategory entity_category{};
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;
@@ -439,7 +562,9 @@ class SensorStateResponse : public ProtoMessage {
float state{0.0f};
bool missing_state{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;
@@ -453,8 +578,13 @@ class ListEntitiesSwitchResponse : public ProtoMessage {
std::string unique_id{};
std::string icon{};
bool assumed_state{false};
bool disabled_by_default{false};
enums::EntityCategory entity_category{};
std::string device_class{};
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;
@@ -466,7 +596,9 @@ class SwitchStateResponse : public ProtoMessage {
uint32_t key{0};
bool state{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;
@@ -477,7 +609,9 @@ class SwitchCommandRequest : public ProtoMessage {
uint32_t key{0};
bool state{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;
@@ -490,12 +624,17 @@ class ListEntitiesTextSensorResponse : public ProtoMessage {
std::string name{};
std::string unique_id{};
std::string icon{};
bool disabled_by_default{false};
enums::EntityCategory entity_category{};
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 TextSensorStateResponse : public ProtoMessage {
public:
@@ -503,7 +642,9 @@ class TextSensorStateResponse : public ProtoMessage {
std::string state{};
bool missing_state{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;
@@ -515,7 +656,9 @@ class SubscribeLogsRequest : public ProtoMessage {
enums::LogLevel level{};
bool dump_config{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;
@@ -523,11 +666,12 @@ class SubscribeLogsRequest : public ProtoMessage {
class SubscribeLogsResponse : public ProtoMessage {
public:
enums::LogLevel level{};
std::string tag{};
std::string message{};
bool send_failed{false};
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;
@@ -536,7 +680,9 @@ class SubscribeLogsResponse : public ProtoMessage {
class SubscribeHomeassistantServicesRequest : public ProtoMessage {
public:
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
};
@@ -545,7 +691,9 @@ class HomeassistantServiceMap : public ProtoMessage {
std::string key{};
std::string value{};
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;
@@ -558,7 +706,9 @@ class HomeassistantServiceResponse : public ProtoMessage {
std::vector<HomeassistantServiceMap> variables{};
bool is_event{false};
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;
@@ -567,7 +717,9 @@ class HomeassistantServiceResponse : public ProtoMessage {
class SubscribeHomeAssistantStatesRequest : public ProtoMessage {
public:
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
};
@@ -576,7 +728,9 @@ class SubscribeHomeAssistantStateResponse : public ProtoMessage {
std::string entity_id{};
std::string attribute{};
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;
@@ -587,7 +741,9 @@ class HomeAssistantStateResponse : public ProtoMessage {
std::string state{};
std::string attribute{};
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;
@@ -595,7 +751,9 @@ class HomeAssistantStateResponse : public ProtoMessage {
class GetTimeRequest : public ProtoMessage {
public:
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
};
@@ -603,7 +761,9 @@ class GetTimeResponse : public ProtoMessage {
public:
uint32_t epoch_seconds{0};
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;
@@ -613,7 +773,9 @@ class ListEntitiesServicesArgument : public ProtoMessage {
std::string name{};
enums::ServiceArgType type{};
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;
@@ -625,7 +787,9 @@ class ListEntitiesServicesResponse : public ProtoMessage {
uint32_t key{0};
std::vector<ListEntitiesServicesArgument> args{};
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;
@@ -643,7 +807,9 @@ class ExecuteServiceArgument : public ProtoMessage {
std::vector<float> float_array{};
std::vector<std::string> string_array{};
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;
@@ -655,7 +821,9 @@ class ExecuteServiceRequest : public ProtoMessage {
uint32_t key{0};
std::vector<ExecuteServiceArgument> args{};
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;
@@ -667,12 +835,18 @@ class ListEntitiesCameraResponse : public ProtoMessage {
uint32_t key{0};
std::string name{};
std::string unique_id{};
bool disabled_by_default{false};
std::string icon{};
enums::EntityCategory entity_category{};
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 CameraImageResponse : public ProtoMessage {
public:
@@ -680,7 +854,9 @@ class CameraImageResponse : public ProtoMessage {
std::string data{};
bool done{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;
@@ -692,7 +868,9 @@ class CameraImageRequest : public ProtoMessage {
bool single{false};
bool stream{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;
@@ -709,15 +887,20 @@ class ListEntitiesClimateResponse : public ProtoMessage {
float visual_min_temperature{0.0f};
float visual_max_temperature{0.0f};
float visual_temperature_step{0.0f};
bool supports_away{false};
bool legacy_supports_away{false};
bool supports_action{false};
std::vector<enums::ClimateFanMode> supported_fan_modes{};
std::vector<enums::ClimateSwingMode> supported_swing_modes{};
std::vector<std::string> supported_custom_fan_modes{};
std::vector<enums::ClimatePreset> supported_presets{};
std::vector<std::string> supported_custom_presets{};
bool disabled_by_default{false};
std::string icon{};
enums::EntityCategory entity_category{};
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;
@@ -732,7 +915,7 @@ class ClimateStateResponse : public ProtoMessage {
float target_temperature{0.0f};
float target_temperature_low{0.0f};
float target_temperature_high{0.0f};
bool away{false};
bool legacy_away{false};
enums::ClimateAction action{};
enums::ClimateFanMode fan_mode{};
enums::ClimateSwingMode swing_mode{};
@@ -740,7 +923,9 @@ class ClimateStateResponse : public ProtoMessage {
enums::ClimatePreset preset{};
std::string custom_preset{};
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;
@@ -758,8 +943,8 @@ class ClimateCommandRequest : public ProtoMessage {
float target_temperature_low{0.0f};
bool has_target_temperature_high{false};
float target_temperature_high{0.0f};
bool has_away{false};
bool away{false};
bool has_legacy_away{false};
bool legacy_away{false};
bool has_fan_mode{false};
enums::ClimateFanMode fan_mode{};
bool has_swing_mode{false};
@@ -771,13 +956,196 @@ class ClimateCommandRequest : public ProtoMessage {
bool has_custom_preset{false};
std::string custom_preset{};
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 ListEntitiesNumberResponse : public ProtoMessage {
public:
std::string object_id{};
uint32_t key{0};
std::string name{};
std::string unique_id{};
std::string icon{};
float min_value{0.0f};
float max_value{0.0f};
float step{0.0f};
bool disabled_by_default{false};
enums::EntityCategory entity_category{};
std::string unit_of_measurement{};
enums::NumberMode mode{};
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 NumberStateResponse : public ProtoMessage {
public:
uint32_t key{0};
float state{0.0f};
bool missing_state{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 NumberCommandRequest : public ProtoMessage {
public:
uint32_t key{0};
float state{0.0f};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
};
class ListEntitiesSelectResponse : public ProtoMessage {
public:
std::string object_id{};
uint32_t key{0};
std::string name{};
std::string unique_id{};
std::string icon{};
std::vector<std::string> options{};
bool disabled_by_default{false};
enums::EntityCategory entity_category{};
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 SelectStateResponse : public ProtoMessage {
public:
uint32_t key{0};
std::string state{};
bool missing_state{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 SelectCommandRequest : public ProtoMessage {
public:
uint32_t key{0};
std::string state{};
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;
};
class ListEntitiesLockResponse : 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 assumed_state{false};
bool supports_open{false};
bool requires_code{false};
std::string code_format{};
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 LockStateResponse : public ProtoMessage {
public:
uint32_t key{0};
enums::LockState state{};
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 LockCommandRequest : public ProtoMessage {
public:
uint32_t key{0};
enums::LockCommand command{};
bool has_code{false};
std::string code{};
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 ListEntitiesButtonResponse : 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{};
std::string device_class{};
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 ButtonCommandRequest : public ProtoMessage {
public:
uint32_t key{0};
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;
};
} // namespace api
} // namespace esphome

View File

@@ -6,61 +6,85 @@
namespace esphome {
namespace api {
static const char *TAG = "api.service";
static const char *const TAG = "api.service";
bool APIServerConnectionBase::send_hello_response(const HelloResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_hello_response: %s", msg.dump().c_str());
#endif
return this->send_message_<HelloResponse>(msg, 2);
}
bool APIServerConnectionBase::send_connect_response(const ConnectResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_connect_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ConnectResponse>(msg, 4);
}
bool APIServerConnectionBase::send_disconnect_request(const DisconnectRequest &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_disconnect_request: %s", msg.dump().c_str());
#endif
return this->send_message_<DisconnectRequest>(msg, 5);
}
bool APIServerConnectionBase::send_disconnect_response(const DisconnectResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_disconnect_response: %s", msg.dump().c_str());
#endif
return this->send_message_<DisconnectResponse>(msg, 6);
}
bool APIServerConnectionBase::send_ping_request(const PingRequest &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_ping_request: %s", msg.dump().c_str());
#endif
return this->send_message_<PingRequest>(msg, 7);
}
bool APIServerConnectionBase::send_ping_response(const PingResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_ping_response: %s", msg.dump().c_str());
#endif
return this->send_message_<PingResponse>(msg, 8);
}
bool APIServerConnectionBase::send_device_info_response(const DeviceInfoResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_device_info_response: %s", msg.dump().c_str());
#endif
return this->send_message_<DeviceInfoResponse>(msg, 10);
}
bool APIServerConnectionBase::send_list_entities_done_response(const ListEntitiesDoneResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_done_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesDoneResponse>(msg, 19);
}
#ifdef USE_BINARY_SENSOR
bool APIServerConnectionBase::send_list_entities_binary_sensor_response(const ListEntitiesBinarySensorResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_binary_sensor_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesBinarySensorResponse>(msg, 12);
}
#endif
#ifdef USE_BINARY_SENSOR
bool APIServerConnectionBase::send_binary_sensor_state_response(const BinarySensorStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_binary_sensor_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BinarySensorStateResponse>(msg, 21);
}
#endif
#ifdef USE_COVER
bool APIServerConnectionBase::send_list_entities_cover_response(const ListEntitiesCoverResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_cover_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesCoverResponse>(msg, 13);
}
#endif
#ifdef USE_COVER
bool APIServerConnectionBase::send_cover_state_response(const CoverStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_cover_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<CoverStateResponse>(msg, 22);
}
#endif
@@ -68,13 +92,17 @@ bool APIServerConnectionBase::send_cover_state_response(const CoverStateResponse
#endif
#ifdef USE_FAN
bool APIServerConnectionBase::send_list_entities_fan_response(const ListEntitiesFanResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_fan_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesFanResponse>(msg, 14);
}
#endif
#ifdef USE_FAN
bool APIServerConnectionBase::send_fan_state_response(const FanStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_fan_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<FanStateResponse>(msg, 23);
}
#endif
@@ -82,13 +110,17 @@ bool APIServerConnectionBase::send_fan_state_response(const FanStateResponse &ms
#endif
#ifdef USE_LIGHT
bool APIServerConnectionBase::send_list_entities_light_response(const ListEntitiesLightResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_light_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesLightResponse>(msg, 15);
}
#endif
#ifdef USE_LIGHT
bool APIServerConnectionBase::send_light_state_response(const LightStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_light_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<LightStateResponse>(msg, 24);
}
#endif
@@ -96,25 +128,33 @@ bool APIServerConnectionBase::send_light_state_response(const LightStateResponse
#endif
#ifdef USE_SENSOR
bool APIServerConnectionBase::send_list_entities_sensor_response(const ListEntitiesSensorResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_sensor_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesSensorResponse>(msg, 16);
}
#endif
#ifdef USE_SENSOR
bool APIServerConnectionBase::send_sensor_state_response(const SensorStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_sensor_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<SensorStateResponse>(msg, 25);
}
#endif
#ifdef USE_SWITCH
bool APIServerConnectionBase::send_list_entities_switch_response(const ListEntitiesSwitchResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_switch_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesSwitchResponse>(msg, 17);
}
#endif
#ifdef USE_SWITCH
bool APIServerConnectionBase::send_switch_state_response(const SwitchStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_switch_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<SwitchStateResponse>(msg, 26);
}
#endif
@@ -122,13 +162,17 @@ bool APIServerConnectionBase::send_switch_state_response(const SwitchStateRespon
#endif
#ifdef USE_TEXT_SENSOR
bool APIServerConnectionBase::send_list_entities_text_sensor_response(const ListEntitiesTextSensorResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_text_sensor_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesTextSensorResponse>(msg, 18);
}
#endif
#ifdef USE_TEXT_SENSOR
bool APIServerConnectionBase::send_text_sensor_state_response(const TextSensorStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_text_sensor_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<TextSensorStateResponse>(msg, 27);
}
#endif
@@ -136,35 +180,49 @@ bool APIServerConnectionBase::send_subscribe_logs_response(const SubscribeLogsRe
return this->send_message_<SubscribeLogsResponse>(msg, 29);
}
bool APIServerConnectionBase::send_homeassistant_service_response(const HomeassistantServiceResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_homeassistant_service_response: %s", msg.dump().c_str());
#endif
return this->send_message_<HomeassistantServiceResponse>(msg, 35);
}
bool APIServerConnectionBase::send_subscribe_home_assistant_state_response(
const SubscribeHomeAssistantStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_subscribe_home_assistant_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<SubscribeHomeAssistantStateResponse>(msg, 39);
}
bool APIServerConnectionBase::send_get_time_request(const GetTimeRequest &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_get_time_request: %s", msg.dump().c_str());
#endif
return this->send_message_<GetTimeRequest>(msg, 36);
}
bool APIServerConnectionBase::send_get_time_response(const GetTimeResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_get_time_response: %s", msg.dump().c_str());
#endif
return this->send_message_<GetTimeResponse>(msg, 37);
}
bool APIServerConnectionBase::send_list_entities_services_response(const ListEntitiesServicesResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_services_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesServicesResponse>(msg, 41);
}
#ifdef USE_ESP32_CAMERA
bool APIServerConnectionBase::send_list_entities_camera_response(const ListEntitiesCameraResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_camera_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesCameraResponse>(msg, 43);
}
#endif
#ifdef USE_ESP32_CAMERA
bool APIServerConnectionBase::send_camera_image_response(const CameraImageResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_camera_image_response: %s", msg.dump().c_str());
#endif
return this->send_message_<CameraImageResponse>(msg, 44);
}
#endif
@@ -172,87 +230,175 @@ bool APIServerConnectionBase::send_camera_image_response(const CameraImageRespon
#endif
#ifdef USE_CLIMATE
bool APIServerConnectionBase::send_list_entities_climate_response(const ListEntitiesClimateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_climate_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesClimateResponse>(msg, 46);
}
#endif
#ifdef USE_CLIMATE
bool APIServerConnectionBase::send_climate_state_response(const ClimateStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_climate_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ClimateStateResponse>(msg, 47);
}
#endif
#ifdef USE_CLIMATE
#endif
#ifdef USE_NUMBER
bool APIServerConnectionBase::send_list_entities_number_response(const ListEntitiesNumberResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_number_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesNumberResponse>(msg, 49);
}
#endif
#ifdef USE_NUMBER
bool APIServerConnectionBase::send_number_state_response(const NumberStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_number_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<NumberStateResponse>(msg, 50);
}
#endif
#ifdef USE_NUMBER
#endif
#ifdef USE_SELECT
bool APIServerConnectionBase::send_list_entities_select_response(const ListEntitiesSelectResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_select_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesSelectResponse>(msg, 52);
}
#endif
#ifdef USE_SELECT
bool APIServerConnectionBase::send_select_state_response(const SelectStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_select_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<SelectStateResponse>(msg, 53);
}
#endif
#ifdef USE_SELECT
#endif
#ifdef USE_LOCK
bool APIServerConnectionBase::send_list_entities_lock_response(const ListEntitiesLockResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_lock_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesLockResponse>(msg, 58);
}
#endif
#ifdef USE_LOCK
bool APIServerConnectionBase::send_lock_state_response(const LockStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_lock_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<LockStateResponse>(msg, 59);
}
#endif
#ifdef USE_LOCK
#endif
#ifdef USE_BUTTON
bool APIServerConnectionBase::send_list_entities_button_response(const ListEntitiesButtonResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_button_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesButtonResponse>(msg, 61);
}
#endif
#ifdef USE_BUTTON
#endif
bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {
switch (msg_type) {
case 1: {
HelloRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_hello_request: %s", msg.dump().c_str());
#endif
this->on_hello_request(msg);
break;
}
case 3: {
ConnectRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_connect_request: %s", msg.dump().c_str());
#endif
this->on_connect_request(msg);
break;
}
case 5: {
DisconnectRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_disconnect_request: %s", msg.dump().c_str());
#endif
this->on_disconnect_request(msg);
break;
}
case 6: {
DisconnectResponse msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_disconnect_response: %s", msg.dump().c_str());
#endif
this->on_disconnect_response(msg);
break;
}
case 7: {
PingRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_ping_request: %s", msg.dump().c_str());
#endif
this->on_ping_request(msg);
break;
}
case 8: {
PingResponse msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_ping_response: %s", msg.dump().c_str());
#endif
this->on_ping_response(msg);
break;
}
case 9: {
DeviceInfoRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_device_info_request: %s", msg.dump().c_str());
#endif
this->on_device_info_request(msg);
break;
}
case 11: {
ListEntitiesRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_list_entities_request: %s", msg.dump().c_str());
#endif
this->on_list_entities_request(msg);
break;
}
case 20: {
SubscribeStatesRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_subscribe_states_request: %s", msg.dump().c_str());
#endif
this->on_subscribe_states_request(msg);
break;
}
case 28: {
SubscribeLogsRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_subscribe_logs_request: %s", msg.dump().c_str());
#endif
this->on_subscribe_logs_request(msg);
break;
}
@@ -260,7 +406,9 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
#ifdef USE_COVER
CoverCommandRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_cover_command_request: %s", msg.dump().c_str());
#endif
this->on_cover_command_request(msg);
#endif
break;
@@ -269,7 +417,9 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
#ifdef USE_FAN
FanCommandRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_fan_command_request: %s", msg.dump().c_str());
#endif
this->on_fan_command_request(msg);
#endif
break;
@@ -278,7 +428,9 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
#ifdef USE_LIGHT
LightCommandRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_light_command_request: %s", msg.dump().c_str());
#endif
this->on_light_command_request(msg);
#endif
break;
@@ -287,7 +439,9 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
#ifdef USE_SWITCH
SwitchCommandRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_switch_command_request: %s", msg.dump().c_str());
#endif
this->on_switch_command_request(msg);
#endif
break;
@@ -295,42 +449,54 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
case 34: {
SubscribeHomeassistantServicesRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_subscribe_homeassistant_services_request: %s", msg.dump().c_str());
#endif
this->on_subscribe_homeassistant_services_request(msg);
break;
}
case 36: {
GetTimeRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_get_time_request: %s", msg.dump().c_str());
#endif
this->on_get_time_request(msg);
break;
}
case 37: {
GetTimeResponse msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_get_time_response: %s", msg.dump().c_str());
#endif
this->on_get_time_response(msg);
break;
}
case 38: {
SubscribeHomeAssistantStatesRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_subscribe_home_assistant_states_request: %s", msg.dump().c_str());
#endif
this->on_subscribe_home_assistant_states_request(msg);
break;
}
case 40: {
HomeAssistantStateResponse msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_home_assistant_state_response: %s", msg.dump().c_str());
#endif
this->on_home_assistant_state_response(msg);
break;
}
case 42: {
ExecuteServiceRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_execute_service_request: %s", msg.dump().c_str());
#endif
this->on_execute_service_request(msg);
break;
}
@@ -338,7 +504,9 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
#ifdef USE_ESP32_CAMERA
CameraImageRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_camera_image_request: %s", msg.dump().c_str());
#endif
this->on_camera_image_request(msg);
#endif
break;
@@ -347,8 +515,54 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
#ifdef USE_CLIMATE
ClimateCommandRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_climate_command_request: %s", msg.dump().c_str());
#endif
this->on_climate_command_request(msg);
#endif
break;
}
case 51: {
#ifdef USE_NUMBER
NumberCommandRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_number_command_request: %s", msg.dump().c_str());
#endif
this->on_number_command_request(msg);
#endif
break;
}
case 54: {
#ifdef USE_SELECT
SelectCommandRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_select_command_request: %s", msg.dump().c_str());
#endif
this->on_select_command_request(msg);
#endif
break;
}
case 60: {
#ifdef USE_LOCK
LockCommandRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_lock_command_request: %s", msg.dump().c_str());
#endif
this->on_lock_command_request(msg);
#endif
break;
}
case 62: {
#ifdef USE_BUTTON
ButtonCommandRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_button_command_request: %s", msg.dump().c_str());
#endif
this->on_button_command_request(msg);
#endif
break;
}
@@ -547,6 +761,58 @@ void APIServerConnection::on_climate_command_request(const ClimateCommandRequest
this->climate_command(msg);
}
#endif
#ifdef USE_NUMBER
void APIServerConnection::on_number_command_request(const NumberCommandRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->number_command(msg);
}
#endif
#ifdef USE_SELECT
void APIServerConnection::on_select_command_request(const SelectCommandRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->select_command(msg);
}
#endif
#ifdef USE_BUTTON
void APIServerConnection::on_button_command_request(const ButtonCommandRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->button_command(msg);
}
#endif
#ifdef USE_LOCK
void APIServerConnection::on_lock_command_request(const LockCommandRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->lock_command(msg);
}
#endif
} // namespace api
} // namespace esphome

View File

@@ -111,6 +111,39 @@ class APIServerConnectionBase : public ProtoService {
#endif
#ifdef USE_CLIMATE
virtual void on_climate_command_request(const ClimateCommandRequest &value){};
#endif
#ifdef USE_NUMBER
bool send_list_entities_number_response(const ListEntitiesNumberResponse &msg);
#endif
#ifdef USE_NUMBER
bool send_number_state_response(const NumberStateResponse &msg);
#endif
#ifdef USE_NUMBER
virtual void on_number_command_request(const NumberCommandRequest &value){};
#endif
#ifdef USE_SELECT
bool send_list_entities_select_response(const ListEntitiesSelectResponse &msg);
#endif
#ifdef USE_SELECT
bool send_select_state_response(const SelectStateResponse &msg);
#endif
#ifdef USE_SELECT
virtual void on_select_command_request(const SelectCommandRequest &value){};
#endif
#ifdef USE_LOCK
bool send_list_entities_lock_response(const ListEntitiesLockResponse &msg);
#endif
#ifdef USE_LOCK
bool send_lock_state_response(const LockStateResponse &msg);
#endif
#ifdef USE_LOCK
virtual void on_lock_command_request(const LockCommandRequest &value){};
#endif
#ifdef USE_BUTTON
bool send_list_entities_button_response(const ListEntitiesButtonResponse &msg);
#endif
#ifdef USE_BUTTON
virtual void on_button_command_request(const ButtonCommandRequest &value){};
#endif
protected:
bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;
@@ -147,6 +180,18 @@ class APIServerConnection : public APIServerConnectionBase {
#endif
#ifdef USE_CLIMATE
virtual void climate_command(const ClimateCommandRequest &msg) = 0;
#endif
#ifdef USE_NUMBER
virtual void number_command(const NumberCommandRequest &msg) = 0;
#endif
#ifdef USE_SELECT
virtual void select_command(const SelectCommandRequest &msg) = 0;
#endif
#ifdef USE_BUTTON
virtual void button_command(const ButtonCommandRequest &msg) = 0;
#endif
#ifdef USE_LOCK
virtual void lock_command(const LockCommandRequest &msg) = 0;
#endif
protected:
void on_hello_request(const HelloRequest &msg) override;
@@ -179,6 +224,18 @@ class APIServerConnection : public APIServerConnectionBase {
#ifdef USE_CLIMATE
void on_climate_command_request(const ClimateCommandRequest &msg) override;
#endif
#ifdef USE_NUMBER
void on_number_command_request(const NumberCommandRequest &msg) override;
#endif
#ifdef USE_SELECT
void on_select_command_request(const SelectCommandRequest &msg) override;
#endif
#ifdef USE_BUTTON
void on_button_command_request(const ButtonCommandRequest &msg) override;
#endif
#ifdef USE_LOCK
void on_lock_command_request(const LockCommandRequest &msg) override;
#endif
};
} // namespace api

View File

@@ -1,10 +1,13 @@
#include "api_server.h"
#include "api_connection.h"
#include "esphome/core/log.h"
#include "esphome/core/application.h"
#include "esphome/core/util.h"
#include "esphome/core/defines.h"
#include "esphome/core/log.h"
#include "esphome/core/util.h"
#include "esphome/core/version.h"
#include "esphome/core/hal.h"
#include "esphome/components/network/util.h"
#include <cerrno>
#ifdef USE_LOGGER
#include "esphome/components/logger/logger.h"
@@ -15,30 +18,58 @@
namespace esphome {
namespace api {
static const char *TAG = "api";
static const char *const TAG = "api";
// APIServer
void APIServer::setup() {
ESP_LOGCONFIG(TAG, "Setting up Home Assistant API server...");
this->setup_controller();
this->server_ = AsyncServer(this->port_);
this->server_.setNoDelay(false);
this->server_.begin();
this->server_.onClient(
[](void *s, AsyncClient *client) {
if (client == nullptr)
return;
socket_ = socket::socket_ip(SOCK_STREAM, 0);
if (socket_ == nullptr) {
ESP_LOGW(TAG, "Could not create socket.");
this->mark_failed();
return;
}
int enable = 1;
int err = socket_->setsockopt(SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
if (err != 0) {
ESP_LOGW(TAG, "Socket unable to set reuseaddr: errno %d", err);
// we can still continue
}
err = socket_->setblocking(false);
if (err != 0) {
ESP_LOGW(TAG, "Socket unable to set nonblocking mode: errno %d", err);
this->mark_failed();
return;
}
struct sockaddr_storage server;
socklen_t sl = socket::set_sockaddr_any((struct sockaddr *) &server, sizeof(server), htons(this->port_));
if (sl == 0) {
ESP_LOGW(TAG, "Socket unable to set sockaddr: errno %d", errno);
this->mark_failed();
return;
}
err = socket_->bind((struct sockaddr *) &server, sl);
if (err != 0) {
ESP_LOGW(TAG, "Socket unable to bind: errno %d", errno);
this->mark_failed();
return;
}
err = socket_->listen(4);
if (err != 0) {
ESP_LOGW(TAG, "Socket unable to listen: errno %d", errno);
this->mark_failed();
return;
}
// can't print here because in lwIP thread
// ESP_LOGD(TAG, "New client connected from %s", client->remoteIP().toString().c_str());
auto *a_this = (APIServer *) s;
a_this->clients_.push_back(new APIConnection(client, a_this));
},
this);
#ifdef USE_LOGGER
if (logger::global_logger != nullptr) {
logger::global_logger->add_on_log_callback([this](int level, const char *tag, const char *message) {
for (auto *c : this->clients_) {
for (auto &c : this->clients_) {
if (!c->remove_)
c->send_log_message(level, tag, message);
}
@@ -49,31 +80,43 @@ void APIServer::setup() {
this->last_connected_ = millis();
#ifdef USE_ESP32_CAMERA
if (esp32_camera::global_esp32_camera != nullptr) {
esp32_camera::global_esp32_camera->add_image_callback([this](std::shared_ptr<esp32_camera::CameraImage> image) {
for (auto *c : this->clients_)
if (!c->remove_)
c->send_camera_state(image);
});
if (esp32_camera::global_esp32_camera != nullptr && !esp32_camera::global_esp32_camera->is_internal()) {
esp32_camera::global_esp32_camera->add_image_callback(
[this](const std::shared_ptr<esp32_camera::CameraImage> &image) {
for (auto &c : this->clients_) {
if (!c->remove_)
c->send_camera_state(image);
}
});
}
#endif
}
void APIServer::loop() {
// Accept new clients
while (true) {
struct sockaddr_storage source_addr;
socklen_t addr_len = sizeof(source_addr);
auto sock = socket_->accept((struct sockaddr *) &source_addr, &addr_len);
if (!sock)
break;
ESP_LOGD(TAG, "Accepted %s", sock->getpeername().c_str());
auto *conn = new APIConnection(std::move(sock), this);
clients_.emplace_back(conn);
conn->start();
}
// Partition clients into remove and active
auto new_end =
std::partition(this->clients_.begin(), this->clients_.end(), [](APIConnection *conn) { return !conn->remove_; });
auto new_end = std::partition(this->clients_.begin(), this->clients_.end(),
[](const std::unique_ptr<APIConnection> &conn) { return !conn->remove_; });
// print disconnection messages
for (auto it = new_end; it != this->clients_.end(); ++it) {
ESP_LOGD(TAG, "Disconnecting %s", (*it)->client_info_.c_str());
ESP_LOGV(TAG, "Removing connection to %s", (*it)->client_info_.c_str());
}
// only then delete the pointers, otherwise log routine
// would access freed memory
for (auto it = new_end; it != this->clients_.end(); ++it)
delete *it;
// resize vector
this->clients_.erase(new_end, this->clients_.end());
for (auto *client : this->clients_) {
for (auto &client : this->clients_) {
client->loop();
}
@@ -93,7 +136,12 @@ void APIServer::loop() {
}
void APIServer::dump_config() {
ESP_LOGCONFIG(TAG, "API Server:");
ESP_LOGCONFIG(TAG, " Address: %s:%u", network_get_address().c_str(), this->port_);
ESP_LOGCONFIG(TAG, " Address: %s:%u", network::get_use_address().c_str(), this->port_);
#ifdef USE_API_NOISE
ESP_LOGCONFIG(TAG, " Using noise encryption: YES");
#else
ESP_LOGCONFIG(TAG, " Using noise encryption: NO");
#endif
}
bool APIServer::uses_password() const { return !this->password_.empty(); }
bool APIServer::check_password(const std::string &password) const {
@@ -129,7 +177,7 @@ void APIServer::handle_disconnect(APIConnection *conn) {}
void APIServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state) {
if (obj->is_internal())
return;
for (auto *c : this->clients_)
for (auto &c : this->clients_)
c->send_binary_sensor_state(obj, state);
}
#endif
@@ -138,16 +186,16 @@ void APIServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool s
void APIServer::on_cover_update(cover::Cover *obj) {
if (obj->is_internal())
return;
for (auto *c : this->clients_)
for (auto &c : this->clients_)
c->send_cover_state(obj);
}
#endif
#ifdef USE_FAN
void APIServer::on_fan_update(fan::FanState *obj) {
void APIServer::on_fan_update(fan::Fan *obj) {
if (obj->is_internal())
return;
for (auto *c : this->clients_)
for (auto &c : this->clients_)
c->send_fan_state(obj);
}
#endif
@@ -156,7 +204,7 @@ void APIServer::on_fan_update(fan::FanState *obj) {
void APIServer::on_light_update(light::LightState *obj) {
if (obj->is_internal())
return;
for (auto *c : this->clients_)
for (auto &c : this->clients_)
c->send_light_state(obj);
}
#endif
@@ -165,7 +213,7 @@ void APIServer::on_light_update(light::LightState *obj) {
void APIServer::on_sensor_update(sensor::Sensor *obj, float state) {
if (obj->is_internal())
return;
for (auto *c : this->clients_)
for (auto &c : this->clients_)
c->send_sensor_state(obj, state);
}
#endif
@@ -174,16 +222,16 @@ void APIServer::on_sensor_update(sensor::Sensor *obj, float state) {
void APIServer::on_switch_update(switch_::Switch *obj, bool state) {
if (obj->is_internal())
return;
for (auto *c : this->clients_)
for (auto &c : this->clients_)
c->send_switch_state(obj, state);
}
#endif
#ifdef USE_TEXT_SENSOR
void APIServer::on_text_sensor_update(text_sensor::TextSensor *obj, std::string state) {
void APIServer::on_text_sensor_update(text_sensor::TextSensor *obj, const std::string &state) {
if (obj->is_internal())
return;
for (auto *c : this->clients_)
for (auto &c : this->clients_)
c->send_text_sensor_state(obj, state);
}
#endif
@@ -192,18 +240,45 @@ void APIServer::on_text_sensor_update(text_sensor::TextSensor *obj, std::string
void APIServer::on_climate_update(climate::Climate *obj) {
if (obj->is_internal())
return;
for (auto *c : this->clients_)
for (auto &c : this->clients_)
c->send_climate_state(obj);
}
#endif
#ifdef USE_NUMBER
void APIServer::on_number_update(number::Number *obj, float state) {
if (obj->is_internal())
return;
for (auto &c : this->clients_)
c->send_number_state(obj, state);
}
#endif
#ifdef USE_SELECT
void APIServer::on_select_update(select::Select *obj, const std::string &state) {
if (obj->is_internal())
return;
for (auto &c : this->clients_)
c->send_select_state(obj, state);
}
#endif
#ifdef USE_LOCK
void APIServer::on_lock_update(lock::Lock *obj) {
if (obj->is_internal())
return;
for (auto &c : this->clients_)
c->send_lock_state(obj, obj->state);
}
#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;
APIServer *global_api_server = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
void APIServer::set_password(const std::string &password) { this->password_ = password; }
void APIServer::send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
for (auto *client : this->clients_) {
for (auto &client : this->clients_) {
client->send_homeassistant_service_call(call);
}
}
@@ -223,7 +298,7 @@ uint16_t APIServer::get_port() const { return this->port_; }
void APIServer::set_reboot_timeout(uint32_t reboot_timeout) { this->reboot_timeout_ = reboot_timeout; }
#ifdef USE_HOMEASSISTANT_TIME
void APIServer::request_time() {
for (auto *client : this->clients_) {
for (auto &client : this->clients_) {
if (!client->remove_ && client->connection_state_ == APIConnection::ConnectionState::CONNECTED)
client->send_time_request();
}
@@ -231,7 +306,7 @@ void APIServer::request_time() {
#endif
bool APIServer::is_connected() const { return !this->clients_.empty(); }
void APIServer::on_shutdown() {
for (auto *c : this->clients_) {
for (auto &c : this->clients_) {
c->send_disconnect_request(DisconnectRequest());
}
delay(10);

View File

@@ -4,20 +4,14 @@
#include "esphome/core/controller.h"
#include "esphome/core/defines.h"
#include "esphome/core/log.h"
#include "esphome/components/socket/socket.h"
#include "api_pb2.h"
#include "api_pb2_service.h"
#include "util.h"
#include "list_entities.h"
#include "subscribe_state.h"
#include "homeassistant_service.h"
#include "user_services.h"
#ifdef ARDUINO_ARCH_ESP32
#include <AsyncTCP.h>
#endif
#ifdef ARDUINO_ARCH_ESP8266
#include <ESPAsyncTCP.h>
#endif
#include "api_noise_context.h"
namespace esphome {
namespace api {
@@ -36,6 +30,12 @@ class APIServer : public Component, public Controller {
void set_port(uint16_t port);
void set_password(const std::string &password);
void set_reboot_timeout(uint32_t reboot_timeout);
#ifdef USE_API_NOISE
void set_noise_psk(psk_t psk) { noise_ctx_->set_psk(psk); }
std::shared_ptr<APINoiseContext> get_noise_ctx() { return noise_ctx_; }
#endif // USE_API_NOISE
void handle_disconnect(APIConnection *conn);
#ifdef USE_BINARY_SENSOR
void on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state) override;
@@ -44,7 +44,7 @@ class APIServer : public Component, public Controller {
void on_cover_update(cover::Cover *obj) override;
#endif
#ifdef USE_FAN
void on_fan_update(fan::FanState *obj) override;
void on_fan_update(fan::Fan *obj) override;
#endif
#ifdef USE_LIGHT
void on_light_update(light::LightState *obj) override;
@@ -56,10 +56,19 @@ class APIServer : public Component, public Controller {
void on_switch_update(switch_::Switch *obj, bool state) override;
#endif
#ifdef USE_TEXT_SENSOR
void on_text_sensor_update(text_sensor::TextSensor *obj, std::string state) override;
void on_text_sensor_update(text_sensor::TextSensor *obj, const std::string &state) override;
#endif
#ifdef USE_CLIMATE
void on_climate_update(climate::Climate *obj) override;
#endif
#ifdef USE_NUMBER
void on_number_update(number::Number *obj, float state) override;
#endif
#ifdef USE_SELECT
void on_select_update(select::Select *obj, const std::string &state) override;
#endif
#ifdef USE_LOCK
void on_lock_update(lock::Lock *obj) override;
#endif
void send_homeassistant_service_call(const HomeassistantServiceResponse &call);
void register_user_service(UserServiceDescriptor *descriptor) { this->user_services_.push_back(descriptor); }
@@ -81,17 +90,21 @@ class APIServer : public Component, public Controller {
const std::vector<UserServiceDescriptor *> &get_user_services() const { return this->user_services_; }
protected:
AsyncServer server_{0};
std::unique_ptr<socket::Socket> socket_ = nullptr;
uint16_t port_{6053};
uint32_t reboot_timeout_{300000};
uint32_t last_connected_{0};
std::vector<APIConnection *> clients_;
std::vector<std::unique_ptr<APIConnection>> clients_;
std::string password_;
std::vector<HomeAssistantStateSubscription> state_subs_;
std::vector<UserServiceDescriptor *> user_services_;
#ifdef USE_API_NOISE
std::shared_ptr<APINoiseContext> noise_ctx_ = std::make_shared<APINoiseContext>();
#endif // USE_API_NOISE
};
extern APIServer *global_api_server;
extern APIServer *global_api_server; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
template<typename... Ts> class APIConnectedCondition : public Condition<Ts...> {
public:

View File

@@ -0,0 +1,71 @@
import asyncio
import logging
from datetime import datetime
from typing import Optional
from aioesphomeapi import APIClient, ReconnectLogic, APIConnectionError, LogLevel
import zeroconf
from esphome.const import CONF_KEY, CONF_PORT, CONF_PASSWORD, __version__
from esphome.util import safe_print
from . import CONF_ENCRYPTION
_LOGGER = logging.getLogger(__name__)
async def async_run_logs(config, address):
conf = config["api"]
port: int = int(conf[CONF_PORT])
password: str = conf[CONF_PASSWORD]
noise_psk: Optional[str] = None
if CONF_ENCRYPTION in conf:
noise_psk = conf[CONF_ENCRYPTION][CONF_KEY]
_LOGGER.info("Starting log output from %s using esphome API", address)
cli = APIClient(
address,
port,
password,
client_info=f"ESPHome Logs {__version__}",
noise_psk=noise_psk,
)
first_connect = True
def on_log(msg):
time_ = datetime.now().time().strftime("[%H:%M:%S]")
text = msg.message.decode("utf8", "backslashreplace")
safe_print(time_ + text)
async def on_connect():
nonlocal first_connect
try:
await cli.subscribe_logs(
on_log,
log_level=LogLevel.LOG_LEVEL_VERY_VERBOSE,
dump_config=first_connect,
)
first_connect = False
except APIConnectionError:
cli.disconnect()
async def on_disconnect():
_LOGGER.warning("Disconnected from API")
zc = zeroconf.Zeroconf()
reconnect = ReconnectLogic(
client=cli,
on_connect=on_connect,
on_disconnect=on_disconnect,
zeroconf_instance=zc,
)
await reconnect.start()
try:
while True:
await asyncio.sleep(60)
except KeyboardInterrupt:
await reconnect.stop()
zc.close()
def run_logs(config, address):
asyncio.run(async_run_logs(config, address))

View File

@@ -49,7 +49,7 @@ class CustomAPIDevice {
template<typename T, typename... Ts>
void register_service(void (T::*callback)(Ts...), const std::string &name,
const std::array<std::string, sizeof...(Ts)> &arg_names) {
auto *service = new CustomAPIDeviceService<T, Ts...>(name, arg_names, (T *) this, callback);
auto *service = new CustomAPIDeviceService<T, Ts...>(name, arg_names, (T *) this, callback); // NOLINT
global_api_server->register_user_service(service);
}
@@ -72,7 +72,7 @@ class CustomAPIDevice {
* @param name The name of the arguments for the service, must match the arguments of the function.
*/
template<typename T> void register_service(void (T::*callback)(), const std::string &name) {
auto *service = new CustomAPIDeviceService<T>(name, {}, (T *) this, callback);
auto *service = new CustomAPIDeviceService<T>(name, {}, (T *) this, callback); // NOLINT
global_api_server->register_user_service(service);
}

View File

@@ -8,6 +8,18 @@
namespace esphome {
namespace api {
template<typename... X> class TemplatableStringValue : public TemplatableValue<std::string, X...> {
public:
TemplatableStringValue() : TemplatableValue<std::string, X...>() {}
template<typename F, enable_if_t<!is_invocable<F, X...>::value, int> = 0>
TemplatableStringValue(F value) : TemplatableValue<std::string, X...>(value) {}
template<typename F, enable_if_t<is_invocable<F, X...>::value, int> = 0>
TemplatableStringValue(F f)
: TemplatableValue<std::string, X...>([f](X... x) -> std::string { return to_string(f(x...)); }) {}
};
template<typename... Ts> class TemplatableKeyValuePair {
public:
template<typename T> TemplatableKeyValuePair(std::string key, T value) : key(std::move(key)), value(value) {}
@@ -19,7 +31,8 @@ template<typename... Ts> class HomeAssistantServiceCallAction : public Action<Ts
public:
explicit HomeAssistantServiceCallAction(APIServer *parent, bool is_event) : parent_(parent), is_event_(is_event) {}
TEMPLATABLE_STRING_VALUE(service);
template<typename T> void set_service(T service) { this->service_ = service; }
template<typename T> void add_data(std::string key, T value) {
this->data_.push_back(TemplatableKeyValuePair<Ts...>(key, value));
}
@@ -58,6 +71,7 @@ template<typename... Ts> class HomeAssistantServiceCallAction : public Action<Ts
protected:
APIServer *parent_;
bool is_event_;
TemplatableStringValue<Ts...> service_{};
std::vector<TemplatableKeyValuePair<Ts...>> data_;
std::vector<TemplatableKeyValuePair<Ts...>> data_template_;
std::vector<TemplatableKeyValuePair<Ts...>> variables_;

View File

@@ -16,7 +16,7 @@ bool ListEntitiesIterator::on_binary_sensor(binary_sensor::BinarySensor *binary_
bool ListEntitiesIterator::on_cover(cover::Cover *cover) { return this->client_->send_cover_info(cover); }
#endif
#ifdef USE_FAN
bool ListEntitiesIterator::on_fan(fan::FanState *fan) { return this->client_->send_fan_info(fan); }
bool ListEntitiesIterator::on_fan(fan::Fan *fan) { return this->client_->send_fan_info(fan); }
#endif
#ifdef USE_LIGHT
bool ListEntitiesIterator::on_light(light::LightState *light) { return this->client_->send_light_info(light); }
@@ -27,11 +27,17 @@ bool ListEntitiesIterator::on_sensor(sensor::Sensor *sensor) { return this->clie
#ifdef USE_SWITCH
bool ListEntitiesIterator::on_switch(switch_::Switch *a_switch) { return this->client_->send_switch_info(a_switch); }
#endif
#ifdef USE_BUTTON
bool ListEntitiesIterator::on_button(button::Button *button) { return this->client_->send_button_info(button); }
#endif
#ifdef USE_TEXT_SENSOR
bool ListEntitiesIterator::on_text_sensor(text_sensor::TextSensor *text_sensor) {
return this->client_->send_text_sensor_info(text_sensor);
}
#endif
#ifdef USE_LOCK
bool ListEntitiesIterator::on_lock(lock::Lock *a_lock) { return this->client_->send_lock_info(a_lock); }
#endif
bool ListEntitiesIterator::on_end() { return this->client_->send_list_info_done(); }
ListEntitiesIterator::ListEntitiesIterator(APIServer *server, APIConnection *client)
@@ -51,5 +57,13 @@ bool ListEntitiesIterator::on_camera(esp32_camera::ESP32Camera *camera) {
bool ListEntitiesIterator::on_climate(climate::Climate *climate) { return this->client_->send_climate_info(climate); }
#endif
#ifdef USE_NUMBER
bool ListEntitiesIterator::on_number(number::Number *number) { return this->client_->send_number_info(number); }
#endif
#ifdef USE_SELECT
bool ListEntitiesIterator::on_select(select::Select *select) { return this->client_->send_select_info(select); }
#endif
} // namespace api
} // namespace esphome

View File

@@ -19,7 +19,7 @@ class ListEntitiesIterator : public ComponentIterator {
bool on_cover(cover::Cover *cover) override;
#endif
#ifdef USE_FAN
bool on_fan(fan::FanState *fan) override;
bool on_fan(fan::Fan *fan) override;
#endif
#ifdef USE_LIGHT
bool on_light(light::LightState *light) override;
@@ -30,6 +30,9 @@ class ListEntitiesIterator : public ComponentIterator {
#ifdef USE_SWITCH
bool on_switch(switch_::Switch *a_switch) override;
#endif
#ifdef USE_BUTTON
bool on_button(button::Button *button) override;
#endif
#ifdef USE_TEXT_SENSOR
bool on_text_sensor(text_sensor::TextSensor *text_sensor) override;
#endif
@@ -39,6 +42,15 @@ class ListEntitiesIterator : public ComponentIterator {
#endif
#ifdef USE_CLIMATE
bool on_climate(climate::Climate *climate) override;
#endif
#ifdef USE_NUMBER
bool on_number(number::Number *number) override;
#endif
#ifdef USE_SELECT
bool on_select(select::Select *select) override;
#endif
#ifdef USE_LOCK
bool on_lock(lock::Lock *a_lock) override;
#endif
bool on_end() override;

View File

@@ -5,7 +5,7 @@
namespace esphome {
namespace api {
static const char *TAG = "api.proto";
static const char *const TAG = "api.proto";
void ProtoMessage::decode(const uint8_t *buffer, size_t length) {
uint32_t i = 0;
@@ -80,11 +80,13 @@ void ProtoMessage::decode(const uint8_t *buffer, size_t length) {
}
}
#ifdef HAS_PROTO_MESSAGE_DUMP
std::string ProtoMessage::dump() const {
std::string out;
this->dump_to(out);
return out;
}
#endif
} // namespace api
} // namespace esphome

View File

@@ -1,8 +1,13 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/log.h"
#include "esphome/core/helpers.h"
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
#define HAS_PROTO_MESSAGE_DUMP
#endif
namespace esphome {
namespace api {
@@ -50,17 +55,19 @@ class ProtoVarInt {
}
int32_t as_sint32() const {
// with ZigZag encoding
if (this->value_ & 1)
if (this->value_ & 1) {
return static_cast<int32_t>(~(this->value_ >> 1));
else
} else {
return static_cast<int32_t>(this->value_ >> 1);
}
}
int64_t as_sint64() const {
// with ZigZag encoding
if (this->value_ & 1)
if (this->value_ & 1) {
return static_cast<int64_t>(~(this->value_ >> 1));
else
} else {
return static_cast<int64_t>(this->value_ >> 1);
}
}
void encode(std::vector<uint8_t> &out) {
uint32_t val = this->value_;
@@ -215,10 +222,11 @@ class ProtoWriteBuffer {
}
void encode_sint32(uint32_t field_id, int32_t value, bool force = false) {
uint32_t uvalue;
if (value < 0)
if (value < 0) {
uvalue = ~(value << 1);
else
} else {
uvalue = value << 1;
}
this->encode_uint32(field_id, uvalue, force);
}
template<class C> void encode_message(uint32_t field_id, const C &value, bool force = false) {
@@ -241,10 +249,13 @@ class ProtoWriteBuffer {
class ProtoMessage {
public:
virtual ~ProtoMessage() = default;
virtual void encode(ProtoWriteBuffer buffer) const = 0;
void decode(const uint8_t *buffer, size_t length);
#ifdef HAS_PROTO_MESSAGE_DUMP
std::string dump() const;
virtual void dump_to(std::string &out) const = 0;
#endif
protected:
virtual bool decode_varint(uint32_t field_id, ProtoVarInt value) { return false; }

View File

@@ -14,7 +14,7 @@ bool InitialStateIterator::on_binary_sensor(binary_sensor::BinarySensor *binary_
bool InitialStateIterator::on_cover(cover::Cover *cover) { return this->client_->send_cover_state(cover); }
#endif
#ifdef USE_FAN
bool InitialStateIterator::on_fan(fan::FanState *fan) { return this->client_->send_fan_state(fan); }
bool InitialStateIterator::on_fan(fan::Fan *fan) { return this->client_->send_fan_state(fan); }
#endif
#ifdef USE_LIGHT
bool InitialStateIterator::on_light(light::LightState *light) { return this->client_->send_light_state(light); }
@@ -37,6 +37,19 @@ bool InitialStateIterator::on_text_sensor(text_sensor::TextSensor *text_sensor)
#ifdef USE_CLIMATE
bool InitialStateIterator::on_climate(climate::Climate *climate) { return this->client_->send_climate_state(climate); }
#endif
#ifdef USE_NUMBER
bool InitialStateIterator::on_number(number::Number *number) {
return this->client_->send_number_state(number, number->state);
}
#endif
#ifdef USE_SELECT
bool InitialStateIterator::on_select(select::Select *select) {
return this->client_->send_select_state(select, select->state);
}
#endif
#ifdef USE_LOCK
bool InitialStateIterator::on_lock(lock::Lock *a_lock) { return this->client_->send_lock_state(a_lock, a_lock->state); }
#endif
InitialStateIterator::InitialStateIterator(APIServer *server, APIConnection *client)
: ComponentIterator(server), client_(client) {}

View File

@@ -20,7 +20,7 @@ class InitialStateIterator : public ComponentIterator {
bool on_cover(cover::Cover *cover) override;
#endif
#ifdef USE_FAN
bool on_fan(fan::FanState *fan) override;
bool on_fan(fan::Fan *fan) override;
#endif
#ifdef USE_LIGHT
bool on_light(light::LightState *light) override;
@@ -31,11 +31,23 @@ class InitialStateIterator : public ComponentIterator {
#ifdef USE_SWITCH
bool on_switch(switch_::Switch *a_switch) override;
#endif
#ifdef USE_BUTTON
bool on_button(button::Button *button) override { return true; };
#endif
#ifdef USE_TEXT_SENSOR
bool on_text_sensor(text_sensor::TextSensor *text_sensor) override;
#endif
#ifdef USE_CLIMATE
bool on_climate(climate::Climate *climate) override;
#endif
#ifdef USE_NUMBER
bool on_number(number::Number *number) override;
#endif
#ifdef USE_SELECT
bool on_select(select::Select *select) override;
#endif
#ifdef USE_LOCK
bool on_lock(lock::Lock *a_lock) override;
#endif
protected:
APIConnection *client_;

View File

@@ -15,7 +15,7 @@ template<> std::string get_execute_arg_value<std::string>(const ExecuteServiceAr
template<> std::vector<bool> get_execute_arg_value<std::vector<bool>>(const ExecuteServiceArgument &arg) {
return arg.bool_array;
}
template<> std::vector<int> get_execute_arg_value<std::vector<int>>(const ExecuteServiceArgument &arg) {
template<> std::vector<int32_t> get_execute_arg_value<std::vector<int32_t>>(const ExecuteServiceArgument &arg) {
return arg.int_array;
}
template<> std::vector<float> get_execute_arg_value<std::vector<float>>(const ExecuteServiceArgument &arg) {

View File

@@ -1,5 +1,7 @@
#pragma once
#include <utility>
#include "esphome/core/component.h"
#include "esphome/core/automation.h"
#include "api_pb2.h"
@@ -20,8 +22,8 @@ template<typename T> enums::ServiceArgType to_service_arg_type();
template<typename... Ts> class UserServiceBase : public UserServiceDescriptor {
public:
UserServiceBase(const std::string &name, const std::array<std::string, sizeof...(Ts)> &arg_names)
: name_(name), arg_names_(arg_names) {
UserServiceBase(std::string name, const std::array<std::string, sizeof...(Ts)> &arg_names)
: name_(std::move(name)), arg_names_(arg_names) {
this->key_ = fnv1_hash(this->name_);
}
@@ -50,7 +52,7 @@ template<typename... Ts> class UserServiceBase : public UserServiceDescriptor {
protected:
virtual void execute(Ts... x) = 0;
template<int... S> void execute_(std::vector<ExecuteServiceArgument> args, seq<S...>) {
template<int... S> void execute_(std::vector<ExecuteServiceArgument> args, seq<S...> type) {
this->execute((get_execute_arg_value<Ts>(args[S]))...);
}

View File

@@ -116,6 +116,21 @@ void ComponentIterator::advance() {
}
break;
#endif
#ifdef USE_BUTTON
case IteratorState::BUTTON:
if (this->at_ >= App.get_buttons().size()) {
advance_platform = true;
} else {
auto *button = App.get_buttons()[this->at_];
if (button->is_internal()) {
success = true;
break;
} else {
success = this->on_button(button);
}
}
break;
#endif
#ifdef USE_TEXT_SENSOR
case IteratorState::TEXT_SENSOR:
if (this->at_ >= App.get_text_sensors().size()) {
@@ -167,6 +182,51 @@ void ComponentIterator::advance() {
}
}
break;
#endif
#ifdef USE_NUMBER
case IteratorState::NUMBER:
if (this->at_ >= App.get_numbers().size()) {
advance_platform = true;
} else {
auto *number = App.get_numbers()[this->at_];
if (number->is_internal()) {
success = true;
break;
} else {
success = this->on_number(number);
}
}
break;
#endif
#ifdef USE_SELECT
case IteratorState::SELECT:
if (this->at_ >= App.get_selects().size()) {
advance_platform = true;
} else {
auto *select = App.get_selects()[this->at_];
if (select->is_internal()) {
success = true;
break;
} else {
success = this->on_select(select);
}
}
break;
#endif
#ifdef USE_LOCK
case IteratorState::LOCK:
if (this->at_ >= App.get_locks().size()) {
advance_platform = true;
} else {
auto *a_lock = App.get_locks()[this->at_];
if (a_lock->is_internal()) {
success = true;
break;
} else {
success = this->on_lock(a_lock);
}
}
break;
#endif
case IteratorState::MAX:
if (this->on_end()) {

View File

@@ -27,7 +27,7 @@ class ComponentIterator {
virtual bool on_cover(cover::Cover *cover) = 0;
#endif
#ifdef USE_FAN
virtual bool on_fan(fan::FanState *fan) = 0;
virtual bool on_fan(fan::Fan *fan) = 0;
#endif
#ifdef USE_LIGHT
virtual bool on_light(light::LightState *light) = 0;
@@ -38,6 +38,9 @@ class ComponentIterator {
#ifdef USE_SWITCH
virtual bool on_switch(switch_::Switch *a_switch) = 0;
#endif
#ifdef USE_BUTTON
virtual bool on_button(button::Button *button) = 0;
#endif
#ifdef USE_TEXT_SENSOR
virtual bool on_text_sensor(text_sensor::TextSensor *text_sensor) = 0;
#endif
@@ -47,6 +50,15 @@ class ComponentIterator {
#endif
#ifdef USE_CLIMATE
virtual bool on_climate(climate::Climate *climate) = 0;
#endif
#ifdef USE_NUMBER
virtual bool on_number(number::Number *number) = 0;
#endif
#ifdef USE_SELECT
virtual bool on_select(select::Select *select) = 0;
#endif
#ifdef USE_LOCK
virtual bool on_lock(lock::Lock *a_lock) = 0;
#endif
virtual bool on_end();
@@ -72,6 +84,9 @@ class ComponentIterator {
#ifdef USE_SWITCH
SWITCH,
#endif
#ifdef USE_BUTTON
BUTTON,
#endif
#ifdef USE_TEXT_SENSOR
TEXT_SENSOR,
#endif
@@ -81,6 +96,15 @@ class ComponentIterator {
#endif
#ifdef USE_CLIMATE
CLIMATE,
#endif
#ifdef USE_NUMBER
NUMBER,
#endif
#ifdef USE_SELECT
SELECT,
#endif
#ifdef USE_LOCK
LOCK,
#endif
MAX,
} state_{IteratorState::NONE};

View File

@@ -4,7 +4,7 @@
namespace esphome {
namespace as3935 {
static const char *TAG = "as3935";
static const char *const TAG = "as3935";
void AS3935Component::setup() {
ESP_LOGCONFIG(TAG, "Setting up AS3935...");
@@ -58,10 +58,11 @@ void AS3935Component::loop() {
void AS3935Component::write_indoor(bool indoor) {
ESP_LOGV(TAG, "Setting indoor to %d", indoor);
if (indoor)
if (indoor) {
this->write_register(AFE_GAIN, GAIN_MASK, INDOOR, 1);
else
} else {
this->write_register(AFE_GAIN, GAIN_MASK, OUTDOOR, 1);
}
}
// REG0x01, bits[3:0], manufacturer default: 0010 (2).
// This setting determines the threshold for events that trigger the

View File

@@ -1,6 +1,7 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/hal.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/binary_sensor/binary_sensor.h"

View File

@@ -5,7 +5,7 @@ from . import AS3935, CONF_AS3935_ID
DEPENDENCIES = ["as3935"]
CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend(
CONFIG_SCHEMA = binary_sensor.binary_sensor_schema().extend(
{
cv.GenerateID(CONF_AS3935_ID): cv.use_id(AS3935),
}

View File

@@ -4,10 +4,7 @@ from esphome.components import sensor
from esphome.const import (
CONF_DISTANCE,
CONF_LIGHTNING_ENERGY,
DEVICE_CLASS_EMPTY,
STATE_CLASS_NONE,
UNIT_KILOMETER,
UNIT_EMPTY,
ICON_SIGNAL_DISTANCE_VARIANT,
ICON_FLASH,
)
@@ -19,14 +16,13 @@ CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_AS3935_ID): cv.use_id(AS3935),
cv.Optional(CONF_DISTANCE): sensor.sensor_schema(
UNIT_KILOMETER,
ICON_SIGNAL_DISTANCE_VARIANT,
1,
DEVICE_CLASS_EMPTY,
STATE_CLASS_NONE,
unit_of_measurement=UNIT_KILOMETER,
icon=ICON_SIGNAL_DISTANCE_VARIANT,
accuracy_decimals=1,
),
cv.Optional(CONF_LIGHTNING_ENERGY): sensor.sensor_schema(
UNIT_EMPTY, ICON_FLASH, 1, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE
icon=ICON_FLASH,
accuracy_decimals=1,
),
}
).extend(cv.COMPONENT_SCHEMA)

View File

@@ -4,7 +4,7 @@
namespace esphome {
namespace as3935_i2c {
static const char *TAG = "as3935_i2c";
static const char *const TAG = "as3935_i2c";
void I2CAS3935Component::write_register(uint8_t reg, uint8_t mask, uint8_t bits, uint8_t start_pos) {
uint8_t write_reg;
@@ -25,8 +25,12 @@ void I2CAS3935Component::write_register(uint8_t reg, uint8_t mask, uint8_t bits,
uint8_t I2CAS3935Component::read_register(uint8_t reg) {
uint8_t value;
if (!this->read_byte(reg, &value, 2)) {
ESP_LOGW(TAG, "Read failed!");
if (write(&reg, 1) != i2c::ERROR_OK) {
ESP_LOGW(TAG, "Writing register failed!");
return 0;
}
if (read(&value, 1) != i2c::ERROR_OK) {
ESP_LOGW(TAG, "Reading register failed!");
return 0;
}
return value;

View File

@@ -4,7 +4,7 @@
namespace esphome {
namespace as3935_spi {
static const char *TAG = "as3935_spi";
static const char *const TAG = "as3935_spi";
void SPIAS3935Component::setup() {
ESP_LOGI(TAG, "SPIAS3935Component setup started!");

View File

@@ -1,9 +1,15 @@
# Dummy integration to allow relying on AsyncTCP
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.core import CORE, coroutine_with_priority
CODEOWNERS = ["@OttoWinter"]
CONFIG_SCHEMA = cv.All(
cv.Schema({}),
cv.only_with_arduino,
)
@coroutine_with_priority(200.0)
async def to_code(config):
@@ -12,4 +18,4 @@ async def to_code(config):
cg.add_library("esphome/AsyncTCP-esphome", "1.2.2")
elif CORE.is_esp8266:
# https://github.com/OttoWinter/ESPAsyncTCP
cg.add_library("ESPAsyncTCP-esphome", "1.2.3")
cg.add_library("ottowinter/ESPAsyncTCP-esphome", "1.2.3")

View File

@@ -1,12 +1,12 @@
#include "atc_mithermometer.h"
#include "esphome/core/log.h"
#ifdef ARDUINO_ARCH_ESP32
#ifdef USE_ESP32
namespace esphome {
namespace atc_mithermometer {
static const char *TAG = "atc_mithermometer";
static const char *const TAG = "atc_mithermometer";
void ATCMiThermometer::dump_config() {
ESP_LOGCONFIG(TAG, "ATC MiThermometer");
@@ -25,14 +25,14 @@ bool ATCMiThermometer::parse_device(const esp32_ble_tracker::ESPBTDevice &device
bool success = false;
for (auto &service_data : device.get_service_datas()) {
auto res = parse_header(service_data);
if (res->is_duplicate) {
auto res = parse_header_(service_data);
if (!res.has_value()) {
continue;
}
if (!(parse_message(service_data.data, *res))) {
if (!(parse_message_(service_data.data, *res))) {
continue;
}
if (!(report_results(res, device.address_str()))) {
if (!(report_results_(res, device.address_str()))) {
continue;
}
if (res->temperature.has_value() && this->temperature_ != nullptr)
@@ -45,15 +45,13 @@ bool ATCMiThermometer::parse_device(const esp32_ble_tracker::ESPBTDevice &device
this->battery_voltage_->publish_state(*res->battery_voltage);
success = true;
}
if (this->signal_strength_ != nullptr)
this->signal_strength_->publish_state(device.get_rssi());
if (!success) {
return false;
}
return true;
return success;
}
optional<ParseResult> ATCMiThermometer::parse_header(const esp32_ble_tracker::ServiceData &service_data) {
optional<ParseResult> ATCMiThermometer::parse_header_(const esp32_ble_tracker::ServiceData &service_data) {
ParseResult result;
if (!service_data.uuid.contains(0x1A, 0x18)) {
ESP_LOGVV(TAG, "parse_header(): no service data UUID magic bytes.");
@@ -64,17 +62,15 @@ optional<ParseResult> ATCMiThermometer::parse_header(const esp32_ble_tracker::Se
static uint8_t last_frame_count = 0;
if (last_frame_count == raw[12]) {
ESP_LOGVV(TAG, "parse_header(): duplicate data packet received (%d).", static_cast<int>(last_frame_count));
result.is_duplicate = true;
ESP_LOGVV(TAG, "parse_header(): duplicate data packet received (%hhu).", last_frame_count);
return {};
}
last_frame_count = raw[12];
result.is_duplicate = false;
return result;
}
bool ATCMiThermometer::parse_message(const std::vector<uint8_t> &message, ParseResult &result) {
bool ATCMiThermometer::parse_message_(const std::vector<uint8_t> &message, ParseResult &result) {
// Byte 0-5 mac in correct order
// Byte 6-7 Temperature in uint16
// Byte 8 Humidity in percent
@@ -107,7 +103,7 @@ bool ATCMiThermometer::parse_message(const std::vector<uint8_t> &message, ParseR
return true;
}
bool ATCMiThermometer::report_results(const optional<ParseResult> &result, const std::string &address) {
bool ATCMiThermometer::report_results_(const optional<ParseResult> &result, const std::string &address) {
if (!result.has_value()) {
ESP_LOGVV(TAG, "report_results(): no results available.");
return false;

View File

@@ -4,7 +4,7 @@
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#ifdef ARDUINO_ARCH_ESP32
#ifdef USE_ESP32
namespace esphome {
namespace atc_mithermometer {
@@ -14,7 +14,6 @@ struct ParseResult {
optional<float> humidity;
optional<float> battery_level;
optional<float> battery_voltage;
bool is_duplicate;
int raw_offset;
};
@@ -29,6 +28,7 @@ class ATCMiThermometer : public Component, public esp32_ble_tracker::ESPBTDevice
void set_humidity(sensor::Sensor *humidity) { humidity_ = humidity; }
void set_battery_level(sensor::Sensor *battery_level) { battery_level_ = battery_level; }
void set_battery_voltage(sensor::Sensor *battery_voltage) { battery_voltage_ = battery_voltage; }
void set_signal_strength(sensor::Sensor *signal_strength) { signal_strength_ = signal_strength; }
protected:
uint64_t address_;
@@ -36,10 +36,11 @@ class ATCMiThermometer : public Component, public esp32_ble_tracker::ESPBTDevice
sensor::Sensor *humidity_{nullptr};
sensor::Sensor *battery_level_{nullptr};
sensor::Sensor *battery_voltage_{nullptr};
sensor::Sensor *signal_strength_{nullptr};
optional<ParseResult> parse_header(const esp32_ble_tracker::ServiceData &service_data);
bool parse_message(const std::vector<uint8_t> &message, ParseResult &result);
bool report_results(const optional<ParseResult> &result, const std::string &address);
optional<ParseResult> parse_header_(const esp32_ble_tracker::ServiceData &service_data);
bool parse_message_(const std::vector<uint8_t> &message, ParseResult &result);
bool report_results_(const optional<ParseResult> &result, const std::string &address);
};
} // namespace atc_mithermometer

View File

@@ -6,15 +6,18 @@ from esphome.const import (
CONF_BATTERY_VOLTAGE,
CONF_MAC_ADDRESS,
CONF_HUMIDITY,
CONF_SIGNAL_STRENGTH,
CONF_TEMPERATURE,
CONF_ID,
DEVICE_CLASS_BATTERY,
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_SIGNAL_STRENGTH,
DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_VOLTAGE,
ICON_EMPTY,
ENTITY_CATEGORY_DIAGNOSTIC,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
UNIT_DECIBEL_MILLIWATT,
UNIT_PERCENT,
UNIT_VOLT,
)
@@ -34,28 +37,37 @@ CONFIG_SCHEMA = (
cv.GenerateID(): cv.declare_id(ATCMiThermometer),
cv.Required(CONF_MAC_ADDRESS): cv.mac_address,
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS,
ICON_EMPTY,
1,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=1,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
UNIT_PERCENT,
ICON_EMPTY,
0,
DEVICE_CLASS_HUMIDITY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_PERCENT,
accuracy_decimals=0,
device_class=DEVICE_CLASS_HUMIDITY,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(
UNIT_PERCENT,
ICON_EMPTY,
0,
DEVICE_CLASS_BATTERY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_PERCENT,
accuracy_decimals=0,
device_class=DEVICE_CLASS_BATTERY,
state_class=STATE_CLASS_MEASUREMENT,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
),
cv.Optional(CONF_BATTERY_VOLTAGE): sensor.sensor_schema(
UNIT_VOLT, ICON_EMPTY, 3, DEVICE_CLASS_VOLTAGE, STATE_CLASS_MEASUREMENT
unit_of_measurement=UNIT_VOLT,
accuracy_decimals=3,
device_class=DEVICE_CLASS_VOLTAGE,
state_class=STATE_CLASS_MEASUREMENT,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
),
cv.Optional(CONF_SIGNAL_STRENGTH): sensor.sensor_schema(
unit_of_measurement=UNIT_DECIBEL_MILLIWATT,
accuracy_decimals=0,
device_class=DEVICE_CLASS_SIGNAL_STRENGTH,
state_class=STATE_CLASS_MEASUREMENT,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
),
}
)
@@ -83,3 +95,6 @@ async def to_code(config):
if CONF_BATTERY_VOLTAGE in config:
sens = await sensor.new_sensor(config[CONF_BATTERY_VOLTAGE])
cg.add(var.set_battery_voltage(sens))
if CONF_SIGNAL_STRENGTH in config:
sens = await sensor.new_sensor(config[CONF_SIGNAL_STRENGTH])
cg.add(var.set_signal_strength(sens))

View File

@@ -5,7 +5,7 @@
namespace esphome {
namespace atm90e32 {
static const char *TAG = "atm90e32";
static const char *const TAG = "atm90e32";
void ATM90E32Component::update() {
if (this->read16_(ATM90E32_REGISTER_METEREN) != 1) {
@@ -265,27 +265,57 @@ float ATM90E32Component::get_power_factor_c_() {
}
float ATM90E32Component::get_forward_active_energy_a_() {
uint16_t val = this->read16_(ATM90E32_REGISTER_APENERGYA);
return (float) val * 10 / 3200; // convert register value to WattHours
if ((UINT32_MAX - this->phase_[0].cumulative_forward_active_energy_) > val) {
this->phase_[0].cumulative_forward_active_energy_ += val;
} else {
this->phase_[0].cumulative_forward_active_energy_ = val;
}
return ((float) this->phase_[0].cumulative_forward_active_energy_ * 10 / 3200);
}
float ATM90E32Component::get_forward_active_energy_b_() {
uint16_t val = this->read16_(ATM90E32_REGISTER_APENERGYB);
return (float) val * 10 / 3200;
if (UINT32_MAX - this->phase_[1].cumulative_forward_active_energy_ > val) {
this->phase_[1].cumulative_forward_active_energy_ += val;
} else {
this->phase_[1].cumulative_forward_active_energy_ = val;
}
return ((float) this->phase_[1].cumulative_forward_active_energy_ * 10 / 3200);
}
float ATM90E32Component::get_forward_active_energy_c_() {
uint16_t val = this->read16_(ATM90E32_REGISTER_APENERGYC);
return (float) val * 10 / 3200;
if (UINT32_MAX - this->phase_[2].cumulative_forward_active_energy_ > val) {
this->phase_[2].cumulative_forward_active_energy_ += val;
} else {
this->phase_[2].cumulative_forward_active_energy_ = val;
}
return ((float) this->phase_[2].cumulative_forward_active_energy_ * 10 / 3200);
}
float ATM90E32Component::get_reverse_active_energy_a_() {
uint16_t val = this->read16_(ATM90E32_REGISTER_ANENERGYA);
return (float) val * 10 / 3200;
if (UINT32_MAX - this->phase_[0].cumulative_reverse_active_energy_ > val) {
this->phase_[0].cumulative_reverse_active_energy_ += val;
} else {
this->phase_[0].cumulative_reverse_active_energy_ = val;
}
return ((float) this->phase_[0].cumulative_reverse_active_energy_ * 10 / 3200);
}
float ATM90E32Component::get_reverse_active_energy_b_() {
uint16_t val = this->read16_(ATM90E32_REGISTER_ANENERGYB);
return (float) val * 10 / 3200;
if (UINT32_MAX - this->phase_[1].cumulative_reverse_active_energy_ > val) {
this->phase_[1].cumulative_reverse_active_energy_ += val;
} else {
this->phase_[1].cumulative_reverse_active_energy_ = val;
}
return ((float) this->phase_[1].cumulative_reverse_active_energy_ * 10 / 3200);
}
float ATM90E32Component::get_reverse_active_energy_c_() {
uint16_t val = this->read16_(ATM90E32_REGISTER_ANENERGYC);
return (float) val * 10 / 3200;
if (UINT32_MAX - this->phase_[2].cumulative_reverse_active_energy_ > val) {
this->phase_[2].cumulative_reverse_active_energy_ += val;
} else {
this->phase_[2].cumulative_reverse_active_energy_ = val;
}
return ((float) this->phase_[2].cumulative_reverse_active_energy_ * 10 / 3200);
}
float ATM90E32Component::get_frequency_() {
uint16_t freq = this->read16_(ATM90E32_REGISTER_FREQ);

View File

@@ -77,6 +77,8 @@ class ATM90E32Component : public PollingComponent,
sensor::Sensor *power_factor_sensor_{nullptr};
sensor::Sensor *forward_active_energy_sensor_{nullptr};
sensor::Sensor *reverse_active_energy_sensor_{nullptr};
uint32_t cumulative_forward_active_energy_{0};
uint32_t cumulative_reverse_active_energy_{0};
} phase_[3];
sensor::Sensor *freq_sensor_{nullptr};
sensor::Sensor *chip_temperature_sensor_{nullptr};

View File

@@ -39,7 +39,7 @@ static const uint16_t ATM90E32_STATUS_S0_OVPHASEBST = 1 << 11; // Over voltage
static const uint16_t ATM90E32_STATUS_S0_OVPHASECST = 1 << 10; // Over voltage on phase C
static const uint16_t ATM90E32_STATUS_S0_UREVWNST = 1 << 9; // Voltage Phase Sequence Error status
static const uint16_t ATM90E32_STATUS_S0_IREVWNST = 1 << 8; // Current Phase Sequence Error status
static const uint16_t ATM90E32_STATUS_S0_INOV0ST = 1 << 7; // Calculated N line current greater tha INWarnTh reg
static const uint16_t ATM90E32_STATUS_S0_INOV0ST = 1 << 7; // Calculated N line current greater than INWarnTh reg
static const uint16_t ATM90E32_STATUS_S0_TQNOLOADST = 1 << 6; // All phase sum reactive power no-load condition status
static const uint16_t ATM90E32_STATUS_S0_TPNOLOADST = 1 << 5; // All phase sum active power no-load condition status
static const uint16_t ATM90E32_STATUS_S0_TASNOLOADST = 1 << 4; // All phase sum apparent power no-load status

View File

@@ -12,21 +12,20 @@ from esphome.const import (
CONF_FORWARD_ACTIVE_ENERGY,
CONF_REVERSE_ACTIVE_ENERGY,
DEVICE_CLASS_CURRENT,
DEVICE_CLASS_EMPTY,
DEVICE_CLASS_ENERGY,
DEVICE_CLASS_POWER,
DEVICE_CLASS_POWER_FACTOR,
DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_VOLTAGE,
ICON_EMPTY,
ENTITY_CATEGORY_DIAGNOSTIC,
ICON_LIGHTBULB,
ICON_CURRENT_AC,
STATE_CLASS_MEASUREMENT,
STATE_CLASS_TOTAL_INCREASING,
UNIT_HERTZ,
UNIT_VOLT,
UNIT_AMPERE,
UNIT_WATT,
UNIT_EMPTY,
UNIT_CELSIUS,
UNIT_VOLT_AMPS_REACTIVE,
UNIT_WATT_HOURS,
@@ -64,37 +63,45 @@ ATM90E32Component = atm90e32_ns.class_(
ATM90E32_PHASE_SCHEMA = cv.Schema(
{
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
UNIT_VOLT,
ICON_EMPTY,
2,
DEVICE_CLASS_VOLTAGE,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_VOLT,
accuracy_decimals=2,
device_class=DEVICE_CLASS_VOLTAGE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_CURRENT): sensor.sensor_schema(
UNIT_AMPERE, ICON_EMPTY, 2, DEVICE_CLASS_CURRENT, STATE_CLASS_MEASUREMENT
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_WATT, ICON_EMPTY, 2, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
unit_of_measurement=UNIT_WATT,
accuracy_decimals=2,
device_class=DEVICE_CLASS_POWER,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_REACTIVE_POWER): sensor.sensor_schema(
UNIT_VOLT_AMPS_REACTIVE,
ICON_LIGHTBULB,
2,
DEVICE_CLASS_EMPTY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_VOLT_AMPS_REACTIVE,
icon=ICON_LIGHTBULB,
accuracy_decimals=2,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_POWER_FACTOR): sensor.sensor_schema(
UNIT_EMPTY,
ICON_EMPTY,
2,
DEVICE_CLASS_POWER_FACTOR,
STATE_CLASS_MEASUREMENT,
accuracy_decimals=2,
device_class=DEVICE_CLASS_POWER_FACTOR,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_FORWARD_ACTIVE_ENERGY): sensor.sensor_schema(
UNIT_WATT_HOURS, ICON_EMPTY, 2, DEVICE_CLASS_ENERGY, STATE_CLASS_MEASUREMENT
unit_of_measurement=UNIT_WATT_HOURS,
accuracy_decimals=2,
device_class=DEVICE_CLASS_ENERGY,
state_class=STATE_CLASS_TOTAL_INCREASING,
),
cv.Optional(CONF_REVERSE_ACTIVE_ENERGY): sensor.sensor_schema(
UNIT_WATT_HOURS, ICON_EMPTY, 2, DEVICE_CLASS_ENERGY, STATE_CLASS_MEASUREMENT
unit_of_measurement=UNIT_WATT_HOURS,
accuracy_decimals=2,
device_class=DEVICE_CLASS_ENERGY,
state_class=STATE_CLASS_TOTAL_INCREASING,
),
cv.Optional(CONF_GAIN_VOLTAGE, default=7305): cv.uint16_t,
cv.Optional(CONF_GAIN_CT, default=27961): cv.uint16_t,
@@ -109,18 +116,17 @@ CONFIG_SCHEMA = (
cv.Optional(CONF_PHASE_B): ATM90E32_PHASE_SCHEMA,
cv.Optional(CONF_PHASE_C): ATM90E32_PHASE_SCHEMA,
cv.Optional(CONF_FREQUENCY): sensor.sensor_schema(
UNIT_HERTZ,
ICON_CURRENT_AC,
1,
DEVICE_CLASS_EMPTY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_HERTZ,
icon=ICON_CURRENT_AC,
accuracy_decimals=1,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_CHIP_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS,
ICON_EMPTY,
1,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=1,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
),
cv.Required(CONF_LINE_FREQUENCY): cv.enum(LINE_FREQS, upper=True),
cv.Optional(CONF_CURRENT_PHASES, default="3"): cv.enum(

View File

@@ -1,12 +1,12 @@
#include "b_parasite.h"
#include "esphome/core/log.h"
#ifdef ARDUINO_ARCH_ESP32
#ifdef USE_ESP32
namespace esphome {
namespace b_parasite {
static const char* TAG = "b_parasite";
static const char *const TAG = "b_parasite";
void BParasite::dump_config() {
ESP_LOGCONFIG(TAG, "b_parasite");
@@ -14,27 +14,37 @@ void BParasite::dump_config() {
LOG_SENSOR(" ", "Temperature", this->temperature_);
LOG_SENSOR(" ", "Humidity", this->humidity_);
LOG_SENSOR(" ", "Soil Moisture", this->soil_moisture_);
LOG_SENSOR(" ", "Illuminance", this->illuminance_);
}
bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice& device) {
bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
if (device.address_uint64() != address_) {
ESP_LOGVV(TAG, "parse_device(): unknown MAC address.");
return false;
}
ESP_LOGVV(TAG, "parse_device(): MAC address %s found.", device.address_str().c_str());
const auto& service_datas = device.get_service_datas();
const auto &service_datas = device.get_service_datas();
if (service_datas.size() != 1) {
ESP_LOGE(TAG, "Unexpected service_datas size (%d)", service_datas.size());
return false;
}
const auto& service_data = service_datas[0];
const auto &service_data = service_datas[0];
ESP_LOGVV(TAG, "Service data:");
for (const uint8_t byte : service_data.data) {
ESP_LOGVV(TAG, "0x%02x", byte);
}
const auto& data = service_data.data;
const auto &data = service_data.data;
const uint8_t protocol_version = data[0] >> 4;
if (protocol_version != 1) {
ESP_LOGE(TAG, "Unsupported protocol version: %u", protocol_version);
return false;
}
// Some b-parasite versions have an (optional) illuminance sensor.
bool has_illuminance = data[0] & 0x1;
// Counter for deduplicating messages.
uint8_t counter = data[1] & 0x0f;
@@ -47,7 +57,7 @@ bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice& device) {
uint16_t battery_millivolt = data[2] << 8 | data[3];
float battery_voltage = battery_millivolt / 1000.0f;
// Temperature in 1000 * Celcius.
// Temperature in 1000 * Celsius.
uint16_t temp_millicelcius = data[4] << 8 | data[5];
float temp_celcius = temp_millicelcius / 1000.0f;
@@ -59,6 +69,9 @@ bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice& device) {
uint16_t soil_moisture = data[8] << 8 | data[9];
float moisture_percent = (100.0f * soil_moisture) / (1 << 16);
// Ambient light in lux.
float illuminance = has_illuminance ? data[16] << 8 | data[17] : 0.0f;
if (battery_voltage_ != nullptr) {
battery_voltage_->publish_state(battery_voltage);
}
@@ -71,6 +84,13 @@ bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice& device) {
if (soil_moisture_ != nullptr) {
soil_moisture_->publish_state(moisture_percent);
}
if (illuminance_ != nullptr) {
if (has_illuminance) {
illuminance_->publish_state(illuminance);
} else {
ESP_LOGE(TAG, "No lux information is present in the BLE packet");
}
}
last_processed_counter_ = counter;
return true;
@@ -79,4 +99,4 @@ bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice& device) {
} // namespace b_parasite
} // namespace esphome
#endif // ARDUINO_ARCH_ESP32
#endif // USE_ESP32

View File

@@ -4,7 +4,7 @@
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#ifdef ARDUINO_ARCH_ESP32
#ifdef USE_ESP32
namespace esphome {
namespace b_parasite {
@@ -22,6 +22,7 @@ class BParasite : public Component, public esp32_ble_tracker::ESPBTDeviceListene
void set_temperature(sensor::Sensor *temperature) { temperature_ = temperature; }
void set_humidity(sensor::Sensor *humidity) { humidity_ = humidity; }
void set_soil_moisture(sensor::Sensor *soil_moisture) { soil_moisture_ = soil_moisture; }
void set_illuminance(sensor::Sensor *illuminance) { illuminance_ = illuminance; }
protected:
// The received advertisement packet contains an unsigned 4 bits wrap-around counter
@@ -32,9 +33,10 @@ class BParasite : public Component, public esp32_ble_tracker::ESPBTDeviceListene
sensor::Sensor *temperature_{nullptr};
sensor::Sensor *humidity_{nullptr};
sensor::Sensor *soil_moisture_{nullptr};
sensor::Sensor *illuminance_{nullptr};
};
} // namespace b_parasite
} // namespace esphome
#endif // ARDUINO_ARCH_ESP32
#endif // USE_ESP32

View File

@@ -5,15 +5,18 @@ from esphome.const import (
CONF_BATTERY_VOLTAGE,
CONF_HUMIDITY,
CONF_ID,
CONF_ILLUMINANCE,
CONF_MOISTURE,
CONF_MAC_ADDRESS,
CONF_TEMPERATURE,
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_ILLUMINANCE,
DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_VOLTAGE,
ICON_EMPTY,
ENTITY_CATEGORY_DIAGNOSTIC,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
UNIT_LUX,
UNIT_PERCENT,
UNIT_VOLT,
)
@@ -33,28 +36,35 @@ CONFIG_SCHEMA = (
cv.GenerateID(): cv.declare_id(BParasite),
cv.Required(CONF_MAC_ADDRESS): cv.mac_address,
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS,
ICON_EMPTY,
1,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=1,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
UNIT_PERCENT,
ICON_EMPTY,
1,
DEVICE_CLASS_HUMIDITY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_PERCENT,
accuracy_decimals=1,
device_class=DEVICE_CLASS_HUMIDITY,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_BATTERY_VOLTAGE): sensor.sensor_schema(
UNIT_VOLT, ICON_EMPTY, 3, DEVICE_CLASS_VOLTAGE, STATE_CLASS_MEASUREMENT
unit_of_measurement=UNIT_VOLT,
accuracy_decimals=3,
device_class=DEVICE_CLASS_VOLTAGE,
state_class=STATE_CLASS_MEASUREMENT,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
),
cv.Optional(CONF_MOISTURE): sensor.sensor_schema(
UNIT_PERCENT,
ICON_EMPTY,
1,
DEVICE_CLASS_HUMIDITY,
STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_PERCENT,
accuracy_decimals=1,
device_class=DEVICE_CLASS_HUMIDITY,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_ILLUMINANCE): sensor.sensor_schema(
unit_of_measurement=UNIT_LUX,
accuracy_decimals=0,
device_class=DEVICE_CLASS_ILLUMINANCE,
state_class=STATE_CLASS_MEASUREMENT,
),
}
)
@@ -75,6 +85,7 @@ async def to_code(config):
(CONF_HUMIDITY, var.set_humidity),
(CONF_BATTERY_VOLTAGE, var.set_battery_voltage),
(CONF_MOISTURE, var.set_soil_moisture),
(CONF_ILLUMINANCE, var.set_illuminance),
]:
if config_key in config:
sens = await sensor.new_sensor(config[config_key])

View File

@@ -0,0 +1,239 @@
#include "ballu.h"
#include "esphome/core/log.h"
namespace esphome {
namespace ballu {
static const char *const TAG = "ballu.climate";
const uint16_t BALLU_HEADER_MARK = 9000;
const uint16_t BALLU_HEADER_SPACE = 4500;
const uint16_t BALLU_BIT_MARK = 575;
const uint16_t BALLU_ONE_SPACE = 1675;
const uint16_t BALLU_ZERO_SPACE = 550;
const uint32_t BALLU_CARRIER_FREQUENCY = 38000;
const uint8_t BALLU_STATE_LENGTH = 13;
const uint8_t BALLU_AUTO = 0;
const uint8_t BALLU_COOL = 0x20;
const uint8_t BALLU_DRY = 0x40;
const uint8_t BALLU_HEAT = 0x80;
const uint8_t BALLU_FAN = 0xc0;
const uint8_t BALLU_FAN_AUTO = 0xa0;
const uint8_t BALLU_FAN_HIGH = 0x20;
const uint8_t BALLU_FAN_MED = 0x40;
const uint8_t BALLU_FAN_LOW = 0x60;
const uint8_t BALLU_SWING_VER = 0x07;
const uint8_t BALLU_SWING_HOR = 0xe0;
const uint8_t BALLU_POWER = 0x20;
void BalluClimate::transmit_state() {
uint8_t remote_state[BALLU_STATE_LENGTH] = {0};
auto temp = (uint8_t) roundf(clamp(this->target_temperature, YKR_K_002E_TEMP_MIN, YKR_K_002E_TEMP_MAX));
auto swing_ver =
((this->swing_mode == climate::CLIMATE_SWING_VERTICAL) || (this->swing_mode == climate::CLIMATE_SWING_BOTH));
auto swing_hor =
((this->swing_mode == climate::CLIMATE_SWING_HORIZONTAL) || (this->swing_mode == climate::CLIMATE_SWING_BOTH));
remote_state[0] = 0xc3;
remote_state[1] = ((temp - 8) << 3) | (swing_ver ? 0 : BALLU_SWING_VER);
remote_state[2] = swing_hor ? 0 : BALLU_SWING_HOR;
remote_state[9] = (this->mode == climate::CLIMATE_MODE_OFF) ? 0 : BALLU_POWER;
remote_state[11] = 0x1e;
// Fan speed
switch (this->fan_mode.value()) {
case climate::CLIMATE_FAN_HIGH:
remote_state[4] |= BALLU_FAN_HIGH;
break;
case climate::CLIMATE_FAN_MEDIUM:
remote_state[4] |= BALLU_FAN_MED;
break;
case climate::CLIMATE_FAN_LOW:
remote_state[4] |= BALLU_FAN_LOW;
break;
case climate::CLIMATE_FAN_AUTO:
remote_state[4] |= BALLU_FAN_AUTO;
break;
default:
break;
}
// Mode
switch (this->mode) {
case climate::CLIMATE_MODE_AUTO:
remote_state[6] |= BALLU_AUTO;
break;
case climate::CLIMATE_MODE_HEAT:
remote_state[6] |= BALLU_HEAT;
break;
case climate::CLIMATE_MODE_COOL:
remote_state[6] |= BALLU_COOL;
break;
case climate::CLIMATE_MODE_DRY:
remote_state[6] |= BALLU_DRY;
break;
case climate::CLIMATE_MODE_FAN_ONLY:
remote_state[6] |= BALLU_FAN;
break;
case climate::CLIMATE_MODE_OFF:
remote_state[6] |= BALLU_AUTO;
default:
break;
}
// Checksum
for (uint8_t i = 0; i < BALLU_STATE_LENGTH - 1; i++)
remote_state[12] += remote_state[i];
ESP_LOGV(TAG, "Sending: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", remote_state[0],
remote_state[1], remote_state[2], remote_state[3], remote_state[4], remote_state[5], remote_state[6],
remote_state[7], remote_state[8], remote_state[9], remote_state[10], remote_state[11], remote_state[12]);
// Send code
auto transmit = this->transmitter_->transmit();
auto *data = transmit.get_data();
data->set_carrier_frequency(38000);
// Header
data->mark(BALLU_HEADER_MARK);
data->space(BALLU_HEADER_SPACE);
// Data
for (uint8_t i : remote_state) {
for (uint8_t j = 0; j < 8; j++) {
data->mark(BALLU_BIT_MARK);
bool bit = i & (1 << j);
data->space(bit ? BALLU_ONE_SPACE : BALLU_ZERO_SPACE);
}
}
// Footer
data->mark(BALLU_BIT_MARK);
transmit.perform();
}
bool BalluClimate::on_receive(remote_base::RemoteReceiveData data) {
// Validate header
if (!data.expect_item(BALLU_HEADER_MARK, BALLU_HEADER_SPACE)) {
ESP_LOGV(TAG, "Header fail");
return false;
}
uint8_t remote_state[BALLU_STATE_LENGTH] = {0};
// Read all bytes.
for (int i = 0; i < BALLU_STATE_LENGTH; i++) {
// Read bit
for (int j = 0; j < 8; j++) {
if (data.expect_item(BALLU_BIT_MARK, BALLU_ONE_SPACE)) {
remote_state[i] |= 1 << j;
} else if (!data.expect_item(BALLU_BIT_MARK, BALLU_ZERO_SPACE)) {
ESP_LOGV(TAG, "Byte %d bit %d fail", i, j);
return false;
}
}
ESP_LOGVV(TAG, "Byte %d %02X", i, remote_state[i]);
}
// Validate footer
if (!data.expect_mark(BALLU_BIT_MARK)) {
ESP_LOGV(TAG, "Footer fail");
return false;
}
uint8_t checksum = 0;
// Calculate checksum and compare with signal value.
for (uint8_t i = 0; i < BALLU_STATE_LENGTH - 1; i++)
checksum += remote_state[i];
if (checksum != remote_state[BALLU_STATE_LENGTH - 1]) {
ESP_LOGVV(TAG, "Checksum fail");
return false;
}
ESP_LOGV(TAG, "Received: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", remote_state[0],
remote_state[1], remote_state[2], remote_state[3], remote_state[4], remote_state[5], remote_state[6],
remote_state[7], remote_state[8], remote_state[9], remote_state[10], remote_state[11], remote_state[12]);
// verify header remote code
if (remote_state[0] != 0xc3)
return false;
// powr on/off button
ESP_LOGV(TAG, "Power: %02X", (remote_state[9] & BALLU_POWER));
if ((remote_state[9] & BALLU_POWER) != BALLU_POWER) {
this->mode = climate::CLIMATE_MODE_OFF;
} else {
auto mode = remote_state[6] & 0xe0;
ESP_LOGV(TAG, "Mode: %02X", mode);
switch (mode) {
case BALLU_HEAT:
this->mode = climate::CLIMATE_MODE_HEAT;
break;
case BALLU_COOL:
this->mode = climate::CLIMATE_MODE_COOL;
break;
case BALLU_DRY:
this->mode = climate::CLIMATE_MODE_DRY;
break;
case BALLU_FAN:
this->mode = climate::CLIMATE_MODE_FAN_ONLY;
break;
case BALLU_AUTO:
this->mode = climate::CLIMATE_MODE_AUTO;
break;
}
}
// Set received temp
int temp = remote_state[1] & 0xf8;
ESP_LOGVV(TAG, "Temperature Raw: %02X", temp);
temp = ((uint8_t) temp >> 3) + 8;
ESP_LOGVV(TAG, "Temperature Climate: %u", temp);
this->target_temperature = temp;
// Set received fan speed
auto fan = remote_state[4] & 0xe0;
ESP_LOGVV(TAG, "Fan: %02X", fan);
switch (fan) {
case BALLU_FAN_HIGH:
this->fan_mode = climate::CLIMATE_FAN_HIGH;
break;
case BALLU_FAN_MED:
this->fan_mode = climate::CLIMATE_FAN_MEDIUM;
break;
case BALLU_FAN_LOW:
this->fan_mode = climate::CLIMATE_FAN_LOW;
break;
case BALLU_FAN_AUTO:
default:
this->fan_mode = climate::CLIMATE_FAN_AUTO;
break;
}
// Set received swing status
ESP_LOGVV(TAG, "Swing status: %02X %02X", remote_state[1] & BALLU_SWING_VER, remote_state[2] & BALLU_SWING_HOR);
if (((remote_state[1] & BALLU_SWING_VER) != BALLU_SWING_VER) &&
((remote_state[2] & BALLU_SWING_HOR) != BALLU_SWING_HOR)) {
this->swing_mode = climate::CLIMATE_SWING_BOTH;
} else if ((remote_state[1] & BALLU_SWING_VER) != BALLU_SWING_VER) {
this->swing_mode = climate::CLIMATE_SWING_VERTICAL;
} else if ((remote_state[2] & BALLU_SWING_HOR) != BALLU_SWING_HOR) {
this->swing_mode = climate::CLIMATE_SWING_HORIZONTAL;
} else {
this->swing_mode = climate::CLIMATE_SWING_OFF;
}
this->publish_state();
return true;
}
} // namespace ballu
} // namespace esphome

View File

@@ -0,0 +1,31 @@
#pragma once
#include "esphome/components/climate_ir/climate_ir.h"
namespace esphome {
namespace ballu {
// Support for Ballu air conditioners with YKR-K/002E remote
// Temperature
const float YKR_K_002E_TEMP_MIN = 16.0;
const float YKR_K_002E_TEMP_MAX = 32.0;
class BalluClimate : public climate_ir::ClimateIR {
public:
BalluClimate()
: climate_ir::ClimateIR(YKR_K_002E_TEMP_MIN, YKR_K_002E_TEMP_MAX, 1.0f, true, true,
{climate::CLIMATE_FAN_AUTO, climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM,
climate::CLIMATE_FAN_HIGH},
{climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_VERTICAL,
climate::CLIMATE_SWING_HORIZONTAL, climate::CLIMATE_SWING_BOTH}) {}
protected:
/// Transmit via IR the state of this climate controller.
void transmit_state() override;
/// Handle received IR Buffer
bool on_receive(remote_base::RemoteReceiveData data) override;
};
} // namespace ballu
} // namespace esphome

View File

@@ -0,0 +1,21 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import climate_ir
from esphome.const import CONF_ID
AUTO_LOAD = ["climate_ir"]
CODEOWNERS = ["@bazuchan"]
ballu_ns = cg.esphome_ns.namespace("ballu")
BalluClimate = ballu_ns.class_("BalluClimate", climate_ir.ClimateIR)
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(BalluClimate),
}
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await climate_ir.register_climate_ir(var, config)

View File

@@ -4,7 +4,7 @@
namespace esphome {
namespace bang_bang {
static const char *TAG = "bang_bang.climate";
static const char *const TAG = "bang_bang.climate";
void BangBangClimate::setup() {
this->sensor_->add_on_state_callback([this](float state) {
@@ -21,7 +21,13 @@ void BangBangClimate::setup() {
restore->to_call(this).perform();
} else {
// restore from defaults, change_away handles those for us
this->mode = climate::CLIMATE_MODE_AUTO;
if (supports_cool_ && supports_heat_) {
this->mode = climate::CLIMATE_MODE_HEAT_COOL;
} else if (supports_cool_) {
this->mode = climate::CLIMATE_MODE_COOL;
} else if (supports_heat_) {
this->mode = climate::CLIMATE_MODE_HEAT;
}
this->change_away_(false);
}
}
@@ -32,8 +38,8 @@ void BangBangClimate::control(const climate::ClimateCall &call) {
this->target_temperature_low = *call.get_target_temperature_low();
if (call.get_target_temperature_high().has_value())
this->target_temperature_high = *call.get_target_temperature_high();
if (call.get_away().has_value())
this->change_away_(*call.get_away());
if (call.get_preset().has_value())
this->change_away_(*call.get_preset() == climate::CLIMATE_PRESET_AWAY);
this->compute_state_();
this->publish_state();
@@ -41,24 +47,32 @@ void BangBangClimate::control(const climate::ClimateCall &call) {
climate::ClimateTraits BangBangClimate::traits() {
auto traits = climate::ClimateTraits();
traits.set_supports_current_temperature(true);
traits.set_supports_auto_mode(true);
traits.set_supports_cool_mode(this->supports_cool_);
traits.set_supports_heat_mode(this->supports_heat_);
traits.set_supported_modes({
climate::CLIMATE_MODE_OFF,
});
if (supports_cool_)
traits.add_supported_mode(climate::CLIMATE_MODE_COOL);
if (supports_heat_)
traits.add_supported_mode(climate::CLIMATE_MODE_HEAT);
if (supports_cool_ && supports_heat_)
traits.add_supported_mode(climate::CLIMATE_MODE_HEAT_COOL);
traits.set_supports_two_point_target_temperature(true);
traits.set_supports_away(this->supports_away_);
if (supports_away_) {
traits.set_supported_presets({
climate::CLIMATE_PRESET_HOME,
climate::CLIMATE_PRESET_AWAY,
});
}
traits.set_supports_action(true);
return traits;
}
void BangBangClimate::compute_state_() {
if (this->mode != climate::CLIMATE_MODE_AUTO) {
// in non-auto mode, switch directly to appropriate action
// - HEAT mode -> HEATING action
// - COOL mode -> COOLING action
// - OFF mode -> OFF action (not IDLE!)
this->switch_to_action_(static_cast<climate::ClimateAction>(this->mode));
if (this->mode == climate::CLIMATE_MODE_OFF) {
this->switch_to_action_(climate::CLIMATE_ACTION_OFF);
return;
}
if (isnan(this->current_temperature) || isnan(this->target_temperature_low) || isnan(this->target_temperature_high)) {
if (std::isnan(this->current_temperature) || std::isnan(this->target_temperature_low) ||
std::isnan(this->target_temperature_high)) {
// if any control parameters are nan, go to OFF action (not IDLE!)
this->switch_to_action_(climate::CLIMATE_ACTION_OFF);
return;
@@ -68,21 +82,25 @@ void BangBangClimate::compute_state_() {
climate::ClimateAction target_action;
if (too_cold) {
// too cold -> enable heating if possible, else idle
if (this->supports_heat_)
// too cold -> enable heating if possible and enabled, else idle
if (this->supports_heat_ &&
(this->mode == climate::CLIMATE_MODE_HEAT_COOL || this->mode == climate::CLIMATE_MODE_HEAT)) {
target_action = climate::CLIMATE_ACTION_HEATING;
else
} else {
target_action = climate::CLIMATE_ACTION_IDLE;
}
} else if (too_hot) {
// too hot -> enable cooling if possible, else idle
if (this->supports_cool_)
// too hot -> enable cooling if possible and enabled, else idle
if (this->supports_cool_ &&
(this->mode == climate::CLIMATE_MODE_HEAT_COOL || this->mode == climate::CLIMATE_MODE_COOL)) {
target_action = climate::CLIMATE_ACTION_COOLING;
else
} else {
target_action = climate::CLIMATE_ACTION_IDLE;
}
} else {
// neither too hot nor too cold -> in range
if (this->supports_cool_ && this->supports_heat_) {
// if supports both ends, go to idle action
if (this->supports_cool_ && this->supports_heat_ && this->mode == climate::CLIMATE_MODE_HEAT_COOL) {
// if supports both ends and both cooling and heating enabled, go to idle action
target_action = climate::CLIMATE_ACTION_IDLE;
} else {
// else use current mode and don't change (hysteresis)
@@ -93,9 +111,10 @@ void BangBangClimate::compute_state_() {
this->switch_to_action_(target_action);
}
void BangBangClimate::switch_to_action_(climate::ClimateAction action) {
if (action == this->action)
if (action == this->action) {
// already in target mode
return;
}
if ((action == climate::CLIMATE_ACTION_OFF && this->action == climate::CLIMATE_ACTION_IDLE) ||
(action == climate::CLIMATE_ACTION_IDLE && this->action == climate::CLIMATE_ACTION_OFF)) {
@@ -140,7 +159,7 @@ void BangBangClimate::change_away_(bool away) {
this->target_temperature_low = this->away_config_.default_temperature_low;
this->target_temperature_high = this->away_config_.default_temperature_high;
}
this->away = away;
this->preset = away ? climate::CLIMATE_PRESET_AWAY : climate::CLIMATE_PRESET_HOME;
}
void BangBangClimate::set_normal_config(const BangBangClimateTargetTempConfig &normal_config) {
this->normal_config_ = normal_config;

View File

@@ -4,23 +4,114 @@
namespace esphome {
namespace bh1750 {
static const char *TAG = "bh1750.sensor";
static const char *const TAG = "bh1750.sensor";
static const uint8_t BH1750_COMMAND_POWER_ON = 0b00000001;
static const uint8_t BH1750_COMMAND_MT_REG_HI = 0b01000000; // last 3 bits
static const uint8_t BH1750_COMMAND_MT_REG_LO = 0b01100000; // last 5 bits
static const uint8_t BH1750_COMMAND_ONE_TIME_L = 0b00100011;
static const uint8_t BH1750_COMMAND_ONE_TIME_H = 0b00100000;
static const uint8_t BH1750_COMMAND_ONE_TIME_H2 = 0b00100001;
/*
bh1750 properties:
L-resolution mode:
- resolution 4lx (@ mtreg=69)
- measurement time: typ=16ms, max=24ms, scaled by MTreg value divided by 69
- formula: counts / 1.2 * (69 / MTreg) lx
H-resolution mode:
- resolution 1lx (@ mtreg=69)
- measurement time: typ=120ms, max=180ms, scaled by MTreg value divided by 69
- formula: counts / 1.2 * (69 / MTreg) lx
H-resolution mode2:
- resolution 0.5lx (@ mtreg=69)
- measurement time: typ=120ms, max=180ms, scaled by MTreg value divided by 69
- formula: counts / 1.2 * (69 / MTreg) / 2 lx
MTreg:
- min=31, default=69, max=254
-> only reason to use l-resolution is faster, but offers no higher range
-> below ~7000lx, makes sense to use H-resolution2 @ MTreg=254
-> try to maximize MTreg to get lowest noise level
*/
void BH1750Sensor::setup() {
ESP_LOGCONFIG(TAG, "Setting up BH1750 '%s'...", this->name_.c_str());
if (!this->write_bytes(BH1750_COMMAND_POWER_ON, nullptr, 0)) {
uint8_t turn_on = BH1750_COMMAND_POWER_ON;
if (this->write(&turn_on, 1) != i2c::ERROR_OK) {
this->mark_failed();
return;
}
}
uint8_t mtreg_hi = (this->measurement_duration_ >> 5) & 0b111;
uint8_t mtreg_lo = (this->measurement_duration_ >> 0) & 0b11111;
this->write_bytes(BH1750_COMMAND_MT_REG_HI | mtreg_hi, nullptr, 0);
this->write_bytes(BH1750_COMMAND_MT_REG_LO | mtreg_lo, nullptr, 0);
void BH1750Sensor::read_lx_(BH1750Mode mode, uint8_t mtreg, const std::function<void(float)> &f) {
// turn on (after one-shot sensor automatically powers down)
uint8_t turn_on = BH1750_COMMAND_POWER_ON;
if (this->write(&turn_on, 1) != i2c::ERROR_OK) {
ESP_LOGW(TAG, "Turning on BH1750 failed");
f(NAN);
return;
}
if (active_mtreg_ != mtreg) {
// set mtreg
uint8_t mtreg_hi = BH1750_COMMAND_MT_REG_HI | ((mtreg >> 5) & 0b111);
uint8_t mtreg_lo = BH1750_COMMAND_MT_REG_LO | ((mtreg >> 0) & 0b11111);
if (this->write(&mtreg_hi, 1) != i2c::ERROR_OK || this->write(&mtreg_lo, 1) != i2c::ERROR_OK) {
ESP_LOGW(TAG, "Setting measurement time for BH1750 failed");
active_mtreg_ = 0;
f(NAN);
return;
}
active_mtreg_ = mtreg;
}
uint8_t cmd;
uint16_t meas_time;
switch (mode) {
case BH1750_MODE_L:
cmd = BH1750_COMMAND_ONE_TIME_L;
meas_time = 24 * mtreg / 69;
break;
case BH1750_MODE_H:
cmd = BH1750_COMMAND_ONE_TIME_H;
meas_time = 180 * mtreg / 69;
break;
case BH1750_MODE_H2:
cmd = BH1750_COMMAND_ONE_TIME_H2;
meas_time = 180 * mtreg / 69;
break;
default:
f(NAN);
return;
}
if (this->write(&cmd, 1) != i2c::ERROR_OK) {
ESP_LOGW(TAG, "Starting measurement for BH1750 failed");
f(NAN);
return;
}
// probably not needed, but adjust for rounding
meas_time++;
this->set_timeout("read", meas_time, [this, mode, mtreg, f]() {
uint16_t raw_value;
if (this->read(reinterpret_cast<uint8_t *>(&raw_value), 2) != i2c::ERROR_OK) {
ESP_LOGW(TAG, "Reading BH1750 data failed");
f(NAN);
return;
}
raw_value = i2c::i2ctohs(raw_value);
float lx = float(raw_value) / 1.2f;
lx *= 69.0f / mtreg;
if (mode == BH1750_MODE_H2)
lx /= 2.0f;
f(lx);
});
}
void BH1750Sensor::dump_config() {
@@ -30,60 +121,49 @@ void BH1750Sensor::dump_config() {
ESP_LOGE(TAG, "Communication with BH1750 failed!");
}
const char *resolution_s;
switch (this->resolution_) {
case BH1750_RESOLUTION_0P5_LX:
resolution_s = "0.5";
break;
case BH1750_RESOLUTION_1P0_LX:
resolution_s = "1";
break;
case BH1750_RESOLUTION_4P0_LX:
resolution_s = "4";
break;
default:
resolution_s = "Unknown";
break;
}
ESP_LOGCONFIG(TAG, " Resolution: %s", resolution_s);
LOG_UPDATE_INTERVAL(this);
}
void BH1750Sensor::update() {
if (!this->write_bytes(this->resolution_, nullptr, 0))
return;
// first do a quick measurement in L-mode with full range
// to find right range
this->read_lx_(BH1750_MODE_L, 31, [this](float val) {
if (std::isnan(val)) {
this->status_set_warning();
this->publish_state(NAN);
return;
}
uint32_t wait = 0;
// use max conversion times
switch (this->resolution_) {
case BH1750_RESOLUTION_0P5_LX:
case BH1750_RESOLUTION_1P0_LX:
wait = 180;
break;
case BH1750_RESOLUTION_4P0_LX:
wait = 24;
break;
}
BH1750Mode use_mode;
uint8_t use_mtreg;
if (val <= 7000) {
use_mode = BH1750_MODE_H2;
use_mtreg = 254;
} else {
use_mode = BH1750_MODE_H;
// lx = counts / 1.2 * (69 / mtreg)
// -> mtreg = counts / 1.2 * (69 / lx)
// calculate for counts=50000 (allow some range to not saturate, but maximize mtreg)
// -> mtreg = 50000*(10/12)*(69/lx)
int ideal_mtreg = 50000 * 10 * 69 / (12 * (int) val);
use_mtreg = std::min(254, std::max(31, ideal_mtreg));
}
ESP_LOGV(TAG, "L result: %f -> Calculated mode=%d, mtreg=%d", val, (int) use_mode, use_mtreg);
this->set_timeout("illuminance", wait, [this]() { this->read_data_(); });
this->read_lx_(use_mode, use_mtreg, [this](float val) {
if (std::isnan(val)) {
this->status_set_warning();
this->publish_state(NAN);
return;
}
ESP_LOGD(TAG, "'%s': Got illuminance=%.1flx", this->get_name().c_str(), val);
this->status_clear_warning();
this->publish_state(val);
});
});
}
float BH1750Sensor::get_setup_priority() const { return setup_priority::DATA; }
void BH1750Sensor::read_data_() {
uint16_t raw_value;
if (!this->parent_->raw_receive_16(this->address_, &raw_value, 1)) {
this->status_set_warning();
return;
}
float lx = float(raw_value) / 1.2f;
lx *= 69.0f / this->measurement_duration_;
ESP_LOGD(TAG, "'%s': Got illuminance=%.1flx", this->get_name().c_str(), lx);
this->publish_state(lx);
this->status_clear_warning();
}
void BH1750Sensor::set_resolution(BH1750Resolution resolution) { this->resolution_ = resolution; }
} // namespace bh1750
} // namespace esphome

View File

@@ -7,29 +7,15 @@
namespace esphome {
namespace bh1750 {
/// Enum listing all resolutions that can be used with the BH1750
enum BH1750Resolution {
BH1750_RESOLUTION_4P0_LX = 0b00100011, // one-time low resolution mode
BH1750_RESOLUTION_1P0_LX = 0b00100000, // one-time high resolution mode 1
BH1750_RESOLUTION_0P5_LX = 0b00100001, // one-time high resolution mode 2
enum BH1750Mode {
BH1750_MODE_L,
BH1750_MODE_H,
BH1750_MODE_H2,
};
/// This class implements support for the i2c-based BH1750 ambient light sensor.
class BH1750Sensor : public sensor::Sensor, public PollingComponent, public i2c::I2CDevice {
public:
/** Set the resolution of this sensor.
*
* Possible values are:
*
* - `BH1750_RESOLUTION_4P0_LX`
* - `BH1750_RESOLUTION_1P0_LX`
* - `BH1750_RESOLUTION_0P5_LX` (default)
*
* @param resolution The new resolution of the sensor.
*/
void set_resolution(BH1750Resolution resolution);
void set_measurement_duration(uint8_t measurement_duration) { measurement_duration_ = measurement_duration; }
// ========== INTERNAL METHODS ==========
// (In most use cases you won't need these)
void setup() override;
@@ -38,10 +24,9 @@ class BH1750Sensor : public sensor::Sensor, public PollingComponent, public i2c:
float get_setup_priority() const override;
protected:
void read_data_();
void read_lx_(BH1750Mode mode, uint8_t mtreg, const std::function<void(float)> &f);
BH1750Resolution resolution_{BH1750_RESOLUTION_0P5_LX};
uint8_t measurement_duration_;
uint8_t active_mtreg_{0};
};
} // namespace bh1750

View File

@@ -2,45 +2,35 @@ import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c, sensor
from esphome.const import (
CONF_ID,
CONF_RESOLUTION,
DEVICE_CLASS_ILLUMINANCE,
ICON_EMPTY,
STATE_CLASS_MEASUREMENT,
UNIT_LUX,
CONF_MEASUREMENT_DURATION,
)
DEPENDENCIES = ["i2c"]
CODEOWNERS = ["@OttoWinter"]
bh1750_ns = cg.esphome_ns.namespace("bh1750")
BH1750Resolution = bh1750_ns.enum("BH1750Resolution")
BH1750_RESOLUTIONS = {
4.0: BH1750Resolution.BH1750_RESOLUTION_4P0_LX,
1.0: BH1750Resolution.BH1750_RESOLUTION_1P0_LX,
0.5: BH1750Resolution.BH1750_RESOLUTION_0P5_LX,
}
BH1750Sensor = bh1750_ns.class_(
"BH1750Sensor", sensor.Sensor, cg.PollingComponent, i2c.I2CDevice
)
CONF_MEASUREMENT_TIME = "measurement_time"
CONFIG_SCHEMA = (
sensor.sensor_schema(
UNIT_LUX, ICON_EMPTY, 1, DEVICE_CLASS_ILLUMINANCE, STATE_CLASS_MEASUREMENT
BH1750Sensor,
unit_of_measurement=UNIT_LUX,
accuracy_decimals=1,
device_class=DEVICE_CLASS_ILLUMINANCE,
state_class=STATE_CLASS_MEASUREMENT,
)
.extend(
{
cv.GenerateID(): cv.declare_id(BH1750Sensor),
cv.Optional(CONF_RESOLUTION, default=0.5): cv.enum(
BH1750_RESOLUTIONS, float=True
cv.Optional("resolution"): cv.invalid(
"The 'resolution' option has been removed. The optimal value is now dynamically calculated."
),
cv.Optional(CONF_MEASUREMENT_DURATION, default=69): cv.int_range(
min=31, max=254
),
cv.Optional(CONF_MEASUREMENT_TIME): cv.invalid(
"The 'measurement_time' option has been replaced with 'measurement_duration' in 1.18.0"
cv.Optional("measurement_duration"): cv.invalid(
"The 'measurement_duration' option has been removed. The optimal value is now dynamically calculated."
),
}
)
@@ -50,10 +40,6 @@ CONFIG_SCHEMA = (
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
var = await sensor.new_sensor(config)
await cg.register_component(var, config)
await sensor.register_sensor(var, config)
await i2c.register_i2c_device(var, config)
cg.add(var.set_resolution(config[CONF_RESOLUTION]))
cg.add(var.set_measurement_duration(config[CONF_MEASUREMENT_DURATION]))

View File

@@ -9,7 +9,7 @@ from esphome.const import (
)
from .. import binary_ns
BinaryFan = binary_ns.class_("BinaryFan", cg.Component)
BinaryFan = binary_ns.class_("BinaryFan", fan.Fan, cg.Component)
CONFIG_SCHEMA = fan.FAN_SCHEMA.extend(
{
@@ -24,9 +24,8 @@ CONFIG_SCHEMA = fan.FAN_SCHEMA.extend(
async def to_code(config):
var = cg.new_Pvariable(config[CONF_OUTPUT_ID])
await cg.register_component(var, config)
await fan.register_fan(var, config)
fan_ = await fan.create_fan_state(config)
cg.add(var.set_fan(fan_))
output_ = await cg.get_variable(config[CONF_OUTPUT])
cg.add(var.set_output(output_))

View File

@@ -4,58 +4,37 @@
namespace esphome {
namespace binary {
static const char *TAG = "binary.fan";
static const char *const TAG = "binary.fan";
void binary::BinaryFan::dump_config() {
ESP_LOGCONFIG(TAG, "Fan '%s':", this->fan_->get_name().c_str());
if (this->fan_->get_traits().supports_oscillation()) {
ESP_LOGCONFIG(TAG, " Oscillation: YES");
}
if (this->fan_->get_traits().supports_direction()) {
ESP_LOGCONFIG(TAG, " Direction: YES");
}
}
void BinaryFan::setup() {
auto traits = fan::FanTraits(this->oscillating_ != nullptr, false, this->direction_ != nullptr, 0);
this->fan_->set_traits(traits);
this->fan_->add_on_state_callback([this]() { this->next_update_ = true; });
}
void BinaryFan::loop() {
if (!this->next_update_) {
return;
}
this->next_update_ = false;
{
bool enable = this->fan_->state;
if (enable)
this->output_->turn_on();
else
this->output_->turn_off();
ESP_LOGD(TAG, "Setting binary state: %s", ONOFF(enable));
}
if (this->oscillating_ != nullptr) {
bool enable = this->fan_->oscillating;
if (enable) {
this->oscillating_->turn_on();
} else {
this->oscillating_->turn_off();
}
ESP_LOGD(TAG, "Setting oscillation: %s", ONOFF(enable));
}
if (this->direction_ != nullptr) {
bool enable = this->fan_->direction == fan::FAN_DIRECTION_REVERSE;
if (enable) {
this->direction_->turn_on();
} else {
this->direction_->turn_off();
}
ESP_LOGD(TAG, "Setting reverse direction: %s", ONOFF(enable));
auto restore = this->restore_state_();
if (restore.has_value()) {
restore->apply(*this);
this->write_state_();
}
}
float BinaryFan::get_setup_priority() const { return setup_priority::DATA; }
void BinaryFan::dump_config() { LOG_FAN("", "Binary Fan", this); }
fan::FanTraits BinaryFan::get_traits() {
return fan::FanTraits(this->oscillating_ != nullptr, false, this->direction_ != nullptr, 0);
}
void BinaryFan::control(const fan::FanCall &call) {
if (call.get_state().has_value())
this->state = *call.get_state();
if (call.get_oscillating().has_value())
this->oscillating = *call.get_oscillating();
if (call.get_direction().has_value())
this->direction = *call.get_direction();
this->write_state_();
this->publish_state();
}
void BinaryFan::write_state_() {
this->output_->set_state(this->state);
if (this->oscillating_ != nullptr)
this->oscillating_->set_state(this->oscillating);
if (this->direction_ != nullptr)
this->direction_->set_state(this->direction == fan::FanDirection::REVERSE);
}
} // namespace binary
} // namespace esphome

View File

@@ -2,28 +2,29 @@
#include "esphome/core/component.h"
#include "esphome/components/output/binary_output.h"
#include "esphome/components/fan/fan_state.h"
#include "esphome/components/fan/fan.h"
namespace esphome {
namespace binary {
class BinaryFan : public Component {
class BinaryFan : public Component, public fan::Fan {
public:
void set_fan(fan::FanState *fan) { fan_ = fan; }
void set_output(output::BinaryOutput *output) { output_ = output; }
void setup() override;
void loop() override;
void dump_config() override;
float get_setup_priority() const override;
void set_output(output::BinaryOutput *output) { this->output_ = output; }
void set_oscillating(output::BinaryOutput *oscillating) { this->oscillating_ = oscillating; }
void set_direction(output::BinaryOutput *direction) { this->direction_ = direction; }
fan::FanTraits get_traits() override;
protected:
fan::FanState *fan_;
void control(const fan::FanCall &call) override;
void write_state_();
output::BinaryOutput *output_;
output::BinaryOutput *oscillating_{nullptr};
output::BinaryOutput *direction_{nullptr};
bool next_update_{true};
};
} // namespace binary

View File

@@ -12,7 +12,7 @@ class BinaryLightOutput : public light::LightOutput {
void set_output(output::BinaryOutput *output) { output_ = output; }
light::LightTraits get_traits() override {
auto traits = light::LightTraits();
traits.set_supports_brightness(false);
traits.set_supported_color_modes({light::ColorMode::ON_OFF});
return traits;
}
void write_state(light::LightState *state) override {

View File

@@ -1,14 +1,17 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.cpp_generator import MockObjClass
from esphome.cpp_helpers import setup_entity
from esphome import automation, core
from esphome.automation import Condition, maybe_simple_id
from esphome.components import mqtt
from esphome.const import (
CONF_DELAY,
CONF_DEVICE_CLASS,
CONF_ENTITY_CATEGORY,
CONF_FILTERS,
CONF_ICON,
CONF_ID,
CONF_INTERNAL,
CONF_INVALID_COOLDOWN,
CONF_INVERTED,
CONF_MAX_LENGTH,
@@ -22,8 +25,6 @@ from esphome.const import (
CONF_STATE,
CONF_TIMING,
CONF_TRIGGER_ID,
CONF_FOR,
CONF_NAME,
CONF_MQTT_ID,
DEVICE_CLASS_EMPTY,
DEVICE_CLASS_BATTERY,
@@ -45,9 +46,12 @@ from esphome.const import (
DEVICE_CLASS_POWER,
DEVICE_CLASS_PRESENCE,
DEVICE_CLASS_PROBLEM,
DEVICE_CLASS_RUNNING,
DEVICE_CLASS_SAFETY,
DEVICE_CLASS_SMOKE,
DEVICE_CLASS_SOUND,
DEVICE_CLASS_TAMPER,
DEVICE_CLASS_UPDATE,
DEVICE_CLASS_VIBRATION,
DEVICE_CLASS_WINDOW,
)
@@ -76,9 +80,12 @@ DEVICE_CLASSES = [
DEVICE_CLASS_POWER,
DEVICE_CLASS_PRESENCE,
DEVICE_CLASS_PROBLEM,
DEVICE_CLASS_RUNNING,
DEVICE_CLASS_SAFETY,
DEVICE_CLASS_SMOKE,
DEVICE_CLASS_SOUND,
DEVICE_CLASS_TAMPER,
DEVICE_CLASS_UPDATE,
DEVICE_CLASS_VIBRATION,
DEVICE_CLASS_WINDOW,
]
@@ -86,7 +93,7 @@ DEVICE_CLASSES = [
IS_PLATFORM_COMPONENT = True
binary_sensor_ns = cg.esphome_ns.namespace("binary_sensor")
BinarySensor = binary_sensor_ns.class_("BinarySensor", cg.Nameable)
BinarySensor = binary_sensor_ns.class_("BinarySensor", cg.EntityBase)
BinarySensorInitiallyOff = binary_sensor_ns.class_(
"BinarySensorInitiallyOff", BinarySensor
)
@@ -229,17 +236,16 @@ def parse_multi_click_timing_str(value):
parts = value.lower().split(" ")
if len(parts) != 5:
raise cv.Invalid(
"Multi click timing grammar consists of exactly 5 words, not {}"
"".format(len(parts))
f"Multi click timing grammar consists of exactly 5 words, not {len(parts)}"
)
try:
state = cv.boolean(parts[0])
except cv.Invalid:
# pylint: disable=raise-missing-from
raise cv.Invalid("First word must either be ON or OFF, not {}".format(parts[0]))
raise cv.Invalid(f"First word must either be ON or OFF, not {parts[0]}")
if parts[1] != "for":
raise cv.Invalid("Second word must be 'for', got {}".format(parts[1]))
raise cv.Invalid(f"Second word must be 'for', got {parts[1]}")
if parts[2] == "at":
if parts[3] == "least":
@@ -248,8 +254,7 @@ def parse_multi_click_timing_str(value):
key = CONF_MAX_LENGTH
else:
raise cv.Invalid(
"Third word after at must either be 'least' or 'most', got {}"
"".format(parts[3])
f"Third word after at must either be 'least' or 'most', got {parts[3]}"
)
try:
length = cv.positive_time_period_milliseconds(parts[4])
@@ -294,13 +299,11 @@ def validate_multi_click_timing(value):
new_state = v_.get(CONF_STATE, not state)
if new_state == state:
raise cv.Invalid(
"Timings must have alternating state. Indices {} and {} have "
"the same state {}".format(i, i + 1, state)
f"Timings must have alternating state. Indices {i} and {i + 1} have the same state {state}"
)
if max_length is not None and max_length < min_length:
raise cv.Invalid(
"Max length ({}) must be larger than min length ({})."
"".format(max_length, min_length)
f"Max length ({max_length}) must be larger than min length ({min_length})."
)
state = new_state
@@ -314,15 +317,16 @@ def validate_multi_click_timing(value):
return timings
device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
BINARY_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend(
BINARY_SENSOR_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend(
{
cv.GenerateID(): cv.declare_id(BinarySensor),
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(
mqtt.MQTTBinarySensorComponent
),
cv.Optional(CONF_DEVICE_CLASS): device_class,
cv.Optional(CONF_DEVICE_CLASS): validate_device_class,
cv.Optional(CONF_FILTERS): validate_filters,
cv.Optional(CONF_ON_PRESS): automation.validate_automation(
{
@@ -372,19 +376,46 @@ BINARY_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend(
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateTrigger),
}
),
cv.Optional(CONF_INVERTED): cv.invalid(
"The inverted binary_sensor property has been replaced by the "
"new 'invert' binary sensor filter. Please see "
"https://esphome.io/components/binary_sensor/index.html."
),
}
)
_UNDEF = object()
def binary_sensor_schema(
class_: MockObjClass = _UNDEF,
*,
icon: str = _UNDEF,
entity_category: str = _UNDEF,
device_class: str = _UNDEF,
) -> cv.Schema:
schema = BINARY_SENSOR_SCHEMA
if class_ is not _UNDEF:
schema = schema.extend({cv.GenerateID(): cv.declare_id(class_)})
if icon is not _UNDEF:
schema = schema.extend({cv.Optional(CONF_ICON, default=icon): cv.icon})
if entity_category is not _UNDEF:
schema = schema.extend(
{
cv.Optional(
CONF_ENTITY_CATEGORY, default=entity_category
): cv.entity_category
}
)
if device_class is not _UNDEF:
schema = schema.extend(
{
cv.Optional(
CONF_DEVICE_CLASS, default=device_class
): validate_device_class
}
)
return schema
async def setup_binary_sensor_core_(var, config):
cg.add(var.set_name(config[CONF_NAME]))
if CONF_INTERNAL in config:
cg.add(var.set_internal(config[CONF_INTERNAL]))
await setup_entity(var, config)
if CONF_DEVICE_CLASS in config:
cg.add(var.set_device_class(config[CONF_DEVICE_CLASS]))
if CONF_INVERTED in config:
@@ -447,7 +478,7 @@ async def register_binary_sensor(var, config):
async def new_binary_sensor(config):
var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME])
var = cg.new_Pvariable(config[CONF_ID])
await register_binary_sensor(var, config)
return var
@@ -455,10 +486,6 @@ async def new_binary_sensor(config):
BINARY_SENSOR_CONDITION_SCHEMA = maybe_simple_id(
{
cv.Required(CONF_ID): cv.use_id(BinarySensor),
cv.Optional(CONF_FOR): cv.invalid(
"This option has been removed in 1.13, please use the "
"'for' condition instead."
),
}
)

View File

@@ -4,7 +4,7 @@
namespace esphome {
namespace binary_sensor {
static const char *TAG = "binary_sensor.automation";
static const char *const TAG = "binary_sensor.automation";
void binary_sensor::MultiClickTrigger::on_state_(bool state) {
// Handle duplicate events
@@ -80,6 +80,10 @@ void binary_sensor::MultiClickTrigger::schedule_cooldown_() {
this->cancel_timeout("is_not_valid");
}
void binary_sensor::MultiClickTrigger::schedule_is_valid_(uint32_t min_length) {
if (min_length == 0) {
this->is_valid_ = true;
return;
}
this->is_valid_ = false;
this->set_timeout("is_valid", min_length, [this]() {
ESP_LOGV(TAG, "Multi Click: You can now %s the button.", this->parent_->state ? "RELEASE" : "PRESS");

View File

@@ -1,7 +1,10 @@
#pragma once
#include <utility>
#include "esphome/core/component.h"
#include "esphome/core/automation.h"
#include "esphome/core/hal.h"
#include "esphome/components/binary_sensor/binary_sensor.h"
namespace esphome {
@@ -87,8 +90,8 @@ class DoubleClickTrigger : public Trigger<> {
class MultiClickTrigger : public Trigger<>, public Component {
public:
explicit MultiClickTrigger(BinarySensor *parent, const std::vector<MultiClickTriggerEvent> &timing)
: parent_(parent), timing_(timing) {}
explicit MultiClickTrigger(BinarySensor *parent, std::vector<MultiClickTriggerEvent> timing)
: parent_(parent), timing_(std::move(timing)) {}
void setup() override {
this->last_state_ = this->parent_->state;

View File

@@ -5,7 +5,7 @@ namespace esphome {
namespace binary_sensor {
static const char *TAG = "binary_sensor";
static const char *const TAG = "binary_sensor";
void BinarySensor::add_on_state_callback(std::function<void(bool)> &&callback) {
this->state_callback_.add(std::move(callback));
@@ -42,13 +42,15 @@ void BinarySensor::send_state_internal(bool state, bool is_initial) {
}
}
std::string BinarySensor::device_class() { return ""; }
BinarySensor::BinarySensor(const std::string &name) : Nameable(name), state(false) {}
BinarySensor::BinarySensor() : BinarySensor("") {}
BinarySensor::BinarySensor() : state(false) {}
void BinarySensor::set_device_class(const std::string &device_class) { this->device_class_ = device_class; }
std::string BinarySensor::get_device_class() {
if (this->device_class_.has_value())
return *this->device_class_;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
return this->device_class();
#pragma GCC diagnostic pop
}
void BinarySensor::add_filter(Filter *filter) {
filter->parent_ = this;
@@ -61,7 +63,7 @@ void BinarySensor::add_filter(Filter *filter) {
last_filter->next_ = filter;
}
}
void BinarySensor::add_filters(std::vector<Filter *> filters) {
void BinarySensor::add_filters(const std::vector<Filter *> &filters) {
for (Filter *filter : filters) {
this->add_filter(filter);
}

View File

@@ -1,6 +1,7 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/entity_base.h"
#include "esphome/core/helpers.h"
#include "esphome/components/binary_sensor/filter.h"
@@ -9,10 +10,10 @@ namespace esphome {
namespace binary_sensor {
#define LOG_BINARY_SENSOR(prefix, type, obj) \
if (obj != nullptr) { \
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, obj->get_name().c_str()); \
if (!obj->get_device_class().empty()) { \
ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, obj->get_device_class().c_str()); \
if ((obj) != nullptr) { \
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \
if (!(obj)->get_device_class().empty()) { \
ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, (obj)->get_device_class().c_str()); \
} \
}
@@ -22,14 +23,9 @@ namespace binary_sensor {
* The sub classes should notify the front-end of new states via the publish_state() method which
* handles inverted inputs for you.
*/
class BinarySensor : public Nameable {
class BinarySensor : public EntityBase {
public:
explicit BinarySensor();
/** Construct a binary sensor with the specified name
*
* @param name Name of this binary sensor.
*/
explicit BinarySensor(const std::string &name);
/** Add a callback to be notified of state changes.
*
@@ -60,7 +56,7 @@ class BinarySensor : public Nameable {
std::string get_device_class();
void add_filter(Filter *filter);
void add_filters(std::vector<Filter *> filters);
void add_filters(const std::vector<Filter *> &filters);
// ========== INTERNAL METHODS ==========
// (In most use cases you won't need these)
@@ -73,7 +69,10 @@ class BinarySensor : public Nameable {
// ========== OVERRIDE METHODS ==========
// (You'll only need this when creating your own custom binary sensor)
/// Get the default device class for this sensor, or empty string for no default.
/** Override this to set the default device class.
*
* @deprecated This method is deprecated, set the property during config validation instead. (2022.1)
*/
virtual std::string device_class();
protected:

View File

@@ -1,11 +1,13 @@
#include "filter.h"
#include "binary_sensor.h"
#include <utility>
namespace esphome {
namespace binary_sensor {
static const char *TAG = "sensor.filter";
static const char *const TAG = "sensor.filter";
void Filter::output(bool value, bool is_initial) {
if (!this->dedup_.next(value))
@@ -64,7 +66,7 @@ float DelayedOffFilter::get_setup_priority() const { return setup_priority::HARD
optional<bool> InvertFilter::new_value(bool value, bool is_initial) { return !value; }
AutorepeatFilter::AutorepeatFilter(const std::vector<AutorepeatFilterTiming> &timings) : timings_(timings) {}
AutorepeatFilter::AutorepeatFilter(std::vector<AutorepeatFilterTiming> timings) : timings_(std::move(timings)) {}
optional<bool> AutorepeatFilter::new_value(bool value, bool is_initial) {
if (value) {
@@ -108,7 +110,7 @@ void AutorepeatFilter::next_value_(bool val) {
float AutorepeatFilter::get_setup_priority() const { return setup_priority::HARDWARE; }
LambdaFilter::LambdaFilter(const std::function<optional<bool>(bool)> &f) : f_(f) {}
LambdaFilter::LambdaFilter(std::function<optional<bool>(bool)> f) : f_(std::move(f)) {}
optional<bool> LambdaFilter::new_value(bool value, bool is_initial) { return this->f_(value); }

View File

@@ -79,7 +79,7 @@ struct AutorepeatFilterTiming {
class AutorepeatFilter : public Filter, public Component {
public:
explicit AutorepeatFilter(const std::vector<AutorepeatFilterTiming> &timings);
explicit AutorepeatFilter(std::vector<AutorepeatFilterTiming> timings);
optional<bool> new_value(bool value, bool is_initial) override;
@@ -95,7 +95,7 @@ class AutorepeatFilter : public Filter, public Component {
class LambdaFilter : public Filter {
public:
explicit LambdaFilter(const std::function<optional<bool>(bool)> &f);
explicit LambdaFilter(std::function<optional<bool>(bool)> f);
optional<bool> new_value(bool value, bool is_initial) override;

View File

@@ -4,7 +4,7 @@
namespace esphome {
namespace binary_sensor_map {
static const char *TAG = "binary_sensor_map";
static const char *const TAG = "binary_sensor_map";
void BinarySensorMap::dump_config() { LOG_SENSOR(" ", "binary_sensor_map", this); }

View File

@@ -3,16 +3,12 @@ import esphome.config_validation as cv
from esphome.components import sensor, binary_sensor
from esphome.const import (
CONF_ID,
CONF_CHANNELS,
CONF_VALUE,
CONF_TYPE,
DEVICE_CLASS_EMPTY,
UNIT_EMPTY,
ICON_CHECK_CIRCLE_OUTLINE,
CONF_BINARY_SENSOR,
CONF_GROUP,
STATE_CLASS_NONE,
)
DEPENDENCIES = ["binary_sensor"]
@@ -35,14 +31,11 @@ entry = {
CONFIG_SCHEMA = cv.typed_schema(
{
CONF_GROUP: sensor.sensor_schema(
UNIT_EMPTY,
ICON_CHECK_CIRCLE_OUTLINE,
0,
DEVICE_CLASS_EMPTY,
STATE_CLASS_NONE,
BinarySensorMap,
icon=ICON_CHECK_CIRCLE_OUTLINE,
accuracy_decimals=0,
).extend(
{
cv.GenerateID(): cv.declare_id(BinarySensorMap),
cv.Required(CONF_CHANNELS): cv.All(
cv.ensure_list(entry), cv.Length(min=1)
),
@@ -54,9 +47,8 @@ CONFIG_SCHEMA = cv.typed_schema(
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
var = await sensor.new_sensor(config)
await cg.register_component(var, config)
await sensor.register_sensor(var, config)
constant = SENSOR_MAP_TYPES[config[CONF_TYPE]]
cg.add(var.set_sensor_type(constant))

View File

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

View File

@@ -0,0 +1,137 @@
#include "bl0940.h"
#include "esphome/core/log.h"
namespace esphome {
namespace bl0940 {
static const char *const TAG = "bl0940";
static const uint8_t BL0940_READ_COMMAND = 0x50; // 0x58 according to documentation
static const uint8_t BL0940_FULL_PACKET = 0xAA;
static const uint8_t BL0940_PACKET_HEADER = 0x55; // 0x58 according to documentation
static const uint8_t BL0940_WRITE_COMMAND = 0xA0; // 0xA8 according to documentation
static const uint8_t BL0940_REG_I_FAST_RMS_CTRL = 0x10;
static const uint8_t BL0940_REG_MODE = 0x18;
static const uint8_t BL0940_REG_SOFT_RESET = 0x19;
static const uint8_t BL0940_REG_USR_WRPROT = 0x1A;
static const uint8_t BL0940_REG_TPS_CTRL = 0x1B;
const uint8_t BL0940_INIT[5][6] = {
// Reset to default
{BL0940_WRITE_COMMAND, BL0940_REG_SOFT_RESET, 0x5A, 0x5A, 0x5A, 0x38},
// Enable User Operation Write
{BL0940_WRITE_COMMAND, BL0940_REG_USR_WRPROT, 0x55, 0x00, 0x00, 0xF0},
// 0x0100 = CF_UNABLE energy pulse, AC_FREQ_SEL 50Hz, RMS_UPDATE_SEL 800mS
{BL0940_WRITE_COMMAND, BL0940_REG_MODE, 0x00, 0x10, 0x00, 0x37},
// 0x47FF = Over-current and leakage alarm on, Automatic temperature measurement, Interval 100mS
{BL0940_WRITE_COMMAND, BL0940_REG_TPS_CTRL, 0xFF, 0x47, 0x00, 0xFE},
// 0x181C = Half cycle, Fast RMS threshold 6172
{BL0940_WRITE_COMMAND, BL0940_REG_I_FAST_RMS_CTRL, 0x1C, 0x18, 0x00, 0x1B}};
void BL0940::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 BL0940::validate_checksum(const DataPacket *data) {
uint8_t checksum = BL0940_READ_COMMAND;
// Whole package but checksum
for (uint32_t i = 0; i < sizeof(data->raw) - 1; i++) {
checksum += data->raw[i];
}
checksum ^= 0xFF;
if (checksum != data->checksum) {
ESP_LOGW(TAG, "BL0940 invalid checksum! 0x%02X != 0x%02X", checksum, data->checksum);
}
return checksum == data->checksum;
}
void BL0940::update() {
this->flush();
this->write_byte(BL0940_READ_COMMAND);
this->write_byte(BL0940_FULL_PACKET);
}
void BL0940::setup() {
for (auto *i : BL0940_INIT) {
this->write_array(i, 6);
delay(1);
}
this->flush();
}
float BL0940::update_temp_(sensor::Sensor *sensor, ube16_t temperature) const {
auto tb = (float) (temperature.h << 8 | temperature.l);
float converted_temp = ((float) 170 / 448) * (tb / 2 - 32) - 45;
if (sensor != nullptr) {
if (sensor->has_state() && std::abs(converted_temp - sensor->get_state()) > max_temperature_diff_) {
ESP_LOGD("bl0940", "Invalid temperature change. Sensor: '%s', Old temperature: %f, New temperature: %f",
sensor->get_name().c_str(), sensor->get_state(), converted_temp);
return 0.0f;
}
sensor->publish_state(converted_temp);
}
return converted_temp;
}
void BL0940::received_package_(const DataPacket *data) const {
// Bad header
if (data->frame_header != BL0940_PACKET_HEADER) {
ESP_LOGI("bl0940", "Invalid data. Header mismatch: %d", data->frame_header);
return;
}
float v_rms = (float) to_uint32_t(data->v_rms) / voltage_reference_;
float i_rms = (float) to_uint32_t(data->i_rms) / current_reference_;
float watt = (float) to_int32_t(data->watt) / power_reference_;
uint32_t cf_cnt = to_uint32_t(data->cf_cnt);
float total_energy_consumption = (float) cf_cnt / energy_reference_;
float tps1 = update_temp_(internal_temperature_sensor_, data->tps1);
float tps2 = update_temp_(external_temperature_sensor_, data->tps2);
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);
}
ESP_LOGV("bl0940", "BL0940: U %fV, I %fA, P %fW, Cnt %d, ∫P %fkWh, T1 %f°C, T2 %f°C", v_rms, i_rms, watt, cf_cnt,
total_energy_consumption, tps1, tps2);
}
void BL0940::dump_config() { // NOLINT(readability-function-cognitive-complexity)
ESP_LOGCONFIG(TAG, "BL0940:");
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("", "Internal temperature", this->internal_temperature_sensor_);
LOG_SENSOR("", "External temperature", this->external_temperature_sensor_);
}
uint32_t BL0940::to_uint32_t(ube24_t input) { return input.h << 16 | input.m << 8 | input.l; }
int32_t BL0940::to_int32_t(sbe24_t input) { return input.h << 16 | input.m << 8 | input.l; }
} // namespace bl0940
} // namespace esphome

View File

@@ -0,0 +1,109 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/uart/uart.h"
#include "esphome/components/sensor/sensor.h"
namespace esphome {
namespace bl0940 {
static const float BL0940_PREF = 1430;
static const float BL0940_UREF = 33000;
static const float BL0940_IREF = 275000; // 2750 from tasmota. Seems to generate values 100 times too high
// Measured to 297J per click according to power consumption of 5 minutes
// Converted to kWh (3.6MJ per kwH). Used to be 256 * 1638.4
static const float BL0940_EREF = 3.6e6 / 297;
struct ube24_t { // NOLINT(readability-identifier-naming,altera-struct-pack-align)
uint8_t l;
uint8_t m;
uint8_t h;
} __attribute__((packed));
struct ube16_t { // NOLINT(readability-identifier-naming,altera-struct-pack-align)
uint8_t l;
uint8_t h;
} __attribute__((packed));
struct sbe24_t { // NOLINT(readability-identifier-naming,altera-struct-pack-align)
uint8_t l;
uint8_t m;
int8_t h;
} __attribute__((packed));
// Caveat: All these values are big endian (low - middle - high)
union DataPacket { // NOLINT(altera-struct-pack-align)
uint8_t raw[35];
struct {
uint8_t frame_header; // value of 0x58 according to docs. 0x55 according to Tasmota real world tests. Reality wins.
ube24_t i_fast_rms; // 0x00
ube24_t i_rms; // 0x04
ube24_t RESERVED0; // reserved
ube24_t v_rms; // 0x06
ube24_t RESERVED1; // reserved
sbe24_t watt; // 0x08
ube24_t RESERVED2; // reserved
ube24_t cf_cnt; // 0x0A
ube24_t RESERVED3; // reserved
ube16_t tps1; // 0x0c
uint8_t RESERVED4; // value of 0x00
ube16_t tps2; // 0x0c
uint8_t RESERVED5; // value of 0x00
uint8_t checksum; // checksum
};
} __attribute__((packed));
class BL0940 : 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_internal_temperature_sensor(sensor::Sensor *internal_temperature_sensor) {
internal_temperature_sensor_ = internal_temperature_sensor;
}
void set_external_temperature_sensor(sensor::Sensor *external_temperature_sensor) {
external_temperature_sensor_ = external_temperature_sensor;
}
void loop() override;
void update() override;
void setup() override;
void dump_config() override;
protected:
sensor::Sensor *voltage_sensor_;
sensor::Sensor *current_sensor_;
// 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_;
// Max difference between two measurements of the temperature. Used to avoid noise.
float max_temperature_diff_{0};
// Divide by this to turn into Watt
float power_reference_ = BL0940_PREF;
// Divide by this to turn into Volt
float voltage_reference_ = BL0940_UREF;
// Divide by this to turn into Ampere
float current_reference_ = BL0940_IREF;
// Divide by this to turn into kWh
float energy_reference_ = BL0940_EREF;
float update_temp_(sensor::Sensor *sensor, ube16_t packed_temperature) const;
static uint32_t to_uint32_t(ube24_t input);
static int32_t to_int32_t(sbe24_t input);
static bool validate_checksum(const DataPacket *data);
void received_package_(const DataPacket *data) const;
};
} // namespace bl0940
} // namespace esphome

View File

@@ -0,0 +1,105 @@
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,
DEVICE_CLASS_CURRENT,
DEVICE_CLASS_ENERGY,
DEVICE_CLASS_POWER,
DEVICE_CLASS_VOLTAGE,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
UNIT_AMPERE,
UNIT_CELSIUS,
UNIT_KILOWATT_HOURS,
UNIT_VOLT,
UNIT_WATT,
)
DEPENDENCIES = ["uart"]
CONF_INTERNAL_TEMPERATURE = "internal_temperature"
CONF_EXTERNAL_TEMPERATURE = "external_temperature"
bl0940_ns = cg.esphome_ns.namespace("bl0940")
BL0940 = bl0940_ns.class_("BL0940", cg.PollingComponent, uart.UARTDevice)
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(BL0940),
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_INTERNAL_TEMPERATURE): sensor.sensor_schema(
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=0,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_EXTERNAL_TEMPERATURE): sensor.sensor_schema(
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=0,
device_class=DEVICE_CLASS_TEMPERATURE,
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_INTERNAL_TEMPERATURE in config:
conf = config[CONF_INTERNAL_TEMPERATURE]
sens = await sensor.new_sensor(conf)
cg.add(var.set_internal_temperature_sensor(sens))
if CONF_EXTERNAL_TEMPERATURE in config:
conf = config[CONF_EXTERNAL_TEMPERATURE]
sens = await sensor.new_sensor(conf)
cg.add(var.set_external_temperature_sensor(sens))

View File

@@ -3,7 +3,7 @@
#include "esphome/core/automation.h"
#include "esphome/components/ble_client/ble_client.h"
#ifdef ARDUINO_ARCH_ESP32
#ifdef USE_ESP32
namespace esphome {
namespace ble_client {
@@ -11,11 +11,12 @@ class BLEClientConnectTrigger : public Trigger<>, public BLEClientNode {
public:
explicit BLEClientConnectTrigger(BLEClient *parent) { parent->register_ble_node(this); }
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) {
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)
this->node_state = espbt::ClientState::Established;
this->node_state = espbt::ClientState::ESTABLISHED;
}
};
@@ -23,11 +24,12 @@ class BLEClientDisconnectTrigger : public Trigger<>, public BLEClientNode {
public:
explicit BLEClientDisconnectTrigger(BLEClient *parent) { parent->register_ble_node(this); }
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) {
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)
this->trigger();
if (event == ESP_GATTC_SEARCH_CMPL_EVT)
this->node_state = espbt::ClientState::Established;
this->node_state = espbt::ClientState::ESTABLISHED;
}
};

View File

@@ -4,12 +4,14 @@
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include "ble_client.h"
#ifdef ARDUINO_ARCH_ESP32
#ifdef USE_ESP32
namespace esphome {
namespace ble_client {
static const char *TAG = "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);
@@ -17,12 +19,12 @@ void BLEClient::setup() {
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);
this->set_states_(espbt::ClientState::IDLE);
this->enabled = true;
}
void BLEClient::loop() {
if (this->state() == espbt::ClientState::Discovered) {
if (this->state() == espbt::ClientState::DISCOVERED) {
this->connect();
}
for (auto *node : this->nodes_)
@@ -39,11 +41,11 @@ bool BLEClient::parse_device(const espbt::ESPBTDevice &device) {
return false;
if (device.address_uint64() != this->address)
return false;
if (this->state() != espbt::ClientState::Idle)
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);
this->set_states_(espbt::ClientState::DISCOVERED);
auto addr = device.address_uint64();
this->remote_bda[0] = (addr >> 40) & 0xFF;
@@ -69,7 +71,7 @@ std::string BLEClient::address_str() const {
void BLEClient::set_enabled(bool enabled) {
if (enabled == this->enabled)
return;
if (!enabled && this->state() != espbt::ClientState::Idle) {
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);
if (ret) {
@@ -84,9 +86,9 @@ void BLEClient::connect() {
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);
this->set_states_(espbt::ClientState::IDLE);
} else {
this->set_states(espbt::ClientState::Connecting);
this->set_states_(espbt::ClientState::CONNECTING);
}
}
@@ -97,7 +99,7 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
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();
bool all_established = this->all_nodes_established_();
switch (event) {
case ESP_GATTC_REG_EVT: {
@@ -113,7 +115,7 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
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);
this->set_states_(espbt::ClientState::IDLE);
break;
}
this->conn_id = param->open.conn_id;
@@ -126,11 +128,11 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
case ESP_GATTC_CFG_MTU_EVT: {
if (param->cfg_mtu.status != ESP_GATT_OK) {
ESP_LOGW(TAG, "cfg_mtu to %s failed, status %d", this->address_str().c_str(), param->cfg_mtu.status);
this->set_states(espbt::ClientState::Idle);
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, NULL);
esp_ble_gattc_search_service(esp_gattc_if, param->cfg_mtu.conn_id, nullptr);
break;
}
case ESP_GATTC_DISCONNECT_EVT: {
@@ -139,13 +141,13 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
}
ESP_LOGV(TAG, "[%s] ESP_GATTC_DISCONNECT_EVT", this->address_str().c_str());
for (auto &svc : this->services_)
delete svc;
delete svc; // NOLINT(cppcoreguidelines-owning-memory)
this->services_.clear();
this->set_states(espbt::ClientState::Idle);
this->set_states_(espbt::ClientState::IDLE);
break;
}
case ESP_GATTC_SEARCH_RES_EVT: {
BLEService *ble_service = new BLEService();
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;
@@ -160,12 +162,12 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
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);
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);
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;
@@ -192,9 +194,9 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
node->gattc_event_handler(event, esp_gattc_if, param);
// Delete characteristics after clients have used them to save RAM.
if (!all_established && this->all_nodes_established()) {
if (!all_established && this->all_nodes_established_()) {
for (auto &svc : this->services_)
delete svc;
delete svc; // NOLINT(cppcoreguidelines-owning-memory)
this->services_.clear();
}
}
@@ -250,16 +252,17 @@ float BLEClient::parse_char_value(uint8_t *value, uint16_t length) {
}
BLEService *BLEClient::get_service(espbt::ESPBTUUID uuid) {
for (auto svc : this->services_)
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);
auto *svc = this->get_service(service);
if (svc == nullptr)
return nullptr;
return svc->get_characteristic(chr);
@@ -270,19 +273,24 @@ BLECharacteristic *BLEClient::get_characteristic(uint16_t service, uint16_t 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)
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)
for (auto &chr : this->characteristics) {
if (chr->uuid == uuid)
return chr;
}
return nullptr;
}
@@ -291,10 +299,10 @@ BLECharacteristic *BLEService::get_characteristic(uint16_t uuid) {
}
BLEDescriptor *BLEClient::get_descriptor(espbt::ESPBTUUID service, espbt::ESPBTUUID chr, espbt::ESPBTUUID descr) {
auto svc = this->get_service(service);
auto *svc = this->get_service(service);
if (svc == nullptr)
return nullptr;
auto ch = svc->get_characteristic(chr);
auto *ch = svc->get_characteristic(chr);
if (ch == nullptr)
return nullptr;
return ch->get_descriptor(descr);
@@ -307,7 +315,7 @@ BLEDescriptor *BLEClient::get_descriptor(uint16_t service, uint16_t chr, uint16_
BLEService::~BLEService() {
for (auto &chr : this->characteristics)
delete chr;
delete chr; // NOLINT(cppcoreguidelines-owning-memory)
}
void BLEService::parse_characteristics() {
@@ -329,7 +337,7 @@ void BLEService::parse_characteristics() {
break;
}
BLECharacteristic *characteristic = new BLECharacteristic();
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;
@@ -344,7 +352,7 @@ void BLEService::parse_characteristics() {
BLECharacteristic::~BLECharacteristic() {
for (auto &desc : this->descriptors)
delete desc;
delete desc; // NOLINT(cppcoreguidelines-owning-memory)
}
void BLECharacteristic::parse_descriptors() {
@@ -366,7 +374,7 @@ void BLECharacteristic::parse_descriptors() {
break;
}
BLEDescriptor *desc = new BLEDescriptor();
BLEDescriptor *desc = new BLEDescriptor(); // NOLINT(cppcoreguidelines-owning-memory)
desc->uuid = espbt::ESPBTUUID::from_uuid(result.uuid);
desc->handle = result.handle;
desc->characteristic = this;
@@ -377,15 +385,29 @@ void BLECharacteristic::parse_descriptors() {
}
BLEDescriptor *BLECharacteristic::get_descriptor(espbt::ESPBTUUID uuid) {
for (auto &desc : this->descriptors)
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);
}
} // namespace ble_client
} // namespace esphome

View File

@@ -4,7 +4,7 @@
#include "esphome/core/helpers.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#ifdef ARDUINO_ARCH_ESP32
#ifdef USE_ESP32
#include <string>
#include <array>
@@ -25,7 +25,7 @@ class BLEClientNode {
public:
virtual void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) = 0;
virtual void loop() = 0;
virtual void loop(){};
void set_address(uint64_t address) { address_ = address; }
espbt::ESPBTClient *client;
// This should be transitioned to Established once the node no longer needs
@@ -59,7 +59,8 @@ class BLECharacteristic {
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;
};
@@ -81,11 +82,13 @@ class BLEClient : public espbt::ESPBTClient, public Component {
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);
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) override;
bool parse_device(const espbt::ESPBTDevice &device) override;
void on_scan_end() override {}
void connect();
void connect() override;
void set_address(uint64_t address) { this->address = address; }
@@ -116,17 +119,18 @@ class BLEClient : public espbt::ESPBTClient, public Component {
std::string address_str() const;
protected:
void set_states(espbt::ClientState st) {
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)
bool all_nodes_established_() {
if (this->state() != espbt::ClientState::ESTABLISHED)
return false;
for (auto &node : nodes_)
if (node->node_state != espbt::ClientState::Established)
for (auto &node : nodes_) {
if (node->node_state != espbt::ClientState::ESTABLISHED)
return false;
}
return true;
}

View File

@@ -0,0 +1,69 @@
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 .. import ble_client_ns
DEPENDENCIES = ["ble_client"]
CONF_CHARACTERISTIC_UUID = "characteristic_uuid"
CONF_REQUIRE_RESPONSE = "require_response"
BLEBinaryOutput = ble_client_ns.class_(
"BLEBinaryOutput", output.BinaryOutput, ble_client.BLEClientNode, cg.Component
)
CONFIG_SCHEMA = cv.All(
output.BINARY_OUTPUT_SCHEMA.extend(
{
cv.Required(CONF_ID): cv.declare_id(BLEBinaryOutput),
cv.Required(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid,
cv.Required(CONF_CHARACTERISTIC_UUID): esp32_ble_tracker.bt_uuid,
cv.Optional(CONF_REQUIRE_RESPONSE, default=False): cv.boolean,
}
)
.extend(cv.COMPONENT_SCHEMA)
.extend(ble_client.BLE_CLIENT_SCHEMA)
)
def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
if len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
cg.add(
var.set_service_uuid16(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]))
)
elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid32_format):
cg.add(
var.set_service_uuid32(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]))
)
elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid128_format):
uuid128 = esp32_ble_tracker.as_reversed_hex_array(config[CONF_SERVICE_UUID])
cg.add(var.set_service_uuid128(uuid128))
if len(config[CONF_CHARACTERISTIC_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
cg.add(
var.set_char_uuid16(
esp32_ble_tracker.as_hex(config[CONF_CHARACTERISTIC_UUID])
)
)
elif len(config[CONF_CHARACTERISTIC_UUID]) == len(
esp32_ble_tracker.bt_uuid32_format
):
cg.add(
var.set_char_uuid32(
esp32_ble_tracker.as_hex(config[CONF_CHARACTERISTIC_UUID])
)
)
elif len(config[CONF_CHARACTERISTIC_UUID]) == len(
esp32_ble_tracker.bt_uuid128_format
):
uuid128 = esp32_ble_tracker.as_reversed_hex_array(
config[CONF_CHARACTERISTIC_UUID]
)
cg.add(var.set_char_uuid128(uuid128))
cg.add(var.set_require_response(config[CONF_REQUIRE_RESPONSE]))
yield output.register_output(var, config)
yield ble_client.register_ble_node(var, config)
yield cg.register_component(var, config)

View File

@@ -0,0 +1,75 @@
#include "ble_binary_output.h"
#include "esphome/core/log.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#ifdef USE_ESP32
namespace esphome {
namespace ble_client {
static const char *const TAG = "ble_binary_output";
void BLEBinaryOutput::dump_config() {
ESP_LOGCONFIG(TAG, "BLE Binary Output:");
ESP_LOGCONFIG(TAG, " MAC address : %s", this->parent_->address_str().c_str());
ESP_LOGCONFIG(TAG, " Service UUID : %s", this->service_uuid_.to_string().c_str());
ESP_LOGCONFIG(TAG, " Characteristic UUID: %s", this->char_uuid_.to_string().c_str());
LOG_BINARY_OUTPUT(this);
}
void BLEBinaryOutput::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) {
switch (event) {
case ESP_GATTC_OPEN_EVT:
this->client_state_ = espbt::ClientState::ESTABLISHED;
ESP_LOGW(TAG, "[%s] Connected successfully!", this->char_uuid_.to_string().c_str());
break;
case ESP_GATTC_DISCONNECT_EVT:
ESP_LOGW(TAG, "[%s] Disconnected", this->char_uuid_.to_string().c_str());
this->client_state_ = espbt::ClientState::IDLE;
break;
case ESP_GATTC_WRITE_CHAR_EVT: {
if (param->write.status == 0) {
break;
}
auto *chr = this->parent()->get_characteristic(this->service_uuid_, this->char_uuid_);
if (chr == nullptr) {
ESP_LOGW(TAG, "[%s] Characteristic not found.", this->char_uuid_.to_string().c_str());
break;
}
if (param->write.handle == chr->handle) {
ESP_LOGW(TAG, "[%s] Write error, status=%d", this->char_uuid_.to_string().c_str(), param->write.status);
}
break;
}
default:
break;
}
}
void BLEBinaryOutput::write_state(bool state) {
if (this->client_state_ != espbt::ClientState::ESTABLISHED) {
ESP_LOGW(TAG, "[%s] Not connected to BLE client. State update can not be written.",
this->char_uuid_.to_string().c_str());
return;
}
auto *chr = this->parent()->get_characteristic(this->service_uuid_, this->char_uuid_);
if (chr == nullptr) {
ESP_LOGW(TAG, "[%s] Characteristic not found. State update can not be written.",
this->char_uuid_.to_string().c_str());
return;
}
uint8_t state_as_uint = (uint8_t) state;
ESP_LOGV(TAG, "[%s] Write State: %d", this->char_uuid_.to_string().c_str(), state_as_uint);
if (this->require_response_) {
chr->write_value(&state_as_uint, sizeof(state_as_uint), ESP_GATT_WRITE_TYPE_RSP);
} else {
chr->write_value(&state_as_uint, sizeof(state_as_uint), ESP_GATT_WRITE_TYPE_NO_RSP);
}
}
} // namespace ble_client
} // namespace esphome
#endif

View File

@@ -0,0 +1,41 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/ble_client/ble_client.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include "esphome/components/output/binary_output.h"
#ifdef USE_ESP32
#include <esp_gattc_api.h>
namespace esphome {
namespace ble_client {
namespace espbt = esphome::esp32_ble_tracker;
class BLEBinaryOutput : public output::BinaryOutput, public BLEClientNode, public Component {
public:
void dump_config() override;
void loop() override {}
float get_setup_priority() const override { return setup_priority::DATA; }
void set_service_uuid16(uint16_t uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_uint16(uuid); }
void set_service_uuid32(uint32_t uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); }
void set_service_uuid128(uint8_t *uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
void set_char_uuid16(uint16_t uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_uint16(uuid); }
void set_char_uuid32(uint32_t uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); }
void set_char_uuid128(uint8_t *uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) override;
void set_require_response(bool response) { this->require_response_ = response; }
protected:
void write_state(bool state) override;
bool require_response_;
espbt::ESPBTUUID service_uuid_;
espbt::ESPBTUUID char_uuid_;
espbt::ClientState client_state_;
};
} // namespace ble_client
} // namespace esphome
#endif

View File

@@ -2,12 +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 (
DEVICE_CLASS_EMPTY,
CONF_ID,
CONF_LAMBDA,
STATE_CLASS_NONE,
UNIT_EMPTY,
ICON_EMPTY,
CONF_TRIGGER_ID,
CONF_SERVICE_UUID,
)
@@ -34,11 +29,11 @@ BLESensorNotifyTrigger = ble_client_ns.class_(
CONFIG_SCHEMA = cv.All(
sensor.sensor_schema(
UNIT_EMPTY, ICON_EMPTY, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE
BLESensor,
accuracy_decimals=0,
)
.extend(
{
cv.GenerateID(): cv.declare_id(BLESensor),
cv.Required(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid,
cv.Required(CONF_CHARACTERISTIC_UUID): esp32_ble_tracker.bt_uuid,
cv.Optional(CONF_DESCRIPTOR_UUID): esp32_ble_tracker.bt_uuid,
@@ -59,7 +54,7 @@ CONFIG_SCHEMA = cv.All(
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
var = await sensor.new_sensor(config)
if len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
cg.add(
var.set_service_uuid16(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]))
@@ -69,7 +64,7 @@ async def to_code(config):
var.set_service_uuid32(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]))
)
elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid128_format):
uuid128 = esp32_ble_tracker.as_hex_array(config[CONF_SERVICE_UUID])
uuid128 = esp32_ble_tracker.as_reversed_hex_array(config[CONF_SERVICE_UUID])
cg.add(var.set_service_uuid128(uuid128))
if len(config[CONF_CHARACTERISTIC_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
@@ -89,7 +84,9 @@ async def to_code(config):
elif len(config[CONF_CHARACTERISTIC_UUID]) == len(
esp32_ble_tracker.bt_uuid128_format
):
uuid128 = esp32_ble_tracker.as_hex_array(config[CONF_CHARACTERISTIC_UUID])
uuid128 = esp32_ble_tracker.as_reversed_hex_array(
config[CONF_CHARACTERISTIC_UUID]
)
cg.add(var.set_char_uuid128(uuid128))
if CONF_DESCRIPTOR_UUID in config:
@@ -110,7 +107,9 @@ async def to_code(config):
elif len(config[CONF_DESCRIPTOR_UUID]) == len(
esp32_ble_tracker.bt_uuid128_format
):
uuid128 = esp32_ble_tracker.as_hex_array(config[CONF_DESCRIPTOR_UUID])
uuid128 = esp32_ble_tracker.as_reversed_hex_array(
config[CONF_DESCRIPTOR_UUID]
)
cg.add(var.set_descr_uuid128(uuid128))
if CONF_LAMBDA in config:
@@ -122,7 +121,6 @@ async def to_code(config):
await cg.register_component(var, config)
await ble_client.register_ble_node(var, config)
cg.add(var.set_enable_notify(config[CONF_NOTIFY]))
await sensor.register_sensor(var, config)
for conf in config.get(CONF_ON_NOTIFY, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await ble_client.register_ble_node(trigger, config)

View File

@@ -3,7 +3,7 @@
#include "esphome/core/automation.h"
#include "esphome/components/ble_client/sensor/ble_sensor.h"
#ifdef ARDUINO_ARCH_ESP32
#ifdef USE_ESP32
namespace esphome {
namespace ble_client {
@@ -11,10 +11,11 @@ namespace ble_client {
class BLESensorNotifyTrigger : public Trigger<float>, public BLESensor {
public:
explicit BLESensorNotifyTrigger(BLESensor *sensor) { sensor_ = sensor; }
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) {
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) override {
switch (event) {
case ESP_GATTC_SEARCH_CMPL_EVT: {
this->sensor_->node_state = espbt::ClientState::Established;
this->sensor_->node_state = espbt::ClientState::ESTABLISHED;
break;
}
case ESP_GATTC_NOTIFY_EVT: {

View File

@@ -4,12 +4,12 @@
#include "esphome/core/helpers.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#ifdef ARDUINO_ARCH_ESP32
#ifdef USE_ESP32
namespace esphome {
namespace ble_client {
static const char *TAG = "ble_sensor";
static const char *const TAG = "ble_sensor";
uint32_t BLESensor::hash_base() { return 343459825UL; }
@@ -43,7 +43,7 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
}
case ESP_GATTC_SEARCH_CMPL_EVT: {
this->handle = 0;
auto chr = this->parent()->get_characteristic(this->service_uuid_, this->char_uuid_);
auto *chr = this->parent()->get_characteristic(this->service_uuid_, this->char_uuid_);
if (chr == nullptr) {
this->status_set_warning();
this->publish_state(NAN);
@@ -53,7 +53,7 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
}
this->handle = chr->handle;
if (this->descr_uuid_.get_uuid().len > 0) {
auto descr = chr->get_descriptor(this->descr_uuid_);
auto *descr = chr->get_descriptor(this->descr_uuid_);
if (descr == nullptr) {
this->status_set_warning();
this->publish_state(NAN);
@@ -71,7 +71,7 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
ESP_LOGW(TAG, "esp_ble_gattc_register_for_notify failed, status=%d", status);
}
} else {
this->node_state = espbt::ClientState::Established;
this->node_state = espbt::ClientState::ESTABLISHED;
}
break;
}
@@ -84,7 +84,7 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
}
if (param->read.handle == this->handle) {
this->status_clear_warning();
this->publish_state(this->parse_data(param->read.value, param->read.value_len));
this->publish_state(this->parse_data_(param->read.value, param->read.value_len));
}
break;
}
@@ -93,11 +93,11 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
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]);
this->publish_state(this->parse_data(param->notify.value, param->notify.value_len));
this->publish_state(this->parse_data_(param->notify.value, param->notify.value_len));
break;
}
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
this->node_state = espbt::ClientState::Established;
this->node_state = espbt::ClientState::ESTABLISHED;
break;
}
default:
@@ -105,7 +105,7 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
}
}
float BLESensor::parse_data(uint8_t *value, uint16_t value_len) {
float BLESensor::parse_data_(uint8_t *value, uint16_t value_len) {
if (this->data_to_value_func_.has_value()) {
std::vector<uint8_t> data(value, value + value_len);
return (*this->data_to_value_func_)(data);
@@ -115,7 +115,7 @@ float BLESensor::parse_data(uint8_t *value, uint16_t value_len) {
}
void BLESensor::update() {
if (this->node_state != espbt::ClientState::Established) {
if (this->node_state != espbt::ClientState::ESTABLISHED) {
ESP_LOGW(TAG, "[%s] Cannot poll, not connected", this->get_name().c_str());
return;
}

View File

@@ -5,7 +5,7 @@
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include "esphome/components/sensor/sensor.h"
#ifdef ARDUINO_ARCH_ESP32
#ifdef USE_ESP32
#include <esp_gattc_api.h>
namespace esphome {
@@ -32,13 +32,13 @@ class BLESensor : public sensor::Sensor, public PollingComponent, public BLEClie
void set_descr_uuid16(uint16_t uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_uint16(uuid); }
void set_descr_uuid32(uint32_t uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); }
void set_descr_uuid128(uint8_t *uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
void set_data_to_value(data_to_value_t &&lambda_) { this->data_to_value_func_ = lambda_; }
void set_data_to_value(data_to_value_t &&lambda) { this->data_to_value_func_ = lambda; }
void set_enable_notify(bool notify) { this->notify_ = notify; }
uint16_t handle;
protected:
uint32_t hash_base() override;
float parse_data(uint8_t *value, uint16_t value_len);
float parse_data_(uint8_t *value, uint16_t value_len);
optional<data_to_value_t> data_to_value_func_{};
bool notify_;
espbt::ESPBTUUID service_uuid_;

View File

@@ -2,12 +2,12 @@
#include "esphome/core/log.h"
#include "esphome/core/application.h"
#ifdef ARDUINO_ARCH_ESP32
#ifdef USE_ESP32
namespace esphome {
namespace ble_client {
static const char *TAG = "ble_switch";
static const char *const TAG = "ble_switch";
void BLEClientSwitch::write_state(bool state) {
this->parent_->set_enabled(state);
@@ -21,10 +21,10 @@ void BLEClientSwitch::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_i
this->publish_state(this->parent_->enabled);
break;
case ESP_GATTC_OPEN_EVT:
this->node_state = espbt::ClientState::Established;
this->node_state = espbt::ClientState::ESTABLISHED;
break;
case ESP_GATTC_DISCONNECT_EVT:
this->node_state = espbt::ClientState::Idle;
this->node_state = espbt::ClientState::IDLE;
this->publish_state(this->parent_->enabled);
break;
default:

View File

@@ -5,7 +5,7 @@
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include "esphome/components/switch/switch.h"
#ifdef ARDUINO_ARCH_ESP32
#ifdef USE_ESP32
#include <esp_gattc_api.h>
namespace esphome {

View File

@@ -0,0 +1,121 @@
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_ID,
CONF_TRIGGER_ID,
CONF_SERVICE_UUID,
)
from esphome import automation
from .. import ble_client_ns
DEPENDENCIES = ["ble_client"]
CONF_CHARACTERISTIC_UUID = "characteristic_uuid"
CONF_DESCRIPTOR_UUID = "descriptor_uuid"
CONF_NOTIFY = "notify"
CONF_ON_NOTIFY = "on_notify"
adv_data_t = cg.std_vector.template(cg.uint8)
adv_data_t_const_ref = adv_data_t.operator("ref").operator("const")
BLETextSensor = ble_client_ns.class_(
"BLETextSensor",
text_sensor.TextSensor,
cg.PollingComponent,
ble_client.BLEClientNode,
)
BLETextSensorNotifyTrigger = ble_client_ns.class_(
"BLETextSensorNotifyTrigger", automation.Trigger.template(cg.std_string)
)
CONFIG_SCHEMA = cv.All(
text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(BLETextSensor),
cv.Required(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid,
cv.Required(CONF_CHARACTERISTIC_UUID): esp32_ble_tracker.bt_uuid,
cv.Optional(CONF_DESCRIPTOR_UUID): esp32_ble_tracker.bt_uuid,
cv.Optional(CONF_NOTIFY, default=False): cv.boolean,
cv.Optional(CONF_ON_NOTIFY): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
BLETextSensorNotifyTrigger
),
}
),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(ble_client.BLE_CLIENT_SCHEMA)
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
if len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
cg.add(
var.set_service_uuid16(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]))
)
elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid32_format):
cg.add(
var.set_service_uuid32(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]))
)
elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid128_format):
uuid128 = esp32_ble_tracker.as_reversed_hex_array(config[CONF_SERVICE_UUID])
cg.add(var.set_service_uuid128(uuid128))
if len(config[CONF_CHARACTERISTIC_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
cg.add(
var.set_char_uuid16(
esp32_ble_tracker.as_hex(config[CONF_CHARACTERISTIC_UUID])
)
)
elif len(config[CONF_CHARACTERISTIC_UUID]) == len(
esp32_ble_tracker.bt_uuid32_format
):
cg.add(
var.set_char_uuid32(
esp32_ble_tracker.as_hex(config[CONF_CHARACTERISTIC_UUID])
)
)
elif len(config[CONF_CHARACTERISTIC_UUID]) == len(
esp32_ble_tracker.bt_uuid128_format
):
uuid128 = esp32_ble_tracker.as_reversed_hex_array(
config[CONF_CHARACTERISTIC_UUID]
)
cg.add(var.set_char_uuid128(uuid128))
if CONF_DESCRIPTOR_UUID in config:
if len(config[CONF_DESCRIPTOR_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
cg.add(
var.set_descr_uuid16(
esp32_ble_tracker.as_hex(config[CONF_DESCRIPTOR_UUID])
)
)
elif len(config[CONF_DESCRIPTOR_UUID]) == len(
esp32_ble_tracker.bt_uuid32_format
):
cg.add(
var.set_descr_uuid32(
esp32_ble_tracker.as_hex(config[CONF_DESCRIPTOR_UUID])
)
)
elif len(config[CONF_DESCRIPTOR_UUID]) == len(
esp32_ble_tracker.bt_uuid128_format
):
uuid128 = esp32_ble_tracker.as_reversed_hex_array(
config[CONF_DESCRIPTOR_UUID]
)
cg.add(var.set_descr_uuid128(uuid128))
await cg.register_component(var, config)
await ble_client.register_ble_node(var, config)
cg.add(var.set_enable_notify(config[CONF_NOTIFY]))
await text_sensor.register_text_sensor(var, config)
for conf in config.get(CONF_ON_NOTIFY, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await ble_client.register_ble_node(trigger, config)
await automation.build_automation(trigger, [(cg.std_string, "x")], conf)

View File

@@ -0,0 +1,38 @@
#pragma once
#include "esphome/core/automation.h"
#include "esphome/components/ble_client/text_sensor/ble_text_sensor.h"
#ifdef USE_ESP32
namespace esphome {
namespace ble_client {
class BLETextSensorNotifyTrigger : public Trigger<std::string>, public BLETextSensor {
public:
explicit BLETextSensorNotifyTrigger(BLETextSensor *sensor) { sensor_ = sensor; }
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) override {
switch (event) {
case ESP_GATTC_SEARCH_CMPL_EVT: {
this->sensor_->node_state = espbt::ClientState::ESTABLISHED;
break;
}
case ESP_GATTC_NOTIFY_EVT: {
if (param->notify.conn_id != this->sensor_->parent()->conn_id || param->notify.handle != this->sensor_->handle)
break;
this->trigger(this->sensor_->parse_data(param->notify.value, param->notify.value_len));
}
default:
break;
}
}
protected:
BLETextSensor *sensor_;
};
} // namespace ble_client
} // namespace esphome
#endif

View File

@@ -0,0 +1,137 @@
#include "ble_text_sensor.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include "esphome/core/application.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#ifdef USE_ESP32
namespace esphome {
namespace ble_client {
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() {
LOG_TEXT_SENSOR("", "BLE Text Sensor", this);
ESP_LOGCONFIG(TAG, " MAC address : %s", this->parent()->address_str().c_str());
ESP_LOGCONFIG(TAG, " Service UUID : %s", this->service_uuid_.to_string().c_str());
ESP_LOGCONFIG(TAG, " Characteristic UUID: %s", this->char_uuid_.to_string().c_str());
ESP_LOGCONFIG(TAG, " Descriptor UUID : %s", this->descr_uuid_.to_string().c_str());
ESP_LOGCONFIG(TAG, " Notifications : %s", YESNO(this->notify_));
LOG_UPDATE_INTERVAL(this);
}
void BLETextSensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) {
switch (event) {
case ESP_GATTC_OPEN_EVT: {
if (param->open.status == ESP_GATT_OK) {
ESP_LOGI(TAG, "[%s] Connected successfully!", this->get_name().c_str());
break;
}
break;
}
case ESP_GATTC_DISCONNECT_EVT: {
ESP_LOGW(TAG, "[%s] Disconnected!", this->get_name().c_str());
this->status_set_warning();
this->publish_state(EMPTY);
break;
}
case ESP_GATTC_SEARCH_CMPL_EVT: {
this->handle = 0;
auto *chr = this->parent()->get_characteristic(this->service_uuid_, this->char_uuid_);
if (chr == nullptr) {
this->status_set_warning();
this->publish_state(EMPTY);
ESP_LOGW(TAG, "No sensor characteristic found at service %s char %s", this->service_uuid_.to_string().c_str(),
this->char_uuid_.to_string().c_str());
break;
}
this->handle = chr->handle;
if (this->descr_uuid_.get_uuid().len > 0) {
auto *descr = chr->get_descriptor(this->descr_uuid_);
if (descr == nullptr) {
this->status_set_warning();
this->publish_state(EMPTY);
ESP_LOGW(TAG, "No sensor descriptor found at service %s char %s descr %s",
this->service_uuid_.to_string().c_str(), this->char_uuid_.to_string().c_str(),
this->descr_uuid_.to_string().c_str());
break;
}
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);
if (status) {
ESP_LOGW(TAG, "esp_ble_gattc_register_for_notify failed, status=%d", status);
}
} else {
this->node_state = espbt::ClientState::ESTABLISHED;
}
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->handle) {
this->status_clear_warning();
this->publish_state(this->parse_data(param->read.value, param->read.value_len));
}
break;
}
case ESP_GATTC_NOTIFY_EVT: {
if (param->notify.conn_id != this->parent()->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]);
this->publish_state(this->parse_data(param->notify.value, param->notify.value_len));
break;
}
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
this->node_state = espbt::ClientState::ESTABLISHED;
break;
}
default:
break;
}
}
std::string BLETextSensor::parse_data(uint8_t *value, uint16_t value_len) {
std::string text(value, value + value_len);
return text;
}
void BLETextSensor::update() {
if (this->node_state != espbt::ClientState::ESTABLISHED) {
ESP_LOGW(TAG, "[%s] Cannot poll, not connected", this->get_name().c_str());
return;
}
if (this->handle == 0) {
ESP_LOGW(TAG, "[%s] Cannot poll, no service or characteristic found", this->get_name().c_str());
return;
}
auto status =
esp_ble_gattc_read_char(this->parent()->gattc_if, this->parent()->conn_id, this->handle, ESP_GATT_AUTH_REQ_NONE);
if (status) {
this->status_set_warning();
this->publish_state(EMPTY);
ESP_LOGW(TAG, "[%s] Error sending read request for sensor, status=%d", this->get_name().c_str(), status);
}
}
} // namespace ble_client
} // namespace esphome
#endif

View File

@@ -0,0 +1,47 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/ble_client/ble_client.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include "esphome/components/text_sensor/text_sensor.h"
#ifdef USE_ESP32
#include <esp_gattc_api.h>
namespace esphome {
namespace ble_client {
namespace espbt = esphome::esp32_ble_tracker;
class BLETextSensor : public text_sensor::TextSensor, public PollingComponent, public BLEClientNode {
public:
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::DATA; }
void set_service_uuid16(uint16_t uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_uint16(uuid); }
void set_service_uuid32(uint32_t uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); }
void set_service_uuid128(uint8_t *uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
void set_char_uuid16(uint16_t uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_uint16(uuid); }
void set_char_uuid32(uint32_t uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); }
void set_char_uuid128(uint8_t *uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
void set_descr_uuid16(uint16_t uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_uint16(uuid); }
void set_descr_uuid32(uint32_t uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); }
void set_descr_uuid128(uint8_t *uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
void set_enable_notify(bool notify) { this->notify_ = notify; }
std::string parse_data(uint8_t *value, uint16_t value_len);
uint16_t handle;
protected:
uint32_t hash_base() override;
bool notify_;
espbt::ESPBTUUID service_uuid_;
espbt::ESPBTUUID char_uuid_;
espbt::ESPBTUUID descr_uuid_;
};
} // namespace ble_client
} // namespace esphome
#endif

View File

@@ -1,7 +1,13 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import binary_sensor, esp32_ble_tracker
from esphome.const import CONF_MAC_ADDRESS, CONF_SERVICE_UUID, CONF_ID
from esphome.const import (
CONF_MAC_ADDRESS,
CONF_SERVICE_UUID,
CONF_IBEACON_MAJOR,
CONF_IBEACON_MINOR,
CONF_IBEACON_UUID,
)
DEPENDENCIES = ["esp32_ble_tracker"]
@@ -13,25 +19,37 @@ BLEPresenceDevice = ble_presence_ns.class_(
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(
binary_sensor.BINARY_SENSOR_SCHEMA.extend(
binary_sensor.binary_sensor_schema(BLEPresenceDevice)
.extend(
{
cv.GenerateID(): cv.declare_id(BLEPresenceDevice),
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,
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
var = await binary_sensor.new_binary_sensor(config)
await cg.register_component(var, config)
await esp32_ble_tracker.register_ble_device(var, config)
await binary_sensor.register_binary_sensor(var, config)
if CONF_MAC_ADDRESS in config:
cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
@@ -50,5 +68,15 @@ async def to_code(config):
)
)
elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid128_format):
uuid128 = esp32_ble_tracker.as_hex_array(config[CONF_SERVICE_UUID])
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

@@ -1,12 +1,12 @@
#include "ble_presence_device.h"
#include "esphome/core/log.h"
#ifdef ARDUINO_ARCH_ESP32
#ifdef USE_ESP32
namespace esphome {
namespace ble_presence {
static const char *TAG = "ble_presence";
static const char *const TAG = "ble_presence";
void BLEPresenceDevice::dump_config() { LOG_BINARY_SENSOR("", "BLE Presence", this); }

View File

@@ -4,7 +4,7 @@
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include "esphome/components/binary_sensor/binary_sensor.h"
#ifdef ARDUINO_ARCH_ESP32
#ifdef USE_ESP32
namespace esphome {
namespace ble_presence {
@@ -14,41 +14,78 @@ class BLEPresenceDevice : public binary_sensor::BinarySensorInitiallyOff,
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(false);
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(true);
this->found_ = true;
return true;
}
} else {
for (auto uuid : device.get_service_uuids()) {
if (this->uuid_ == uuid) {
this->publish_state(device.get_rssi());
switch (this->match_by_) {
case MATCH_BY_MAC_ADDRESS:
if (device.address_uint64() == this->address_) {
this->publish_state(true);
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;
}
@@ -56,10 +93,20 @@ class BLEPresenceDevice : public binary_sensor::BinarySensorInitiallyOff,
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_presence

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