1
0
mirror of https://github.com/esphome/esphome.git synced 2025-11-04 17:11:51 +00:00

Compare commits

...

116 Commits

Author SHA1 Message Date
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
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
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
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
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
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
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
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
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
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
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
Jesse Hills
9ce3a2059f Merge pull request #1919 from esphome/bump-1.19.0b5
1.19.0b5
2021-06-16 09:45:10 +12:00
Jesse Hills
69e6cf2c0c Bump version to v1.19.0b5 2021-06-16 09:06:50 +12:00
Paulus Schoutsen
28635124f9 Bump dashboard to 20210615.0 (#1918) 2021-06-16 09:06:50 +12:00
Paulus Schoutsen
25b116048c Bump dashboard to 20210615.0 (#1918) 2021-06-16 09:04:17 +12:00
Jesse Hills
035be87a83 Merge pull request #1914 from esphome/bump-1.19.0b4
1.19.0b4
2021-06-15 20:47:38 +12:00
Jesse Hills
e8bdbc45a9 Bump version to v1.19.0b4 2021-06-15 20:26:53 +12:00
Guillermo Ruffino
429caccefa fixes compatibility with esphome cfg vscode (#1911) 2021-06-15 20:26:53 +12:00
Paulus Schoutsen
744ca1af7c Bump frontend to 20210614.0 (#1912) 2021-06-15 20:26:52 +12:00
Jesse Hills
106f0d611f Validate that either networks, ap, or improv is set up (#1910) 2021-06-15 20:26:52 +12:00
Jesse Hills
d826416684 Allow no networks or AP to be set. (#1908)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2021-06-15 20:26:52 +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
604 changed files with 11136 additions and 4971 deletions

View File

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

View File

@@ -5,30 +5,19 @@ Checks: >-
-android-*, -android-*,
-boost-*, -boost-*,
-bugprone-branch-clone, -bugprone-branch-clone,
-bugprone-macro-parentheses,
-bugprone-narrowing-conversions, -bugprone-narrowing-conversions,
-bugprone-reserved-identifier,
-bugprone-signed-char-misuse, -bugprone-signed-char-misuse,
-bugprone-suspicious-include,
-bugprone-too-small-loop-variable, -bugprone-too-small-loop-variable,
-bugprone-unhandled-self-assignment,
-cert-dcl37-c,
-cert-dcl50-cpp, -cert-dcl50-cpp,
-cert-dcl51-cpp,
-cert-err58-cpp, -cert-err58-cpp,
-cert-oop54-cpp,
-cert-oop57-cpp, -cert-oop57-cpp,
-cert-str34-c, -cert-str34-c,
-clang-analyzer-core.CallAndMessage, -clang-analyzer-optin.cplusplus.UninitializedObject,
-clang-analyzer-optin.*,
-clang-analyzer-osx.*, -clang-analyzer-osx.*,
-clang-analyzer-security.*,
-clang-diagnostic-shadow-field, -clang-diagnostic-shadow-field,
-cppcoreguidelines-avoid-c-arrays, -cppcoreguidelines-avoid-c-arrays,
-cppcoreguidelines-avoid-goto, -cppcoreguidelines-avoid-goto,
-cppcoreguidelines-avoid-magic-numbers, -cppcoreguidelines-avoid-magic-numbers,
-cppcoreguidelines-avoid-non-const-global-variables,
-cppcoreguidelines-c-copy-assignment-signature,
-cppcoreguidelines-init-variables, -cppcoreguidelines-init-variables,
-cppcoreguidelines-macro-usage, -cppcoreguidelines-macro-usage,
-cppcoreguidelines-narrowing-conversions, -cppcoreguidelines-narrowing-conversions,
@@ -45,17 +34,17 @@ Checks: >-
-cppcoreguidelines-pro-type-union-access, -cppcoreguidelines-pro-type-union-access,
-cppcoreguidelines-pro-type-vararg, -cppcoreguidelines-pro-type-vararg,
-cppcoreguidelines-special-member-functions, -cppcoreguidelines-special-member-functions,
-fuchsia-*,
-fuchsia-default-arguments, -fuchsia-default-arguments,
-fuchsia-multiple-inheritance, -fuchsia-multiple-inheritance,
-fuchsia-overloaded-operator, -fuchsia-overloaded-operator,
-fuchsia-statically-constructed-objects, -fuchsia-statically-constructed-objects,
-fuchsia-default-arguments-declarations,
-fuchsia-default-arguments-calls,
-google-build-using-namespace, -google-build-using-namespace,
-google-explicit-constructor, -google-explicit-constructor,
-google-readability-braces-around-statements, -google-readability-braces-around-statements,
-google-readability-casting, -google-readability-casting,
-google-readability-todo, -google-readability-todo,
-google-runtime-int,
-google-runtime-references, -google-runtime-references,
-hicpp-*, -hicpp-*,
-llvm-else-after-return, -llvm-else-after-return,
@@ -65,12 +54,8 @@ Checks: >-
-llvmlibc-*, -llvmlibc-*,
-misc-non-private-member-variables-in-classes, -misc-non-private-member-variables-in-classes,
-misc-no-recursion, -misc-no-recursion,
-misc-unconventional-assign-operator,
-misc-unused-parameters, -misc-unused-parameters,
-modernize-avoid-c-arrays, -modernize-avoid-c-arrays,
-modernize-deprecated-headers,
-modernize-pass-by-value,
-modernize-pass-by-value,
-modernize-return-braced-init-list, -modernize-return-braced-init-list,
-modernize-use-auto, -modernize-use-auto,
-modernize-use-default-member-init, -modernize-use-default-member-init,
@@ -78,7 +63,6 @@ Checks: >-
-modernize-use-trailing-return-type, -modernize-use-trailing-return-type,
-mpi-*, -mpi-*,
-objc-*, -objc-*,
-performance-unnecessary-value-param,
-readability-braces-around-statements, -readability-braces-around-statements,
-readability-const-return-type, -readability-const-return-type,
-readability-convert-member-functions-to-static, -readability-convert-member-functions-to-static,
@@ -94,8 +78,7 @@ Checks: >-
-readability-redundant-string-init, -readability-redundant-string-init,
-readability-uppercase-literal-suffix, -readability-uppercase-literal-suffix,
-readability-use-anyofallof, -readability-use-anyofallof,
-warnings-as-errors, -warnings-as-errors
-zircon-*
WarningsAsErrors: '*' WarningsAsErrors: '*'
HeaderFilterRegex: '^.*/src/esphome/.*' HeaderFilterRegex: '^.*/src/esphome/.*'
AnalyzeTemporaryDtors: false AnalyzeTemporaryDtors: false

View File

@@ -2,16 +2,29 @@
"name": "ESPHome Dev", "name": "ESPHome Dev",
"context": "..", "context": "..",
"dockerFile": "../docker/Dockerfile.dev", "dockerFile": "../docker/Dockerfile.dev",
"postCreateCommand": "mkdir -p config && pip3 install -e .", "postCreateCommand": [
"runArgs": ["--privileged", "-e", "ESPHOME_DASHBOARD_USE_PING=1"], "script/devcontainer-post-create"
],
"runArgs": [
"--privileged",
"-e",
"ESPHOME_DASHBOARD_USE_PING=1"
],
"appPort": 6052, "appPort": 6052,
"extensions": [ "extensions": [
// python
"ms-python.python", "ms-python.python",
"visualstudioexptteam.vscodeintellicode", "visualstudioexptteam.vscodeintellicode",
"redhat.vscode-yaml" // yaml
"redhat.vscode-yaml",
// cpp
"ms-vscode.cpptools",
// editorconfig
"editorconfig.editorconfig",
], ],
"settings": { "settings": {
"python.pythonPath": "/usr/local/bin/python", "python.languageServer": "Pylance",
"python.pythonPath": "/usr/bin/python3",
"python.linting.pylintEnabled": true, "python.linting.pylintEnabled": true,
"python.linting.enabled": true, "python.linting.enabled": true,
"python.formatting.provider": "black", "python.formatting.provider": "black",
@@ -19,7 +32,7 @@
"editor.formatOnSave": true, "editor.formatOnSave": true,
"editor.formatOnType": true, "editor.formatOnType": true,
"files.trimTrailingWhitespace": true, "files.trimTrailingWhitespace": true,
"terminal.integrated.shell.linux": "/bin/bash", "terminal.integrated.defaultProfile.linux": "bash",
"yaml.customTags": [ "yaml.customTags": [
"!secret scalar", "!secret scalar",
"!lambda scalar", "!lambda scalar",
@@ -27,6 +40,18 @@
"!include_dir_list scalar", "!include_dir_list scalar",
"!include_dir_merge_list scalar", "!include_dir_merge_list scalar",
"!include_dir_merge_named 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
.mypy_cache/ .mypy_cache/
# PlatformIO
.pio/
# ESPHome
config/ config/
examples/ examples/
Dockerfile Dockerfile

View File

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

View File

@@ -3,7 +3,7 @@ name: CI for docker images
# Only run when docker paths change # Only run when docker paths change
on: on:
push: push:
branches: [dev, beta, master] branches: [dev, beta, release]
paths: paths:
- 'docker/**' - 'docker/**'
- '.github/workflows/**' - '.github/workflows/**'
@@ -18,38 +18,23 @@ jobs:
name: Build docker containers name: Build docker containers
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
fail-fast: false
matrix: matrix:
arch: [amd64, armv7, aarch64] arch: [amd64, armv7, aarch64]
build_type: ["hassio", "docker"] build_type: ["ha-addon", "docker", "lint"]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Set up env variables - name: Set up Python
run: | uses: actions/setup-python@v2
base_version="3.4.0" with:
python-version: '3.9'
- name: Set TAG
run: |
echo "TAG=check" >> $GITHUB_ENV
if [[ "${{ matrix.build_type }}" == "hassio" ]]; then - name: Run build
build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}" run: |
build_to="esphome/esphome-hassio-${{ matrix.arch }}" docker/build.py \
dockerfile="docker/Dockerfile.hassio" --tag "${TAG}" \
else --arch "${{ matrix.arch }}" \
build_from="esphome/esphome-base-${{ matrix.arch }}:${base_version}" --build-type "${{ matrix.build_type }}" \
build_to="esphome/esphome-${{ matrix.arch }}" build
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=ci" \
--cache-from "${BUILD_TO}:dev" \
--file "${DOCKERFILE}" \
.

View File

@@ -4,40 +4,36 @@ name: CI
on: on:
push: push:
# On dev branch release-dev already performs CI checks branches: [dev, beta, release]
# On other branches the `pull_request` trigger will be used
branches: [beta, master]
pull_request: pull_request:
jobs: jobs:
lint-clang-format: ci-with-container:
name: ${{ matrix.name }}
runs-on: ubuntu-latest 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: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
split: [1, 2, 3, 4] include:
- id: clang-format
name: Run script/clang-format
- id: clang-tidy
name: Run script/clang-tidy 1/4
split: 1
- id: clang-tidy
name: Run script/clang-tidy 2/4
split: 2
- id: clang-tidy
name: Run script/clang-tidy 3/4
split: 3
- id: clang-tidy
name: Run script/clang-tidy 4/4
split: 4
# cpp lint job runs with esphome-lint docker image so that clang-format-*
# doesn't have to be installed
container: ghcr.io/esphome/esphome-lint:1.1
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
# Set up the pio project so that the cpp checks know how files are compiled # Set up the pio project so that the cpp checks know how files are compiled
@@ -45,26 +41,57 @@ jobs:
- name: Set up platformio environment - name: Set up platformio environment
run: pio init --ide atom run: pio init --ide atom
- name: Register problem matchers - name: Register problem matchers
run: | run: |
echo "::add-matcher::.github/workflows/matchers/clang-tidy.json" echo "::add-matcher::.github/workflows/matchers/clang-tidy.json"
echo "::add-matcher::.github/workflows/matchers/gcc.json" echo "::add-matcher::.github/workflows/matchers/gcc.json"
- name: Run clang-format
run: script/clang-format -i
if: ${{ matrix.id == 'clang-format' }}
- name: Run clang-tidy - name: Run clang-tidy
run: script/clang-tidy --all-headers --fix --split-num 4 --split-at ${{ matrix.split }} run: script/clang-tidy --all-headers --fix --split-num 4 --split-at ${{ matrix.split }}
if: ${{ matrix.id == 'clang-tidy' }}
- name: Suggest changes - name: Suggest changes
run: script/ci-suggest-changes run: script/ci-suggest-changes
lint-python: ci:
# Don't use the esphome-lint docker image because it may contain outdated requirements. # Don't use the esphome-lint docker image because it may contain outdated requirements.
# This way, all dependencies are cached via the cache action. # This way, all dependencies are cached via the cache action.
name: ${{ matrix.name }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
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
- id: test
file: tests/test2.yaml
name: Test tests/test2.yaml
- id: test
file: tests/test3.yaml
name: Test tests/test3.yaml
- id: test
file: tests/test4.yaml
name: Test tests/test4.yaml
- id: pytest
name: Run pytest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v2 uses: actions/setup-python@v2
with: with:
python-version: '3.7' python-version: '3.7'
- name: Cache pip modules - name: Cache pip modules
uses: actions/cache@v1 uses: actions/cache@v1
with: with:
@@ -72,6 +99,17 @@ jobs:
key: esphome-pip-3.7-${{ hashFiles('setup.py') }} key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
restore-keys: | restore-keys: |
esphome-pip-3.7- 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.file }}-${{ hashFiles('esphome/core/config.py') }}
restore-keys: |
test-home-platformio-${{ matrix.file }}-
if: ${{ matrix.id == 'test' }}
- name: Set up python environment - name: Set up python environment
run: script/setup run: script/setup
@@ -80,82 +118,22 @@ jobs:
echo "::add-matcher::.github/workflows/matchers/ci-custom.json" echo "::add-matcher::.github/workflows/matchers/ci-custom.json"
echo "::add-matcher::.github/workflows/matchers/lint-python.json" echo "::add-matcher::.github/workflows/matchers/lint-python.json"
echo "::add-matcher::.github/workflows/matchers/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"
- name: Lint Custom - 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 - name: Lint Python
run: script/lint-python run: script/lint-python
- name: Lint CODEOWNERS if: ${{ matrix.id == 'lint-python' }}
run: script/build_codeowners.py --check
test: - run: esphome compile ${{ matrix.file }}
runs-on: ubuntu-latest if: ${{ matrix.id == 'test' }}
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 - name: Run pytest
run: | run: |
pytest \ pytest -vv --tb=native tests
-qq \ if: ${{ matrix.id == 'pytest' }}
--durations=10 \
-o console_output_style=count \
tests

View File

@@ -13,30 +13,88 @@ on:
- '.github/workflows/docker-lint-build.yml' - '.github/workflows/docker-lint-build.yml'
jobs: jobs:
publish-docker-lint-iage: deploy-docker:
name: Build docker containers name: Build and publish docker containers
if: github.repository == 'esphome/esphome'
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy:
matrix:
arch: [amd64, armv7, aarch64]
build_type: ["lint"]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Set TAG - name: Set up Python
run: | uses: actions/setup-python@v2
echo "TAG=1.1" >> $GITHUB_ENV with:
- name: Pull for cache python-version: '3.9'
run: | - name: Set TAG
docker pull "esphome/esphome-lint:latest" || true run: |
- name: Build echo "TAG=1.1" >> $GITHUB_ENV
run: |
docker build \ - name: Run build
--cache-from "esphome/esphome-lint:latest" \ run: |
--file "docker/Dockerfile.lint" \ docker/build.py \
--tag "esphome/esphome-lint:latest" \ --tag "${TAG}" \
--tag "esphome/esphome-lint:${TAG}" \ --arch "${{ matrix.arch }}" \
. --build-type "${{ matrix.build_type }}" \
- name: Log in to docker hub build
env:
DOCKER_USER: ${{ secrets.DOCKER_USER }} - name: Log in to docker hub
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} uses: docker/login-action@v1
run: docker login -u "${DOCKER_USER}" -p "${DOCKER_PASSWORD}" with:
- run: | username: ${{ secrets.DOCKER_USER }}
docker push "esphome/esphome-lint:${TAG}" password: ${{ secrets.DOCKER_PASSWORD }}
docker push "esphome/esphome-lint:latest" - 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: Run push
run: |
docker/build.py \
--tag "${TAG}" \
--arch "${{ matrix.arch }}" \
--build-type "${{ matrix.build_type }}" \
push
deploy-docker-manifest:
if: github.repository == 'esphome/esphome'
runs-on: ubuntu-latest
needs: [deploy-docker]
strategy:
matrix:
build_type: ["lint"]
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: Set TAG
run: |
echo "TAG=1.1" >> $GITHUB_ENV
- name: Enable experimental manifest support
run: |
mkdir -p ~/.docker
echo "{\"experimental\": \"enabled\"}" > ~/.docker/config.json
- name: Log in to docker hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Log in to the GitHub container registry
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Run manifest
run: |
docker/build.py \
--tag "${TAG}" \
--build-type "${{ matrix.build_type }}" \
manifest

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,35 @@
name: Publish Release name: Publish Release
on: on:
workflow_dispatch:
release: release:
types: [published] types: [published]
schedule:
- cron: "0 2 * * *"
jobs: jobs:
# THE LINT/TEST JOBS ARE COPIED FROM ci.yaml init:
name: Initialize build
lint-clang-format:
runs-on: ubuntu-latest runs-on: ubuntu-latest
# cpp lint job runs with esphome-lint docker image so that clang-format-* outputs:
# doesn't have to be installed tag: ${{ steps.tag.outputs.tag }}
container: esphome/esphome-lint:1.1
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
# Set up the pio project so that the cpp checks know how files are compiled - name: Get tag
# (build flags, libraries etc) id: tag
- 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: | run: |
echo "::add-matcher::.github/workflows/matchers/clang-tidy.json" if [[ "$GITHUB_EVENT_NAME" = "release" ]]; then
echo "::add-matcher::.github/workflows/matchers/gcc.json" TAG="${GITHUB_REF#refs/tags/v}"
- name: Run clang-tidy else
run: script/clang-tidy --all-headers --fix --split-num 4 --split-at ${{ matrix.split }} TAG=$(cat esphome/const.py | sed -n -E "s/^__version__\s+=\s+\"(.+)\"$/\1/p")
- name: Suggest changes today="$(date --utc '+%Y%m%d')"
run: script/ci-suggest-changes TAG="${TAG}${today}"
fi
lint-python: echo "::set-output name=tag::${TAG}"
# 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-pypi: deploy-pypi:
name: Build and publish to PyPi name: Build and publish to PyPi
if: github.repository == 'esphome/esphome' if: github.repository == 'esphome/esphome' && github.event_name == 'release'
needs: [lint-clang-format, lint-clang-tidy, lint-python, test, pytest]
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
@@ -182,119 +53,85 @@ jobs:
name: Build and publish docker containers name: Build and publish docker containers
if: github.repository == 'esphome/esphome' if: github.repository == 'esphome/esphome'
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [lint-clang-format, lint-clang-tidy, lint-python, test, pytest] needs: [init]
strategy: strategy:
matrix: matrix:
arch: [amd64, armv7, aarch64] arch: [amd64, armv7, aarch64]
build_type: ["hassio", "docker"] build_type: ["ha-addon", "docker"]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Set TAG - name: Set up Python
run: | uses: actions/setup-python@v2
TAG="${GITHUB_REF#refs/tags/v}" with:
echo "TAG=${TAG}" >> $GITHUB_ENV python-version: '3.9'
- name: Set up env variables
run: |
base_version="3.4.0"
if [[ "${{ matrix.build_type }}" == "hassio" ]]; then - name: Run build
build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}" run: |
build_to="esphome/esphome-hassio-${{ matrix.arch }}" docker/build.py \
dockerfile="docker/Dockerfile.hassio" --tag "${{ needs.init.outputs.tag }}" \
else --arch "${{ matrix.arch }}" \
build_from="esphome/esphome-base-${{ matrix.arch }}:${base_version}" --build-type "${{ matrix.build_type }}" \
build_to="esphome/esphome-${{ matrix.arch }}" build
dockerfile="docker/Dockerfile"
fi
if [[ "${{ github.event.release.prerelease }}" == "true" ]]; then - name: Log in to docker hub
cache_tag="beta" uses: docker/login-action@v1
else with:
cache_tag="latest" username: ${{ secrets.DOCKER_USER }}
fi 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 - name: Run push
echo "BUILD_FROM=${build_from}" >> $GITHUB_ENV run: |
echo "BUILD_TO=${build_to}" >> $GITHUB_ENV docker/build.py \
echo "DOCKERFILE=${dockerfile}" >> $GITHUB_ENV --tag "${{ needs.init.outputs.tag }}" \
echo "CACHE_TAG=${cache_tag}" >> $GITHUB_ENV --arch "${{ matrix.arch }}" \
- name: Pull for cache --build-type "${{ matrix.build_type }}" \
run: | push
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"
deploy-docker-manifest: deploy-docker-manifest:
if: github.repository == 'esphome/esphome' if: github.repository == 'esphome/esphome'
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [deploy-docker] needs: [init, deploy-docker]
strategy:
matrix:
build_type: ["ha-addon", "docker"]
steps: steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: Enable experimental manifest support - name: Enable experimental manifest support
run: | run: |
mkdir -p ~/.docker mkdir -p ~/.docker
echo "{\"experimental\": \"enabled\"}" > ~/.docker/config.json 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 - name: Log in to docker hub
env: uses: docker/login-action@v1
DOCKER_USER: ${{ secrets.DOCKER_USER }} with:
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} username: ${{ secrets.DOCKER_USER }}
run: docker login -u "${DOCKER_USER}" -p "${DOCKER_PASSWORD}" password: ${{ secrets.DOCKER_PASSWORD }}
- name: "Create the manifest" - name: Log in to the GitHub container registry
run: | uses: docker/login-action@v1
docker manifest create esphome/esphome:${TAG} \ with:
esphome/esphome-aarch64:${TAG} \ registry: ghcr.io
esphome/esphome-amd64:${TAG} \ username: ${{ github.actor }}
esphome/esphome-armv7:${TAG} password: ${{ secrets.GITHUB_TOKEN }}
docker manifest push esphome/esphome:${TAG}
- name: Publish docker beta tag - name: Run manifest
run: | run: |
docker manifest create esphome/esphome:beta \ docker/build.py \
esphome/esphome-aarch64:${TAG} \ --tag "${{ needs.init.outputs.tag }}" \
esphome/esphome-amd64:${TAG} \ --build-type "${{ matrix.build_type }}" \
esphome/esphome-armv7:${TAG} manifest
docker manifest push esphome/esphome:beta
- 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: deploy-hassio-repo:
if: github.repository == 'esphome/esphome' if: github.repository == 'esphome/esphome' && github.event_name == 'release'
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [deploy-docker] needs: [deploy-docker]
steps: steps:
@@ -307,4 +144,4 @@ jobs:
-X POST \ -X POST \
-H "Accept: application/vnd.github.v3+json" \ -H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/esphome/hassio/actions/workflows/bump-version.yml/dispatches \ https://api.github.com/repos/esphome/hassio/actions/workflows/bump-version.yml/dispatches \
-d "{\"ref\":\"master\",\"inputs\":{\"version\":\"$TAG\"}}" -d "{\"ref\":\"main\",\"inputs\":{\"version\":\"$TAG\"}}"

3
.gitignore vendored
View File

@@ -13,6 +13,9 @@ __pycache__/
# Intellij Idea # Intellij Idea
.idea .idea
# Vim
*.swp
# Hide some OS X stuff # Hide some OS X stuff
.DS_Store .DS_Store
.AppleDouble .AppleDouble

View File

@@ -23,5 +23,5 @@ repos:
- id: no-commit-to-branch - id: no-commit-to-branch
args: args:
- --branch=dev - --branch=dev
- --branch=master - --branch=release
- --branch=beta - --branch=beta

35
.vscode/tasks.json vendored
View File

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

View File

@@ -15,10 +15,12 @@ esphome/components/ac_dimmer/* @glmnet
esphome/components/adc/* @esphome/core esphome/components/adc/* @esphome/core
esphome/components/addressable_light/* @justfalter esphome/components/addressable_light/* @justfalter
esphome/components/animation/* @syndlex esphome/components/animation/* @syndlex
esphome/components/anova/* @buxtronix
esphome/components/api/* @OttoWinter esphome/components/api/* @OttoWinter
esphome/components/async_tcp/* @OttoWinter esphome/components/async_tcp/* @OttoWinter
esphome/components/atc_mithermometer/* @ahpohl esphome/components/atc_mithermometer/* @ahpohl
esphome/components/b_parasite/* @rbaron esphome/components/b_parasite/* @rbaron
esphome/components/ballu/* @bazuchan
esphome/components/bang_bang/* @OttoWinter esphome/components/bang_bang/* @OttoWinter
esphome/components/binary_sensor/* @esphome/core esphome/components/binary_sensor/* @esphome/core
esphome/components/ble_client/* @buxtronix esphome/components/ble_client/* @buxtronix
@@ -45,6 +47,7 @@ esphome/components/fingerprint_grow/* @OnFreund @loongyh
esphome/components/globals/* @esphome/core esphome/components/globals/* @esphome/core
esphome/components/gpio/* @esphome/core esphome/components/gpio/* @esphome/core
esphome/components/gps/* @coogle esphome/components/gps/* @coogle
esphome/components/havells_solar/* @sourabhjaiswal
esphome/components/homeassistant/* @OttoWinter esphome/components/homeassistant/* @OttoWinter
esphome/components/i2c/* @esphome/core esphome/components/i2c/* @esphome/core
esphome/components/improv/* @jesserockz esphome/components/improv/* @jesserockz
@@ -70,7 +73,13 @@ esphome/components/midea_ac/* @dudanov
esphome/components/midea_dongle/* @dudanov esphome/components/midea_dongle/* @dudanov
esphome/components/mitsubishi/* @RubyBailey esphome/components/mitsubishi/* @RubyBailey
esphome/components/network/* @esphome/core 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/nfc/* @jesserockz
esphome/components/number/* @esphome/core
esphome/components/ota/* @esphome/core esphome/components/ota/* @esphome/core
esphome/components/output/* @esphome/core esphome/components/output/* @esphome/core
esphome/components/pid/* @OttoWinter esphome/components/pid/* @OttoWinter

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/) [![ESPHome Logo](https://esphome.io/_images/logo-text.png)](https://esphome.io/)

View File

@@ -1,4 +1,4 @@
ARG BUILD_FROM=esphome/esphome-base-amd64:3.4.0 ARG BUILD_FROM=esphome/esphome-base:latest
FROM ${BUILD_FROM} FROM ${BUILD_FROM}
# First install requirements to leverage caching when requirements don't change # First install requirements to leverage caching when requirements don't change

View File

@@ -1,13 +1 @@
FROM esphome/esphome-base-amd64:3.4.0 FROM esphome/esphome-lint:1.1
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,4 +1,4 @@
ARG BUILD_FROM ARG BUILD_FROM=esphome/esphome-hassio-base:latest
FROM ${BUILD_FROM} FROM ${BUILD_FROM}
# First install requirements to leverage caching when requirements don't change # First install requirements to leverage caching when requirements don't change

View File

@@ -1,4 +1,5 @@
FROM esphome/esphome-lint-base:3.4.0 ARG BUILD_FROM=esphome/esphome-lint-base:latest
FROM ${BUILD_FROM}
COPY requirements.txt requirements_optional.txt requirements_test.txt docker/platformio_install_deps.py platformio.ini / COPY requirements.txt requirements_optional.txt requirements_test.txt docker/platformio_install_deps.py platformio.ini /
RUN \ RUN \

177
docker/build.py Executable file
View File

@@ -0,0 +1,177 @@
#!/usr/bin/env python3
from dataclasses import dataclass
import subprocess
import argparse
import platform
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]
BASE_VERSION = "3.6.0"
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")
push_parser = subparsers.add_parser("push", help="Tag the already built image and push it to docker hub")
manifest_parser = subparsers.add_parser("manifest", help="Create a manifest from already pushed images")
# only lists some possibilities, doesn't have to be perfect
# https://stackoverflow.com/a/45125525
UNAME_TO_ARCH = {
"x86_64": ARCH_AMD64,
"aarch64": ARCH_AARCH64,
"aarch64_be": ARCH_AARCH64,
"arm": ARCH_ARMV7,
}
@dataclass(frozen=True)
class DockerParams:
build_from: str
build_to: str
manifest_to: str
dockerfile: 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_from = f"ghcr.io/{prefix}-base-{arch}:{BASE_VERSION}"
build_to = f"{prefix}-{arch}"
dockerfile = {
TYPE_DOCKER: "docker/Dockerfile",
TYPE_HA_ADDON: "docker/Dockerfile.hassio",
TYPE_LINT: "docker/Dockerfile.lint",
}[build_type]
return cls(
build_from=build_from,
build_to=build_to,
manifest_to=prefix,
dockerfile=dockerfile
)
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: "dev",
CHANNEL_BETA: "beta",
CHANNEL_RELEASE: "latest",
}[channel]
cache_img = f"ghcr.io/{params.build_to}:{cache_tag}"
run_command("docker", "pull", cache_img, ignore_error=True)
# 2. register QEMU binfmt (if not host arch)
is_native = UNAME_TO_ARCH.get(platform.machine()) == args.arch
if not is_native:
run_command(
"docker", "run", "--rm", "--privileged", "multiarch/qemu-user-static:5.2.0-2",
"--reset", "-p", "yes"
)
# 3. build
run_command(
"docker", "build",
"--build-arg", f"BUILD_FROM={params.build_from}",
"--build-arg", f"BUILD_VERSION={args.tag}",
"--tag", f"{params.build_to}:{args.tag}",
"--cache-from", cache_img,
"--file", params.dockerfile,
"."
)
elif args.command == "push":
params = DockerParams.for_type_arch(args.build_type, args.arch)
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]
src = imgs[0]
# 1. tag images
for img in imgs[1:]:
run_command(
"docker", "tag", src, img
)
# 2. push images
for img in imgs:
run_command(
"docker", "push", img
)
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()

View File

@@ -394,7 +394,7 @@ def command_update_all(args):
import click import click
success = {} success = {}
files = list_yaml_files(args.configuration) files = list_yaml_files(args.configuration[0])
twidth = 60 twidth = 60
def print_bar(middle_text): def print_bar(middle_text):
@@ -408,7 +408,7 @@ def command_update_all(args):
print("-" * twidth) print("-" * twidth)
print() print()
rc = run_external_process( rc = run_external_process(
"esphome", "--dashboard", "run", f, "--no-logs", "--device", "OTA" "esphome", "--dashboard", "run", "--no-logs", "--device", "OTA", f
) )
if rc == 0: if rc == 0:
print_bar("[{}] {}".format(color(Fore.BOLD_GREEN, "SUCCESS"), f)) print_bar("[{}] {}".format(color(Fore.BOLD_GREEN, "SUCCESS"), f))
@@ -504,6 +504,7 @@ def parse_args(argv):
"version", "version",
"clean", "clean",
"dashboard", "dashboard",
"vscode",
], ],
) )
@@ -513,14 +514,26 @@ def parse_args(argv):
compat_parser.error = _raise compat_parser.error = _raise
try: deprecated_argv_suggestion = None
result, unparsed = compat_parser.parse_known_args(argv[1:])
last_option = len(argv) - len(unparsed) - 1 - len(result.configuration) if ["dashboard", "config"] == argv[1:3] or ["version"] == argv[1:2]:
argv = argv[0:last_option] + [result.command] + result.configuration + unparsed # this is most likely meant in new-style arg format. do not try compat parsing
deprecated_argv_suggestion = argv pass
except argparse.ArgumentError: else:
# This is not an old-style command line, so we don't have to do anything. try:
deprecated_argv_suggestion = None result, unparsed = compat_parser.parse_known_args(argv[1:])
last_option = len(argv) - len(unparsed) - 1 - len(result.configuration)
unparsed = [
"--device" if arg in ("--upload-port", "--serial-port") else arg
for arg in unparsed
]
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.
pass
# And continue on with regular parsing # And continue on with regular parsing
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
@@ -686,7 +699,7 @@ def run_esphome(argv):
CORE.dashboard = args.dashboard CORE.dashboard = args.dashboard
setup_log(args.verbose, args.quiet) setup_log(args.verbose, args.quiet)
if args.deprecated_argv_suggestion is not None: if args.deprecated_argv_suggestion is not None and args.command != "vscode":
_LOGGER.warning( _LOGGER.warning(
"Calling ESPHome with the configuration before the command is deprecated " "Calling ESPHome with the configuration before the command is deprecated "
"and will be removed in the future. " "and will be removed in the future. "

View File

@@ -60,6 +60,7 @@ from esphome.cpp_types import ( # noqa
uint8, uint8,
uint16, uint16,
uint32, uint32,
uint64,
int32, int32,
const_char_ptr, const_char_ptr,
NAN, NAN,

View File

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

View File

@@ -9,15 +9,15 @@
namespace esphome { namespace esphome {
namespace ac_dimmer { namespace ac_dimmer {
static const char *TAG = "ac_dimmer"; static const char *const TAG = "ac_dimmer";
// Global array to store dimmer objects // 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 /// Time in microseconds the gate should be held high
/// 10µs should be long enough for most triacs /// 10µs should be long enough for most triacs
/// For reference: BT136 datasheet says 2µs nominal (page 7) /// For reference: BT136 datasheet says 2µs nominal (page 7)
static uint32_t GATE_ENABLE_TIME = 10; static const uint32_t GATE_ENABLE_TIME = 10;
/// Function called from timer interrupt /// Function called from timer interrupt
/// Input is current time in microseconds (micros()) /// Input is current time in microseconds (micros())

View File

@@ -4,7 +4,7 @@
namespace esphome { namespace esphome {
namespace adalight { 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_ACK_INTERVAL = 1000;
static const uint32_t ADALIGHT_RECEIVE_TIMEOUT = 1000; static const uint32_t ADALIGHT_RECEIVE_TIMEOUT = 1000;

View File

@@ -8,7 +8,7 @@ ADC_MODE(ADC_VCC)
namespace esphome { namespace esphome {
namespace adc { namespace adc {
static const char *TAG = "adc"; static const char *const TAG = "adc";
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
void ADCSensor::set_attenuation(adc_attenuation_t attenuation) { this->attenuation_ = attenuation; } void ADCSensor::set_attenuation(adc_attenuation_t attenuation) { this->attenuation_ = attenuation; }

View File

@@ -4,7 +4,7 @@
namespace esphome { namespace esphome {
namespace addressable_light { 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_width_internal() { return this->width_; }
int AddressableLightDisplay::get_height_internal() { return this->height_; } int AddressableLightDisplay::get_height_internal() { return this->height_; }
@@ -24,7 +24,7 @@ void AddressableLightDisplay::update() {
void AddressableLightDisplay::display() { void AddressableLightDisplay::display() {
bool dirty = false; bool dirty = false;
uint8_t old_r, old_g, old_b, old_w; 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++) { for (uint32_t offset = 0; offset < this->addressable_light_buffer_.size(); offset++) {
c = &(this->addressable_light_buffer_[offset]); c = &(this->addressable_light_buffer_[offset]);

View File

@@ -4,7 +4,7 @@
namespace esphome { namespace esphome {
namespace ade7953 { namespace ade7953 {
static const char *TAG = "ade7953"; static const char *const TAG = "ade7953";
void ADE7953::dump_config() { void ADE7953::dump_config() {
ESP_LOGCONFIG(TAG, "ADE7953:"); ESP_LOGCONFIG(TAG, "ADE7953:");
@@ -21,8 +21,8 @@ void ADE7953::dump_config() {
} }
#define ADE_PUBLISH_(name, factor) \ #define ADE_PUBLISH_(name, factor) \
if (name && this->name##_sensor_) { \ if ((name) && this->name##_sensor_) { \
float value = *name / factor; \ float value = *(name) / (factor); \
this->name##_sensor_->publish_state(value); \ this->name##_sensor_->publish_state(value); \
} }
#define ADE_PUBLISH(name, factor) ADE_PUBLISH_(name, factor) #define ADE_PUBLISH(name, factor) ADE_PUBLISH_(name, factor)

View File

@@ -4,7 +4,7 @@
namespace esphome { namespace esphome {
namespace ads1115 { 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_CONVERSION = 0x00;
static const uint8_t ADS1115_REGISTER_CONFIG = 0x01; static const uint8_t ADS1115_REGISTER_CONFIG = 0x01;

View File

@@ -18,7 +18,7 @@
namespace esphome { namespace esphome {
namespace aht10 { 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_CALIBRATE_CMD[] = {0xE1};
static const uint8_t AHT10_MEASURE_CMD[] = {0xAC, 0x33, 0x00}; 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_DEFAULT_DELAY = 5; // ms, for calibration and temperature measurement

View File

@@ -9,7 +9,7 @@
namespace esphome { namespace esphome {
namespace am2320 { namespace am2320 {
static const char *TAG = "am2320"; static const char *const TAG = "am2320";
// ---=== Calc CRC16 ===--- // ---=== Calc CRC16 ===---
uint16_t crc_16(uint8_t *ptr, uint8_t length) { uint16_t crc_16(uint8_t *ptr, uint8_t length) {

View File

@@ -0,0 +1,140 @@
#include "anova.h"
#include "esphome/core/log.h"
#ifdef ARDUINO_ARCH_ESP32
namespace esphome {
namespace anova {
static const char *TAG = "anova";
using namespace esphome::climate;
void Anova::dump_config() { LOG_CLIMATE("", "Anova BLE Cooker", this); }
void Anova::setup() {
this->codec_ = new 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());
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;
}
this->publish_state();
if (this->current_request_ > 0) {
AnovaPacket *pkt = nullptr;
switch (this->current_request_++) {
case 1:
pkt = this->codec_->get_read_target_temp_request();
break;
case 2:
pkt = this->codec_->get_read_current_temp_request();
break;
default:
this->current_request_ = 0;
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::update() {
if (this->node_state != espbt::ClientState::Established)
return;
if (this->current_request_ == 0) {
auto pkt = this->codec_->get_read_device_status_request();
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,50 @@
#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 ARDUINO_ARCH_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() {
auto traits = climate::ClimateTraits();
traits.set_supports_current_temperature(true);
traits.set_supports_heat_mode(true);
traits.set_visual_min_temperature(25.0);
traits.set_visual_max_temperature(100.0);
traits.set_visual_temperature_step(0.1);
return traits;
}
protected:
AnovaCodec *codec_;
void control(const climate::ClimateCall &call) override;
uint16_t char_handle_;
uint8_t current_request_;
};
} // namespace anova
} // namespace esphome
#endif

View File

@@ -0,0 +1,119 @@
#include "anova_base.h"
namespace esphome {
namespace anova {
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;
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) {
memset(this->buf_, 0, 32);
strncpy(this->buf_, (char *) data, length);
ESP_LOGV("anova", "Received: %s\n", this->buf_);
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(this->buf_, "stopped", 7)) {
this->has_running_ = true;
this->running_ = false;
}
if (!strncmp(this->buf_, "running", 7)) {
this->has_running_ = true;
this->running_ = true;
}
break;
}
case START: {
if (!strncmp(this->buf_, "start", 5)) {
this->has_running_ = true;
this->running_ = true;
}
break;
}
case STOP: {
if (!strncmp(this->buf_, "stop", 4)) {
this->has_running_ = true;
this->running_ = false;
}
break;
}
case READ_TARGET_TEMPERATURE: {
this->target_temp_ = strtof(this->buf_, nullptr);
this->has_target_temp_ = true;
break;
}
case SET_TARGET_TEMPERATURE: {
this->target_temp_ = strtof(this->buf_, nullptr);
this->has_target_temp_ = true;
break;
}
case READ_CURRENT_TEMPERATURE: {
this->current_temp_ = strtof(this->buf_, nullptr);
this->has_current_temp_ = 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_;
char buf_[32];
CurrentQuery current_query_;
};
} // namespace anova
} // namespace esphome

View File

@@ -0,0 +1,25 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import climate, ble_client
from esphome.const import CONF_ID
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)})
.extend(ble_client.BLE_CLIENT_SCHEMA)
.extend(cv.polling_component_schema("60s"))
)
def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config)
yield climate.register_climate(var, config)
yield ble_client.register_ble_node(var, config)

View File

@@ -4,10 +4,10 @@
namespace esphome { namespace esphome {
namespace apds9960 { namespace apds9960 {
static const char *TAG = "apds9960"; static const char *const TAG = "apds9960";
#define APDS9960_ERROR_CHECK(func) \ #define APDS9960_ERROR_CHECK(func) \
if (!func) { \ if (!(func)) { \
this->mark_failed(); \ this->mark_failed(); \
return; \ return; \
} }

View File

@@ -38,6 +38,7 @@ service APIConnection {
rpc switch_command (SwitchCommandRequest) returns (void) {} rpc switch_command (SwitchCommandRequest) returns (void) {}
rpc camera_image (CameraImageRequest) returns (void) {} rpc camera_image (CameraImageRequest) returns (void) {}
rpc climate_command (ClimateCommandRequest) returns (void) {} rpc climate_command (ClimateCommandRequest) returns (void) {}
rpc number_command (NumberCommandRequest) returns (void) {}
} }
@@ -378,6 +379,7 @@ message LightStateResponse {
fixed32 key = 1; fixed32 key = 1;
bool state = 2; bool state = 2;
float brightness = 3; float brightness = 3;
float color_brightness = 10;
float red = 4; float red = 4;
float green = 5; float green = 5;
float blue = 6; float blue = 6;
@@ -396,6 +398,8 @@ message LightCommandRequest {
bool state = 3; bool state = 3;
bool has_brightness = 4; bool has_brightness = 4;
float brightness = 5; float brightness = 5;
bool has_color_brightness = 20;
float color_brightness = 21;
bool has_rgb = 6; bool has_rgb = 6;
float red = 7; float red = 7;
float green = 8; float green = 8;
@@ -710,13 +714,14 @@ enum ClimateAction {
CLIMATE_ACTION_FAN = 6; CLIMATE_ACTION_FAN = 6;
} }
enum ClimatePreset { enum ClimatePreset {
CLIMATE_PRESET_ECO = 0; CLIMATE_PRESET_NONE = 0;
CLIMATE_PRESET_AWAY = 1; CLIMATE_PRESET_HOME = 1;
CLIMATE_PRESET_BOOST = 2; CLIMATE_PRESET_AWAY = 2;
CLIMATE_PRESET_COMFORT = 3; CLIMATE_PRESET_BOOST = 3;
CLIMATE_PRESET_HOME = 4; CLIMATE_PRESET_COMFORT = 4;
CLIMATE_PRESET_SLEEP = 5; CLIMATE_PRESET_ECO = 5;
CLIMATE_PRESET_ACTIVITY = 6; CLIMATE_PRESET_SLEEP = 6;
CLIMATE_PRESET_ACTIVITY = 7;
} }
message ListEntitiesClimateResponse { message ListEntitiesClimateResponse {
option (id) = 46; option (id) = 46;
@@ -734,7 +739,9 @@ message ListEntitiesClimateResponse {
float visual_min_temperature = 8; float visual_min_temperature = 8;
float visual_max_temperature = 9; float visual_max_temperature = 9;
float visual_temperature_step = 10; 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; bool supports_action = 12;
repeated ClimateFanMode supported_fan_modes = 13; repeated ClimateFanMode supported_fan_modes = 13;
repeated ClimateSwingMode supported_swing_modes = 14; repeated ClimateSwingMode supported_swing_modes = 14;
@@ -754,7 +761,8 @@ message ClimateStateResponse {
float target_temperature = 4; float target_temperature = 4;
float target_temperature_low = 5; float target_temperature_low = 5;
float target_temperature_high = 6; float target_temperature_high = 6;
bool away = 7; // For older peers, equal to preset == CLIMATE_PRESET_AWAY
bool legacy_away = 7;
ClimateAction action = 8; ClimateAction action = 8;
ClimateFanMode fan_mode = 9; ClimateFanMode fan_mode = 9;
ClimateSwingMode swing_mode = 10; ClimateSwingMode swing_mode = 10;
@@ -777,8 +785,9 @@ message ClimateCommandRequest {
float target_temperature_low = 7; float target_temperature_low = 7;
bool has_target_temperature_high = 8; bool has_target_temperature_high = 8;
float target_temperature_high = 9; float target_temperature_high = 9;
bool has_away = 10; // legacy, for older peers, newer ones should use CLIMATE_PRESET_AWAY in preset
bool away = 11; bool has_legacy_away = 10;
bool legacy_away = 11;
bool has_fan_mode = 12; bool has_fan_mode = 12;
ClimateFanMode fan_mode = 13; ClimateFanMode fan_mode = 13;
bool has_swing_mode = 14; bool has_swing_mode = 14;
@@ -790,3 +799,41 @@ message ClimateCommandRequest {
bool has_custom_preset = 20; bool has_custom_preset = 20;
string custom_preset = 21; string custom_preset = 21;
} }
// ==================== NUMBER ====================
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;
}
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;
}

View File

@@ -16,7 +16,7 @@
namespace esphome { namespace esphome {
namespace api { namespace api {
static const char *TAG = "api.connection"; static const char *const TAG = "api.connection";
APIConnection::APIConnection(AsyncClient *client, APIServer *parent) APIConnection::APIConnection(AsyncClient *client, APIServer *parent)
: client_(client), parent_(parent), initial_state_iterator_(parent, this), list_entities_iterator_(parent, this) { : client_(client), parent_(parent), initial_state_iterator_(parent, this), list_entities_iterator_(parent, this) {
@@ -308,6 +308,7 @@ bool APIConnection::send_light_state(light::LightState *light) {
if (traits.get_supports_brightness()) if (traits.get_supports_brightness())
resp.brightness = values.get_brightness(); resp.brightness = values.get_brightness();
if (traits.get_supports_rgb()) { if (traits.get_supports_rgb()) {
resp.color_brightness = values.get_color_brightness();
resp.red = values.get_red(); resp.red = values.get_red();
resp.green = values.get_green(); resp.green = values.get_green();
resp.blue = values.get_blue(); resp.blue = values.get_blue();
@@ -352,6 +353,8 @@ void APIConnection::light_command(const LightCommandRequest &msg) {
call.set_state(msg.state); call.set_state(msg.state);
if (msg.has_brightness) if (msg.has_brightness)
call.set_brightness(msg.brightness); call.set_brightness(msg.brightness);
if (msg.has_color_brightness)
call.set_color_brightness(msg.color_brightness);
if (msg.has_rgb) { if (msg.has_rgb) {
call.set_red(msg.red); call.set_red(msg.red);
call.set_green(msg.green); call.set_green(msg.green);
@@ -475,14 +478,14 @@ bool APIConnection::send_climate_state(climate::Climate *climate) {
} else { } else {
resp.target_temperature = climate->target_temperature; 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()) if (traits.get_supports_fan_modes() && climate->fan_mode.has_value())
resp.fan_mode = static_cast<enums::ClimateFanMode>(climate->fan_mode.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()) if (!traits.get_supported_custom_fan_modes().empty() && climate->custom_fan_mode.has_value())
resp.custom_fan_mode = climate->custom_fan_mode.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.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()) if (!traits.get_supported_custom_presets().empty() && climate->custom_preset.has_value())
resp.custom_preset = climate->custom_preset.value(); resp.custom_preset = climate->custom_preset.value();
if (traits.get_supports_swing_modes()) if (traits.get_supports_swing_modes())
@@ -498,40 +501,26 @@ bool APIConnection::send_climate_info(climate::Climate *climate) {
msg.unique_id = get_default_unique_id("climate", climate); msg.unique_id = get_default_unique_id("climate", climate);
msg.supports_current_temperature = traits.get_supports_current_temperature(); msg.supports_current_temperature = traits.get_supports_current_temperature();
msg.supports_two_point_target_temperature = traits.get_supports_two_point_target_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, for (auto mode : traits.get_supported_modes())
climate::CLIMATE_MODE_DRY, climate::CLIMATE_MODE_FAN_ONLY, climate::CLIMATE_MODE_HEAT_COOL}) { msg.supported_modes.push_back(static_cast<enums::ClimateMode>(mode));
if (traits.supports_mode(mode))
msg.supported_modes.push_back(static_cast<enums::ClimateMode>(mode));
}
msg.visual_min_temperature = traits.get_visual_min_temperature(); msg.visual_min_temperature = traits.get_visual_min_temperature();
msg.visual_max_temperature = traits.get_visual_max_temperature(); msg.visual_max_temperature = traits.get_visual_max_temperature();
msg.visual_temperature_step = traits.get_visual_temperature_step(); 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(); 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, for (auto fan_mode : traits.get_supported_fan_modes())
climate::CLIMATE_FAN_MIDDLE, climate::CLIMATE_FAN_FOCUS, climate::CLIMATE_FAN_DIFFUSE}) { msg.supported_fan_modes.push_back(static_cast<enums::ClimateFanMode>(fan_mode));
if (traits.supports_fan_mode(fan_mode)) for (auto const &custom_fan_mode : traits.get_supported_custom_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); msg.supported_custom_fan_modes.push_back(custom_fan_mode);
} for (auto preset : traits.get_supported_presets())
for (auto preset : {climate::CLIMATE_PRESET_ECO, climate::CLIMATE_PRESET_AWAY, climate::CLIMATE_PRESET_BOOST, msg.supported_presets.push_back(static_cast<enums::ClimatePreset>(preset));
climate::CLIMATE_PRESET_COMFORT, climate::CLIMATE_PRESET_HOME, climate::CLIMATE_PRESET_SLEEP, for (auto const &custom_preset : traits.get_supported_custom_presets())
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()) {
msg.supported_custom_presets.push_back(custom_preset); msg.supported_custom_presets.push_back(custom_preset);
} for (auto swing_mode : traits.get_supported_swing_modes())
for (auto swing_mode : {climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_BOTH, climate::CLIMATE_SWING_VERTICAL, msg.supported_swing_modes.push_back(static_cast<enums::ClimateSwingMode>(swing_mode));
climate::CLIMATE_SWING_HORIZONTAL}) {
if (traits.supports_swing_mode(swing_mode))
msg.supported_swing_modes.push_back(static_cast<enums::ClimateSwingMode>(swing_mode));
}
return this->send_list_entities_climate_response(msg); return this->send_list_entities_climate_response(msg);
} }
void APIConnection::climate_command(const ClimateCommandRequest &msg) { void APIConnection::climate_command(const ClimateCommandRequest &msg) {
@@ -548,8 +537,8 @@ void APIConnection::climate_command(const ClimateCommandRequest &msg) {
call.set_target_temperature_low(msg.target_temperature_low); call.set_target_temperature_low(msg.target_temperature_low);
if (msg.has_target_temperature_high) if (msg.has_target_temperature_high)
call.set_target_temperature_high(msg.target_temperature_high); call.set_target_temperature_high(msg.target_temperature_high);
if (msg.has_away) if (msg.has_legacy_away)
call.set_away(msg.away); call.set_preset(msg.legacy_away ? climate::CLIMATE_PRESET_AWAY : climate::CLIMATE_PRESET_HOME);
if (msg.has_fan_mode) if (msg.has_fan_mode)
call.set_fan_mode(static_cast<climate::ClimateFanMode>(msg.fan_mode)); call.set_fan_mode(static_cast<climate::ClimateFanMode>(msg.fan_mode));
if (msg.has_custom_fan_mode) if (msg.has_custom_fan_mode)
@@ -564,6 +553,42 @@ void APIConnection::climate_command(const ClimateCommandRequest &msg) {
} }
#endif #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->traits.get_icon();
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_ESP32_CAMERA #ifdef USE_ESP32_CAMERA
void APIConnection::send_camera_state(std::shared_ptr<esp32_camera::CameraImage> image) { void APIConnection::send_camera_state(std::shared_ptr<esp32_camera::CameraImage> image) {
if (!this->state_subscription_) if (!this->state_subscription_)
@@ -629,7 +654,7 @@ HelloResponse APIConnection::hello(const HelloRequest &msg) {
HelloResponse resp; HelloResponse resp;
resp.api_version_major = 1; resp.api_version_major = 1;
resp.api_version_minor = 4; resp.api_version_minor = 5;
resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")"; resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")";
this->connection_state_ = ConnectionState::CONNECTED; this->connection_state_ = ConnectionState::CONNECTED;
return resp; return resp;

View File

@@ -62,6 +62,11 @@ class APIConnection : public APIServerConnection {
bool send_climate_state(climate::Climate *climate); bool send_climate_state(climate::Climate *climate);
bool send_climate_info(climate::Climate *climate); bool send_climate_info(climate::Climate *climate);
void climate_command(const ClimateCommandRequest &msg) override; 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 #endif
bool send_log_message(int level, const char *tag, const char *line); bool send_log_message(int level, const char *tag, const char *line);
void send_homeassistant_service_call(const HomeassistantServiceResponse &call) { void send_homeassistant_service_call(const HomeassistantServiceResponse &call) {

View File

@@ -192,16 +192,18 @@ template<> const char *proto_enum_to_string<enums::ClimateAction>(enums::Climate
} }
template<> const char *proto_enum_to_string<enums::ClimatePreset>(enums::ClimatePreset value) { template<> const char *proto_enum_to_string<enums::ClimatePreset>(enums::ClimatePreset value) {
switch (value) { switch (value) {
case enums::CLIMATE_PRESET_ECO: case enums::CLIMATE_PRESET_NONE:
return "CLIMATE_PRESET_ECO"; return "CLIMATE_PRESET_NONE";
case enums::CLIMATE_PRESET_HOME:
return "CLIMATE_PRESET_HOME";
case enums::CLIMATE_PRESET_AWAY: case enums::CLIMATE_PRESET_AWAY:
return "CLIMATE_PRESET_AWAY"; return "CLIMATE_PRESET_AWAY";
case enums::CLIMATE_PRESET_BOOST: case enums::CLIMATE_PRESET_BOOST:
return "CLIMATE_PRESET_BOOST"; return "CLIMATE_PRESET_BOOST";
case enums::CLIMATE_PRESET_COMFORT: case enums::CLIMATE_PRESET_COMFORT:
return "CLIMATE_PRESET_COMFORT"; return "CLIMATE_PRESET_COMFORT";
case enums::CLIMATE_PRESET_HOME: case enums::CLIMATE_PRESET_ECO:
return "CLIMATE_PRESET_HOME"; return "CLIMATE_PRESET_ECO";
case enums::CLIMATE_PRESET_SLEEP: case enums::CLIMATE_PRESET_SLEEP:
return "CLIMATE_PRESET_SLEEP"; return "CLIMATE_PRESET_SLEEP";
case enums::CLIMATE_PRESET_ACTIVITY: case enums::CLIMATE_PRESET_ACTIVITY:
@@ -1261,6 +1263,10 @@ bool LightStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
this->brightness = value.as_float(); this->brightness = value.as_float();
return true; return true;
} }
case 10: {
this->color_brightness = value.as_float();
return true;
}
case 4: { case 4: {
this->red = value.as_float(); this->red = value.as_float();
return true; return true;
@@ -1289,6 +1295,7 @@ void LightStateResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_fixed32(1, this->key); buffer.encode_fixed32(1, this->key);
buffer.encode_bool(2, this->state); buffer.encode_bool(2, this->state);
buffer.encode_float(3, this->brightness); buffer.encode_float(3, this->brightness);
buffer.encode_float(10, this->color_brightness);
buffer.encode_float(4, this->red); buffer.encode_float(4, this->red);
buffer.encode_float(5, this->green); buffer.encode_float(5, this->green);
buffer.encode_float(6, this->blue); buffer.encode_float(6, this->blue);
@@ -1313,6 +1320,11 @@ void LightStateResponse::dump_to(std::string &out) const {
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
out.append(" color_brightness: ");
sprintf(buffer, "%g", this->color_brightness);
out.append(buffer);
out.append("\n");
out.append(" red: "); out.append(" red: ");
sprintf(buffer, "%g", this->red); sprintf(buffer, "%g", this->red);
out.append(buffer); out.append(buffer);
@@ -1357,6 +1369,10 @@ bool LightCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
this->has_brightness = value.as_bool(); this->has_brightness = value.as_bool();
return true; return true;
} }
case 20: {
this->has_color_brightness = value.as_bool();
return true;
}
case 6: { case 6: {
this->has_rgb = value.as_bool(); this->has_rgb = value.as_bool();
return true; return true;
@@ -1413,6 +1429,10 @@ bool LightCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
this->brightness = value.as_float(); this->brightness = value.as_float();
return true; return true;
} }
case 21: {
this->color_brightness = value.as_float();
return true;
}
case 7: { case 7: {
this->red = value.as_float(); this->red = value.as_float();
return true; return true;
@@ -1443,6 +1463,8 @@ void LightCommandRequest::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(3, this->state); buffer.encode_bool(3, this->state);
buffer.encode_bool(4, this->has_brightness); buffer.encode_bool(4, this->has_brightness);
buffer.encode_float(5, this->brightness); buffer.encode_float(5, this->brightness);
buffer.encode_bool(20, this->has_color_brightness);
buffer.encode_float(21, this->color_brightness);
buffer.encode_bool(6, this->has_rgb); buffer.encode_bool(6, this->has_rgb);
buffer.encode_float(7, this->red); buffer.encode_float(7, this->red);
buffer.encode_float(8, this->green); buffer.encode_float(8, this->green);
@@ -1483,6 +1505,15 @@ void LightCommandRequest::dump_to(std::string &out) const {
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
out.append(" has_color_brightness: ");
out.append(YESNO(this->has_color_brightness));
out.append("\n");
out.append(" color_brightness: ");
sprintf(buffer, "%g", this->color_brightness);
out.append(buffer);
out.append("\n");
out.append(" has_rgb: "); out.append(" has_rgb: ");
out.append(YESNO(this->has_rgb)); out.append(YESNO(this->has_rgb));
out.append("\n"); out.append("\n");
@@ -2672,7 +2703,7 @@ bool ListEntitiesClimateResponse::decode_varint(uint32_t field_id, ProtoVarInt v
return true; return true;
} }
case 11: { case 11: {
this->supports_away = value.as_bool(); this->legacy_supports_away = value.as_bool();
return true; return true;
} }
case 12: { case 12: {
@@ -2756,7 +2787,7 @@ void ListEntitiesClimateResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_float(8, this->visual_min_temperature); buffer.encode_float(8, this->visual_min_temperature);
buffer.encode_float(9, this->visual_max_temperature); buffer.encode_float(9, this->visual_max_temperature);
buffer.encode_float(10, this->visual_temperature_step); buffer.encode_float(10, this->visual_temperature_step);
buffer.encode_bool(11, this->supports_away); buffer.encode_bool(11, this->legacy_supports_away);
buffer.encode_bool(12, this->supports_action); buffer.encode_bool(12, this->supports_action);
for (auto &it : this->supported_fan_modes) { for (auto &it : this->supported_fan_modes) {
buffer.encode_enum<enums::ClimateFanMode>(13, it, true); buffer.encode_enum<enums::ClimateFanMode>(13, it, true);
@@ -2823,8 +2854,8 @@ void ListEntitiesClimateResponse::dump_to(std::string &out) const {
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
out.append(" supports_away: "); out.append(" legacy_supports_away: ");
out.append(YESNO(this->supports_away)); out.append(YESNO(this->legacy_supports_away));
out.append("\n"); out.append("\n");
out.append(" supports_action: "); out.append(" supports_action: ");
@@ -2869,7 +2900,7 @@ bool ClimateStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
return true; return true;
} }
case 7: { case 7: {
this->away = value.as_bool(); this->legacy_away = value.as_bool();
return true; return true;
} }
case 8: { case 8: {
@@ -2939,7 +2970,7 @@ void ClimateStateResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_float(4, this->target_temperature); buffer.encode_float(4, this->target_temperature);
buffer.encode_float(5, this->target_temperature_low); buffer.encode_float(5, this->target_temperature_low);
buffer.encode_float(6, this->target_temperature_high); buffer.encode_float(6, this->target_temperature_high);
buffer.encode_bool(7, this->away); buffer.encode_bool(7, this->legacy_away);
buffer.encode_enum<enums::ClimateAction>(8, this->action); buffer.encode_enum<enums::ClimateAction>(8, this->action);
buffer.encode_enum<enums::ClimateFanMode>(9, this->fan_mode); buffer.encode_enum<enums::ClimateFanMode>(9, this->fan_mode);
buffer.encode_enum<enums::ClimateSwingMode>(10, this->swing_mode); buffer.encode_enum<enums::ClimateSwingMode>(10, this->swing_mode);
@@ -2979,8 +3010,8 @@ void ClimateStateResponse::dump_to(std::string &out) const {
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
out.append(" away: "); out.append(" legacy_away: ");
out.append(YESNO(this->away)); out.append(YESNO(this->legacy_away));
out.append("\n"); out.append("\n");
out.append(" action: "); out.append(" action: ");
@@ -3031,11 +3062,11 @@ bool ClimateCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value)
return true; return true;
} }
case 10: { case 10: {
this->has_away = value.as_bool(); this->has_legacy_away = value.as_bool();
return true; return true;
} }
case 11: { case 11: {
this->away = value.as_bool(); this->legacy_away = value.as_bool();
return true; return true;
} }
case 12: { case 12: {
@@ -3120,8 +3151,8 @@ void ClimateCommandRequest::encode(ProtoWriteBuffer buffer) const {
buffer.encode_float(7, this->target_temperature_low); buffer.encode_float(7, this->target_temperature_low);
buffer.encode_bool(8, this->has_target_temperature_high); buffer.encode_bool(8, this->has_target_temperature_high);
buffer.encode_float(9, this->target_temperature_high); buffer.encode_float(9, this->target_temperature_high);
buffer.encode_bool(10, this->has_away); buffer.encode_bool(10, this->has_legacy_away);
buffer.encode_bool(11, this->away); buffer.encode_bool(11, this->legacy_away);
buffer.encode_bool(12, this->has_fan_mode); buffer.encode_bool(12, this->has_fan_mode);
buffer.encode_enum<enums::ClimateFanMode>(13, this->fan_mode); buffer.encode_enum<enums::ClimateFanMode>(13, this->fan_mode);
buffer.encode_bool(14, this->has_swing_mode); buffer.encode_bool(14, this->has_swing_mode);
@@ -3176,12 +3207,12 @@ void ClimateCommandRequest::dump_to(std::string &out) const {
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
out.append(" has_away: "); out.append(" has_legacy_away: ");
out.append(YESNO(this->has_away)); out.append(YESNO(this->has_legacy_away));
out.append("\n"); out.append("\n");
out.append(" away: "); out.append(" legacy_away: ");
out.append(YESNO(this->away)); out.append(YESNO(this->legacy_away));
out.append("\n"); out.append("\n");
out.append(" has_fan_mode: "); out.append(" has_fan_mode: ");
@@ -3225,6 +3256,179 @@ void ClimateCommandRequest::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append("}"); out.append("}");
} }
bool ListEntitiesNumberResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 1: {
this->object_id = value.as_string();
return true;
}
case 3: {
this->name = value.as_string();
return true;
}
case 4: {
this->unique_id = value.as_string();
return true;
}
case 5: {
this->icon = value.as_string();
return true;
}
default:
return false;
}
}
bool ListEntitiesNumberResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
switch (field_id) {
case 2: {
this->key = value.as_fixed32();
return true;
}
case 6: {
this->min_value = value.as_float();
return true;
}
case 7: {
this->max_value = value.as_float();
return true;
}
case 8: {
this->step = value.as_float();
return true;
}
default:
return false;
}
}
void ListEntitiesNumberResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(1, this->object_id);
buffer.encode_fixed32(2, this->key);
buffer.encode_string(3, this->name);
buffer.encode_string(4, this->unique_id);
buffer.encode_string(5, this->icon);
buffer.encode_float(6, this->min_value);
buffer.encode_float(7, this->max_value);
buffer.encode_float(8, this->step);
}
void ListEntitiesNumberResponse::dump_to(std::string &out) const {
char buffer[64];
out.append("ListEntitiesNumberResponse {\n");
out.append(" object_id: ");
out.append("'").append(this->object_id).append("'");
out.append("\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
out.append(buffer);
out.append("\n");
out.append(" name: ");
out.append("'").append(this->name).append("'");
out.append("\n");
out.append(" unique_id: ");
out.append("'").append(this->unique_id).append("'");
out.append("\n");
out.append(" icon: ");
out.append("'").append(this->icon).append("'");
out.append("\n");
out.append(" min_value: ");
sprintf(buffer, "%g", this->min_value);
out.append(buffer);
out.append("\n");
out.append(" max_value: ");
sprintf(buffer, "%g", this->max_value);
out.append(buffer);
out.append("\n");
out.append(" step: ");
sprintf(buffer, "%g", this->step);
out.append(buffer);
out.append("\n");
out.append("}");
}
bool NumberStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 3: {
this->missing_state = value.as_bool();
return true;
}
default:
return false;
}
}
bool NumberStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
switch (field_id) {
case 1: {
this->key = value.as_fixed32();
return true;
}
case 2: {
this->state = value.as_float();
return true;
}
default:
return false;
}
}
void NumberStateResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_fixed32(1, this->key);
buffer.encode_float(2, this->state);
buffer.encode_bool(3, this->missing_state);
}
void NumberStateResponse::dump_to(std::string &out) const {
char buffer[64];
out.append("NumberStateResponse {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
out.append(buffer);
out.append("\n");
out.append(" state: ");
sprintf(buffer, "%g", this->state);
out.append(buffer);
out.append("\n");
out.append(" missing_state: ");
out.append(YESNO(this->missing_state));
out.append("\n");
out.append("}");
}
bool NumberCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
switch (field_id) {
case 1: {
this->key = value.as_fixed32();
return true;
}
case 2: {
this->state = value.as_float();
return true;
}
default:
return false;
}
}
void NumberCommandRequest::encode(ProtoWriteBuffer buffer) const {
buffer.encode_fixed32(1, this->key);
buffer.encode_float(2, this->state);
}
void NumberCommandRequest::dump_to(std::string &out) const {
char buffer[64];
out.append("NumberCommandRequest {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
out.append(buffer);
out.append("\n");
out.append(" state: ");
sprintf(buffer, "%g", this->state);
out.append(buffer);
out.append("\n");
out.append("}");
}
} // namespace api } // namespace api
} // namespace esphome } // namespace esphome

View File

@@ -90,13 +90,14 @@ enum ClimateAction : uint32_t {
CLIMATE_ACTION_FAN = 6, CLIMATE_ACTION_FAN = 6,
}; };
enum ClimatePreset : uint32_t { enum ClimatePreset : uint32_t {
CLIMATE_PRESET_ECO = 0, CLIMATE_PRESET_NONE = 0,
CLIMATE_PRESET_AWAY = 1, CLIMATE_PRESET_HOME = 1,
CLIMATE_PRESET_BOOST = 2, CLIMATE_PRESET_AWAY = 2,
CLIMATE_PRESET_COMFORT = 3, CLIMATE_PRESET_BOOST = 3,
CLIMATE_PRESET_HOME = 4, CLIMATE_PRESET_COMFORT = 4,
CLIMATE_PRESET_SLEEP = 5, CLIMATE_PRESET_ECO = 5,
CLIMATE_PRESET_ACTIVITY = 6, CLIMATE_PRESET_SLEEP = 6,
CLIMATE_PRESET_ACTIVITY = 7,
}; };
} // namespace enums } // namespace enums
@@ -370,6 +371,7 @@ class LightStateResponse : public ProtoMessage {
uint32_t key{0}; uint32_t key{0};
bool state{false}; bool state{false};
float brightness{0.0f}; float brightness{0.0f};
float color_brightness{0.0f};
float red{0.0f}; float red{0.0f};
float green{0.0f}; float green{0.0f};
float blue{0.0f}; float blue{0.0f};
@@ -391,6 +393,8 @@ class LightCommandRequest : public ProtoMessage {
bool state{false}; bool state{false};
bool has_brightness{false}; bool has_brightness{false};
float brightness{0.0f}; float brightness{0.0f};
bool has_color_brightness{false};
float color_brightness{0.0f};
bool has_rgb{false}; bool has_rgb{false};
float red{0.0f}; float red{0.0f};
float green{0.0f}; float green{0.0f};
@@ -709,7 +713,7 @@ class ListEntitiesClimateResponse : public ProtoMessage {
float visual_min_temperature{0.0f}; float visual_min_temperature{0.0f};
float visual_max_temperature{0.0f}; float visual_max_temperature{0.0f};
float visual_temperature_step{0.0f}; float visual_temperature_step{0.0f};
bool supports_away{false}; bool legacy_supports_away{false};
bool supports_action{false}; bool supports_action{false};
std::vector<enums::ClimateFanMode> supported_fan_modes{}; std::vector<enums::ClimateFanMode> supported_fan_modes{};
std::vector<enums::ClimateSwingMode> supported_swing_modes{}; std::vector<enums::ClimateSwingMode> supported_swing_modes{};
@@ -732,7 +736,7 @@ class ClimateStateResponse : public ProtoMessage {
float target_temperature{0.0f}; float target_temperature{0.0f};
float target_temperature_low{0.0f}; float target_temperature_low{0.0f};
float target_temperature_high{0.0f}; float target_temperature_high{0.0f};
bool away{false}; bool legacy_away{false};
enums::ClimateAction action{}; enums::ClimateAction action{};
enums::ClimateFanMode fan_mode{}; enums::ClimateFanMode fan_mode{};
enums::ClimateSwingMode swing_mode{}; enums::ClimateSwingMode swing_mode{};
@@ -758,8 +762,8 @@ class ClimateCommandRequest : public ProtoMessage {
float target_temperature_low{0.0f}; float target_temperature_low{0.0f};
bool has_target_temperature_high{false}; bool has_target_temperature_high{false};
float target_temperature_high{0.0f}; float target_temperature_high{0.0f};
bool has_away{false}; bool has_legacy_away{false};
bool away{false}; bool legacy_away{false};
bool has_fan_mode{false}; bool has_fan_mode{false};
enums::ClimateFanMode fan_mode{}; enums::ClimateFanMode fan_mode{};
bool has_swing_mode{false}; bool has_swing_mode{false};
@@ -778,6 +782,45 @@ class ClimateCommandRequest : public ProtoMessage {
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt 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};
void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override;
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited 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;
void dump_to(std::string &out) const override;
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;
void dump_to(std::string &out) const override;
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
};
} // namespace api } // namespace api
} // namespace esphome } // namespace esphome

View File

@@ -6,7 +6,7 @@
namespace esphome { namespace esphome {
namespace api { namespace api {
static const char *TAG = "api.service"; static const char *const TAG = "api.service";
bool APIServerConnectionBase::send_hello_response(const HelloResponse &msg) { bool APIServerConnectionBase::send_hello_response(const HelloResponse &msg) {
ESP_LOGVV(TAG, "send_hello_response: %s", msg.dump().c_str()); ESP_LOGVV(TAG, "send_hello_response: %s", msg.dump().c_str());
@@ -184,6 +184,20 @@ bool APIServerConnectionBase::send_climate_state_response(const ClimateStateResp
#endif #endif
#ifdef USE_CLIMATE #ifdef USE_CLIMATE
#endif #endif
#ifdef USE_NUMBER
bool APIServerConnectionBase::send_list_entities_number_response(const ListEntitiesNumberResponse &msg) {
ESP_LOGVV(TAG, "send_list_entities_number_response: %s", msg.dump().c_str());
return this->send_message_<ListEntitiesNumberResponse>(msg, 49);
}
#endif
#ifdef USE_NUMBER
bool APIServerConnectionBase::send_number_state_response(const NumberStateResponse &msg) {
ESP_LOGVV(TAG, "send_number_state_response: %s", msg.dump().c_str());
return this->send_message_<NumberStateResponse>(msg, 50);
}
#endif
#ifdef USE_NUMBER
#endif
bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) { bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {
switch (msg_type) { switch (msg_type) {
case 1: { case 1: {
@@ -349,6 +363,15 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
msg.decode(msg_data, msg_size); msg.decode(msg_data, msg_size);
ESP_LOGVV(TAG, "on_climate_command_request: %s", msg.dump().c_str()); ESP_LOGVV(TAG, "on_climate_command_request: %s", msg.dump().c_str());
this->on_climate_command_request(msg); this->on_climate_command_request(msg);
#endif
break;
}
case 51: {
#ifdef USE_NUMBER
NumberCommandRequest msg;
msg.decode(msg_data, msg_size);
ESP_LOGVV(TAG, "on_number_command_request: %s", msg.dump().c_str());
this->on_number_command_request(msg);
#endif #endif
break; break;
} }
@@ -547,6 +570,19 @@ void APIServerConnection::on_climate_command_request(const ClimateCommandRequest
this->climate_command(msg); this->climate_command(msg);
} }
#endif #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
} // namespace api } // namespace api
} // namespace esphome } // namespace esphome

View File

@@ -111,6 +111,15 @@ class APIServerConnectionBase : public ProtoService {
#endif #endif
#ifdef USE_CLIMATE #ifdef USE_CLIMATE
virtual void on_climate_command_request(const ClimateCommandRequest &value){}; 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 #endif
protected: protected:
bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override; bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;
@@ -147,6 +156,9 @@ class APIServerConnection : public APIServerConnectionBase {
#endif #endif
#ifdef USE_CLIMATE #ifdef USE_CLIMATE
virtual void climate_command(const ClimateCommandRequest &msg) = 0; virtual void climate_command(const ClimateCommandRequest &msg) = 0;
#endif
#ifdef USE_NUMBER
virtual void number_command(const NumberCommandRequest &msg) = 0;
#endif #endif
protected: protected:
void on_hello_request(const HelloRequest &msg) override; void on_hello_request(const HelloRequest &msg) override;
@@ -179,6 +191,9 @@ class APIServerConnection : public APIServerConnectionBase {
#ifdef USE_CLIMATE #ifdef USE_CLIMATE
void on_climate_command_request(const ClimateCommandRequest &msg) override; void on_climate_command_request(const ClimateCommandRequest &msg) override;
#endif #endif
#ifdef USE_NUMBER
void on_number_command_request(const NumberCommandRequest &msg) override;
#endif
}; };
} // namespace api } // namespace api

View File

@@ -15,7 +15,7 @@
namespace esphome { namespace esphome {
namespace api { namespace api {
static const char *TAG = "api"; static const char *const TAG = "api";
// APIServer // APIServer
void APIServer::setup() { void APIServer::setup() {
@@ -180,7 +180,7 @@ void APIServer::on_switch_update(switch_::Switch *obj, bool state) {
#endif #endif
#ifdef USE_TEXT_SENSOR #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()) if (obj->is_internal())
return; return;
for (auto *c : this->clients_) for (auto *c : this->clients_)
@@ -197,9 +197,18 @@ void APIServer::on_climate_update(climate::Climate *obj) {
} }
#endif #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
float APIServer::get_setup_priority() const { return setup_priority::AFTER_WIFI; } float APIServer::get_setup_priority() const { return setup_priority::AFTER_WIFI; }
void APIServer::set_port(uint16_t port) { this->port_ = port; } 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::set_password(const std::string &password) { this->password_ = password; }
void APIServer::send_homeassistant_service_call(const HomeassistantServiceResponse &call) { void APIServer::send_homeassistant_service_call(const HomeassistantServiceResponse &call) {

View File

@@ -56,10 +56,13 @@ class APIServer : public Component, public Controller {
void on_switch_update(switch_::Switch *obj, bool state) override; void on_switch_update(switch_::Switch *obj, bool state) override;
#endif #endif
#ifdef USE_TEXT_SENSOR #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 #endif
#ifdef USE_CLIMATE #ifdef USE_CLIMATE
void on_climate_update(climate::Climate *obj) override; void on_climate_update(climate::Climate *obj) override;
#endif
#ifdef USE_NUMBER
void on_number_update(number::Number *obj, float state) override;
#endif #endif
void send_homeassistant_service_call(const HomeassistantServiceResponse &call); void send_homeassistant_service_call(const HomeassistantServiceResponse &call);
void register_user_service(UserServiceDescriptor *descriptor) { this->user_services_.push_back(descriptor); } void register_user_service(UserServiceDescriptor *descriptor) { this->user_services_.push_back(descriptor); }
@@ -91,7 +94,7 @@ class APIServer : public Component, public Controller {
std::vector<UserServiceDescriptor *> user_services_; std::vector<UserServiceDescriptor *> user_services_;
}; };
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...> { template<typename... Ts> class APIConnectedCondition : public Condition<Ts...> {
public: public:

View File

@@ -51,5 +51,9 @@ bool ListEntitiesIterator::on_camera(esp32_camera::ESP32Camera *camera) {
bool ListEntitiesIterator::on_climate(climate::Climate *climate) { return this->client_->send_climate_info(climate); } bool ListEntitiesIterator::on_climate(climate::Climate *climate) { return this->client_->send_climate_info(climate); }
#endif #endif
#ifdef USE_NUMBER
bool ListEntitiesIterator::on_number(number::Number *number) { return this->client_->send_number_info(number); }
#endif
} // namespace api } // namespace api
} // namespace esphome } // namespace esphome

View File

@@ -39,6 +39,9 @@ class ListEntitiesIterator : public ComponentIterator {
#endif #endif
#ifdef USE_CLIMATE #ifdef USE_CLIMATE
bool on_climate(climate::Climate *climate) override; bool on_climate(climate::Climate *climate) override;
#endif
#ifdef USE_NUMBER
bool on_number(number::Number *number) override;
#endif #endif
bool on_end() override; bool on_end() override;

View File

@@ -5,7 +5,7 @@
namespace esphome { namespace esphome {
namespace api { 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) { void ProtoMessage::decode(const uint8_t *buffer, size_t length) {
uint32_t i = 0; uint32_t i = 0;

View File

@@ -37,6 +37,11 @@ bool InitialStateIterator::on_text_sensor(text_sensor::TextSensor *text_sensor)
#ifdef USE_CLIMATE #ifdef USE_CLIMATE
bool InitialStateIterator::on_climate(climate::Climate *climate) { return this->client_->send_climate_state(climate); } bool InitialStateIterator::on_climate(climate::Climate *climate) { return this->client_->send_climate_state(climate); }
#endif #endif
#ifdef USE_NUMBER
bool InitialStateIterator::on_number(number::Number *number) {
return this->client_->send_number_state(number, number->state);
}
#endif
InitialStateIterator::InitialStateIterator(APIServer *server, APIConnection *client) InitialStateIterator::InitialStateIterator(APIServer *server, APIConnection *client)
: ComponentIterator(server), client_(client) {} : ComponentIterator(server), client_(client) {}

View File

@@ -36,6 +36,9 @@ class InitialStateIterator : public ComponentIterator {
#endif #endif
#ifdef USE_CLIMATE #ifdef USE_CLIMATE
bool on_climate(climate::Climate *climate) override; bool on_climate(climate::Climate *climate) override;
#endif
#ifdef USE_NUMBER
bool on_number(number::Number *number) override;
#endif #endif
protected: protected:
APIConnection *client_; APIConnection *client_;

View File

@@ -1,5 +1,7 @@
#pragma once #pragma once
#include <utility>
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/core/automation.h" #include "esphome/core/automation.h"
#include "api_pb2.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 { template<typename... Ts> class UserServiceBase : public UserServiceDescriptor {
public: public:
UserServiceBase(const std::string &name, const std::array<std::string, sizeof...(Ts)> &arg_names) UserServiceBase(std::string name, const std::array<std::string, sizeof...(Ts)> &arg_names)
: name_(name), arg_names_(arg_names) { : name_(std::move(name)), arg_names_(arg_names) {
this->key_ = fnv1_hash(this->name_); this->key_ = fnv1_hash(this->name_);
} }

View File

@@ -167,6 +167,21 @@ void ComponentIterator::advance() {
} }
} }
break; 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 #endif
case IteratorState::MAX: case IteratorState::MAX:
if (this->on_end()) { if (this->on_end()) {

View File

@@ -47,6 +47,9 @@ class ComponentIterator {
#endif #endif
#ifdef USE_CLIMATE #ifdef USE_CLIMATE
virtual bool on_climate(climate::Climate *climate) = 0; virtual bool on_climate(climate::Climate *climate) = 0;
#endif
#ifdef USE_NUMBER
virtual bool on_number(number::Number *number) = 0;
#endif #endif
virtual bool on_end(); virtual bool on_end();
@@ -81,6 +84,9 @@ class ComponentIterator {
#endif #endif
#ifdef USE_CLIMATE #ifdef USE_CLIMATE
CLIMATE, CLIMATE,
#endif
#ifdef USE_NUMBER
NUMBER,
#endif #endif
MAX, MAX,
} state_{IteratorState::NONE}; } state_{IteratorState::NONE};

View File

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

View File

@@ -4,7 +4,7 @@
namespace esphome { namespace esphome {
namespace as3935_i2c { 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) { void I2CAS3935Component::write_register(uint8_t reg, uint8_t mask, uint8_t bits, uint8_t start_pos) {
uint8_t write_reg; uint8_t write_reg;

View File

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

View File

@@ -6,7 +6,7 @@
namespace esphome { namespace esphome {
namespace atc_mithermometer { namespace atc_mithermometer {
static const char *TAG = "atc_mithermometer"; static const char *const TAG = "atc_mithermometer";
void ATCMiThermometer::dump_config() { void ATCMiThermometer::dump_config() {
ESP_LOGCONFIG(TAG, "ATC MiThermometer"); ESP_LOGCONFIG(TAG, "ATC MiThermometer");

View File

@@ -5,7 +5,7 @@
namespace esphome { namespace esphome {
namespace atm90e32 { namespace atm90e32 {
static const char *TAG = "atm90e32"; static const char *const TAG = "atm90e32";
void ATM90E32Component::update() { void ATM90E32Component::update() {
if (this->read16_(ATM90E32_REGISTER_METEREN) != 1) { if (this->read16_(ATM90E32_REGISTER_METEREN) != 1) {

View File

@@ -6,7 +6,7 @@
namespace esphome { namespace esphome {
namespace b_parasite { namespace b_parasite {
static const char* TAG = "b_parasite"; static const char *const TAG = "b_parasite";
void BParasite::dump_config() { void BParasite::dump_config() {
ESP_LOGCONFIG(TAG, "b_parasite"); ESP_LOGCONFIG(TAG, "b_parasite");
@@ -16,25 +16,25 @@ void BParasite::dump_config() {
LOG_SENSOR(" ", "Soil Moisture", this->soil_moisture_); LOG_SENSOR(" ", "Soil Moisture", this->soil_moisture_);
} }
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_) { if (device.address_uint64() != address_) {
ESP_LOGVV(TAG, "parse_device(): unknown MAC address."); ESP_LOGVV(TAG, "parse_device(): unknown MAC address.");
return false; return false;
} }
ESP_LOGVV(TAG, "parse_device(): MAC address %s found.", device.address_str().c_str()); 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) { if (service_datas.size() != 1) {
ESP_LOGE(TAG, "Unexpected service_datas size (%d)", service_datas.size()); ESP_LOGE(TAG, "Unexpected service_datas size (%d)", service_datas.size());
return false; return false;
} }
const auto& service_data = service_datas[0]; const auto &service_data = service_datas[0];
ESP_LOGVV(TAG, "Service data:"); ESP_LOGVV(TAG, "Service data:");
for (const uint8_t byte : service_data.data) { for (const uint8_t byte : service_data.data) {
ESP_LOGVV(TAG, "0x%02x", byte); ESP_LOGVV(TAG, "0x%02x", byte);
} }
const auto& data = service_data.data; const auto &data = service_data.data;
// Counter for deduplicating messages. // Counter for deduplicating messages.
uint8_t counter = data[1] & 0x0f; uint8_t counter = data[1] & 0x0f;

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 esphome {
namespace bang_bang { namespace bang_bang {
static const char *TAG = "bang_bang.climate"; static const char *const TAG = "bang_bang.climate";
void BangBangClimate::setup() { void BangBangClimate::setup() {
this->sensor_->add_on_state_callback([this](float state) { this->sensor_->add_on_state_callback([this](float state) {
@@ -21,7 +21,12 @@ void BangBangClimate::setup() {
restore->to_call(this).perform(); restore->to_call(this).perform();
} else { } else {
// restore from defaults, change_away handles those for us // 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); this->change_away_(false);
} }
} }
@@ -32,8 +37,8 @@ void BangBangClimate::control(const climate::ClimateCall &call) {
this->target_temperature_low = *call.get_target_temperature_low(); this->target_temperature_low = *call.get_target_temperature_low();
if (call.get_target_temperature_high().has_value()) if (call.get_target_temperature_high().has_value())
this->target_temperature_high = *call.get_target_temperature_high(); this->target_temperature_high = *call.get_target_temperature_high();
if (call.get_away().has_value()) if (call.get_preset().has_value())
this->change_away_(*call.get_away()); this->change_away_(*call.get_preset() == climate::CLIMATE_PRESET_AWAY);
this->compute_state_(); this->compute_state_();
this->publish_state(); this->publish_state();
@@ -41,21 +46,27 @@ void BangBangClimate::control(const climate::ClimateCall &call) {
climate::ClimateTraits BangBangClimate::traits() { climate::ClimateTraits BangBangClimate::traits() {
auto traits = climate::ClimateTraits(); auto traits = climate::ClimateTraits();
traits.set_supports_current_temperature(true); traits.set_supports_current_temperature(true);
traits.set_supports_auto_mode(true); traits.set_supported_modes({
traits.set_supports_cool_mode(this->supports_cool_); climate::CLIMATE_MODE_OFF,
traits.set_supports_heat_mode(this->supports_heat_); });
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_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); traits.set_supports_action(true);
return traits; return traits;
} }
void BangBangClimate::compute_state_() { void BangBangClimate::compute_state_() {
if (this->mode != climate::CLIMATE_MODE_AUTO) { if (this->mode == climate::CLIMATE_MODE_OFF) {
// in non-auto mode, switch directly to appropriate action this->switch_to_action_(climate::CLIMATE_ACTION_OFF);
// - HEAT mode -> HEATING action
// - COOL mode -> COOLING action
// - OFF mode -> OFF action (not IDLE!)
this->switch_to_action_(static_cast<climate::ClimateAction>(this->mode));
return; return;
} }
if (isnan(this->current_temperature) || isnan(this->target_temperature_low) || isnan(this->target_temperature_high)) { if (isnan(this->current_temperature) || isnan(this->target_temperature_low) || isnan(this->target_temperature_high)) {
@@ -140,7 +151,7 @@ void BangBangClimate::change_away_(bool away) {
this->target_temperature_low = this->away_config_.default_temperature_low; this->target_temperature_low = this->away_config_.default_temperature_low;
this->target_temperature_high = this->away_config_.default_temperature_high; 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) { void BangBangClimate::set_normal_config(const BangBangClimateTargetTempConfig &normal_config) {
this->normal_config_ = normal_config; this->normal_config_ = normal_config;

View File

@@ -4,7 +4,7 @@
namespace esphome { namespace esphome {
namespace bh1750 { 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_POWER_ON = 0b00000001;
static const uint8_t BH1750_COMMAND_MT_REG_HI = 0b01000000; // last 3 bits static const uint8_t BH1750_COMMAND_MT_REG_HI = 0b01000000; // last 3 bits

View File

@@ -4,7 +4,7 @@
namespace esphome { namespace esphome {
namespace binary { namespace binary {
static const char *TAG = "binary.fan"; static const char *const TAG = "binary.fan";
void binary::BinaryFan::dump_config() { void binary::BinaryFan::dump_config() {
ESP_LOGCONFIG(TAG, "Fan '%s':", this->fan_->get_name().c_str()); ESP_LOGCONFIG(TAG, "Fan '%s':", this->fan_->get_name().c_str());

View File

@@ -22,7 +22,6 @@ from esphome.const import (
CONF_STATE, CONF_STATE,
CONF_TIMING, CONF_TIMING,
CONF_TRIGGER_ID, CONF_TRIGGER_ID,
CONF_FOR,
CONF_NAME, CONF_NAME,
CONF_MQTT_ID, CONF_MQTT_ID,
DEVICE_CLASS_EMPTY, DEVICE_CLASS_EMPTY,
@@ -372,11 +371,6 @@ BINARY_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend(
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateTrigger), 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."
),
} }
) )
@@ -455,10 +449,6 @@ async def new_binary_sensor(config):
BINARY_SENSOR_CONDITION_SCHEMA = maybe_simple_id( BINARY_SENSOR_CONDITION_SCHEMA = maybe_simple_id(
{ {
cv.Required(CONF_ID): cv.use_id(BinarySensor), 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 esphome {
namespace binary_sensor { 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) { void binary_sensor::MultiClickTrigger::on_state_(bool state) {
// Handle duplicate events // Handle duplicate events
@@ -80,6 +80,10 @@ void binary_sensor::MultiClickTrigger::schedule_cooldown_() {
this->cancel_timeout("is_not_valid"); this->cancel_timeout("is_not_valid");
} }
void binary_sensor::MultiClickTrigger::schedule_is_valid_(uint32_t min_length) { 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->is_valid_ = false;
this->set_timeout("is_valid", min_length, [this]() { this->set_timeout("is_valid", min_length, [this]() {
ESP_LOGV(TAG, "Multi Click: You can now %s the button.", this->parent_->state ? "RELEASE" : "PRESS"); ESP_LOGV(TAG, "Multi Click: You can now %s the button.", this->parent_->state ? "RELEASE" : "PRESS");

View File

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

View File

@@ -5,7 +5,7 @@ namespace esphome {
namespace binary_sensor { 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) { void BinarySensor::add_on_state_callback(std::function<void(bool)> &&callback) {
this->state_callback_.add(std::move(callback)); this->state_callback_.add(std::move(callback));
@@ -61,7 +61,7 @@ void BinarySensor::add_filter(Filter *filter) {
last_filter->next_ = 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) { for (Filter *filter : filters) {
this->add_filter(filter); this->add_filter(filter);
} }

View File

@@ -9,10 +9,10 @@ namespace esphome {
namespace binary_sensor { namespace binary_sensor {
#define LOG_BINARY_SENSOR(prefix, type, obj) \ #define LOG_BINARY_SENSOR(prefix, type, obj) \
if (obj != nullptr) { \ if ((obj) != nullptr) { \
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, obj->get_name().c_str()); \ ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, (obj)->get_name().c_str()); \
if (!obj->get_device_class().empty()) { \ if (!(obj)->get_device_class().empty()) { \
ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, obj->get_device_class().c_str()); \ ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, (obj)->get_device_class().c_str()); \
} \ } \
} }
@@ -60,7 +60,7 @@ class BinarySensor : public Nameable {
std::string get_device_class(); std::string get_device_class();
void add_filter(Filter *filter); void add_filter(Filter *filter);
void add_filters(std::vector<Filter *> filters); void add_filters(const std::vector<Filter *> &filters);
// ========== INTERNAL METHODS ========== // ========== INTERNAL METHODS ==========
// (In most use cases you won't need these) // (In most use cases you won't need these)

View File

@@ -1,11 +1,13 @@
#include "filter.h" #include "filter.h"
#include "binary_sensor.h" #include "binary_sensor.h"
#include <utility>
namespace esphome { namespace esphome {
namespace binary_sensor { namespace binary_sensor {
static const char *TAG = "sensor.filter"; static const char *const TAG = "sensor.filter";
void Filter::output(bool value, bool is_initial) { void Filter::output(bool value, bool is_initial) {
if (!this->dedup_.next(value)) 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; } 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) { optional<bool> AutorepeatFilter::new_value(bool value, bool is_initial) {
if (value) { if (value) {
@@ -108,7 +110,7 @@ void AutorepeatFilter::next_value_(bool val) {
float AutorepeatFilter::get_setup_priority() const { return setup_priority::HARDWARE; } 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); } 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 { class AutorepeatFilter : public Filter, public Component {
public: public:
explicit AutorepeatFilter(const std::vector<AutorepeatFilterTiming> &timings); explicit AutorepeatFilter(std::vector<AutorepeatFilterTiming> timings);
optional<bool> new_value(bool value, bool is_initial) override; optional<bool> new_value(bool value, bool is_initial) override;
@@ -95,7 +95,7 @@ class AutorepeatFilter : public Filter, public Component {
class LambdaFilter : public Filter { class LambdaFilter : public Filter {
public: 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; optional<bool> new_value(bool value, bool is_initial) override;

View File

@@ -4,7 +4,7 @@
namespace esphome { namespace esphome {
namespace binary_sensor_map { 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); } void BinarySensorMap::dump_config() { LOG_SENSOR(" ", "binary_sensor_map", this); }

View File

@@ -9,7 +9,7 @@
namespace esphome { namespace esphome {
namespace ble_client { namespace ble_client {
static const char *TAG = "ble_client"; static const char *const TAG = "ble_client";
void BLEClient::setup() { void BLEClient::setup() {
auto ret = esp_ble_gattc_app_register(this->app_id); auto ret = esp_ble_gattc_app_register(this->app_id);

View File

@@ -9,7 +9,7 @@
namespace esphome { namespace esphome {
namespace ble_client { namespace ble_client {
static const char *TAG = "ble_sensor"; static const char *const TAG = "ble_sensor";
uint32_t BLESensor::hash_base() { return 343459825UL; } uint32_t BLESensor::hash_base() { return 343459825UL; }

View File

@@ -7,7 +7,7 @@
namespace esphome { namespace esphome {
namespace ble_client { namespace ble_client {
static const char *TAG = "ble_switch"; static const char *const TAG = "ble_switch";
void BLEClientSwitch::write_state(bool state) { void BLEClientSwitch::write_state(bool state) {
this->parent_->set_enabled(state); this->parent_->set_enabled(state);

View File

@@ -6,7 +6,7 @@
namespace esphome { namespace esphome {
namespace ble_presence { 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); } void BLEPresenceDevice::dump_config() { LOG_BINARY_SENSOR("", "BLE Presence", this); }

View File

@@ -6,7 +6,7 @@
namespace esphome { namespace esphome {
namespace ble_rssi { namespace ble_rssi {
static const char *TAG = "ble_rssi"; static const char *const TAG = "ble_rssi";
void BLERSSISensor::dump_config() { LOG_SENSOR("", "BLE RSSI Sensor", this); } void BLERSSISensor::dump_config() { LOG_SENSOR("", "BLE RSSI Sensor", this); }

View File

@@ -6,7 +6,7 @@
namespace esphome { namespace esphome {
namespace ble_scanner { namespace ble_scanner {
static const char *TAG = "ble_scanner"; static const char *const TAG = "ble_scanner";
void BLEScanner::dump_config() { LOG_TEXT_SENSOR("", "BLE Scanner", this); } void BLEScanner::dump_config() { LOG_TEXT_SENSOR("", "BLE Scanner", this); }

View File

@@ -4,7 +4,7 @@
namespace esphome { namespace esphome {
namespace bme280 { namespace bme280 {
static const char *TAG = "bme280.sensor"; static const char *const TAG = "bme280.sensor";
static const uint8_t BME280_REGISTER_DIG_T1 = 0x88; static const uint8_t BME280_REGISTER_DIG_T1 = 0x88;
static const uint8_t BME280_REGISTER_DIG_T2 = 0x8A; static const uint8_t BME280_REGISTER_DIG_T2 = 0x8A;

View File

@@ -4,7 +4,7 @@
namespace esphome { namespace esphome {
namespace bme680 { namespace bme680 {
static const char *TAG = "bme680.sensor"; static const char *const TAG = "bme680.sensor";
static const uint8_t BME680_REGISTER_COEFF1 = 0x89; static const uint8_t BME680_REGISTER_COEFF1 = 0x89;
static const uint8_t BME680_REGISTER_COEFF2 = 0xE1; static const uint8_t BME680_REGISTER_COEFF2 = 0xE1;

View File

@@ -6,7 +6,7 @@
namespace esphome { namespace esphome {
namespace bme680_bsec { namespace bme680_bsec {
#ifdef USE_BSEC #ifdef USE_BSEC
static const char *TAG = "bme680_bsec.sensor"; static const char *const TAG = "bme680_bsec.sensor";
static const std::string IAQ_ACCURACY_STATES[4] = {"Stabilizing", "Uncertain", "Calibrating", "Calibrated"}; static const std::string IAQ_ACCURACY_STATES[4] = {"Stabilizing", "Uncertain", "Calibrating", "Calibrated"};

View File

@@ -4,7 +4,7 @@
namespace esphome { namespace esphome {
namespace bmp085 { namespace bmp085 {
static const char *TAG = "bmp085.sensor"; static const char *const TAG = "bmp085.sensor";
static const uint8_t BMP085_ADDRESS = 0x77; static const uint8_t BMP085_ADDRESS = 0x77;
static const uint8_t BMP085_REGISTER_AC1_H = 0xAA; static const uint8_t BMP085_REGISTER_AC1_H = 0xAA;

View File

@@ -4,7 +4,7 @@
namespace esphome { namespace esphome {
namespace bmp280 { namespace bmp280 {
static const char *TAG = "bmp280.sensor"; static const char *const TAG = "bmp280.sensor";
static const uint8_t BMP280_REGISTER_STATUS = 0xF3; static const uint8_t BMP280_REGISTER_STATUS = 0xF3;
static const uint8_t BMP280_REGISTER_CONTROL = 0xF4; static const uint8_t BMP280_REGISTER_CONTROL = 0xF4;

View File

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

View File

@@ -6,7 +6,7 @@
namespace esphome { namespace esphome {
namespace captive_portal { namespace captive_portal {
static const char *TAG = "captive_portal"; static const char *const TAG = "captive_portal";
void CaptivePortal::handle_index(AsyncWebServerRequest *request) { void CaptivePortal::handle_index(AsyncWebServerRequest *request) {
AsyncResponseStream *stream = request->beginResponseStream("text/html"); AsyncResponseStream *stream = request->beginResponseStream("text/html");
@@ -147,7 +147,7 @@ float CaptivePortal::get_setup_priority() const {
} }
void CaptivePortal::dump_config() { ESP_LOGCONFIG(TAG, "Captive Portal:"); } void CaptivePortal::dump_config() { ESP_LOGCONFIG(TAG, "Captive Portal:"); }
CaptivePortal *global_captive_portal = nullptr; CaptivePortal *global_captive_portal = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
} // namespace captive_portal } // namespace captive_portal
} // namespace esphome } // namespace esphome

View File

@@ -68,7 +68,7 @@ class CaptivePortal : public AsyncWebHandler, public Component {
DNSServer *dns_server_{nullptr}; DNSServer *dns_server_{nullptr};
}; };
extern CaptivePortal *global_captive_portal; extern CaptivePortal *global_captive_portal; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
} // namespace captive_portal } // namespace captive_portal
} // namespace esphome } // namespace esphome

View File

@@ -4,7 +4,7 @@
namespace esphome { namespace esphome {
namespace ccs811 { namespace ccs811 {
static const char *TAG = "ccs811"; static const char *const TAG = "ccs811";
// based on // based on
// - https://cdn.sparkfun.com/datasheets/BreakoutBoards/CCS811_Programming_Guide.pdf // - https://cdn.sparkfun.com/datasheets/BreakoutBoards/CCS811_Programming_Guide.pdf

View File

@@ -8,6 +8,8 @@ from esphome.const import (
STATE_CLASS_MEASUREMENT, STATE_CLASS_MEASUREMENT,
UNIT_PARTS_PER_MILLION, UNIT_PARTS_PER_MILLION,
UNIT_PARTS_PER_BILLION, UNIT_PARTS_PER_BILLION,
CONF_BASELINE,
CONF_ECO2,
CONF_TEMPERATURE, CONF_TEMPERATURE,
CONF_TVOC, CONF_TVOC,
CONF_HUMIDITY, CONF_HUMIDITY,
@@ -21,9 +23,6 @@ CCS811Component = ccs811_ns.class_(
"CCS811Component", cg.PollingComponent, i2c.I2CDevice "CCS811Component", cg.PollingComponent, i2c.I2CDevice
) )
CONF_ECO2 = "eco2"
CONF_BASELINE = "baseline"
CONFIG_SCHEMA = ( CONFIG_SCHEMA = (
cv.Schema( cv.Schema(
{ {

View File

@@ -36,7 +36,7 @@ ClimateTraits = climate_ns.class_("ClimateTraits")
ClimateMode = climate_ns.enum("ClimateMode") ClimateMode = climate_ns.enum("ClimateMode")
CLIMATE_MODES = { CLIMATE_MODES = {
"OFF": ClimateMode.CLIMATE_MODE_OFF, "OFF": ClimateMode.CLIMATE_MODE_OFF,
"HEAT_COOL": ClimateMode.CLIMATE_HEAT_COOL, "HEAT_COOL": ClimateMode.CLIMATE_MODE_HEAT_COOL,
"COOL": ClimateMode.CLIMATE_MODE_COOL, "COOL": ClimateMode.CLIMATE_MODE_COOL,
"HEAT": ClimateMode.CLIMATE_MODE_HEAT, "HEAT": ClimateMode.CLIMATE_MODE_HEAT,
"DRY": ClimateMode.CLIMATE_MODE_DRY, "DRY": ClimateMode.CLIMATE_MODE_DRY,

View File

@@ -27,7 +27,9 @@ template<typename... Ts> class ControlAction : public Action<Ts...> {
call.set_target_temperature(this->target_temperature_.optional_value(x...)); call.set_target_temperature(this->target_temperature_.optional_value(x...));
call.set_target_temperature_low(this->target_temperature_low_.optional_value(x...)); call.set_target_temperature_low(this->target_temperature_low_.optional_value(x...));
call.set_target_temperature_high(this->target_temperature_high_.optional_value(x...)); call.set_target_temperature_high(this->target_temperature_high_.optional_value(x...));
call.set_away(this->away_.optional_value(x...)); if (away_.has_value()) {
call.set_preset(away_.value(x...) ? CLIMATE_PRESET_AWAY : CLIMATE_PRESET_HOME);
}
call.set_fan_mode(this->fan_mode_.optional_value(x...)); call.set_fan_mode(this->fan_mode_.optional_value(x...));
call.set_fan_mode(this->custom_fan_mode_.optional_value(x...)); call.set_fan_mode(this->custom_fan_mode_.optional_value(x...));
call.set_preset(this->preset_.optional_value(x...)); call.set_preset(this->preset_.optional_value(x...));

View File

@@ -3,7 +3,7 @@
namespace esphome { namespace esphome {
namespace climate { namespace climate {
static const char *TAG = "climate"; static const char *const TAG = "climate";
void ClimateCall::perform() { void ClimateCall::perform() {
ESP_LOGD(TAG, "'%s' - Setting", this->parent_->get_name().c_str()); ESP_LOGD(TAG, "'%s' - Setting", this->parent_->get_name().c_str());
@@ -43,9 +43,6 @@ void ClimateCall::perform() {
if (this->target_temperature_high_.has_value()) { if (this->target_temperature_high_.has_value()) {
ESP_LOGD(TAG, " Target Temperature High: %.2f", *this->target_temperature_high_); ESP_LOGD(TAG, " Target Temperature High: %.2f", *this->target_temperature_high_);
} }
if (this->away_.has_value()) {
ESP_LOGD(TAG, " Away Mode: %s", ONOFF(*this->away_));
}
this->parent_->control(*this); this->parent_->control(*this);
} }
void ClimateCall::validate_() { void ClimateCall::validate_() {
@@ -125,12 +122,6 @@ void ClimateCall::validate_() {
this->target_temperature_high_.reset(); this->target_temperature_high_.reset();
} }
} }
if (this->away_.has_value()) {
if (!traits.get_supports_away()) {
ESP_LOGW(TAG, " Cannot set away mode for this device!");
this->away_.reset();
}
}
} }
ClimateCall &ClimateCall::set_mode(ClimateMode mode) { ClimateCall &ClimateCall::set_mode(ClimateMode mode) {
this->mode_ = mode; this->mode_ = mode;
@@ -181,8 +172,7 @@ ClimateCall &ClimateCall::set_fan_mode(const std::string &fan_mode) {
} else if (str_equals_case_insensitive(fan_mode, "DIFFUSE")) { } else if (str_equals_case_insensitive(fan_mode, "DIFFUSE")) {
this->set_fan_mode(CLIMATE_FAN_DIFFUSE); this->set_fan_mode(CLIMATE_FAN_DIFFUSE);
} else { } else {
auto custom_fan_modes = this->parent_->get_traits().get_supported_custom_fan_modes(); if (this->parent_->get_traits().supports_custom_fan_mode(fan_mode)) {
if (std::find(custom_fan_modes.begin(), custom_fan_modes.end(), fan_mode) != custom_fan_modes.end()) {
this->custom_fan_mode_ = fan_mode; this->custom_fan_mode_ = fan_mode;
this->fan_mode_.reset(); this->fan_mode_.reset();
} else { } else {
@@ -218,8 +208,7 @@ ClimateCall &ClimateCall::set_preset(const std::string &preset) {
} else if (str_equals_case_insensitive(preset, "ACTIVITY")) { } else if (str_equals_case_insensitive(preset, "ACTIVITY")) {
this->set_preset(CLIMATE_PRESET_ACTIVITY); this->set_preset(CLIMATE_PRESET_ACTIVITY);
} else { } else {
auto custom_presets = this->parent_->get_traits().get_supported_custom_presets(); if (this->parent_->get_traits().supports_custom_preset(preset)) {
if (std::find(custom_presets.begin(), custom_presets.end(), preset) != custom_presets.end()) {
this->custom_preset_ = preset; this->custom_preset_ = preset;
this->preset_.reset(); this->preset_.reset();
} else { } else {
@@ -269,18 +258,23 @@ const optional<ClimateMode> &ClimateCall::get_mode() const { return this->mode_;
const optional<float> &ClimateCall::get_target_temperature() const { return this->target_temperature_; } const optional<float> &ClimateCall::get_target_temperature() const { return this->target_temperature_; }
const optional<float> &ClimateCall::get_target_temperature_low() const { return this->target_temperature_low_; } const optional<float> &ClimateCall::get_target_temperature_low() const { return this->target_temperature_low_; }
const optional<float> &ClimateCall::get_target_temperature_high() const { return this->target_temperature_high_; } const optional<float> &ClimateCall::get_target_temperature_high() const { return this->target_temperature_high_; }
const optional<bool> &ClimateCall::get_away() const { return this->away_; } optional<bool> ClimateCall::get_away() const {
if (!this->preset_.has_value())
return {};
return *this->preset_ == ClimatePreset::CLIMATE_PRESET_AWAY;
}
const optional<ClimateFanMode> &ClimateCall::get_fan_mode() const { return this->fan_mode_; } const optional<ClimateFanMode> &ClimateCall::get_fan_mode() const { return this->fan_mode_; }
const optional<std::string> &ClimateCall::get_custom_fan_mode() const { return this->custom_fan_mode_; } const optional<std::string> &ClimateCall::get_custom_fan_mode() const { return this->custom_fan_mode_; }
const optional<ClimatePreset> &ClimateCall::get_preset() const { return this->preset_; } const optional<ClimatePreset> &ClimateCall::get_preset() const { return this->preset_; }
const optional<std::string> &ClimateCall::get_custom_preset() const { return this->custom_preset_; } const optional<std::string> &ClimateCall::get_custom_preset() const { return this->custom_preset_; }
const optional<ClimateSwingMode> &ClimateCall::get_swing_mode() const { return this->swing_mode_; } const optional<ClimateSwingMode> &ClimateCall::get_swing_mode() const { return this->swing_mode_; }
ClimateCall &ClimateCall::set_away(bool away) { ClimateCall &ClimateCall::set_away(bool away) {
this->away_ = away; this->preset_ = away ? CLIMATE_PRESET_AWAY : CLIMATE_PRESET_HOME;
return *this; return *this;
} }
ClimateCall &ClimateCall::set_away(optional<bool> away) { ClimateCall &ClimateCall::set_away(optional<bool> away) {
this->away_ = away; if (away.has_value())
this->preset_ = *away ? CLIMATE_PRESET_AWAY : CLIMATE_PRESET_HOME;
return *this; return *this;
} }
ClimateCall &ClimateCall::set_target_temperature_high(optional<float> target_temperature_high) { ClimateCall &ClimateCall::set_target_temperature_high(optional<float> target_temperature_high) {
@@ -338,20 +332,17 @@ void Climate::save_state_() {
} else { } else {
state.target_temperature = this->target_temperature; state.target_temperature = this->target_temperature;
} }
if (traits.get_supports_away()) {
state.away = this->away;
}
if (traits.get_supports_fan_modes() && fan_mode.has_value()) { if (traits.get_supports_fan_modes() && fan_mode.has_value()) {
state.uses_custom_fan_mode = false; state.uses_custom_fan_mode = false;
state.fan_mode = this->fan_mode.value(); state.fan_mode = this->fan_mode.value();
} }
if (!traits.get_supported_custom_fan_modes().empty() && custom_fan_mode.has_value()) { if (!traits.get_supported_custom_fan_modes().empty() && custom_fan_mode.has_value()) {
state.uses_custom_fan_mode = true; state.uses_custom_fan_mode = true;
auto &custom_fan_modes = traits.get_supported_custom_fan_modes(); const auto &supported = traits.get_supported_custom_fan_modes();
auto it = std::find(custom_fan_modes.begin(), custom_fan_modes.end(), this->custom_fan_mode.value()); std::vector<std::string> vec{supported.begin(), supported.end()};
// only set custom fan mode if value exists, otherwise leave it as is auto it = std::find(vec.begin(), vec.end(), custom_fan_mode);
if (it != custom_fan_modes.cend()) { if (it != vec.end()) {
state.custom_fan_mode = std::distance(custom_fan_modes.begin(), it); state.custom_fan_mode = std::distance(vec.begin(), it);
} }
} }
if (traits.get_supports_presets() && preset.has_value()) { if (traits.get_supports_presets() && preset.has_value()) {
@@ -360,11 +351,12 @@ void Climate::save_state_() {
} }
if (!traits.get_supported_custom_presets().empty() && custom_preset.has_value()) { if (!traits.get_supported_custom_presets().empty() && custom_preset.has_value()) {
state.uses_custom_preset = true; state.uses_custom_preset = true;
auto custom_presets = traits.get_supported_custom_presets(); const auto &supported = traits.get_supported_custom_presets();
auto it = std::find(custom_presets.begin(), custom_presets.end(), this->custom_preset.value()); std::vector<std::string> vec{supported.begin(), supported.end()};
auto it = std::find(vec.begin(), vec.end(), custom_preset);
// only set custom preset if value exists, otherwise leave it as is // only set custom preset if value exists, otherwise leave it as is
if (it != custom_presets.cend()) { if (it != vec.cend()) {
state.custom_preset = std::distance(custom_presets.begin(), it); state.custom_preset = std::distance(vec.begin(), it);
} }
} }
if (traits.get_supports_swing_modes()) { if (traits.get_supports_swing_modes()) {
@@ -405,9 +397,6 @@ void Climate::publish_state() {
} else { } else {
ESP_LOGD(TAG, " Target Temperature: %.2f°C", this->target_temperature); ESP_LOGD(TAG, " Target Temperature: %.2f°C", this->target_temperature);
} }
if (traits.get_supports_away()) {
ESP_LOGD(TAG, " Away: %s", ONOFF(this->away));
}
// Send state to frontend // Send state to frontend
this->state_callback_.call(); this->state_callback_.call();
@@ -453,9 +442,6 @@ ClimateCall ClimateDeviceRestoreState::to_call(Climate *climate) {
} else { } else {
call.set_target_temperature(this->target_temperature); call.set_target_temperature(this->target_temperature);
} }
if (traits.get_supports_away()) {
call.set_away(this->away);
}
if (traits.get_supports_fan_modes() || !traits.get_supported_custom_fan_modes().empty()) { if (traits.get_supports_fan_modes() || !traits.get_supported_custom_fan_modes().empty()) {
call.set_fan_mode(this->fan_mode); call.set_fan_mode(this->fan_mode);
} }
@@ -476,23 +462,27 @@ void ClimateDeviceRestoreState::apply(Climate *climate) {
} else { } else {
climate->target_temperature = this->target_temperature; climate->target_temperature = this->target_temperature;
} }
if (traits.get_supports_away()) {
climate->away = this->away;
}
if (traits.get_supports_fan_modes() && !this->uses_custom_fan_mode) { if (traits.get_supports_fan_modes() && !this->uses_custom_fan_mode) {
climate->fan_mode = this->fan_mode; climate->fan_mode = this->fan_mode;
} }
if (!traits.get_supported_custom_fan_modes().empty() && this->uses_custom_fan_mode) { if (!traits.get_supported_custom_fan_modes().empty() && this->uses_custom_fan_mode) {
climate->custom_fan_mode = traits.get_supported_custom_fan_modes()[this->custom_fan_mode]; // std::set has consistent order (lexicographic for strings), so this is ok
const auto &modes = traits.get_supported_custom_fan_modes();
std::vector<std::string> modes_vec{modes.begin(), modes.end()};
if (custom_fan_mode < modes_vec.size()) {
climate->custom_fan_mode = modes_vec[this->custom_fan_mode];
}
} }
if (traits.get_supports_presets() && !this->uses_custom_preset) { if (traits.get_supports_presets() && !this->uses_custom_preset) {
climate->preset = this->preset; climate->preset = this->preset;
} }
if (!traits.get_supported_custom_presets().empty() && this->uses_custom_preset) {
climate->custom_preset = traits.get_supported_custom_presets()[this->custom_preset];
}
if (!traits.get_supported_custom_presets().empty() && uses_custom_preset) { if (!traits.get_supported_custom_presets().empty() && uses_custom_preset) {
climate->custom_preset = traits.get_supported_custom_presets()[this->preset]; // std::set has consistent order (lexicographic for strings), so this is ok
const auto &presets = traits.get_supported_custom_presets();
std::vector<std::string> presets_vec{presets.begin(), presets.end()};
if (custom_preset < presets_vec.size()) {
climate->custom_preset = presets_vec[this->custom_preset];
}
} }
if (traits.get_supports_swing_modes()) { if (traits.get_supports_swing_modes()) {
climate->swing_mode = this->swing_mode; climate->swing_mode = this->swing_mode;

View File

@@ -11,8 +11,8 @@ namespace esphome {
namespace climate { namespace climate {
#define LOG_CLIMATE(prefix, type, obj) \ #define LOG_CLIMATE(prefix, type, obj) \
if (obj != nullptr) { \ if ((obj) != nullptr) { \
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, obj->get_name().c_str()); \ ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, (obj)->get_name().c_str()); \
} }
class Climate; class Climate;
@@ -63,7 +63,9 @@ class ClimateCall {
* For climate devices with two point target temperature control * For climate devices with two point target temperature control
*/ */
ClimateCall &set_target_temperature_high(optional<float> target_temperature_high); ClimateCall &set_target_temperature_high(optional<float> target_temperature_high);
ESPDEPRECATED("set_away() is deprecated, please use .set_preset(CLIMATE_PRESET_AWAY) instead")
ClimateCall &set_away(bool away); ClimateCall &set_away(bool away);
ESPDEPRECATED("set_away() is deprecated, please use .set_preset(CLIMATE_PRESET_AWAY) instead")
ClimateCall &set_away(optional<bool> away); ClimateCall &set_away(optional<bool> away);
/// Set the fan mode of the climate device. /// Set the fan mode of the climate device.
ClimateCall &set_fan_mode(ClimateFanMode fan_mode); ClimateCall &set_fan_mode(ClimateFanMode fan_mode);
@@ -94,7 +96,8 @@ class ClimateCall {
const optional<float> &get_target_temperature() const; const optional<float> &get_target_temperature() const;
const optional<float> &get_target_temperature_low() const; const optional<float> &get_target_temperature_low() const;
const optional<float> &get_target_temperature_high() const; const optional<float> &get_target_temperature_high() const;
const optional<bool> &get_away() const; ESPDEPRECATED("get_away() is deprecated, please use .get_preset() instead")
optional<bool> get_away() const;
const optional<ClimateFanMode> &get_fan_mode() const; const optional<ClimateFanMode> &get_fan_mode() const;
const optional<ClimateSwingMode> &get_swing_mode() const; const optional<ClimateSwingMode> &get_swing_mode() const;
const optional<std::string> &get_custom_fan_mode() const; const optional<std::string> &get_custom_fan_mode() const;
@@ -109,7 +112,6 @@ class ClimateCall {
optional<float> target_temperature_; optional<float> target_temperature_;
optional<float> target_temperature_low_; optional<float> target_temperature_low_;
optional<float> target_temperature_high_; optional<float> target_temperature_high_;
optional<bool> away_;
optional<ClimateFanMode> fan_mode_; optional<ClimateFanMode> fan_mode_;
optional<ClimateSwingMode> swing_mode_; optional<ClimateSwingMode> swing_mode_;
optional<std::string> custom_fan_mode_; optional<std::string> custom_fan_mode_;
@@ -120,7 +122,6 @@ class ClimateCall {
/// Struct used to save the state of the climate device in restore memory. /// Struct used to save the state of the climate device in restore memory.
struct ClimateDeviceRestoreState { struct ClimateDeviceRestoreState {
ClimateMode mode; ClimateMode mode;
bool away;
bool uses_custom_fan_mode{false}; bool uses_custom_fan_mode{false};
union { union {
ClimateFanMode fan_mode; ClimateFanMode fan_mode;
@@ -159,7 +160,7 @@ struct ClimateDeviceRestoreState {
* *
* The entire state of the climate device is encoded in public properties of the base class (current_temperature, * The entire state of the climate device is encoded in public properties of the base class (current_temperature,
* mode etc). These are read-only for the user and rw for integrations. The reason these are public * mode etc). These are read-only for the user and rw for integrations. The reason these are public
* is for simple access to them from lambdas `if (id(my_climate).mode == climate::CLIMATE_MODE_AUTO) ...` * is for simple access to them from lambdas `if (id(my_climate).mode == climate::CLIMATE_MODE_HEAT_COOL) ...`
*/ */
class Climate : public Nameable { class Climate : public Nameable {
public: public:
@@ -191,6 +192,7 @@ class Climate : public Nameable {
* Away allows climate devices to have two different target temperature configs: * Away allows climate devices to have two different target temperature configs:
* one for normal mode and one for away mode. * one for normal mode and one for away mode.
*/ */
ESPDEPRECATED("away is deprecated, use preset instead")
bool away{false}; bool away{false};
/// The active fan mode of the climate device. /// The active fan mode of the climate device.

View File

@@ -84,6 +84,10 @@ const char *climate_swing_mode_to_string(ClimateSwingMode swing_mode) {
const char *climate_preset_to_string(ClimatePreset preset) { const char *climate_preset_to_string(ClimatePreset preset) {
switch (preset) { switch (preset) {
case climate::CLIMATE_PRESET_NONE:
return "NONE";
case climate::CLIMATE_PRESET_HOME:
return "HOME";
case climate::CLIMATE_PRESET_ECO: case climate::CLIMATE_PRESET_ECO:
return "ECO"; return "ECO";
case climate::CLIMATE_PRESET_AWAY: case climate::CLIMATE_PRESET_AWAY:
@@ -92,8 +96,6 @@ const char *climate_preset_to_string(ClimatePreset preset) {
return "BOOST"; return "BOOST";
case climate::CLIMATE_PRESET_COMFORT: case climate::CLIMATE_PRESET_COMFORT:
return "COMFORT"; return "COMFORT";
case climate::CLIMATE_PRESET_HOME:
return "HOME";
case climate::CLIMATE_PRESET_SLEEP: case climate::CLIMATE_PRESET_SLEEP:
return "SLEEP"; return "SLEEP";
case climate::CLIMATE_PRESET_ACTIVITY: case climate::CLIMATE_PRESET_ACTIVITY:

View File

@@ -7,19 +7,22 @@ namespace climate {
/// Enum for all modes a climate device can be in. /// Enum for all modes a climate device can be in.
enum ClimateMode : uint8_t { enum ClimateMode : uint8_t {
/// The climate device is off (not in auto, heat or cool mode) /// The climate device is off
CLIMATE_MODE_OFF = 0, CLIMATE_MODE_OFF = 0,
/// The climate device is set to automatically change the heating/cooling cycle /// The climate device is set to heat/cool to reach the target temperature.
CLIMATE_MODE_HEAT_COOL = 1, CLIMATE_MODE_HEAT_COOL = 1,
/// The climate device is manually set to cool mode (not in auto mode!) /// The climate device is set to cool to reach the target temperature
CLIMATE_MODE_COOL = 2, CLIMATE_MODE_COOL = 2,
/// The climate device is manually set to heat mode (not in auto mode!) /// The climate device is set to heat to reach the target temperature
CLIMATE_MODE_HEAT = 3, CLIMATE_MODE_HEAT = 3,
/// The climate device is manually set to fan only mode /// The climate device only has the fan enabled, no heating or cooling is taking place
CLIMATE_MODE_FAN_ONLY = 4, CLIMATE_MODE_FAN_ONLY = 4,
/// The climate device is manually set to dry mode /// The climate device is set to dry/humidity mode
CLIMATE_MODE_DRY = 5, CLIMATE_MODE_DRY = 5,
/// The climate device is manually set to heat-cool mode /** The climate device is adjusting the temperatre dynamically.
* For example, the target temperature can be adjusted based on a schedule, or learned behavior.
* The target temperature can't be adjusted when in this mode.
*/
CLIMATE_MODE_AUTO = 6 CLIMATE_MODE_AUTO = 6
}; };
@@ -27,19 +30,18 @@ enum ClimateMode : uint8_t {
enum ClimateAction : uint8_t { enum ClimateAction : uint8_t {
/// The climate device is off (inactive or no power) /// The climate device is off (inactive or no power)
CLIMATE_ACTION_OFF = 0, CLIMATE_ACTION_OFF = 0,
/// The climate device is actively cooling (usually in cool or auto mode) /// The climate device is actively cooling
CLIMATE_ACTION_COOLING = 2, CLIMATE_ACTION_COOLING = 2,
/// The climate device is actively heating (usually in heat or auto mode) /// The climate device is actively heating
CLIMATE_ACTION_HEATING = 3, CLIMATE_ACTION_HEATING = 3,
/// The climate device is idle (monitoring climate but no action needed) /// The climate device is idle (monitoring climate but no action needed)
CLIMATE_ACTION_IDLE = 4, CLIMATE_ACTION_IDLE = 4,
/// The climate device is drying (either mode DRY or AUTO) /// The climate device is drying
CLIMATE_ACTION_DRYING = 5, CLIMATE_ACTION_DRYING = 5,
/// The climate device is in fan only mode (either mode FAN_ONLY or AUTO) /// The climate device is in fan only mode
CLIMATE_ACTION_FAN = 6, CLIMATE_ACTION_FAN = 6,
}; };
/// Enum for all modes a climate fan can be in
enum ClimateFanMode : uint8_t { enum ClimateFanMode : uint8_t {
/// The fan mode is set to On /// The fan mode is set to On
CLIMATE_FAN_ON = 0, CLIMATE_FAN_ON = 0,
@@ -75,20 +77,22 @@ enum ClimateSwingMode : uint8_t {
/// Enum for all modes a climate swing can be in /// Enum for all modes a climate swing can be in
enum ClimatePreset : uint8_t { enum ClimatePreset : uint8_t {
/// Preset is set to ECO /// No preset is active
CLIMATE_PRESET_ECO = 0, CLIMATE_PRESET_NONE = 0,
/// Preset is set to AWAY /// Device is in home preset
CLIMATE_PRESET_AWAY = 1, CLIMATE_PRESET_HOME = 1,
/// Preset is set to BOOST /// Device is in away preset
CLIMATE_PRESET_BOOST = 2, CLIMATE_PRESET_AWAY = 2,
/// Preset is set to COMFORT /// Device is in boost preset
CLIMATE_PRESET_COMFORT = 3, CLIMATE_PRESET_BOOST = 3,
/// Preset is set to HOME /// Device is in comfort preset
CLIMATE_PRESET_HOME = 4, CLIMATE_PRESET_COMFORT = 4,
/// Preset is set to SLEEP /// Device is running an energy-saving preset
CLIMATE_PRESET_SLEEP = 5, CLIMATE_PRESET_ECO = 5,
/// Preset is set to ACTIVITY /// Device is prepared for sleep
CLIMATE_PRESET_ACTIVITY = 6, CLIMATE_PRESET_SLEEP = 6,
/// Device is reacting to activity (e.g., movement sensors)
CLIMATE_PRESET_ACTIVITY = 7,
}; };
/// Convert the given ClimateMode to a human-readable string. /// Convert the given ClimateMode to a human-readable string.

View File

@@ -4,50 +4,6 @@
namespace esphome { namespace esphome {
namespace climate { namespace climate {
bool ClimateTraits::supports_mode(ClimateMode mode) const {
switch (mode) {
case CLIMATE_MODE_OFF:
return true;
case CLIMATE_MODE_AUTO:
return this->supports_auto_mode_;
case CLIMATE_MODE_COOL:
return this->supports_cool_mode_;
case CLIMATE_MODE_HEAT:
return this->supports_heat_mode_;
case CLIMATE_MODE_FAN_ONLY:
return this->supports_fan_only_mode_;
case CLIMATE_MODE_DRY:
return this->supports_dry_mode_;
default:
return false;
}
}
bool ClimateTraits::get_supports_current_temperature() const { return supports_current_temperature_; }
void ClimateTraits::set_supports_current_temperature(bool supports_current_temperature) {
supports_current_temperature_ = supports_current_temperature;
}
bool ClimateTraits::get_supports_two_point_target_temperature() const { return supports_two_point_target_temperature_; }
void ClimateTraits::set_supports_two_point_target_temperature(bool supports_two_point_target_temperature) {
supports_two_point_target_temperature_ = supports_two_point_target_temperature;
}
void ClimateTraits::set_supports_auto_mode(bool supports_auto_mode) { supports_auto_mode_ = supports_auto_mode; }
void ClimateTraits::set_supports_cool_mode(bool supports_cool_mode) { supports_cool_mode_ = supports_cool_mode; }
void ClimateTraits::set_supports_heat_mode(bool supports_heat_mode) { supports_heat_mode_ = supports_heat_mode; }
void ClimateTraits::set_supports_fan_only_mode(bool supports_fan_only_mode) {
supports_fan_only_mode_ = supports_fan_only_mode;
}
void ClimateTraits::set_supports_dry_mode(bool supports_dry_mode) { supports_dry_mode_ = supports_dry_mode; }
void ClimateTraits::set_supports_away(bool supports_away) { supports_away_ = supports_away; }
void ClimateTraits::set_supports_action(bool supports_action) { supports_action_ = supports_action; }
float ClimateTraits::get_visual_min_temperature() const { return visual_min_temperature_; }
void ClimateTraits::set_visual_min_temperature(float visual_min_temperature) {
visual_min_temperature_ = visual_min_temperature;
}
float ClimateTraits::get_visual_max_temperature() const { return visual_max_temperature_; }
void ClimateTraits::set_visual_max_temperature(float visual_max_temperature) {
visual_max_temperature_ = visual_max_temperature;
}
float ClimateTraits::get_visual_temperature_step() const { return visual_temperature_step_; }
int8_t ClimateTraits::get_temperature_accuracy_decimals() const { int8_t ClimateTraits::get_temperature_accuracy_decimals() const {
// use printf %g to find number of digits based on temperature step // use printf %g to find number of digits based on temperature step
char buf[32]; char buf[32];
@@ -59,160 +15,6 @@ int8_t ClimateTraits::get_temperature_accuracy_decimals() const {
return str.length() - dot_pos - 1; return str.length() - dot_pos - 1;
} }
void ClimateTraits::set_visual_temperature_step(float temperature_step) { visual_temperature_step_ = temperature_step; }
bool ClimateTraits::get_supports_away() const { return supports_away_; }
bool ClimateTraits::get_supports_action() const { return supports_action_; }
void ClimateTraits::set_supports_fan_mode_on(bool supports_fan_mode_on) {
this->supports_fan_mode_on_ = supports_fan_mode_on;
}
void ClimateTraits::set_supports_fan_mode_off(bool supports_fan_mode_off) {
this->supports_fan_mode_off_ = supports_fan_mode_off;
}
void ClimateTraits::set_supports_fan_mode_auto(bool supports_fan_mode_auto) {
this->supports_fan_mode_auto_ = supports_fan_mode_auto;
}
void ClimateTraits::set_supports_fan_mode_low(bool supports_fan_mode_low) {
this->supports_fan_mode_low_ = supports_fan_mode_low;
}
void ClimateTraits::set_supports_fan_mode_medium(bool supports_fan_mode_medium) {
this->supports_fan_mode_medium_ = supports_fan_mode_medium;
}
void ClimateTraits::set_supports_fan_mode_high(bool supports_fan_mode_high) {
this->supports_fan_mode_high_ = supports_fan_mode_high;
}
void ClimateTraits::set_supports_fan_mode_middle(bool supports_fan_mode_middle) {
this->supports_fan_mode_middle_ = supports_fan_mode_middle;
}
void ClimateTraits::set_supports_fan_mode_focus(bool supports_fan_mode_focus) {
this->supports_fan_mode_focus_ = supports_fan_mode_focus;
}
void ClimateTraits::set_supports_fan_mode_diffuse(bool supports_fan_mode_diffuse) {
this->supports_fan_mode_diffuse_ = supports_fan_mode_diffuse;
}
bool ClimateTraits::supports_fan_mode(ClimateFanMode fan_mode) const {
switch (fan_mode) {
case climate::CLIMATE_FAN_ON:
return this->supports_fan_mode_on_;
case climate::CLIMATE_FAN_OFF:
return this->supports_fan_mode_off_;
case climate::CLIMATE_FAN_AUTO:
return this->supports_fan_mode_auto_;
case climate::CLIMATE_FAN_LOW:
return this->supports_fan_mode_low_;
case climate::CLIMATE_FAN_MEDIUM:
return this->supports_fan_mode_medium_;
case climate::CLIMATE_FAN_HIGH:
return this->supports_fan_mode_high_;
case climate::CLIMATE_FAN_MIDDLE:
return this->supports_fan_mode_middle_;
case climate::CLIMATE_FAN_FOCUS:
return this->supports_fan_mode_focus_;
case climate::CLIMATE_FAN_DIFFUSE:
return this->supports_fan_mode_diffuse_;
default:
return false;
}
}
bool ClimateTraits::get_supports_fan_modes() const {
return this->supports_fan_mode_on_ || this->supports_fan_mode_off_ || this->supports_fan_mode_auto_ ||
this->supports_fan_mode_low_ || this->supports_fan_mode_medium_ || this->supports_fan_mode_high_ ||
this->supports_fan_mode_middle_ || this->supports_fan_mode_focus_ || this->supports_fan_mode_diffuse_;
}
void ClimateTraits::set_supported_custom_fan_modes(std::vector<std::string> &supported_custom_fan_modes) {
this->supported_custom_fan_modes_ = supported_custom_fan_modes;
}
const std::vector<std::string> ClimateTraits::get_supported_custom_fan_modes() const {
return this->supported_custom_fan_modes_;
}
bool ClimateTraits::supports_custom_fan_mode(std::string &custom_fan_mode) const {
return std::count(this->supported_custom_fan_modes_.begin(), this->supported_custom_fan_modes_.end(),
custom_fan_mode);
}
bool ClimateTraits::supports_preset(ClimatePreset preset) const {
switch (preset) {
case climate::CLIMATE_PRESET_ECO:
return this->supports_preset_eco_;
case climate::CLIMATE_PRESET_AWAY:
return this->supports_preset_away_;
case climate::CLIMATE_PRESET_BOOST:
return this->supports_preset_boost_;
case climate::CLIMATE_PRESET_COMFORT:
return this->supports_preset_comfort_;
case climate::CLIMATE_PRESET_HOME:
return this->supports_preset_home_;
case climate::CLIMATE_PRESET_SLEEP:
return this->supports_preset_sleep_;
case climate::CLIMATE_PRESET_ACTIVITY:
return this->supports_preset_activity_;
default:
return false;
}
}
void ClimateTraits::set_supports_preset_eco(bool supports_preset_eco) {
this->supports_preset_eco_ = supports_preset_eco;
}
void ClimateTraits::set_supports_preset_away(bool supports_preset_away) {
this->supports_preset_away_ = supports_preset_away;
}
void ClimateTraits::set_supports_preset_boost(bool supports_preset_boost) {
this->supports_preset_boost_ = supports_preset_boost;
}
void ClimateTraits::set_supports_preset_comfort(bool supports_preset_comfort) {
this->supports_preset_comfort_ = supports_preset_comfort;
}
void ClimateTraits::set_supports_preset_home(bool supports_preset_home) {
this->supports_preset_home_ = supports_preset_home;
}
void ClimateTraits::set_supports_preset_sleep(bool supports_preset_sleep) {
this->supports_preset_sleep_ = supports_preset_sleep;
}
void ClimateTraits::set_supports_preset_activity(bool supports_preset_activity) {
this->supports_preset_activity_ = supports_preset_activity;
}
bool ClimateTraits::get_supports_presets() const {
return this->supports_preset_eco_ || this->supports_preset_away_ || this->supports_preset_boost_ ||
this->supports_preset_comfort_ || this->supports_preset_home_ || this->supports_preset_sleep_ ||
this->supports_preset_activity_;
}
void ClimateTraits::set_supported_custom_presets(std::vector<std::string> &supported_custom_presets) {
this->supported_custom_presets_ = supported_custom_presets;
}
const std::vector<std::string> ClimateTraits::get_supported_custom_presets() const {
return this->supported_custom_presets_;
}
bool ClimateTraits::supports_custom_preset(std::string &custom_preset) const {
return std::count(this->supported_custom_presets_.begin(), this->supported_custom_presets_.end(), custom_preset);
}
void ClimateTraits::set_supports_swing_mode_off(bool supports_swing_mode_off) {
this->supports_swing_mode_off_ = supports_swing_mode_off;
}
void ClimateTraits::set_supports_swing_mode_both(bool supports_swing_mode_both) {
this->supports_swing_mode_both_ = supports_swing_mode_both;
}
void ClimateTraits::set_supports_swing_mode_vertical(bool supports_swing_mode_vertical) {
this->supports_swing_mode_vertical_ = supports_swing_mode_vertical;
}
void ClimateTraits::set_supports_swing_mode_horizontal(bool supports_swing_mode_horizontal) {
this->supports_swing_mode_horizontal_ = supports_swing_mode_horizontal;
}
bool ClimateTraits::supports_swing_mode(ClimateSwingMode swing_mode) const {
switch (swing_mode) {
case climate::CLIMATE_SWING_OFF:
return this->supports_swing_mode_off_;
case climate::CLIMATE_SWING_BOTH:
return this->supports_swing_mode_both_;
case climate::CLIMATE_SWING_VERTICAL:
return this->supports_swing_mode_vertical_;
case climate::CLIMATE_SWING_HORIZONTAL:
return this->supports_swing_mode_horizontal_;
default:
return false;
}
}
bool ClimateTraits::get_supports_swing_modes() const {
return this->supports_swing_mode_off_ || this->supports_swing_mode_both_ || supports_swing_mode_vertical_ ||
supports_swing_mode_horizontal_;
}
} // namespace climate } // namespace climate
} // namespace esphome } // namespace esphome

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