Compare commits

...

506 Commits

Author SHA1 Message Date
Jesse Hills
8558d3bbb4 Add to tests 2021-12-20 08:18:14 +13:00
Jesse Hills
f135ff285a Add codeowner 2021-12-20 08:14:52 +13:00
Jesse Hills
0ab8f2be2e Create safe mode button platform 2021-12-20 08:04:40 +13:00
Martin
1fb0a7109d Modbus: use multiply for publishing number (#2916) 2021-12-15 22:38:23 +13:00
sveip
192eb49589 ESP32 CAM add Automatic Exposure Control option (#2892)
Co-authored-by: Peter <psv@tsat.net>
Co-authored-by: Carlos Garcia Saura <CarlosGS@users.noreply.github.com>
2021-12-15 07:46:43 +13:00
Petr Vraník
5d70ff702b quantile filter support (#2900)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
Co-authored-by: pvranik <petr.vranik@mgm-tp.com>
2021-12-15 07:43:42 +13:00
jddonovan
a7b05db2a1 Adding Pascal unit to constants (#2914) 2021-12-15 07:39:50 +13:00
wilberforce
45e346cf1b Allow button POST on press from web server (#2913) 2021-12-14 15:08:01 +13:00
dependabot[bot]
80e2bfada3 Bump black from 21.11b1 to 21.12b0 (#2879)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2021-12-13 19:15:49 +01:00
Martin
16e7bd0388 fix multi-line comment warning/error (#2891) 2021-12-13 19:15:22 +01:00
Oxan van Leeuwen
b3fb35783e Set text sensor state property to filter output (#2893) 2021-12-13 15:21:09 +13:00
myhomeiot
a79c6aa9e0 Added access to ble_scan_result_evt_param as get_scan_result (#2854) 2021-12-13 13:08:18 +13:00
Martin
4bb58b2de9 Add gpio 12 to strapping pin list (#2902) 2021-12-13 11:03:08 +13:00
Jesse Hills
4e10881331 Log the actual value in modbus number (#2901) 2021-12-13 10:28:19 +13:00
Ben Owen
cec4a81e14 Add reset_duration option for waveshare epaper HAT rev 2.1 (#1481)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-12-13 10:27:11 +13:00
Carlos Garcia Saura
da45923d05 Turn verbose a debug statement in bme280 (#2906) 2021-12-13 09:32:37 +13:00
Carlos Garcia Saura
31a61b598b Reduce timing noise in duty_cycle (#2881) 2021-12-13 09:30:47 +13:00
tony
9c0506592b Add light.on_state trigger (#2868) 2021-12-13 09:19:57 +13:00
Oxan van Leeuwen
beeb0c7c5a Introduce hex parsing & formatting helper functions (#2882) 2021-12-13 09:15:23 +13:00
Martin
b2f05faee0 Move i2c scan to setup (#2869)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2021-12-13 09:12:50 +13:00
Jesse Hills
8375e1d64d Bump esphome-dashboard to 20211211.0 (#2904) 2021-12-11 21:03:41 +13:00
Keith Burzinski
cf5193d3e5 Fix for two points setting when fan_only_cooling is disabled (#2903)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
Co-authored-by: Keith Burzinski <kburzinski@kbx-mbp2021.ad.kbx81.net>
2021-12-11 20:03:22 +13:00
Guillermo Ruffino
c490388e80 Modbus number/output use write single (#2896)
Co-authored-by: Martin <25747549+martgras@users.noreply.github.com>
2021-12-10 09:44:43 +13:00
Jesse Hills
24ec5a6e9d Fix published state for modbus number (#2894) 2021-12-10 09:32:34 +13:00
Oxan van Leeuwen
6df1d5222d Drop unused xSemaphoreWait define (#2888) 2021-12-08 12:46:36 +13:00
Jesse Hills
58fb7a02f6 Bump esphome-dashboard to 20211208.0 (#2887) 2021-12-08 12:42:50 +13:00
Jesse Hills
3d51ac8df0 Use new platform component config blocks for wizard (#2885) 2021-12-08 09:22:03 +13:00
Oxan van Leeuwen
6fe4ff7f85 Drop len parameter from parse_number() (#2883) 2021-12-08 08:46:25 +13:00
Yuval Brik
2253d4bc16 Support different run duration for non-timer wakeup (#2861) 2021-12-06 23:30:27 +01:00
Carlos Garcia Saura
e5cc19de43 Feed watchdog while setting up OTA (#2876) 2021-12-06 23:26:06 +01:00
Jesse Hills
5404617d43 Bump esphome-dashboard to 20211207.0 (#2877) 2021-12-07 07:41:40 +13:00
Oxan van Leeuwen
12467a18e6 Feed watchdog when no component loops (#2857) 2021-12-07 07:24:20 +13:00
Jesse Hills
1db7043a4d Allow wizard to specify secrets (#2875) 2021-12-06 20:58:51 +13:00
Jesse Hills
49932747b3 Adopt using wifi secrets that should exist at this point (#2874) 2021-12-06 20:57:56 +13:00
Jesse Hills
55db190875 Add endpoint to fetch secrets keys (#2873) 2021-12-06 20:15:34 +13:00
Massimiliano Ravelli
71fe2f7ed3 Ignore already stopped dhcp for ethernet (#2862)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-12-06 20:01:50 +13:00
Oxan van Leeuwen
ffc112c9d0 Don't disable idle task WDT when it's not enabled (#2856) 2021-12-06 20:01:14 +13:00
Oxan van Leeuwen
d3e48e296f Fix MCP23x17 not disabling pullup after config change (#2855) 2021-12-06 19:59:50 +13:00
Martin
14f6ae75ea SPS30 : fix i2c read size (#2866) 2021-12-06 19:58:26 +13:00
Carlos Garcia Saura
c84efe64d3 ADC: Turn verbose the debugging "got voltage" (#2863) 2021-12-06 19:56:53 +13:00
Martin
10e89a7dbb tlc59208f : fix compilation error (#2867) 2021-12-06 19:54:46 +13:00
Jesse Hills
ef44acbf10 Bump esphome-dashboard to 20211206.0 (#2870) 2021-12-06 19:42:49 +13:00
dependabot[bot]
06da540ab0 Bump pylint from 2.12.1 to 2.12.2 (#2858)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-05 13:29:08 +01:00
Oxan van Leeuwen
40c017fd54 Update ota_component.cpp (#2852) 2021-12-03 07:52:56 +13:00
Jesse Hills
f0bcf81a98 Add a simple helper to remap values (#2850) 2021-12-02 09:23:11 +01:00
Jesse Hills
6a0b343289 Bump version to 2022.1.0-dev 2021-12-02 19:38:49 +13:00
Martin
9ca4e8f32a modbus_controller: bugfix: enable overriding calculated register size (#2845)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-12-02 15:45:11 +13:00
Alexandre-Jacques St-Jacques
1b88b7a166 Fix wifi not working with manual_ip using esp-idf (#2849) 2021-12-02 15:33:48 +13:00
Paul Nicholls
caf352ff06 Tuya Cover improvements (#2637) 2021-12-02 15:26:56 +13:00
Oxan van Leeuwen
54106179a1 Set ESP32 watchdog to loop task (#2846) 2021-12-02 09:05:42 +13:00
Oxan van Leeuwen
607601b3a4 Enable a bunch of clang-tidy checks (#2149) 2021-12-02 09:03:51 +13:00
Oxan van Leeuwen
f58828cb82 Support setting manual_ip under networks option (#2839) 2021-12-02 08:55:27 +13:00
Leon Loopik
11330af05f Expand uart invert feature to ESP8266 (#1727) 2021-12-01 20:31:04 +01:00
Oxan van Leeuwen
fbe1bca1b9 Fix compilation using subprocesses (#2834) 2021-12-01 17:37:24 +01:00
Mark Dietzer
24a5325db3 Declare arch_get_cpu_cycle_count for esp8266 as IRAM (#2843) 2021-12-01 10:01:15 +01:00
Yuval Brik
1ec3140759 ESP32 Deep Sleep: correct level value (#2812)
Upon registering for ESP32 deep sleep, DeepSleepComponent::begin_sleep
calculates the level value to wake up on.
As part of PR #2303, the level was changed to be based on `inverted`
instead of `!inverted`:
Before:
1e8e471dec/esphome/components/deep_sleep/deep_sleep_component.cpp (L76)
After:
2b04152482/esphome/components/deep_sleep/deep_sleep_component.cpp (L80)

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

Also revert the changes of #2644.

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

* Move esp8266 flash define

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

* Introduce convert_big_endian() function

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

* convert tab to spaces

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

* Make inclusion of the SPI library unconditional

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

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

* Fix Python lint issue

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

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

* Adding timers to measure how long processing DSMR takes

* Handle chunked output from smart meter.

* Cleaning up and commenting the new chunk handling code

* Remove debug code.

* Fixing clang-tidy issues.

* Implementing chunked reading support for encrypted telegrams.

* Remove redundant extra delay for encrypted reader

* Beware not to flush crypted telegram headers

* Use insane data timeout for testing

* Improve logging

* Make clang-tidy happy

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

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

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

This flag applies to displays that use DisplayBuffer.

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

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

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

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

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

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

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

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

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

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

* CSE7761: Added test at test3.yaml

* CSE7761: changed string style

* CSE7761: fixed cpp lint

* CSE7761: Added codeowners

* Lots of code cleanup

* Revert incorrect setup_priority suggestion

* Added error log in read with retries.

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

* Improved log messages

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

* revert  override of default interval
2021-10-28 20:57:39 +02:00
Jesse Hills
73accf747f Allow cloning/fetching Github PR branches in external_components (#2639) 2021-10-29 07:12:05 +13:00
Alex Iribarren
0d3e6b2c4c Expose web_server port via the API (#2467) 2021-10-28 11:46:55 +13:00
dependabot[bot]
b3d7cc637b Bump aioesphomeapi from 10.0.3 to 10.1.0 (#2638)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-28 09:17:30 +13:00
Sean Brogan
2147bcbc29 Remove autoload of xiaomi_ble and ruuvi_ble (#2617) 2021-10-28 09:16:12 +13:00
niklasweber
980c2d4cae Add publish_initial_value option to rotary encoder (#2503) 2021-10-28 08:09:37 +13:00
dependabot[bot]
68316cbcf9 Bump esptool from 3.1 to 3.2 (#2632) 2021-10-26 22:14:44 +02:00
dependabot[bot]
2f4b9263c3 Bump tzlocal from 4.0.1 to 4.0.2 (#2631) 2021-10-26 22:12:37 +02:00
Jesse Hills
c2623a08e3 Fix select.set using lambda (#2633) 2021-10-27 08:27:51 +13:00
Jan Čermák
9f625ee7d1 Fix pin number validation for sn74hc595 (#2621) 2021-10-26 18:10:45 +02:00
Martin
2f85c27a05 fix modbus output (#2630) 2021-10-26 11:30:25 +02:00
Otto Winter
c612a3bf60 Constrain GH Actions workflows permissions (#2625) 2021-10-26 10:55:27 +02:00
Jesse Hills
a01f5f5cf1 Remove power and energy from sensors that are not true power (#2628) 2021-10-26 10:55:20 +02:00
Oxan van Leeuwen
87328686a0 Allow setting URL as platform_version (#2598) 2021-10-26 10:55:09 +02:00
Martin
81c11ba1f7 relax max entities checking (#2629) 2021-10-26 08:53:47 +13:00
Otto Winter
49b17c5a2d Switch issue-close-app to GH Actions and workflow cleanup (#2624) 2021-10-23 21:58:04 +02:00
Otto Winter
de06a781ff ESP8266 disable PIO LDF (#2608) 2021-10-23 19:44:55 +02:00
Otto Winter
8e77e3c685 Fix glue code missing micros() (#2623) 2021-10-23 19:25:53 +02:00
0hax
a687b083ae Teleinfo ptec (#2599)
* teleinfo: handle historical mode correctly.

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

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

This make our data parsing fails.

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

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

* teleinfo: fix compilation with loglevel set to debug.

Signed-off-by: 0hax <0hax@protonmail.com>
2021-10-23 19:01:23 +02:00
Stefan Agner
b9e5c7eb35 Autodetect flash size (#2615) 2021-10-23 13:25:46 +02:00
Otto Winter
1a6a063e04 Move default build path to .esphome directory (#2586) 2021-10-23 12:38:57 +02:00
Otto Winter
d85b7a6bd0 Bump platform-espressif8266 from 2.6.2 to 2.6.3 (#2620) 2021-10-23 12:37:50 +02:00
Andreas Hergert
1c4700f447 fixed dependency for pca9685 component (#2614)
Co-authored-by: Otto Winter <otto@otto-winter.com>
Co-authored-by: Andreas <andreas.hergert@otrs.com>
2021-10-22 18:52:47 +02:00
Andreas Hergert
83400d0417 Bugfix tca9548a and idf refactor anh (#2612)
Co-authored-by: Andreas Hergert <andreas.hergert@otrs.com>
2021-10-22 18:20:57 +02:00
Otto Winter
77a6461c9d Fix ESP8266 OTA compression only starting framework v2.7.0 (#2610) 2021-10-22 17:23:31 +02:00
Otto Winter
6db9d1122f Fix compiler warnings and update platformio line filter (#2607) 2021-10-22 16:52:43 +02:00
Otto Winter
83bef85415 Add owner to all libraries used (#2604) 2021-10-22 14:14:14 +02:00
Otto Winter
b5b3914bbf Re-raise keyboardinterrupt (#2603) 2021-10-22 14:14:07 +02:00
Otto Winter
0d90ef94ae Add OTA upload compression for ESP8266 (#2601) 2021-10-22 13:02:55 +02:00
Otto Winter
c08b21b7cd Bump noise-c from 0.1.3 to 0.1.4 (#2602) 2021-10-22 12:12:07 +02:00
Paul Monigatti
be3cb9ef00 Add EntityBase properties to ESP32 Camera (#2600) 2021-10-22 12:10:29 +02:00
Oxan van Leeuwen
f7b3f52731 Limit hostnames to 31 characters (#2531) 2021-10-22 12:09:47 +02:00
Otto Winter
9220d9fc52 Fix socket connection closed not detected (#2587) 2021-10-22 10:46:44 +02:00
Otto Winter
68c8547067 Add IDF support to dallas (#2578) 2021-10-21 22:48:28 +02:00
dependabot[bot]
1468acfced Bump tzlocal from 3.0 to 4.0.1 (#2553)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2021-10-21 22:46:05 +02:00
dependabot[bot]
b141aea4c0 Bump aioesphomeapi from 10.0.0 to 10.0.3 (#2595)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-21 20:54:12 +02:00
Jesse Hills
a88c022406 Logging a proper url allows terminals to make it clickable (#2554) 2021-10-21 20:53:06 +02:00
dependabot[bot]
5389382798 Bump paho-mqtt from 1.6.0 to 1.6.1 (#2596)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-21 20:09:29 +02:00
Otto Winter
4765173778 Update docker base images (#2583) 2021-10-21 20:07:44 +02:00
Oxan van Leeuwen
07a9cb910f Fix validation of addressable light IDs (#2588) 2021-10-21 20:07:37 +02:00
dependabot[bot]
f408f074c4 Bump platformio from 5.2.1 to 5.2.2 (#2569)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2021-10-21 20:01:27 +02:00
dependabot[bot]
f1f2640d0e Bump esphome-dashboard from 20211021.0 to 20211021.1 (#2594)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-21 20:01:03 +02:00
Oxan van Leeuwen
27d7d7ca69 Fix old-style arduino_version on ESP8266 and with magic values (#2591) 2021-10-21 19:56:47 +02:00
Oxan van Leeuwen
c0fc5b48ae Fix pin/component switchup in SX1509 pin configuration (#2593) 2021-10-21 19:55:19 +02:00
Oxan van Leeuwen
8735d3b83e Fix PlatformIO version for latest Arduino framework (#2590) 2021-10-21 18:59:49 +02:00
Otto Winter
ca59dd1302 Fix HeatpumpIR pin (#2585) 2021-10-21 18:57:03 +02:00
Otto Winter
eccdef8211 Fix mDNS ESP8266 log not included (#2589) 2021-10-21 18:53:08 +02:00
Maurice Makaay
f2ebfe7aef Add mDNS config dump (#2576) 2021-10-21 16:02:28 +02:00
Otto Winter
cac5b356db ESP32 ADC use factory calibration data (#2574) 2021-10-21 16:01:33 +02:00
Otto Winter
156104d5f5 Fix platformio_install_deps no longer installing all lib_deps (#2584) 2021-10-21 15:29:32 +02:00
Otto Winter
c248ba4043 Fix platformio version in Dockerfile doesn't match requirements (#2582) 2021-10-21 14:53:08 +02:00
Otto Winter
c615dc573a Fix ESP8266 dallas GPIO16 INPUT_PULLUP (#2581) 2021-10-21 14:39:36 +02:00
Otto Winter
1caabb6419 Fix ESP8266 OTA adds unnecessary Update library (#2579) 2021-10-21 14:20:57 +02:00
Otto Winter
f41f7994a3 Arduino global delay/millis/... symbols workaround (#2575) 2021-10-21 14:20:23 +02:00
Otto Winter
e39f314e7a Fix wifi ble coexistence check (#2573) 2021-10-21 12:24:01 +02:00
Otto Winter
7f34561e53 Fix ESP8266 GPIO0 Pullup Validation (#2572) 2021-10-21 12:23:37 +02:00
Maurice Makaay
34606b0f1f Fix MDNS for ESP8266 devices (#2571)
Co-authored-by: Maurice Makaay <account-github@makaay.nl>
Co-authored-by: Otto winter <otto@otto-winter.com>
Co-authored-by: Maurice Makaay <mmakaay1@xs4all.net>
2021-10-21 12:23:21 +02:00
dependabot[bot]
c51b509501 Bump paho-mqtt from 1.5.1 to 1.6.0 (#2568)
Bumps [paho-mqtt](https://github.com/eclipse/paho.mqtt.python) from 1.5.1 to 1.6.0.
- [Release notes](https://github.com/eclipse/paho.mqtt.python/releases)
- [Changelog](https://github.com/eclipse/paho.mqtt.python/blob/master/ChangeLog.txt)
- [Commits](https://github.com/eclipse/paho.mqtt.python/commits)

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

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

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

* Update wizard.py

* Link to relevant section of guide

* Formatting

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

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

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-15 19:38:09 +02:00
Dmitriy Lopatko
dc15d1c8ec use no hold master mode for si7021/htu21d (#2528) 2021-10-15 16:52:03 +02:00
Martin
6beb9e568a Fix bug in register name definition (#2526) 2021-10-15 20:27:56 +13:00
Keith Burzinski
7178f10bda Fix Nextion HTTPClient error for ESP32 (#2524) 2021-10-15 20:26:26 +13:00
Paul Monigatti
1308236429 Revert "Added test for bme680_bsec" (#2518)
This reverts commit 7f6a50d291b14935b17802b4dce52135fad1e49e due to BSEC library license restrictions.
2021-10-14 12:31:52 +02:00
Oxan van Leeuwen
63d6b610b8 Don't define UART_SELECTION_UART2 when UART2 is unavailable (#2512) 2021-10-14 11:25:10 +02:00
Martin
8823024509 Add pressure compensation during runtime (#2493)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2021-10-14 11:24:57 +02:00
Paul Monigatti
4896f870f0 Fix: Color modes not being correctly used in light partitions (#2513) 2021-10-14 21:04:50 +13:00
Dmitriy Lopatko
7e482901d9 add missing include in sgp30 (#2517) 2021-10-14 21:00:53 +13:00
Paul Monigatti
07b309e65d Fix BME680_BSEC compilation issue with ESP32 (#2516) 2021-10-14 20:58:35 +13:00
Oxan van Leeuwen
6bbb5e9b56 Disallow using UART2 for logger on ESP-32 variants that lack it (#2510) 2021-10-14 09:21:43 +13:00
Paul Monigatti
867fecd157 Fix: Light flash not restoring previous LightState (#2383)
* Update light state when transformer has finished

* Revert writing direct to output

* Correct handling of zero-length light transformers

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

* Removed log.h

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

* clang-format

* Step progress from 0.0f to 1.0f at t=start_time for zero-length transforms to avoid divide-by-zero
2021-10-13 21:59:52 +02:00
Oxan van Leeuwen
05388d2dfc Fix light state remaining on after turn off with transition (#2509) 2021-10-14 08:53:00 +13:00
Maurice Makaay
e06b6d7140 Add ESP32 IDF as a test env for PRs (#2494)
Co-authored-by: Maurice Makaay <account-github@makaay.nl>
2021-10-14 08:22:57 +13:00
Carlos Garcia Saura
859e508392 change millis() to micros() in feed_wdt for 3ms check (#2492) 2021-10-13 18:50:27 +02:00
razorback16
534ce11d54 TCS34725 BugFix and GA factor (#2445)
- Fixed endianness bug on tcs34725 data read
- Fixed lux adjustments based on gain, integration time and GA factor
- Added glass attenuation factor to allow using this sensor behind
  semi transparent glass

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

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

* Renamed NAMEABLE_SCHEMA to ENTITY_BASE_SCHEMA (Python)

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

* Remove redundant use of CONF_NAME from esp32_touch

* Remove redundant use of CONF_NAME from mcp3008

* Updated test

* Moved EntityBase from Component.h and Component.cpp

* Added icon property to EntityBase

* Added CONF_ICON to ENTITY_BASE_SCHEMA and added setup_entity function to cpp_helpers

* Added MQTT component getters for icon and disabled_by_default

* Lint

* Removed icon field from MQTT components

* Code generation now uses setup_entity to setENTITY_BASE_SCHEMA fields

* Removed unused import

* Added cstdint include

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

* Remove icon from NumberTraits and SelectTraits

* Removed unused import

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

* Minor comment correction

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

* Update esphome/components/tsl2591/tsl2591.h

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

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

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

* Consolidated EntityBase fields in MQTT components

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

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

* Add missing configuration values for SGP40

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

* Address review comments

* Format according to clang-tidy

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

* Split off the individual OTA backends.

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

* After successfull flashing, activate the new boot partition.

* Fix linting issues.

* Minor cleanup

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

* Added test and implemented optional correctly.

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

* Disable CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE instead of enable, dummy!

* Rename CONF_IGNORE_EFUSE_MAC_CRC to CONF_ESP32_IGNORE_EFUSE_MAC_CRC.

* Removed unused import.

* Fix ordering of constants.

* Moved all MAC address logic to core helpers.

* Use pretty MAC address for the log.

* Use standard MAC formatter function for debug component.

* Fix clang-formatting.

* Fix clang-formatting.

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

* Processed code review by @OttoWinter

* Add USE_ESP32_IGNORE_EFUSE_MAC_CRC to defines.h

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

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

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

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

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

* Update web_server.cpp

split line to meet clang format requirement.

* Update web_server.cpp

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

* fixed verbose logging

* add append, prepend, substitute filters

* add to lower, get to upper working without dummy

* clang lint

* more linting...

* std::move append and prepend filters

* fix verbose filter::input logging

* value.c_str() in input print

* lambda filter verbose log fix

* correct log tag, neaten to upper and to lower

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

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

* fix typo

* Support for HSV color in Tuya

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

* clang-format fixes

* Update CODEOWNERS

* clang-format fixes

* Fix merge error

* Fix missing return

Co-authored-by: Otto winter <otto@otto-winter.com>
2021-09-28 22:10:25 +02:00
Paul Monigatti
2b9054d3b2 Initialised ESPPreferenceObject::backend_ to nullptr (#2411) 2021-09-28 16:26:46 +02:00
Jesse Hills
2234f6aacf Fix lint issues in web_server_base (#2409) 2021-09-28 15:33:30 +13:00
Otto Winter
be965a60eb Merge pull request from GHSA-48mj-p7x2-5jfm
* Move web_server auth to web_server_base

* Fix

* Fix

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

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

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

* teleinfo: read extra timestamp field for some tags.

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

29339c14f9

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

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

* teleinfo: handle DATE tag correctly.

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

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

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

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

* Move the cover toggle logic to perform()

* Add clang-tidy CI suggestion

* Implement cover toggle action as cover trait

* Handle toggle correctly if cover fully closed on POR

* Fix CI finding

* Add deprecated warning

* Don't add already deprecated interface

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

* Don't add already deprecated interface

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

* Don't add already deprecated interface

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

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

* simplifying code

* Implementing malfunction protection

* Adding test

* Fixing too long lines

* Fixing test sensor names

* Adding missing id's in ade7953 tests

* Adding code owners as requested

* Fixing issue setting position when stop reached

* Fixing issue setting position when stop reached

* Black formatting

* Fixing format issues

* Fix for concurrent changes

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

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

* Allow private members

* Fix clang-tidy detections

* Run clang-format

* Fix remaining detections

* Fix graph

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

* Add SSD1306 brightness validation tip

* Add more description, limit offset range

* Changes according to linter

* Fix test

* Raise error instead of using warning

* Fix wrong logic

* Remove logger

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

* Remove logging import

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

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

* Code comment

* Adding scan results to test

* Removing redundant call

* linting

* Better method to update wifi info

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

* Getting loop back

At least for now.

* Trying out suggestion again

* Applying cr suggestions

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

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

* added new topics to paython code validation/generation

* reformatted with black

* formatting corrected

* use dump_config_ mechanism to skip internal components

* use dump_config_ mechanism to skip internal components

* style issues resolved

* do_dump_config removed

* formatting fixed

* formatting fixed

* Drop parent dump_config() calls

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

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

* Fix linting errors

* Fix more linting errors

* Linting fixes continued

* Linting forever

* Another one

* Fix regression and linting

* Fix narrowing conversion

* Add test and bugfix

* Add codeowners

* Update CODEOWNERS

* Update sensor defs

* Reformatted with black

* Fixed device class import

* Update CODEOWNERS

* Update CODEOWNERS

* Adding all config options

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

* Moving test to different config file

test1.yml runs out of memory

* Update according to comments

* Add safety clause to reading modes

* Fix clang-tidy complaint

* Revert change to i2c component

* Fix for changes in dev

* Revert "Revert change to i2c component"

This reverts commit 2810df59e9c05311df6d32149ed79a393676503b.

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

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

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

* Use drawing commands specific to bichromatic displays

* Fix inaccurate comment

* Fix merge error

* Formatting

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

* check if message contains impedance

* auto detect scale version

* remove xiaomi_miscale2

* fix lint errors

* Apply suggestions from code review

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

* Apply suggestions from code review on old code

* Fix clang-tidy warnings

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

* Fix clang-format and python lint

* Fix const declaration

* Add code owner

* Fix malloc with std::vector

* Fix with suggestions

* Revert "Fix with suggestions"

This reverts commit bc618f20cf83e3df903fdbbca8d2529d946264b0.

* Fix last commit

* Fix Python Lint

* Fix typo

* Use std::vector instead pointer and fix loop

* Fix typo

* Add test configuration to test3.yaml

* Fix test3.yaml

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

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

* Create default sdkconfig for static analysis

* Add more compiler flags to clang ignore list

* Clean-up platformio.ini

* Remove unnecessary blank line

* Fix accidentally dropped library

* Don't gitignore sdkconfig.defaults

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

* esp-idf temp

* Fixes

* Echo component and noise

* Add noise API transport support

* Updates

* ESP-IDF

* Complete

* Fixes

* Fixes

* Versions update

* New i2c APIs

* Complete i2c refactor

* SPI migration

* Revert ESP Preferences migration, too complex for now

* OTA support

* Remove echo again

* Remove ssl again

* GPIOFlags updates

* Rename esphal and ICACHE_RAM_ATTR

* Make ESP32 arduino compilable again

* Fix GPIO flags

* Complete pin registry refactor and fixes

* Fixes to make test1 compile

* Remove sdkconfig file

* Ignore sdkconfig file

* Fixes in reviewing

* Make test2 compile

* Make test4 compile

* Make test5 compile

* Run clang-format

* Fix lint errors

* Use esp-idf APIs instead of btStart

* Another round of fixes

* Start implementing ESP8266

* Make test3 compile

* Guard esp8266 code

* Lint

* Reformat

* Fixes

* Fixes v2

* more fixes

* ESP-IDF tidy target

* Convert ARDUINO_ARCH_ESPxx

* Update WiFiSignalSensor

* Update time ifdefs

* OTA needs millis from hal

* RestartSwitch needs delay from hal

* ESP-IDF Uart

* Fix OTA blank password

* Allow setting sdkconfig

* Fix idf partitions and allow setting sdkconfig from yaml

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

* Fix esp8266 store log strings in flash

* Fix ESP32 arduino preferences not initialized

* Update ifdefs

* Change how sdkconfig change is detected

* Add checks to ci-custom and fix them

* Run clang-format

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

* Fixes from clang-tidy idf round 2

* Fixes from compiling tests with esp-idf

* Run clang-format

* Switch test5.yaml to esp-idf

* Implement ESP8266 Preferences

* Lint

* Re-do PIO package version selection a bit

* Fix arduinoespressif32 package version

* Fix unit tests

* Lint

* Lint fixes

* Fix readv/writev not defined

* Fix graphing component

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

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

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

* Add namespace to all platformio library references

* Add cmake-build-* to gitignore

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

* add explanation on longer gate enable time
2021-09-15 08:40:52 -03:00
Stefan Rado
2db8c42e1d Support direct relay state feedback for tuya climate component (#1668)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-09-15 20:23:35 +12:00
Jesse Hills
0ea77de98c Start a wifi scan after saving station details (#2315) 2021-09-15 19:00:51 +12:00
Maurice Makaay
19014331d8 Fix aioesphomeapi API logger with explicit api.port in the YAML. (#2310) 2021-09-15 08:48:27 +02:00
Jesse Hills
b276ac0588 Simple time.sleep in place of threading wait due to upgraded zeroconf (#2307) 2021-09-15 16:39:13 +12:00
Guillermo Ruffino
de33cbd7e7 Dsmr updates (#2157)
* add option to use check_crc

* ignore newline before ( in parsing

* add gas delivered text for raw sensor

* fix compile issue when not listing any sensor

* make gas_mbus_id configurable

* update dsmr lib for clang
2021-09-14 22:14:49 -03:00
JonasEr
103ba4c696 Bug fix of NFC message & records becoming inaccessible in on_tag lambdas (#2309) 2021-09-15 10:06:43 +12:00
jsuanet
5a90b83f63 Fix unit of measurement fields for DSMR power consumed/delivered fields (#2304)
Co-authored-by: Jos Suanet <jos@suanet.net>
2021-09-15 09:22:45 +12:00
Oxan van Leeuwen
716039e452 Use standard version of make_unique when available (#2292) 2021-09-14 14:27:35 +02:00
Jesse Hills
4cc2817fcd Fix binary strobe (#2301) 2021-09-14 22:59:15 +12:00
Jesse Hills
d437cc915c Allow simple hostname for sntp servers (#2300) 2021-09-14 22:40:45 +12:00
Otto Winter
dd3f2f6c7e Fix api noise explicit reject (#2297) 2021-09-14 11:53:49 +02:00
Otto Winter
5f61897bec Add stale/lock bots (#2299) 2021-09-14 10:35:37 +02:00
dependabot[bot]
c5d26a5b4a Bump click from 7.1.2 to 8.0.1 (#1824)
Bumps [click](https://github.com/pallets/click) from 7.1.2 to 8.0.1.
- [Release notes](https://github.com/pallets/click/releases)
- [Changelog](https://github.com/pallets/click/blob/main/CHANGES.rst)
- [Commits](https://github.com/pallets/click/compare/7.1.2...8.0.1)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-09-14 10:26:49 +02:00
Otto Winter
855112dfc3 API Noise logging (#2298) 2021-09-14 09:53:37 +02:00
Otto Winter
b9767bdcbc Bump platformio to 5.2.0 (#2291) 2021-09-13 21:16:13 +02:00
Oxan van Leeuwen
e6b0a0ca2b Clean-up sensor integration (#2275) 2021-09-13 18:58:49 +02:00
Oxan van Leeuwen
924df1e7de Run clang-tidy against Arduino 3 (#2146)
* Add macros header with more usable Arduino version defines

* Change Arduino version checking to use our version defines

* Add missing ESP8266 check

* Rename Arduino version macro to ARDUINO_VERSION_CODE

* Upgrade clang-tidy to use Arduino 3

* Fix clang-tidy warnings

* Upgrade NeoPixelBus to upstream 2.6.7

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

* Remove now unnecessary CLANG_TIDY ifdefs

* Add preprocessor hackery to avoid including pgmspace.h

* Bump base image to 4.1.1 and update lint

* Fix nfctag

* Fix make_unique ambiguous

* Fix ignore name

* Fix ambiguous v2

* Remove unused begin

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

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

* Fix compile error against beta

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

* Lint

* Lint

* Fix issue with new NfcTag constructor

* Update pointers for number and select

* Add back the NOLINT to display buffer

* Fix merge

* DSMR fixes

* Nextion fixes

* Fix pipsolar

* Fix lwip socket

* Format

* Change socket fix

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

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

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

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

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

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

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

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

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

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

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

* Force type for std::min in pn532

* Fix variable size where size_t is different on exp32-c3
2021-09-10 12:10:28 +12:00
Jesse Hills
bad161a5c1 Bump version to 2021.10.0-dev 2021-09-09 10:28:34 +12:00
1013 changed files with 31503 additions and 10770 deletions

View File

@@ -2,12 +2,11 @@
Checks: >-
*,
-abseil-*,
-altera-*,
-android-*,
-boost-*,
-bugprone-branch-clone,
-bugprone-narrowing-conversions,
-bugprone-signed-char-misuse,
-bugprone-too-small-loop-variable,
-cert-dcl50-cpp,
-cert-err58-cpp,
-cert-oop57-cpp,
@@ -17,17 +16,15 @@ Checks: >-
-clang-diagnostic-delete-abstract-non-virtual-dtor,
-clang-diagnostic-delete-non-abstract-non-virtual-dtor,
-clang-diagnostic-shadow-field,
-clang-diagnostic-sign-compare,
-clang-diagnostic-unused-variable,
-clang-diagnostic-unused-const-variable,
-clang-diagnostic-unused-parameter,
-concurrency-*,
-cppcoreguidelines-avoid-c-arrays,
-cppcoreguidelines-avoid-goto,
-cppcoreguidelines-avoid-magic-numbers,
-cppcoreguidelines-init-variables,
-cppcoreguidelines-macro-usage,
-cppcoreguidelines-narrowing-conversions,
-cppcoreguidelines-non-private-member-variables-in-classes,
-cppcoreguidelines-owning-memory,
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
-cppcoreguidelines-pro-bounds-constant-array-index,
-cppcoreguidelines-pro-bounds-pointer-arithmetic,
@@ -39,7 +36,6 @@ Checks: >-
-cppcoreguidelines-pro-type-union-access,
-cppcoreguidelines-pro-type-vararg,
-cppcoreguidelines-special-member-functions,
-fuchsia-default-arguments,
-fuchsia-multiple-inheritance,
-fuchsia-overloaded-operator,
-fuchsia-statically-constructed-objects,
@@ -49,6 +45,7 @@ Checks: >-
-google-explicit-constructor,
-google-readability-braces-around-statements,
-google-readability-casting,
-google-readability-namespace-comments,
-google-readability-todo,
-google-runtime-references,
-hicpp-*,
@@ -61,17 +58,21 @@ Checks: >-
-misc-no-recursion,
-misc-unused-parameters,
-modernize-avoid-c-arrays,
-modernize-avoid-bind,
-modernize-concat-nested-namespaces,
-modernize-return-braced-init-list,
-modernize-use-auto,
-modernize-use-default-member-init,
-modernize-use-equals-default,
-modernize-use-trailing-return-type,
-modernize-use-nodiscard,
-mpi-*,
-objc-*,
-readability-braces-around-statements,
-readability-const-return-type,
-readability-convert-member-functions-to-static,
-readability-else-after-return,
-readability-function-cognitive-complexity,
-readability-implicit-bool-conversion,
-readability-isolate-declaration,
-readability-magic-numbers,
@@ -83,7 +84,6 @@ Checks: >-
-readability-redundant-string-init,
-readability-uppercase-literal-suffix,
-readability-use-anyofallof,
-warnings-as-errors
WarningsAsErrors: '*'
AnalyzeTemporaryDtors: false
FormatStyle: google
@@ -92,9 +92,11 @@ CheckOptions:
value: '1'
- key: google-readability-function-size.StatementThreshold
value: '800'
- key: google-readability-namespace-comments.ShortNamespaceLines
- key: google-runtime-int.TypeSuffix
value: '_t'
- key: llvm-namespace-comment.ShortNamespaceLines
value: '10'
- key: google-readability-namespace-comments.SpacesBeforeComments
- key: llvm-namespace-comment.SpacesBeforeComments
value: '2'
- key: modernize-loop-convert.MaxCopySize
value: '16'
@@ -108,6 +110,10 @@ CheckOptions:
value: llvm
- key: modernize-use-nullptr.NullMacros
value: 'NULL'
- key: modernize-make-unique.MakeSmartPtrFunction
value: 'make_unique'
- key: modernize-make-unique.MakeSmartPtrFunctionHeader
value: 'esphome/core/helpers.h'
- key: readability-identifier-naming.LocalVariableCase
value: 'lower_case'
- key: readability-identifier-naming.ClassCase
@@ -121,15 +127,19 @@ CheckOptions:
- key: readability-identifier-naming.StaticConstantCase
value: 'UPPER_CASE'
- key: readability-identifier-naming.StaticVariableCase
value: 'UPPER_CASE'
value: 'lower_case'
- key: readability-identifier-naming.GlobalConstantCase
value: 'UPPER_CASE'
- key: readability-identifier-naming.ParameterCase
value: 'lower_case'
- key: readability-identifier-naming.PrivateMemberPrefix
value: 'NO_PRIVATE_MEMBERS_ALWAYS_USE_PROTECTED'
- key: readability-identifier-naming.PrivateMethodPrefix
value: 'NO_PRIVATE_METHODS_ALWAYS_USE_PROTECTED'
- key: readability-identifier-naming.PrivateMemberCase
value: 'lower_case'
- key: readability-identifier-naming.PrivateMemberSuffix
value: '_'
- key: readability-identifier-naming.PrivateMethodCase
value: 'lower_case'
- key: readability-identifier-naming.PrivateMethodSuffix
value: '_'
- key: readability-identifier-naming.ClassMemberCase
value: 'lower_case'
- key: readability-identifier-naming.ClassMemberCase

View File

@@ -1,7 +1,6 @@
{
"name": "ESPHome Dev",
"context": "..",
"dockerFile": "../docker/Dockerfile.dev",
"image": "esphome/esphome-lint:dev",
"postCreateCommand": [
"script/devcontainer-post-create"
],

2
.gitattributes vendored Normal file
View File

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

View File

@@ -16,6 +16,7 @@ Quick description and explanation of changes
## Test Environment
- [ ] ESP32
- [ ] ESP32 IDF
- [ ] ESP8266
## Example entry for `config.yaml`:

View File

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

36
.github/lock.yml vendored
View File

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

59
.github/stale.yml vendored
View File

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

View File

@@ -7,11 +7,19 @@ on:
paths:
- 'docker/**'
- '.github/workflows/**'
- 'requirements*.txt'
- 'platformio.ini'
pull_request:
paths:
- 'docker/**'
- '.github/workflows/**'
- 'requirements*.txt'
- 'platformio.ini'
permissions:
contents: read
packages: read
jobs:
check-docker:
@@ -27,6 +35,11 @@ jobs:
uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set TAG
run: |
echo "TAG=check" >> $GITHUB_ENV

View File

@@ -1,5 +1,3 @@
# THESE JOBS ARE COPIED IN release.yml and release-dev.yml
# PLEASE ALSO UPDATE THOSE FILES WHEN CHANGING LINES HERE
name: CI
on:
@@ -8,59 +6,15 @@ on:
pull_request:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
ci-with-container:
name: ${{ matrix.name }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
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:
- uses: actions/checkout@v2
- name: Register problem matchers
run: |
echo "::add-matcher::.github/workflows/matchers/clang-tidy.json"
echo "::add-matcher::.github/workflows/matchers/gcc.json"
# Also run git-diff-index so that the step is marked as failed on formatting errors,
# since clang-format doesn't do anything but change files if -i is passed.
- name: Run clang-format
run: |
script/clang-format -i
git diff-index --quiet HEAD --
if: ${{ matrix.id == 'clang-format' }}
- name: Run clang-tidy
run: script/clang-tidy --all-headers --fix --split-num 4 --split-at ${{ matrix.split }}
if: ${{ matrix.id == 'clang-tidy' }}
- name: Suggested changes
run: script/ci-suggest-changes
if: always()
ci:
# Don't use the esphome-lint docker image because it may contain outdated requirements.
# This way, all dependencies are cached via the cache action.
name: ${{ matrix.name }}
runs-on: ubuntu-latest
strategy:
@@ -74,48 +28,92 @@ jobs:
- id: test
file: tests/test1.yaml
name: Test tests/test1.yaml
pio_cache_key: test1
- id: test
file: tests/test2.yaml
name: Test tests/test2.yaml
pio_cache_key: test2
- id: test
file: tests/test3.yaml
name: Test tests/test3.yaml
pio_cache_key: test3
- id: test
file: tests/test4.yaml
name: Test tests/test4.yaml
pio_cache_key: test4
- id: test
file: tests/test5.yaml
name: Test tests/test5.yaml
pio_cache_key: test5
- id: pytest
name: Run pytest
- id: clang-format
name: Run script/clang-format
- id: clang-tidy
name: Run script/clang-tidy for ESP8266
options: --environment esp8266-tidy --grep USE_ESP8266
pio_cache_key: tidyesp8266
- id: clang-tidy
name: Run script/clang-tidy for ESP32 1/4
options: --environment esp32-tidy --split-num 4 --split-at 1
pio_cache_key: tidyesp32
- id: clang-tidy
name: Run script/clang-tidy for ESP32 2/4
options: --environment esp32-tidy --split-num 4 --split-at 2
pio_cache_key: tidyesp32
- id: clang-tidy
name: Run script/clang-tidy for ESP32 3/4
options: --environment esp32-tidy --split-num 4 --split-at 3
pio_cache_key: tidyesp32
- id: clang-tidy
name: Run script/clang-tidy for ESP32 4/4
options: --environment esp32-tidy --split-num 4 --split-at 4
pio_cache_key: tidyesp32
- id: clang-tidy
name: Run script/clang-tidy for ESP32 esp-idf
options: --environment esp32-idf-tidy --grep USE_ESP_IDF
pio_cache_key: tidyesp32-idf
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
id: python
with:
python-version: '3.7'
- name: Cache pip modules
uses: actions/cache@v1
- name: Cache virtualenv
uses: actions/cache@v2
with:
path: ~/.cache/pip
key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
path: .venv
key: venv-${{ steps.python.outputs.python-version }}-${{ hashFiles('requirements*.txt') }}
restore-keys: |
esphome-pip-3.7-
venv-${{ steps.python.outputs.python-version }}-
# Use per test platformio cache because tests have different platform versions
- name: Cache ~/.platformio
uses: actions/cache@v1
- name: Set up virtualenv
run: |
python -m venv .venv
source .venv/bin/activate
pip install -U pip
pip install -r requirements.txt -r requirements_optional.txt -r requirements_test.txt
pip install -e .
echo "$GITHUB_WORKSPACE/.venv/bin" >> $GITHUB_PATH
echo "VIRTUAL_ENV=$GITHUB_WORKSPACE/.venv" >> $GITHUB_ENV
# Use per check platformio cache because checks use different parts
- name: Cache platformio
uses: actions/cache@v2
with:
path: ~/.platformio
key: test-home-platformio-${{ matrix.file }}-${{ hashFiles('esphome/core/config.py') }}
restore-keys: |
test-home-platformio-${{ matrix.file }}-
if: ${{ matrix.id == 'test' }}
key: platformio-${{ matrix.pio_cache_key }}-${{ hashFiles('platformio.ini') }}
if: matrix.id == 'test' || matrix.id == 'clang-tidy'
- name: Set up python environment
run: script/setup
- name: Install clang tools
run: |
sudo apt-get install \
clang-format-11 \
clang-tidy-11
if: matrix.id == 'clang-tidy' || matrix.id == 'clang-format'
- name: Register problem matchers
run: |
@@ -124,20 +122,45 @@ jobs:
echo "::add-matcher::.github/workflows/matchers/python.json"
echo "::add-matcher::.github/workflows/matchers/pytest.json"
echo "::add-matcher::.github/workflows/matchers/gcc.json"
echo "::add-matcher::.github/workflows/matchers/clang-tidy.json"
- name: Lint Custom
run: |
script/ci-custom.py
script/build_codeowners.py --check
if: ${{ matrix.id == 'ci-custom' }}
if: matrix.id == 'ci-custom'
- name: Lint Python
run: script/lint-python
if: ${{ matrix.id == 'lint-python' }}
if: matrix.id == 'lint-python'
- run: esphome compile ${{ matrix.file }}
if: ${{ matrix.id == 'test' }}
if: matrix.id == 'test'
env:
# Also cache libdeps, store them in a ~/.platformio subfolder
PLATFORMIO_LIBDEPS_DIR: ~/.platformio/libdeps
- name: Run pytest
run: |
pytest -vv --tb=native tests
if: ${{ matrix.id == 'pytest' }}
if: matrix.id == 'pytest'
# Also run git-diff-index so that the step is marked as failed on formatting errors,
# since clang-format doesn't do anything but change files if -i is passed.
- name: Run clang-format
run: |
script/clang-format -i
git diff-index --quiet HEAD --
if: matrix.id == 'clang-format'
- name: Run clang-tidy
run: |
script/clang-tidy --all-headers --fix ${{ matrix.options }}
if: matrix.id == 'clang-tidy'
env:
# Also cache libdeps, store them in a ~/.platformio subfolder
PLATFORMIO_LIBDEPS_DIR: ~/.platformio/libdeps
- name: Suggested changes
run: script/ci-suggest-changes
if: always() && (matrix.id == 'clang-tidy' || matrix.id == 'clang-format')

View File

@@ -1,100 +0,0 @@
name: Build and publish lint docker image
# Only run when docker paths change
on:
push:
branches: [dev]
paths:
- 'docker/Dockerfile.lint'
- 'requirements.txt'
- 'requirements_optional.txt'
- 'requirements_test.txt'
- 'platformio.ini'
- '.github/workflows/docker-lint-build.yml'
jobs:
deploy-docker:
name: Build and publish docker containers
if: github.repository == 'esphome/esphome'
runs-on: ubuntu-latest
strategy:
matrix:
arch: [amd64, armv7, aarch64]
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: Run build
run: |
docker/build.py \
--tag "${TAG}" \
--arch "${{ matrix.arch }}" \
--build-type "${{ matrix.build_type }}" \
build
- 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 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

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

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

View File

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

View File

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

View File

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

View File

@@ -7,6 +7,9 @@ on:
schedule:
- cron: "0 2 * * *"
permissions:
contents: read
jobs:
init:
name: Initialize build
@@ -52,12 +55,15 @@ jobs:
deploy-docker:
name: Build and publish docker containers
if: github.repository == 'esphome/esphome'
permissions:
contents: read
packages: write
runs-on: ubuntu-latest
needs: [init]
strategy:
matrix:
arch: [amd64, armv7, aarch64]
build_type: ["ha-addon", "docker"]
build_type: ["ha-addon", "docker", "lint"]
steps:
- uses: actions/checkout@v2
- name: Set up Python
@@ -65,13 +71,10 @@ jobs:
with:
python-version: '3.9'
- name: Run build
run: |
docker/build.py \
--tag "${{ needs.init.outputs.tag }}" \
--arch "${{ matrix.arch }}" \
--build-type "${{ matrix.build_type }}" \
build
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Log in to docker hub
uses: docker/login-action@v1
@@ -85,21 +88,25 @@ jobs:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Run push
- name: Build and push
run: |
docker/build.py \
--tag "${{ needs.init.outputs.tag }}" \
--arch "${{ matrix.arch }}" \
--build-type "${{ matrix.build_type }}" \
push
build \
--push
deploy-docker-manifest:
if: github.repository == 'esphome/esphome'
permissions:
contents: read
packages: write
runs-on: ubuntu-latest
needs: [init, deploy-docker]
strategy:
matrix:
build_type: ["ha-addon", "docker"]
build_type: ["ha-addon", "docker", "lint"]
steps:
- uses: actions/checkout@v2
- name: Set up Python

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

@@ -0,0 +1,48 @@
name: Stale
on:
schedule:
- cron: '30 0 * * *'
workflow_dispatch:
permissions:
issues: write
pull-requests: write
concurrency:
group: lock
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v4
with:
days-before-pr-stale: 90
days-before-pr-close: 7
days-before-issue-stale: -1
days-before-issue-close: -1
remove-stale-when-updated: true
stale-pr-label: "stale"
exempt-pr-labels: "no-stale"
stale-pr-message: >
There hasn't been any activity on this pull request recently. This
pull request has been automatically marked as stale because of that
and will be closed if no further activity occurs within 7 days.
Thank you for your contributions.
# Use stale to automatically close issues with a reference to the issue tracker
close-issues:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v4
with:
days-before-pr-stale: -1
days-before-pr-close: -1
days-before-issue-stale: 1
days-before-issue-close: 1
remove-stale-when-updated: true
stale-issue-label: "stale"
exempt-issue-labels: "not-stale"
stale-issue-message: >
https://github.com/esphome/esphome/issues/430

8
.gitignore vendored
View File

@@ -102,10 +102,7 @@ CMakeLists.txt
.idea/**/dynamic.xml
# CMake
cmake-build-debug/
cmake-build-livingroom8266/
cmake-build-livingroom32/
cmake-build-release/
cmake-build-*/
CMakeCache.txt
CMakeFiles
@@ -127,3 +124,6 @@ tests/.esphome/
/.temp-clang-tidy.cpp
/.temp/
.pio/
sdkconfig.*
!sdkconfig.defaults

View File

@@ -15,6 +15,7 @@ esphome/components/ac_dimmer/* @glmnet
esphome/components/adc/* @esphome/core
esphome/components/addressable_light/* @justfalter
esphome/components/airthings_ble/* @jeromelaban
esphome/components/airthings_wave_mini/* @ncareau
esphome/components/airthings_wave_plus/* @jeromelaban
esphome/components/am43/* @buxtronix
esphome/components/am43/cover/* @buxtronix
@@ -29,7 +30,9 @@ esphome/components/bang_bang/* @OttoWinter
esphome/components/binary_sensor/* @esphome/core
esphome/components/ble_client/* @buxtronix
esphome/components/bme680_bsec/* @trvrnrth
esphome/components/button/* @esphome/core
esphome/components/canbus/* @danielschramm @mvturnho
esphome/components/cap1188/* @MrEditor97
esphome/components/captive_portal/* @OttoWinter
esphome/components/ccs811/* @habbie
esphome/components/climate/* @esphome/core
@@ -38,15 +41,22 @@ esphome/components/color_temperature/* @jesserockz
esphome/components/coolix/* @glmnet
esphome/components/cover/* @esphome/core
esphome/components/cs5460a/* @balrog-kun
esphome/components/cse7761/* @berfenger
esphome/components/ct_clamp/* @jesserockz
esphome/components/current_based/* @djwmarcx
esphome/components/daly_bms/* @s1lvi0
esphome/components/dashboard_import/* @esphome/core
esphome/components/debug/* @OttoWinter
esphome/components/dfplayer/* @glmnet
esphome/components/dht/* @OttoWinter
esphome/components/ds1307/* @badbadc0ffee
esphome/components/dsmr/* @glmnet @zuidwijk
esphome/components/esp32/* @esphome/core
esphome/components/esp32_ble/* @jesserockz
esphome/components/esp32_ble_server/* @jesserockz
esphome/components/esp32_camera_web_server/* @ayufan
esphome/components/esp32_improv/* @jesserockz
esphome/components/esp8266/* @esphome/core
esphome/components/exposure_notifications/* @OttoWinter
esphome/components/ezo/* @ssieb
esphome/components/fastled_base/* @OttoWinter
@@ -54,14 +64,16 @@ esphome/components/fingerprint_grow/* @OnFreund @loongyh
esphome/components/globals/* @esphome/core
esphome/components/gpio/* @esphome/core
esphome/components/gps/* @coogle
esphome/components/graph/* @synco
esphome/components/havells_solar/* @sourabhjaiswal
esphome/components/hbridge/fan/* @WeekendWarrior
esphome/components/hbridge/light/* @DotNetDann
esphome/components/heatpumpir/* @rob-deutsch
esphome/components/hitachi_ac424/* @sourabhjaiswal
esphome/components/homeassistant/* @OttoWinter
esphome/components/hrxl_maxsonar_wr/* @netmikey
esphome/components/i2c/* @esphome/core
esphome/components/improv/* @jesserockz
esphome/components/improv_serial/* @esphome/core
esphome/components/inkbird_ibsth1_mini/* @fkirill
esphome/components/inkplate6/* @jesserockz
esphome/components/integration/* @OttoWinter
@@ -70,6 +82,7 @@ esphome/components/json/* @OttoWinter
esphome/components/ledc/* @OttoWinter
esphome/components/light/* @esphome/core
esphome/components/logger/* @esphome/core
esphome/components/ltr390/* @sjtrny
esphome/components/max7219digit/* @rspaargaren
esphome/components/mcp23008/* @jesserockz
esphome/components/mcp23017/* @jesserockz
@@ -80,8 +93,17 @@ esphome/components/mcp23x17_base/* @jesserockz
esphome/components/mcp23xxx_base/* @jesserockz
esphome/components/mcp2515/* @danielschramm @mvturnho
esphome/components/mcp9808/* @k7hpn
esphome/components/md5/* @esphome/core
esphome/components/mdns/* @esphome/core
esphome/components/midea/* @dudanov
esphome/components/mitsubishi/* @RubyBailey
esphome/components/modbus_controller/* @martgras
esphome/components/modbus_controller/binary_sensor/* @martgras
esphome/components/modbus_controller/number/* @martgras
esphome/components/modbus_controller/output/* @martgras
esphome/components/modbus_controller/sensor/* @martgras
esphome/components/modbus_controller/switch/* @martgras
esphome/components/modbus_controller/text_sensor/* @martgras
esphome/components/network/* @esphome/core
esphome/components/nextion/* @senexcrenshaw
esphome/components/nextion/binary_sensor/* @senexcrenshaw
@@ -100,6 +122,7 @@ esphome/components/pn532/* @OttoWinter @jesserockz
esphome/components/pn532_i2c/* @OttoWinter @jesserockz
esphome/components/pn532_spi/* @OttoWinter @jesserockz
esphome/components/power_supply/* @esphome/core
esphome/components/preferences/* @esphome/core
esphome/components/pulse_meter/* @stevebaxter
esphome/components/pvvx_mithermometer/* @pasiz
esphome/components/rc522/* @glmnet
@@ -109,6 +132,9 @@ esphome/components/restart/* @esphome/core
esphome/components/rf_bridge/* @jesserockz
esphome/components/rgbct/* @jesserockz
esphome/components/rtttl/* @glmnet
esphome/components/safe_mode/* @paulmonigatti
esphome/components/safe_mode/button/* @jesserockz
esphome/components/scd4x/* @sjtrny
esphome/components/script/* @esphome/core
esphome/components/sdm_meter/* @jesserockz @polyfaces
esphome/components/sdp3x/* @Azimath
@@ -156,6 +182,7 @@ esphome/components/tuya/binary_sensor/* @jesserockz
esphome/components/tuya/climate/* @jesserockz
esphome/components/tuya/sensor/* @jesserockz
esphome/components/tuya/switch/* @jesserockz
esphome/components/tuya/text_sensor/* @dentra
esphome/components/uart/* @esphome/core
esphome/components/ultrasonic/* @OttoWinter
esphome/components/version/* @esphome/core

View File

@@ -1,10 +1,6 @@
# Contributing to ESPHome
This python project is responsible for reading in YAML configuration files,
converting them to C++ code. This code is then converted to a platformio project and compiled
with [esphome-core](https://github.com/esphome/esphome-core), the C++ framework behind the project.
For a detailed guide, please see https://esphome.io/guides/contributing.html#contributing-to-esphomeyaml
For a detailed guide, please see https://esphome.io/guides/contributing.html#contributing-to-esphome
Things to note when contributing:

View File

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

View File

@@ -1 +0,0 @@
FROM esphome/esphome-lint:1.1

View File

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

View File

@@ -1,10 +0,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 /
RUN \
pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt -r /requirements_test.txt \
&& /platformio_install_deps.py /platformio.ini
VOLUME ["/esphome"]
WORKDIR /esphome

View File

@@ -2,7 +2,7 @@
from dataclasses import dataclass
import subprocess
import argparse
import platform
from platform import machine
import shlex
import re
import sys
@@ -24,9 +24,6 @@ TYPE_LINT = 'lint'
TYPES = [TYPE_DOCKER, TYPE_HA_ADDON, TYPE_LINT]
BASE_VERSION = "4.2.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")
@@ -34,27 +31,17 @@ parser.add_argument("--build-type", choices=TYPES, required=True, help="The type
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")
build_parser.add_argument("--push", help="Also push the images", action="store_true")
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
baseimgtype: str
platform: str
target: str
@classmethod
def for_type_arch(cls, build_type, arch):
@@ -63,18 +50,28 @@ class DockerParams:
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",
baseimgtype = {
TYPE_DOCKER: "docker",
TYPE_HA_ADDON: "hassio",
TYPE_LINT: "docker",
}[build_type]
platform = {
ARCH_AMD64: "linux/amd64",
ARCH_ARMV7: "linux/arm/v7",
ARCH_AARCH64: "linux/arm64",
}[arch]
target = {
TYPE_DOCKER: "docker",
TYPE_HA_ADDON: "hassio",
TYPE_LINT: "lint",
}[build_type]
return cls(
build_from=build_from,
build_to=build_to,
manifest_to=prefix,
dockerfile=dockerfile
baseimgtype=baseimgtype,
platform=platform,
target=target,
)
@@ -112,46 +109,31 @@ def main():
# 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_DEV: "cache-dev",
CHANNEL_BETA: "cache-beta",
CHANNEL_RELEASE: "cache-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
# 3. build
cmd = [
"docker", "buildx", "build",
"--build-arg", f"BASEIMGTYPE={params.baseimgtype}",
"--build-arg", f"BUILD_VERSION={args.tag}",
"--cache-from", f"type=registry,ref={cache_img}",
"--file", "docker/Dockerfile",
"--platform", params.platform,
"--target", params.target,
]
for img in imgs:
run_command(
"docker", "push", img
)
cmd += ["--tag", img]
if args.push:
cmd += ["--push", "--cache-to", f"type=registry,ref={cache_img},mode=max"]
run_command(*cmd, ".")
elif args.command == "manifest":
manifest = DockerParams.for_type_arch(args.build_type, ARCH_AMD64).manifest_to

24
docker/docker_entrypoint.sh Executable file
View File

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

View File

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

View File

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

View File

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

View File

@@ -18,6 +18,7 @@ from esphome.const import (
CONF_PORT,
CONF_ESPHOME,
CONF_PLATFORMIO_OPTIONS,
SECRETS_FILES,
)
from esphome.core import CORE, EsphomeError, coroutine
from esphome.helpers import indent
@@ -72,7 +73,7 @@ def choose_upload_log_host(default, check_default, show_ota, show_mqtt, show_api
if default == "OTA":
return CORE.address
if show_mqtt and "mqtt" in CORE.config:
options.append(("MQTT ({})".format(CORE.config["mqtt"][CONF_BROKER]), "MQTT"))
options.append((f"MQTT ({CORE.config['mqtt'][CONF_BROKER]})", "MQTT"))
if default == "OTA":
return "MQTT"
if default is not None:
@@ -144,6 +145,8 @@ def wrap_to_code(name, comp):
if comp.config_schema is not None:
conf_str = yaml_util.dump(conf)
conf_str = conf_str.replace("//", "")
# remove tailing \ to avoid multi-line comment warning
conf_str = conf_str.replace("\\\n", "\n")
cg.add(cg.LineComment(indent(conf_str)))
await coro(conf)
@@ -180,16 +183,37 @@ def compile_program(args, config):
from esphome import platformio_api
_LOGGER.info("Compiling app...")
return platformio_api.run_compile(config, CORE.verbose)
rc = platformio_api.run_compile(config, CORE.verbose)
if rc != 0:
return rc
idedata = platformio_api.get_idedata(config)
return 0 if idedata is not None else 1
def upload_using_esptool(config, port):
path = CORE.firmware_bin
from esphome import platformio_api
first_baudrate = config[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS].get(
"upload_speed", 460800
)
def run_esptool(baud_rate):
idedata = platformio_api.get_idedata(config)
firmware_offset = "0x10000" if CORE.is_esp32 else "0x0"
flash_images = [
platformio_api.FlashImage(
path=idedata.firmware_bin_path, offset=firmware_offset
),
*idedata.extra_flash_images,
]
mcu = "esp8266"
if CORE.is_esp32:
from esphome.components.esp32 import get_esp32_variant
mcu = get_esp32_variant().lower()
cmd = [
"esptool.py",
"--before",
@@ -198,14 +222,17 @@ def upload_using_esptool(config, port):
"hard_reset",
"--baud",
str(baud_rate),
"--chip",
"esp8266",
"--port",
port,
"--chip",
mcu,
"write_flash",
"0x0",
path,
"-z",
"--flash_size",
"detect",
]
for img in flash_images:
cmd += [img.offset, img.path]
if os.environ.get("ESPHOME_USE_SUBPROCESS") is None:
import esptool
@@ -229,11 +256,7 @@ def upload_using_esptool(config, port):
def upload_program(config, args, host):
# if upload is to a serial port use platformio, otherwise assume ota
if get_port_type(host) == "SERIAL":
from esphome import platformio_api
if CORE.is_esp8266:
return upload_using_esptool(config, host)
return platformio_api.run_upload(config, CORE.verbose, host)
return upload_using_esptool(config, host)
from esphome import espota2
@@ -245,7 +268,7 @@ def upload_program(config, args, host):
ota_conf = config[CONF_OTA]
remote_port = ota_conf[CONF_PORT]
password = ota_conf[CONF_PASSWORD]
password = ota_conf.get(CONF_PASSWORD, "")
return espota2.run_ota(host, remote_port, password, CORE.firmware_bin)
@@ -415,34 +438,49 @@ def command_update_all(args):
click.echo(f"{half_line}{middle_text}{half_line}")
for f in files:
print("Updating {}".format(color(Fore.CYAN, f)))
print(f"Updating {color(Fore.CYAN, f)}")
print("-" * twidth)
print()
rc = run_external_process(
"esphome", "--dashboard", "run", f, "--no-logs", "--device", "OTA"
)
if rc == 0:
print_bar("[{}] {}".format(color(Fore.BOLD_GREEN, "SUCCESS"), f))
print_bar(f"[{color(Fore.BOLD_GREEN, 'SUCCESS')}] {f}")
success[f] = True
else:
print_bar("[{}] {}".format(color(Fore.BOLD_RED, "ERROR"), f))
print_bar(f"[{color(Fore.BOLD_RED, 'ERROR')}] {f}")
success[f] = False
print()
print()
print()
print_bar("[{}]".format(color(Fore.BOLD_WHITE, "SUMMARY")))
print_bar(f"[{color(Fore.BOLD_WHITE, 'SUMMARY')}]")
failed = 0
for f in files:
if success[f]:
print(" - {}: {}".format(f, color(Fore.GREEN, "SUCCESS")))
print(f" - {f}: {color(Fore.GREEN, 'SUCCESS')}")
else:
print(" - {}: {}".format(f, color(Fore.BOLD_RED, "FAILED")))
print(f" - {f}: {color(Fore.BOLD_RED, 'FAILED')}")
failed += 1
return failed
def command_idedata(args, config):
from esphome import platformio_api
import json
logging.disable(logging.INFO)
logging.disable(logging.WARNING)
idedata = platformio_api.get_idedata(config)
if idedata is None:
return 1
print(json.dumps(idedata.raw, indent=2) + "\n")
return 0
PRE_CONFIG_ACTIONS = {
"wizard": command_wizard,
"version": command_version,
@@ -460,6 +498,7 @@ POST_CONFIG_ACTIONS = {
"clean-mqtt": command_clean_mqtt,
"mqtt-fingerprint": command_mqtt_fingerprint,
"clean": command_clean,
"idedata": command_idedata,
}
@@ -570,10 +609,7 @@ def parse_args(argv):
"wizard",
help="A helpful setup wizard that will guide you through setting up ESPHome.",
)
parser_wizard.add_argument(
"configuration",
help="Your YAML configuration file.",
)
parser_wizard.add_argument("configuration", help="Your YAML configuration file.")
parser_fingerprint = subparsers.add_parser(
"mqtt-fingerprint", help="Get the SSL fingerprint from a MQTT broker."
@@ -595,8 +631,7 @@ def parse_args(argv):
"dashboard", help="Create a simple web server for a dashboard."
)
parser_dashboard.add_argument(
"configuration",
help="Your YAML configuration file directory.",
"configuration", help="Your YAML configuration file directory."
)
parser_dashboard.add_argument(
"--port",
@@ -604,6 +639,12 @@ def parse_args(argv):
type=int,
default=6052,
)
parser_dashboard.add_argument(
"--address",
help="The address to bind to.",
type=str,
default="0.0.0.0",
)
parser_dashboard.add_argument(
"--username",
help="The optional username to require for authentication.",
@@ -635,6 +676,11 @@ def parse_args(argv):
"configuration", help="Your YAML configuration file directories.", nargs="+"
)
parser_idedata = subparsers.add_parser("idedata")
parser_idedata.add_argument(
"configuration", help="Your YAML configuration file(s).", nargs=1
)
# Keep backward compatibility with the old command line format of
# esphome <config> <command>.
#
@@ -718,7 +764,12 @@ def run_esphome(argv):
args = parse_args(argv)
CORE.dashboard = args.dashboard
setup_log(args.verbose, args.quiet)
setup_log(
args.verbose,
args.quiet,
# Show timestamp for dashboard access logs
args.command == "dashboard",
)
if args.deprecated_argv_suggestion is not None and args.command != "vscode":
_LOGGER.warning(
"Calling ESPHome with the configuration before the command is deprecated "
@@ -742,12 +793,16 @@ def run_esphome(argv):
return 1
for conf_path in args.configuration:
if any(os.path.basename(conf_path) == x for x in SECRETS_FILES):
_LOGGER.warning("Skipping secrets file %s", conf_path)
continue
CORE.config_path = conf_path
CORE.dashboard = args.dashboard
config = read_config(dict(args.substitution) if args.substitution else {})
if config is None:
return 1
return 2
CORE.config = config
if args.command not in POST_CONFIG_ACTIONS:

View File

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

View File

@@ -30,6 +30,7 @@ from esphome.cpp_generator import ( # noqa
add_library,
add_build_flag,
add_define,
add_platformio_option,
get_variable,
get_variable_with_full_id,
process_lambda,
@@ -66,7 +67,7 @@ from esphome.cpp_types import ( # noqa
NAN,
esphome_ns,
App,
Nameable,
EntityBase,
Component,
ComponentPtr,
PollingComponent,
@@ -78,4 +79,7 @@ from esphome.cpp_types import ( # noqa
JsonObjectConstRef,
Controller,
GPIOPin,
InternalGPIOPin,
gpio_Flags,
EntityCategory,
)

View File

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

View File

@@ -1,10 +1,16 @@
#ifdef USE_ARDUINO
#include "ac_dimmer.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include <cmath>
#ifdef ARDUINO_ARCH_ESP8266
#ifdef USE_ESP8266
#include <core_esp8266_waveform.h>
#endif
#ifdef USE_ESP32_FRAMEWORK_ARDUINO
#include <esp32-hal-timer.h>
#endif
namespace esphome {
namespace ac_dimmer {
@@ -17,12 +23,15 @@ static AcDimmerDataStore *all_dimmers[32]; // NOLINT(cppcoreguidelines-avoid-no
/// Time in microseconds the gate should be held high
/// 10µs should be long enough for most triacs
/// For reference: BT136 datasheet says 2µs nominal (page 7)
static const uint32_t GATE_ENABLE_TIME = 10;
/// However other factors like gate driver propagation time
/// are also considered and a really low value is not important
/// See also: https://github.com/esphome/issues/issues/1632
static const uint32_t GATE_ENABLE_TIME = 50;
/// Function called from timer interrupt
/// Input is current time in microseconds (micros())
/// Returns when next "event" is expected in µs, or 0 if no such event known.
uint32_t ICACHE_RAM_ATTR HOT AcDimmerDataStore::timer_intr(uint32_t now) {
uint32_t IRAM_ATTR HOT AcDimmerDataStore::timer_intr(uint32_t now) {
// If no ZC signal received yet.
if (this->crossed_zero_at == 0)
return 0;
@@ -34,13 +43,13 @@ uint32_t ICACHE_RAM_ATTR HOT AcDimmerDataStore::timer_intr(uint32_t now) {
if (this->enable_time_us != 0 && time_since_zc >= this->enable_time_us) {
this->enable_time_us = 0;
this->gate_pin->digital_write(true);
this->gate_pin.digital_write(true);
// Prevent too short pulses
this->disable_time_us = max(this->disable_time_us, time_since_zc + GATE_ENABLE_TIME);
this->disable_time_us = std::max(this->disable_time_us, time_since_zc + GATE_ENABLE_TIME);
}
if (this->disable_time_us != 0 && time_since_zc >= this->disable_time_us) {
this->disable_time_us = 0;
this->gate_pin->digital_write(false);
this->gate_pin.digital_write(false);
}
if (time_since_zc < this->enable_time_us)
@@ -60,7 +69,7 @@ uint32_t ICACHE_RAM_ATTR HOT AcDimmerDataStore::timer_intr(uint32_t now) {
}
/// Run timer interrupt code and return in how many µs the next event is expected
uint32_t ICACHE_RAM_ATTR HOT timer_interrupt() {
uint32_t IRAM_ATTR HOT timer_interrupt() {
// run at least with 1kHz
uint32_t min_dt_us = 1000;
uint32_t now = micros();
@@ -77,7 +86,7 @@ uint32_t ICACHE_RAM_ATTR HOT timer_interrupt() {
}
/// GPIO interrupt routine, called when ZC pin triggers
void ICACHE_RAM_ATTR HOT AcDimmerDataStore::gpio_intr() {
void IRAM_ATTR HOT AcDimmerDataStore::gpio_intr() {
uint32_t prev_crossed = this->crossed_zero_at;
// 50Hz mains frequency should give a half cycle of 10ms a 60Hz will give 8.33ms
@@ -94,7 +103,7 @@ void ICACHE_RAM_ATTR HOT AcDimmerDataStore::gpio_intr() {
if (this->value == 65535) {
// fully on, enable output immediately
this->gate_pin->digital_write(true);
this->gate_pin.digital_write(true);
} else if (this->init_cycle) {
// send a full cycle
this->init_cycle = false;
@@ -102,29 +111,29 @@ void ICACHE_RAM_ATTR HOT AcDimmerDataStore::gpio_intr() {
this->disable_time_us = cycle_time_us;
} else if (this->value == 0) {
// fully off, disable output immediately
this->gate_pin->digital_write(false);
this->gate_pin.digital_write(false);
} else {
if (this->method == DIM_METHOD_TRAILING) {
this->enable_time_us = 1; // cannot be 0
this->disable_time_us = max((uint32_t) 10, this->value * this->cycle_time_us / 65535);
this->disable_time_us = std::max((uint32_t) 10, this->value * this->cycle_time_us / 65535);
} else {
// calculate time until enable in µs: (1.0-value)*cycle_time, but with integer arithmetic
// also take into account min_power
auto min_us = this->cycle_time_us * this->min_power / 1000;
this->enable_time_us = max((uint32_t) 1, ((65535 - this->value) * (this->cycle_time_us - min_us)) / 65535);
this->enable_time_us = std::max((uint32_t) 1, ((65535 - this->value) * (this->cycle_time_us - min_us)) / 65535);
if (this->method == DIM_METHOD_LEADING_PULSE) {
// Minimum pulse time should be enough for the triac to trigger when it is close to the ZC zone
// this is for brightness near 99%
this->disable_time_us = max(this->enable_time_us + GATE_ENABLE_TIME, (uint32_t) cycle_time_us / 10);
this->disable_time_us = std::max(this->enable_time_us + GATE_ENABLE_TIME, (uint32_t) cycle_time_us / 10);
} else {
this->gate_pin->digital_write(false);
this->gate_pin.digital_write(false);
this->disable_time_us = this->cycle_time_us;
}
}
}
}
void ICACHE_RAM_ATTR HOT AcDimmerDataStore::s_gpio_intr(AcDimmerDataStore *store) {
void IRAM_ATTR HOT AcDimmerDataStore::s_gpio_intr(AcDimmerDataStore *store) {
// Attaching pin interrupts on the same pin will override the previous interrupt
// However, the user expects that multiple dimmers sharing the same ZC pin will work.
// We solve this in a bit of a hacky way: On each pin interrupt, we check all dimmers
@@ -138,11 +147,11 @@ void ICACHE_RAM_ATTR HOT AcDimmerDataStore::s_gpio_intr(AcDimmerDataStore *store
}
}
#ifdef ARDUINO_ARCH_ESP32
#ifdef USE_ESP32
// ESP32 implementation, uses basically the same code but needs to wrap
// timer_interrupt() function to auto-reschedule
static hw_timer_t *dimmer_timer = nullptr;
void ICACHE_RAM_ATTR HOT AcDimmerDataStore::s_timer_intr() { timer_interrupt(); }
static hw_timer_t *dimmer_timer = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
void IRAM_ATTR HOT AcDimmerDataStore::s_timer_intr() { timer_interrupt(); }
#endif
void AcDimmer::setup() {
@@ -171,15 +180,16 @@ void AcDimmer::setup() {
if (setup_zero_cross_pin) {
this->zero_cross_pin_->setup();
this->store_.zero_cross_pin = this->zero_cross_pin_->to_isr();
this->zero_cross_pin_->attach_interrupt(&AcDimmerDataStore::s_gpio_intr, &this->store_, FALLING);
this->zero_cross_pin_->attach_interrupt(&AcDimmerDataStore::s_gpio_intr, &this->store_,
gpio::INTERRUPT_FALLING_EDGE);
}
#ifdef ARDUINO_ARCH_ESP8266
#ifdef USE_ESP8266
// Uses ESP8266 waveform (soft PWM) class
// PWM and AcDimmer can even run at the same time this way
setTimer1Callback(&timer_interrupt);
#endif
#ifdef ARDUINO_ARCH_ESP32
#ifdef USE_ESP32
// 80 Divider -> 1 count=1µs
dimmer_timer = timerBegin(0, 80, true);
timerAttachInterrupt(dimmer_timer, &AcDimmerDataStore::s_timer_intr, true);
@@ -215,3 +225,5 @@ void AcDimmer::dump_config() {
} // namespace ac_dimmer
} // namespace esphome
#endif // USE_ARDUINO

View File

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

View File

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

View File

@@ -25,7 +25,7 @@ void AdalightLightEffect::stop() {
AddressableLightEffect::stop();
}
int AdalightLightEffect::get_frame_size_(int led_count) const {
unsigned int AdalightLightEffect::get_frame_size_(int led_count) const {
// 3 bytes: Ada
// 2 bytes: LED count
// 1 byte: checksum

View File

@@ -25,7 +25,7 @@ class AdalightLightEffect : public light::AddressableLightEffect, public uart::U
CONSUMED,
};
int get_frame_size_(int led_count) const;
unsigned int get_frame_size_(int led_count) const;
void reset_frame_(light::AddressableLight &it);
void blank_all_leds_(light::AddressableLight &it);
Frame parse_frame_(light::AddressableLight &it);

View File

@@ -1,8 +1,14 @@
#include "adc_sensor.h"
#include "esphome/core/log.h"
#include "esphome/core/helpers.h"
#ifdef USE_ESP8266
#ifdef USE_ADC_SENSOR_VCC
#include <Esp.h>
ADC_MODE(ADC_VCC)
#else
#include <Arduino.h>
#endif
#endif
namespace esphome {
@@ -10,151 +16,153 @@ namespace adc {
static const char *const TAG = "adc";
#ifdef ARDUINO_ARCH_ESP32
void ADCSensor::set_attenuation(adc_atten_t attenuation) { this->attenuation_ = attenuation; }
inline adc1_channel_t gpio_to_adc1(uint8_t pin) {
#if CONFIG_IDF_TARGET_ESP32
switch (pin) {
case 36:
return ADC1_CHANNEL_0;
case 37:
return ADC1_CHANNEL_1;
case 38:
return ADC1_CHANNEL_2;
case 39:
return ADC1_CHANNEL_3;
case 32:
return ADC1_CHANNEL_4;
case 33:
return ADC1_CHANNEL_5;
case 34:
return ADC1_CHANNEL_6;
case 35:
return ADC1_CHANNEL_7;
default:
return ADC1_CHANNEL_MAX;
}
#elif CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32H2
switch (pin) {
case 0:
return ADC1_CHANNEL_0;
case 1:
return ADC1_CHANNEL_1;
case 2:
return ADC1_CHANNEL_2;
case 3:
return ADC1_CHANNEL_3;
case 4:
return ADC1_CHANNEL_4;
default:
return ADC1_CHANNEL_MAX;
}
#endif
}
#endif
void ADCSensor::setup() {
ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
#ifndef USE_ADC_SENSOR_VCC
GPIOPin(this->pin_, INPUT).setup();
pin_->setup();
#endif
#ifdef ARDUINO_ARCH_ESP32
adc1_config_channel_atten(gpio_to_adc1(pin_), attenuation_);
#ifdef USE_ESP32
adc1_config_width(ADC_WIDTH_BIT_12);
#if !CONFIG_IDF_TARGET_ESP32C3 && !CONFIG_IDF_TARGET_ESP32H2
adc_gpio_init(ADC_UNIT_1, (adc_channel_t) gpio_to_adc1(pin_));
#endif
if (!autorange_) {
adc1_config_channel_atten(channel_, attenuation_);
}
// load characteristics for each attenuation
for (int i = 0; i < (int) ADC_ATTEN_MAX; i++) {
auto cal_value = esp_adc_cal_characterize(ADC_UNIT_1, (adc_atten_t) i, ADC_WIDTH_BIT_12,
1100, // default vref
&cal_characteristics_[i]);
switch (cal_value) {
case ESP_ADC_CAL_VAL_EFUSE_VREF:
ESP_LOGV(TAG, "Using eFuse Vref for calibration");
break;
case ESP_ADC_CAL_VAL_EFUSE_TP:
ESP_LOGV(TAG, "Using two-point eFuse Vref for calibration");
break;
case ESP_ADC_CAL_VAL_DEFAULT_VREF:
default:
break;
}
}
// adc_gpio_init doesn't exist on ESP32-C3 or ESP32-H2
#if !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32H2)
adc_gpio_init(ADC_UNIT_1, (adc_channel_t) channel_);
#endif
#endif // USE_ESP32
}
void ADCSensor::dump_config() {
LOG_SENSOR("", "ADC Sensor", this);
#ifdef ARDUINO_ARCH_ESP8266
#ifdef USE_ESP8266
#ifdef USE_ADC_SENSOR_VCC
ESP_LOGCONFIG(TAG, " Pin: VCC");
#else
ESP_LOGCONFIG(TAG, " Pin: %u", this->pin_);
#endif
#endif
#ifdef ARDUINO_ARCH_ESP32
ESP_LOGCONFIG(TAG, " Pin: %u", this->pin_);
switch (this->attenuation_) {
case ADC_ATTEN_DB_0:
ESP_LOGCONFIG(TAG, " Attenuation: 0db (max 1.1V)");
break;
case ADC_ATTEN_DB_2_5:
ESP_LOGCONFIG(TAG, " Attenuation: 2.5db (max 1.5V)");
break;
case ADC_ATTEN_DB_6:
ESP_LOGCONFIG(TAG, " Attenuation: 6db (max 2.2V)");
break;
case ADC_ATTEN_DB_11:
ESP_LOGCONFIG(TAG, " Attenuation: 11db (max 3.9V)");
break;
default: // This is to satisfy the unused ADC_ATTEN_MAX
break;
}
LOG_PIN(" Pin: ", pin_);
#endif
#endif // USE_ESP8266
#ifdef USE_ESP32
LOG_PIN(" Pin: ", pin_);
if (autorange_)
ESP_LOGCONFIG(TAG, " Attenuation: auto");
else
switch (this->attenuation_) {
case ADC_ATTEN_DB_0:
ESP_LOGCONFIG(TAG, " Attenuation: 0db (max 1.1V)");
break;
case ADC_ATTEN_DB_2_5:
ESP_LOGCONFIG(TAG, " Attenuation: 2.5db (max 1.5V)");
break;
case ADC_ATTEN_DB_6:
ESP_LOGCONFIG(TAG, " Attenuation: 6db (max 2.2V)");
break;
case ADC_ATTEN_DB_11:
ESP_LOGCONFIG(TAG, " Attenuation: 11db (max 3.9V)");
break;
default: // This is to satisfy the unused ADC_ATTEN_MAX
break;
}
#endif // USE_ESP32
LOG_UPDATE_INTERVAL(this);
}
float ADCSensor::get_setup_priority() const { return setup_priority::DATA; }
void ADCSensor::update() {
float value_v = this->sample();
ESP_LOGD(TAG, "'%s': Got voltage=%.2fV", this->get_name().c_str(), value_v);
ESP_LOGV(TAG, "'%s': Got voltage=%.4fV", this->get_name().c_str(), value_v);
this->publish_state(value_v);
}
#ifdef USE_ESP8266
float ADCSensor::sample() {
#ifdef ARDUINO_ARCH_ESP32
int raw = adc1_get_raw(gpio_to_adc1(pin_));
float value_v = raw / 4095.0f;
#if CONFIG_IDF_TARGET_ESP32
switch (this->attenuation_) {
case ADC_ATTEN_DB_0:
value_v *= 1.1;
break;
case ADC_ATTEN_DB_2_5:
value_v *= 1.5;
break;
case ADC_ATTEN_DB_6:
value_v *= 2.2;
break;
case ADC_ATTEN_DB_11:
value_v *= 3.9;
break;
default: // This is to satisfy the unused ADC_ATTEN_MAX
break;
}
#elif CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32H2
switch (this->attenuation_) {
case ADC_ATTEN_DB_0:
value_v *= 0.84;
break;
case ADC_ATTEN_DB_2_5:
value_v *= 1.13;
break;
case ADC_ATTEN_DB_6:
value_v *= 1.56;
break;
case ADC_ATTEN_DB_11:
value_v *= 3.0;
break;
default: // This is to satisfy the unused ADC_ATTEN_MAX
break;
}
#ifdef USE_ADC_SENSOR_VCC
int raw = ESP.getVcc(); // NOLINT(readability-static-accessed-through-instance)
#else
int raw = analogRead(this->pin_->get_pin()); // NOLINT
#endif
return value_v;
if (output_raw_) {
return raw;
}
return raw / 1024.0f;
}
#endif
#ifdef ARDUINO_ARCH_ESP8266
#ifdef USE_ADC_SENSOR_VCC
return ESP.getVcc() / 1024.0f;
#else
return analogRead(this->pin_) / 1024.0f; // NOLINT
#endif
#endif
#ifdef USE_ESP32
float ADCSensor::sample() {
if (!autorange_) {
int raw = adc1_get_raw(channel_);
if (raw == -1) {
return NAN;
}
if (output_raw_) {
return raw;
}
uint32_t mv = esp_adc_cal_raw_to_voltage(raw, &cal_characteristics_[(int) attenuation_]);
return mv / 1000.0f;
}
int raw11, raw6 = 4095, raw2 = 4095, raw0 = 4095;
adc1_config_channel_atten(channel_, ADC_ATTEN_DB_11);
raw11 = adc1_get_raw(channel_);
if (raw11 < 4095) {
adc1_config_channel_atten(channel_, ADC_ATTEN_DB_6);
raw6 = adc1_get_raw(channel_);
if (raw6 < 4095) {
adc1_config_channel_atten(channel_, ADC_ATTEN_DB_2_5);
raw2 = adc1_get_raw(channel_);
if (raw2 < 4095) {
adc1_config_channel_atten(channel_, ADC_ATTEN_DB_0);
raw0 = adc1_get_raw(channel_);
}
}
}
if (raw0 == -1 || raw2 == -1 || raw6 == -1 || raw11 == -1) {
return NAN;
}
uint32_t mv11 = esp_adc_cal_raw_to_voltage(raw11, &cal_characteristics_[(int) ADC_ATTEN_DB_11]);
uint32_t mv6 = esp_adc_cal_raw_to_voltage(raw6, &cal_characteristics_[(int) ADC_ATTEN_DB_6]);
uint32_t mv2 = esp_adc_cal_raw_to_voltage(raw2, &cal_characteristics_[(int) ADC_ATTEN_DB_2_5]);
uint32_t mv0 = esp_adc_cal_raw_to_voltage(raw0, &cal_characteristics_[(int) ADC_ATTEN_DB_0]);
// Contribution of each value, in range 0-2048
uint32_t c11 = std::min(raw11, 2048);
uint32_t c6 = 2048 - std::abs(raw6 - 2048);
uint32_t c2 = 2048 - std::abs(raw2 - 2048);
uint32_t c0 = std::min(4095 - raw0, 2048);
// max theoretical csum value is 2048*4 = 8192
uint32_t csum = c11 + c6 + c2 + c0;
// each mv is max 3900; so max value is 3900*2048*4, fits in unsigned
uint32_t mv_scaled = (mv11 * c11) + (mv6 * c6) + (mv2 * c2) + (mv0 * c0);
return mv_scaled / (float) (csum * 1000U);
}
#ifdef ARDUINO_ARCH_ESP8266
#endif // USE_ESP32
#ifdef USE_ESP8266
std::string ADCSensor::unique_id() { return get_mac_address() + "-adc"; }
#endif

View File

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

View File

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

View File

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

View File

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

View File

@@ -29,7 +29,7 @@ CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(ADE7953),
cv.Optional(CONF_IRQ_PIN): pins.input_pin,
cv.Optional(CONF_IRQ_PIN): pins.internal_gpio_input_pin_schema,
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
unit_of_measurement=UNIT_VOLT,
accuracy_decimals=1,
@@ -73,7 +73,8 @@ async def to_code(config):
await i2c.register_i2c_device(var, config)
if CONF_IRQ_PIN in config:
cg.add(var.set_irq_pin(config[CONF_IRQ_PIN]))
irq_pin = await cg.gpio_pin_expression(config[CONF_IRQ_PIN])
cg.add(var.set_irq_pin(irq_pin))
for key in [
CONF_VOLTAGE,

View File

@@ -1,5 +1,6 @@
#include "ads1115.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
namespace esphome {
namespace ads1115 {
@@ -159,7 +160,7 @@ float ADS1115Component::request_measurement(ADS1115Sensor *sensor) {
float ADS1115Sensor::sample() { return this->parent_->request_measurement(this); }
void ADS1115Sensor::update() {
float v = this->parent_->request_measurement(this);
if (!isnan(v)) {
if (!std::isnan(v)) {
ESP_LOGD(TAG, "'%s': Got Voltage=%fV", this->get_name().c_str(), v);
this->publish_state(v);
}

View File

@@ -14,6 +14,7 @@
#include "aht10.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
namespace esphome {
namespace aht10 {
@@ -33,8 +34,19 @@ void AHT10Component::setup() {
this->mark_failed();
return;
}
uint8_t data;
if (!this->read_byte(0, &data, AHT10_DEFAULT_DELAY)) {
uint8_t data = 0;
if (this->write(&data, 1) != i2c::ERROR_OK) {
ESP_LOGD(TAG, "Communication with AHT10 failed!");
this->mark_failed();
return;
}
delay(AHT10_DEFAULT_DELAY);
if (this->read(&data, 1) != i2c::ERROR_OK) {
ESP_LOGD(TAG, "Communication with AHT10 failed!");
this->mark_failed();
return;
}
if (this->read(&data, 1) != i2c::ERROR_OK) {
ESP_LOGD(TAG, "Communication with AHT10 failed!");
this->mark_failed();
return;
@@ -55,15 +67,19 @@ void AHT10Component::update() {
return;
}
uint8_t data[6];
uint8_t delay = AHT10_DEFAULT_DELAY;
uint8_t delay_ms = AHT10_DEFAULT_DELAY;
if (this->humidity_sensor_ != nullptr)
delay = AHT10_HUMIDITY_DELAY;
delay_ms = AHT10_HUMIDITY_DELAY;
bool success = false;
for (int i = 0; i < AHT10_ATTEMPTS; ++i) {
ESP_LOGVV(TAG, "Attempt %u at %6ld", i, millis());
delay_microseconds_accurate(4);
if (!this->read_bytes(0, data, 6, delay)) {
ESP_LOGVV(TAG, "Attempt %d at %6u", i, millis());
delay(delay_ms);
if (this->read(data, 6) != i2c::ERROR_OK) {
ESP_LOGD(TAG, "Communication with AHT10 failed, waiting...");
} else if ((data[0] & 0x80) == 0x80) { // Bit[7] = 0b1, device is busy
continue;
}
if ((data[0] & 0x80) == 0x80) { // Bit[7] = 0b1, device is busy
ESP_LOGD(TAG, "AHT10 is busy, waiting...");
} else if (data[1] == 0x0 && data[2] == 0x0 && (data[3] >> 4) == 0x0) {
// Unrealistic humidity (0x0)
@@ -80,11 +96,12 @@ void AHT10Component::update() {
}
} else {
// data is valid, we can break the loop
ESP_LOGVV(TAG, "Answer at %6ld", millis());
ESP_LOGVV(TAG, "Answer at %6u", millis());
success = true;
break;
}
}
if ((data[0] & 0x80) == 0x80) {
if (!success || (data[0] & 0x80) == 0x80) {
ESP_LOGE(TAG, "Measurements reading timed-out!");
this->status_set_warning();
return;
@@ -93,19 +110,19 @@ void AHT10Component::update() {
uint32_t raw_temperature = ((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5];
uint32_t raw_humidity = ((data[1] << 16) | (data[2] << 8) | data[3]) >> 4;
float temperature = ((200.0 * (float) raw_temperature) / 1048576.0) - 50.0;
float temperature = ((200.0f * (float) raw_temperature) / 1048576.0f) - 50.0f;
float humidity;
if (raw_humidity == 0) { // unrealistic value
humidity = NAN;
} else {
humidity = (float) raw_humidity * 100.0 / 1048576.0;
humidity = (float) raw_humidity * 100.0f / 1048576.0f;
}
if (this->temperature_sensor_ != nullptr) {
this->temperature_sensor_->publish_state(temperature);
}
if (this->humidity_sensor_ != nullptr) {
if (isnan(humidity))
if (std::isnan(humidity))
ESP_LOGW(TAG, "Invalid humidity! Sensor reported 0%% Hum");
this->humidity_sensor_->publish_state(humidity);
}

View File

@@ -1,12 +1,12 @@
#include "airthings_listener.h"
#include "esphome/core/log.h"
#ifdef ARDUINO_ARCH_ESP32
#ifdef USE_ESP32
namespace esphome {
namespace airthings_ble {
static const char *TAG = "airthings_ble";
static const char *const TAG = "airthings_ble";
bool AirthingsListener::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
for (auto &it : device.get_manufacturer_datas()) {

View File

@@ -1,10 +1,9 @@
#pragma once
#ifdef ARDUINO_ARCH_ESP32
#ifdef USE_ESP32
#include "esphome/core/component.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include <BLEDevice.h>
namespace esphome {
namespace airthings_ble {

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,10 +1,12 @@
#include "airthings_wave_plus.h"
#ifdef ARDUINO_ARCH_ESP32
#ifdef USE_ESP32
namespace esphome {
namespace airthings_wave_plus {
static const char *const TAG = "airthings_wave_plus";
void AirthingsWavePlus::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) {
switch (event) {
@@ -21,15 +23,15 @@ void AirthingsWavePlus::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt
}
case ESP_GATTC_SEARCH_CMPL_EVT: {
this->handle = 0;
auto chr = this->parent()->get_characteristic(service_uuid, sensors_data_characteristic_uuid);
this->handle_ = 0;
auto chr = this->parent()->get_characteristic(service_uuid_, sensors_data_characteristic_uuid_);
if (chr == nullptr) {
ESP_LOGW(TAG, "No sensor characteristic found at service %s char %s", service_uuid.to_string().c_str(),
sensors_data_characteristic_uuid.to_string().c_str());
ESP_LOGW(TAG, "No sensor characteristic found at service %s char %s", service_uuid_.to_string().c_str(),
sensors_data_characteristic_uuid_.to_string().c_str());
break;
}
this->handle = chr->handle;
this->node_state = espbt::ClientState::Established;
this->handle_ = chr->handle;
this->node_state = esp32_ble_tracker::ClientState::ESTABLISHED;
request_read_values_();
break;
@@ -42,7 +44,7 @@ void AirthingsWavePlus::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt
ESP_LOGW(TAG, "Error reading char at handle %d, status=%d", param->read.handle, param->read.status);
break;
}
if (param->read.handle == this->handle) {
if (param->read.handle == this->handle_) {
read_sensors_(param->read.value, param->read.value_len);
}
break;
@@ -88,16 +90,14 @@ void AirthingsWavePlus::read_sensors_(uint8_t *raw_value, uint16_t value_len) {
}
}
bool AirthingsWavePlus::is_valid_radon_value_(short radon) { return 0 <= radon && radon <= 16383; }
bool AirthingsWavePlus::is_valid_radon_value_(uint16_t radon) { return 0 <= radon && radon <= 16383; }
bool AirthingsWavePlus::is_valid_voc_value_(short voc) { return 0 <= voc && voc <= 16383; }
bool AirthingsWavePlus::is_valid_voc_value_(uint16_t voc) { return 0 <= voc && voc <= 16383; }
bool AirthingsWavePlus::is_valid_co2_value_(short co2) { return 0 <= co2 && co2 <= 16383; }
void AirthingsWavePlus::loop() {}
bool AirthingsWavePlus::is_valid_co2_value_(uint16_t co2) { return 0 <= co2 && co2 <= 16383; }
void AirthingsWavePlus::update() {
if (this->node_state != espbt::ClientState::Established) {
if (this->node_state != esp32_ble_tracker::ClientState::ESTABLISHED) {
if (!parent()->enabled) {
ESP_LOGW(TAG, "Reconnecting to device");
parent()->set_enabled(true);
@@ -110,7 +110,7 @@ void AirthingsWavePlus::update() {
void AirthingsWavePlus::request_read_values_() {
auto status =
esp_ble_gattc_read_char(this->parent()->gattc_if, this->parent()->conn_id, this->handle, ESP_GATT_AUTH_REQ_NONE);
esp_ble_gattc_read_char(this->parent()->gattc_if, this->parent()->conn_id, this->handle_, ESP_GATT_AUTH_REQ_NONE);
if (status) {
ESP_LOGW(TAG, "Error sending read request for sensor, status=%d", status);
}
@@ -126,17 +126,12 @@ void AirthingsWavePlus::dump_config() {
LOG_SENSOR(" ", "TVOC", this->tvoc_sensor_);
}
AirthingsWavePlus::AirthingsWavePlus() : PollingComponent(10000) {
auto service_bt = *BLEUUID::fromString(std::string("b42e1c08-ade7-11e4-89d3-123b93f75cba")).getNative();
auto characteristic_bt = *BLEUUID::fromString(std::string("b42e2a68-ade7-11e4-89d3-123b93f75cba")).getNative();
service_uuid = espbt::ESPBTUUID::from_uuid(service_bt);
sensors_data_characteristic_uuid = espbt::ESPBTUUID::from_uuid(characteristic_bt);
}
void AirthingsWavePlus::setup() {}
AirthingsWavePlus::AirthingsWavePlus()
: PollingComponent(10000),
service_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(SERVICE_UUID)),
sensors_data_characteristic_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(CHARACTERISTIC_UUID)) {}
} // namespace airthings_wave_plus
} // namespace esphome
#endif // ARDUINO_ARCH_ESP32
#endif // USE_ESP32

View File

@@ -1,32 +1,28 @@
#pragma once
#include "esphome/core/component.h"
#ifdef USE_ESP32
#include <esp_gattc_api.h>
#include <algorithm>
#include <iterator>
#include "esphome/components/ble_client/ble_client.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/core/component.h"
#include "esphome/core/log.h"
#include <algorithm>
#include <iterator>
#ifdef ARDUINO_ARCH_ESP32
#include <esp_gattc_api.h>
#include <BLEDevice.h>
using namespace esphome::ble_client;
namespace esphome {
namespace airthings_wave_plus {
static const char *TAG = "airthings_wave_plus";
static const char *const SERVICE_UUID = "b42e1c08-ade7-11e4-89d3-123b93f75cba";
static const char *const CHARACTERISTIC_UUID = "b42e2a68-ade7-11e4-89d3-123b93f75cba";
class AirthingsWavePlus : public PollingComponent, public BLEClientNode {
class AirthingsWavePlus : public PollingComponent, public ble_client::BLEClientNode {
public:
AirthingsWavePlus();
void setup() override;
void dump_config() override;
void update() override;
void loop() override;
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) override;
@@ -40,9 +36,9 @@ class AirthingsWavePlus : public PollingComponent, public BLEClientNode {
void set_tvoc(sensor::Sensor *tvoc) { tvoc_sensor_ = tvoc; }
protected:
bool is_valid_radon_value_(short radon);
bool is_valid_voc_value_(short voc);
bool is_valid_co2_value_(short co2);
bool is_valid_radon_value_(uint16_t radon);
bool is_valid_voc_value_(uint16_t voc);
bool is_valid_co2_value_(uint16_t co2);
void read_sensors_(uint8_t *value, uint16_t value_len);
void request_read_values_();
@@ -55,9 +51,9 @@ class AirthingsWavePlus : public PollingComponent, public BLEClientNode {
sensor::Sensor *co2_sensor_{nullptr};
sensor::Sensor *tvoc_sensor_{nullptr};
uint16_t handle;
espbt::ESPBTUUID service_uuid;
espbt::ESPBTUUID sensors_data_characteristic_uuid;
uint16_t handle_;
esp32_ble_tracker::ESPBTUUID service_uuid_;
esp32_ble_tracker::ESPBTUUID sensors_data_characteristic_uuid_;
struct WavePlusReadings {
uint8_t version;
@@ -76,4 +72,4 @@ class AirthingsWavePlus : public PollingComponent, public BLEClientNode {
} // namespace airthings_wave_plus
} // namespace esphome
#endif // ARDUINO_ARCH_ESP32
#endif // USE_ESP32

View File

@@ -34,7 +34,7 @@ AirthingsWavePlus = airthings_wave_plus_ns.class_(
)
CONFIG_SCHEMA = (
CONFIG_SCHEMA = cv.All(
cv.Schema(
{
cv.GenerateID(): cv.declare_id(AirthingsWavePlus),
@@ -82,8 +82,8 @@ CONFIG_SCHEMA = (
),
}
)
.extend(cv.polling_component_schema("5mins"))
.extend(ble_client.BLE_CLIENT_SCHEMA)
.extend(cv.polling_component_schema("5min"))
.extend(ble_client.BLE_CLIENT_SCHEMA),
)

View File

@@ -5,6 +5,7 @@
#include "am2320.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
namespace esphome {
namespace am2320 {
@@ -37,9 +38,9 @@ void AM2320Component::update() {
return;
}
float temperature = (((data[4] & 0x7F) << 8) + data[5]) / 10.0;
float temperature = (((data[4] & 0x7F) << 8) + data[5]) / 10.0f;
temperature = (data[4] & 0x80) ? -temperature : temperature;
float humidity = ((data[2] << 8) + data[3]) / 10.0;
float humidity = ((data[2] << 8) + data[3]) / 10.0f;
ESP_LOGD(TAG, "Got temperature=%.1f°C humidity=%.1f%%", temperature, humidity);
if (this->temperature_sensor_ != nullptr)
@@ -77,7 +78,7 @@ bool AM2320Component::read_bytes_(uint8_t a_register, uint8_t *data, uint8_t len
if (conversion > 0)
delay(conversion);
return this->parent_->raw_receive(this->address_, data, len);
return this->read(data, len) == i2c::ERROR_OK;
}
bool AM2320Component::read_data_(uint8_t *data) {

View File

@@ -1,12 +1,13 @@
#include "am43.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
#ifdef ARDUINO_ARCH_ESP32
#ifdef USE_ESP32
namespace esphome {
namespace am43 {
static const char *TAG = "am43";
static const char *const TAG = "am43";
void Am43::dump_config() {
ESP_LOGCONFIG(TAG, "AM43");
@@ -15,8 +16,8 @@ void Am43::dump_config() {
}
void Am43::setup() {
this->encoder_ = new Am43Encoder();
this->decoder_ = new Am43Decoder();
this->encoder_ = make_unique<Am43Encoder>();
this->decoder_ = make_unique<Am43Decoder>();
this->logged_in_ = false;
this->last_battery_update_ = 0;
this->current_sensor_ = 0;
@@ -30,7 +31,7 @@ void Am43::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_i
}
case ESP_GATTC_DISCONNECT_EVT: {
this->logged_in_ = false;
this->node_state = espbt::ClientState::Idle;
this->node_state = espbt::ClientState::IDLE;
if (this->battery_ != nullptr)
this->battery_->publish_state(NAN);
if (this->illuminance_ != nullptr)
@@ -53,7 +54,7 @@ void Am43::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_i
break;
}
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
this->node_state = espbt::ClientState::Established;
this->node_state = espbt::ClientState::ESTABLISHED;
this->update();
break;
}
@@ -92,7 +93,7 @@ void Am43::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_i
}
void Am43::update() {
if (this->node_state != espbt::ClientState::Established) {
if (this->node_state != espbt::ClientState::ESTABLISHED) {
ESP_LOGW(TAG, "[%s] Cannot poll, not connected", this->parent_->address_str().c_str());
return;
}

View File

@@ -6,7 +6,7 @@
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/am43/am43_base.h"
#ifdef ARDUINO_ARCH_ESP32
#ifdef USE_ESP32
#include <esp_gattc_api.h>
@@ -28,8 +28,8 @@ class Am43 : public esphome::ble_client::BLEClientNode, public PollingComponent
protected:
uint16_t char_handle_;
Am43Encoder *encoder_;
Am43Decoder *decoder_;
std::unique_ptr<Am43Encoder> encoder_;
std::unique_ptr<Am43Decoder> decoder_;
bool logged_in_;
sensor::Sensor *battery_{nullptr};
sensor::Sensor *illuminance_{nullptr};

View File

@@ -1,4 +1,6 @@
#include "am43_base.h"
#include <cstring>
#include <cstdio>
namespace esphome {
namespace am43 {

View File

@@ -1,12 +1,12 @@
#include "am43_cover.h"
#include "esphome/core/log.h"
#ifdef ARDUINO_ARCH_ESP32
#ifdef USE_ESP32
namespace esphome {
namespace am43 {
static const char *TAG = "am43_cover";
static const char *const TAG = "am43_cover";
using namespace esphome::cover;
@@ -18,13 +18,13 @@ void Am43Component::dump_config() {
void Am43Component::setup() {
this->position = COVER_OPEN;
this->encoder_ = new Am43Encoder();
this->decoder_ = new Am43Decoder();
this->encoder_ = make_unique<Am43Encoder>();
this->decoder_ = make_unique<Am43Decoder>();
this->logged_in_ = false;
}
void Am43Component::loop() {
if (this->node_state == espbt::ClientState::Established && !this->logged_in_) {
if (this->node_state == espbt::ClientState::ESTABLISHED && !this->logged_in_) {
auto packet = this->encoder_->get_send_pin_request(this->pin_);
auto status =
esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length,
@@ -46,7 +46,7 @@ CoverTraits Am43Component::get_traits() {
}
void Am43Component::control(const CoverCall &call) {
if (this->node_state != espbt::ClientState::Established) {
if (this->node_state != espbt::ClientState::ESTABLISHED) {
ESP_LOGW(TAG, "[%s] Cannot send cover control, not connected", this->get_name().c_str());
return;
}
@@ -98,7 +98,7 @@ void Am43Component::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
break;
}
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
this->node_state = espbt::ClientState::Established;
this->node_state = espbt::ClientState::ESTABLISHED;
break;
}
case ESP_GATTC_NOTIFY_EVT: {

View File

@@ -6,7 +6,7 @@
#include "esphome/components/cover/cover.h"
#include "esphome/components/am43/am43_base.h"
#ifdef ARDUINO_ARCH_ESP32
#ifdef USE_ESP32
#include <esp_gattc_api.h>
@@ -32,8 +32,8 @@ class Am43Component : public cover::Cover, public esphome::ble_client::BLEClient
uint16_t char_handle_;
uint16_t pin_;
bool invert_position_;
Am43Encoder *encoder_;
Am43Decoder *decoder_;
std::unique_ptr<Am43Encoder> encoder_;
std::unique_ptr<Am43Decoder> decoder_;
bool logged_in_;
float position_;

View File

@@ -4,7 +4,8 @@ from esphome.components import sensor, ble_client
from esphome.const import (
CONF_ID,
CONF_BATTERY_LEVEL,
ICON_BATTERY,
DEVICE_CLASS_BATTERY,
ENTITY_CATEGORY_DIAGNOSTIC,
CONF_ILLUMINANCE,
ICON_BRIGHTNESS_5,
UNIT_PERCENT,
@@ -20,10 +21,15 @@ CONFIG_SCHEMA = (
{
cv.GenerateID(): cv.declare_id(Am43),
cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(
UNIT_PERCENT, ICON_BATTERY, 0
unit_of_measurement=UNIT_PERCENT,
device_class=DEVICE_CLASS_BATTERY,
accuracy_decimals=0,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
),
cv.Optional(CONF_ILLUMINANCE): sensor.sensor_schema(
UNIT_PERCENT, ICON_BRIGHTNESS_5, 0
unit_of_measurement=UNIT_PERCENT,
icon=ICON_BRIGHTNESS_5,
accuracy_decimals=0,
),
}
)

View File

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

View File

@@ -1,19 +1,19 @@
#include "anova.h"
#include "esphome/core/log.h"
#ifdef ARDUINO_ARCH_ESP32
#ifdef USE_ESP32
namespace esphome {
namespace anova {
static const char *TAG = "anova";
static const char *const TAG = "anova";
using namespace esphome::climate;
void Anova::dump_config() { LOG_CLIMATE("", "Anova BLE Cooker", this); }
void Anova::setup() {
this->codec_ = new AnovaCodec();
this->codec_ = make_unique<AnovaCodec>();
this->current_request_ = 0;
}
@@ -72,7 +72,7 @@ void Anova::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_
break;
}
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
this->node_state = espbt::ClientState::Established;
this->node_state = espbt::ClientState::ESTABLISHED;
this->current_request_ = 0;
this->update();
break;
@@ -129,13 +129,13 @@ void Anova::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_
void Anova::set_unit_of_measurement(const char *unit) { this->fahrenheit_ = !strncmp(unit, "f", 1); }
void Anova::update() {
if (this->node_state != espbt::ClientState::Established)
if (this->node_state != espbt::ClientState::ESTABLISHED)
return;
if (this->current_request_ < 2) {
auto pkt = this->codec_->get_read_device_status_request();
if (this->current_request_ == 0)
auto pkt = this->codec_->get_set_unit_request(this->fahrenheit_ ? 'f' : 'c');
this->codec_->get_set_unit_request(this->fahrenheit_ ? 'f' : 'c');
auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_,
pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
if (status)

View File

@@ -6,7 +6,7 @@
#include "esphome/components/climate/climate.h"
#include "anova_base.h"
#ifdef ARDUINO_ARCH_ESP32
#ifdef USE_ESP32
#include <esp_gattc_api.h>
@@ -27,10 +27,10 @@ class Anova : public climate::Climate, public esphome::ble_client::BLEClientNode
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() {
climate::ClimateTraits traits() override {
auto traits = climate::ClimateTraits();
traits.set_supports_current_temperature(true);
traits.set_supports_heat_mode(true);
traits.set_supported_modes({climate::CLIMATE_MODE_OFF, climate::ClimateMode::CLIMATE_MODE_HEAT});
traits.set_visual_min_temperature(25.0);
traits.set_visual_max_temperature(100.0);
traits.set_visual_temperature_step(0.1);
@@ -39,7 +39,7 @@ class Anova : public climate::Climate, public esphome::ble_client::BLEClientNode
void set_unit_of_measurement(const char *);
protected:
AnovaCodec *codec_;
std::unique_ptr<AnovaCodec> codec_;
void control(const climate::ClimateCall &call) override;
uint16_t char_handle_;
uint8_t current_request_;

View File

@@ -1,4 +1,6 @@
#include "anova_base.h"
#include <cstdio>
#include <cstring>
namespace esphome {
namespace anova {
@@ -71,51 +73,46 @@ AnovaPacket *AnovaCodec::get_stop_request() {
}
void AnovaCodec::decode(const uint8_t *data, uint16_t length) {
memset(this->buf_, 0, 32);
strncpy(this->buf_, (char *) data, length);
char buf[32];
memset(buf, 0, sizeof(buf));
strncpy(buf, (char *) data, std::min<uint16_t>(length, sizeof(buf) - 1));
this->has_target_temp_ = this->has_current_temp_ = this->has_unit_ = this->has_running_ = false;
switch (this->current_query_) {
case READ_DEVICE_STATUS: {
if (!strncmp(this->buf_, "stopped", 7)) {
if (!strncmp(buf, "stopped", 7)) {
this->has_running_ = true;
this->running_ = false;
}
if (!strncmp(this->buf_, "running", 7)) {
if (!strncmp(buf, "running", 7)) {
this->has_running_ = true;
this->running_ = true;
}
break;
}
case START: {
if (!strncmp(this->buf_, "start", 5)) {
if (!strncmp(buf, "start", 5)) {
this->has_running_ = true;
this->running_ = true;
}
break;
}
case STOP: {
if (!strncmp(this->buf_, "stop", 4)) {
if (!strncmp(buf, "stop", 4)) {
this->has_running_ = true;
this->running_ = false;
}
break;
}
case READ_TARGET_TEMPERATURE: {
this->target_temp_ = strtof(this->buf_, nullptr);
if (this->fahrenheit_)
this->target_temp_ = ftoc(this->target_temp_);
this->has_target_temp_ = true;
break;
}
case READ_TARGET_TEMPERATURE:
case SET_TARGET_TEMPERATURE: {
this->target_temp_ = strtof(this->buf_, nullptr);
this->target_temp_ = parse_number<float>(str_until(buf, '\r')).value_or(0.0f);
if (this->fahrenheit_)
this->target_temp_ = ftoc(this->target_temp_);
this->has_target_temp_ = true;
break;
}
case READ_CURRENT_TEMPERATURE: {
this->current_temp_ = strtof(this->buf_, nullptr);
this->current_temp_ = parse_number<float>(str_until(buf, '\r')).value_or(0.0f);
if (this->fahrenheit_)
this->current_temp_ = ftoc(this->current_temp_);
this->has_current_temp_ = true;
@@ -123,8 +120,8 @@ void AnovaCodec::decode(const uint8_t *data, uint16_t length) {
}
case SET_UNIT:
case READ_UNIT: {
this->unit_ = this->buf_[0];
this->fahrenheit_ = this->buf_[0] == 'f';
this->unit_ = buf[0];
this->fahrenheit_ = buf[0] == 'f';
this->has_unit_ = true;
break;
}

View File

@@ -70,7 +70,6 @@ class AnovaCodec {
bool has_current_temp_;
bool has_unit_;
bool has_running_;
char buf_[32];
bool fahrenheit_;
CurrentQuery current_query_;

View File

@@ -1,5 +1,6 @@
#include "apds9960.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
namespace esphome {
namespace apds9960 {

View File

@@ -121,7 +121,7 @@ async def to_code(config):
decoded = base64.b64decode(conf[CONF_KEY])
cg.add(var.set_noise_psk(list(decoded)))
cg.add_define("USE_API_NOISE")
cg.add_library("esphome/noise-c", "0.1.3")
cg.add_library("esphome/noise-c", "0.1.4")
else:
cg.add_define("USE_API_PLAINTEXT")

View File

@@ -40,6 +40,7 @@ service APIConnection {
rpc climate_command (ClimateCommandRequest) returns (void) {}
rpc number_command (NumberCommandRequest) returns (void) {}
rpc select_command (SelectCommandRequest) returns (void) {}
rpc button_command (ButtonCommandRequest) returns (void) {}
}
@@ -182,6 +183,8 @@ message DeviceInfoResponse {
// The esphome project details if set
string project_name = 8;
string project_version = 9;
uint32 webserver_port = 10;
}
message ListEntitiesRequest {
@@ -201,6 +204,14 @@ message SubscribeStatesRequest {
// Empty
}
// ==================== COMMON =====================
enum EntityCategory {
ENTITY_CATEGORY_NONE = 0;
ENTITY_CATEGORY_CONFIG = 1;
ENTITY_CATEGORY_DIAGNOSTIC = 2;
}
// ==================== BINARY SENSOR ====================
message ListEntitiesBinarySensorResponse {
option (id) = 12;
@@ -215,6 +226,8 @@ message ListEntitiesBinarySensorResponse {
string device_class = 5;
bool is_status_binary_sensor = 6;
bool disabled_by_default = 7;
string icon = 8;
EntityCategory entity_category = 9;
}
message BinarySensorStateResponse {
option (id) = 21;
@@ -245,6 +258,8 @@ message ListEntitiesCoverResponse {
bool supports_tilt = 7;
string device_class = 8;
bool disabled_by_default = 9;
string icon = 10;
EntityCategory entity_category = 11;
}
enum LegacyCoverState {
@@ -313,6 +328,8 @@ message ListEntitiesFanResponse {
bool supports_direction = 7;
int32 supported_speed_count = 8;
bool disabled_by_default = 9;
string icon = 10;
EntityCategory entity_category = 11;
}
enum FanSpeed {
FAN_SPEED_LOW = 0;
@@ -388,6 +405,8 @@ message ListEntitiesLightResponse {
float max_mireds = 10;
repeated string effects = 11;
bool disabled_by_default = 13;
string icon = 14;
EntityCategory entity_category = 15;
}
message LightStateResponse {
option (id) = 24;
@@ -476,6 +495,7 @@ message ListEntitiesSensorResponse {
// Last reset type removed in 2021.9.0
SensorLastResetType legacy_last_reset_type = 11;
bool disabled_by_default = 12;
EntityCategory entity_category = 13;
}
message SensorStateResponse {
option (id) = 25;
@@ -504,6 +524,7 @@ message ListEntitiesSwitchResponse {
string icon = 5;
bool assumed_state = 6;
bool disabled_by_default = 7;
EntityCategory entity_category = 8;
}
message SwitchStateResponse {
option (id) = 26;
@@ -537,6 +558,7 @@ message ListEntitiesTextSensorResponse {
string icon = 5;
bool disabled_by_default = 6;
EntityCategory entity_category = 7;
}
message TextSensorStateResponse {
option (id) = 27;
@@ -697,6 +719,8 @@ message ListEntitiesCameraResponse {
string name = 3;
string unique_id = 4;
bool disabled_by_default = 5;
string icon = 6;
EntityCategory entity_category = 7;
}
message CameraImageResponse {
@@ -790,6 +814,8 @@ message ListEntitiesClimateResponse {
repeated ClimatePreset supported_presets = 16;
repeated string supported_custom_presets = 17;
bool disabled_by_default = 18;
string icon = 19;
EntityCategory entity_category = 20;
}
message ClimateStateResponse {
option (id) = 47;
@@ -843,6 +869,11 @@ message ClimateCommandRequest {
}
// ==================== NUMBER ====================
enum NumberMode {
NUMBER_MODE_AUTO = 0;
NUMBER_MODE_BOX = 1;
NUMBER_MODE_SLIDER = 2;
}
message ListEntitiesNumberResponse {
option (id) = 49;
option (source) = SOURCE_SERVER;
@@ -858,6 +889,9 @@ message ListEntitiesNumberResponse {
float max_value = 7;
float step = 8;
bool disabled_by_default = 9;
EntityCategory entity_category = 10;
string unit_of_measurement = 11;
NumberMode mode = 12;
}
message NumberStateResponse {
option (id) = 50;
@@ -895,6 +929,7 @@ message ListEntitiesSelectResponse {
string icon = 5;
repeated string options = 6;
bool disabled_by_default = 7;
EntityCategory entity_category = 8;
}
message SelectStateResponse {
option (id) = 53;
@@ -917,3 +952,28 @@ message SelectCommandRequest {
fixed32 key = 1;
string state = 2;
}
// ==================== BUTTON ====================
message ListEntitiesButtonResponse {
option (id) = 61;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_BUTTON";
string object_id = 1;
fixed32 key = 2;
string name = 3;
string unique_id = 4;
string icon = 5;
bool disabled_by_default = 6;
EntityCategory entity_category = 7;
string device_class = 8;
}
message ButtonCommandRequest {
option (id) = 62;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_BUTTON";
option (no_delay) = true;
fixed32 key = 1;
}

View File

@@ -1,7 +1,9 @@
#include "api_connection.h"
#include "esphome/core/entity_base.h"
#include "esphome/core/log.h"
#include "esphome/core/util.h"
#include "esphome/components/network/util.h"
#include "esphome/core/version.h"
#include "esphome/core/hal.h"
#include <cerrno>
#ifdef USE_DEEP_SLEEP
@@ -48,7 +50,7 @@ void APIConnection::loop() {
if (this->remove_)
return;
if (!network_is_connected()) {
if (!network::is_connected()) {
// when network is disconnected force disconnect immediately
// don't wait for timeout
this->on_fatal_error();
@@ -76,6 +78,8 @@ void APIConnection::loop() {
on_fatal_error();
if (err == APIError::SOCKET_READ_FAILED && errno == ECONNRESET) {
ESP_LOGW(TAG, "%s: Connection reset", client_info_.c_str());
} else if (err == APIError::CONNECTION_CLOSED) {
ESP_LOGW(TAG, "%s: Connection closed", client_info_.c_str());
} else {
ESP_LOGW(TAG, "%s: Reading failed: %s errno=%d", client_info_.c_str(), api_error_to_str(err), errno);
}
@@ -128,7 +132,7 @@ void APIConnection::loop() {
if (state_subs_at_ != -1) {
const auto &subs = this->parent_->get_state_subs();
if (state_subs_at_ >= subs.size()) {
if (state_subs_at_ >= (int) subs.size()) {
state_subs_at_ = -1;
} else {
auto &it = subs[state_subs_at_];
@@ -142,8 +146,8 @@ void APIConnection::loop() {
}
}
std::string get_default_unique_id(const std::string &component_type, Nameable *nameable) {
return App.get_name() + component_type + nameable->get_object_id();
std::string get_default_unique_id(const std::string &component_type, EntityBase *entity) {
return App.get_name() + component_type + entity->get_object_id();
}
DisconnectResponse APIConnection::disconnect(const DisconnectRequest &msg) {
@@ -179,6 +183,8 @@ bool APIConnection::send_binary_sensor_info(binary_sensor::BinarySensor *binary_
msg.device_class = binary_sensor->get_device_class();
msg.is_status_binary_sensor = binary_sensor->is_status_binary_sensor();
msg.disabled_by_default = binary_sensor->is_disabled_by_default();
msg.icon = binary_sensor->get_icon();
msg.entity_category = static_cast<enums::EntityCategory>(binary_sensor->get_entity_category());
return this->send_list_entities_binary_sensor_response(msg);
}
#endif
@@ -211,6 +217,8 @@ bool APIConnection::send_cover_info(cover::Cover *cover) {
msg.supports_tilt = traits.get_supports_tilt();
msg.device_class = cover->get_device_class();
msg.disabled_by_default = cover->is_disabled_by_default();
msg.icon = cover->get_icon();
msg.entity_category = static_cast<enums::EntityCategory>(cover->get_entity_category());
return this->send_list_entities_cover_response(msg);
}
void APIConnection::cover_command(const CoverCommandRequest &msg) {
@@ -276,6 +284,8 @@ bool APIConnection::send_fan_info(fan::FanState *fan) {
msg.supports_direction = traits.supports_direction();
msg.supported_speed_count = traits.supported_speed_count();
msg.disabled_by_default = fan->is_disabled_by_default();
msg.icon = fan->get_icon();
msg.entity_category = static_cast<enums::EntityCategory>(fan->get_entity_category());
return this->send_list_entities_fan_response(msg);
}
void APIConnection::fan_command(const FanCommandRequest &msg) {
@@ -338,6 +348,8 @@ bool APIConnection::send_light_info(light::LightState *light) {
msg.unique_id = get_default_unique_id("light", light);
msg.disabled_by_default = light->is_disabled_by_default();
msg.icon = light->get_icon();
msg.entity_category = static_cast<enums::EntityCategory>(light->get_entity_category());
for (auto mode : traits.get_supported_color_modes())
msg.supported_color_modes.push_back(static_cast<enums::ColorMode>(mode));
@@ -424,7 +436,7 @@ bool APIConnection::send_sensor_info(sensor::Sensor *sensor) {
msg.device_class = sensor->get_device_class();
msg.state_class = static_cast<enums::SensorStateClass>(sensor->get_state_class());
msg.disabled_by_default = sensor->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(sensor->get_entity_category());
return this->send_list_entities_sensor_response(msg);
}
#endif
@@ -448,6 +460,7 @@ bool APIConnection::send_switch_info(switch_::Switch *a_switch) {
msg.icon = a_switch->get_icon();
msg.assumed_state = a_switch->assumed_state();
msg.disabled_by_default = a_switch->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(a_switch->get_entity_category());
return this->send_list_entities_switch_response(msg);
}
void APIConnection::switch_command(const SwitchCommandRequest &msg) {
@@ -483,6 +496,7 @@ bool APIConnection::send_text_sensor_info(text_sensor::TextSensor *text_sensor)
msg.unique_id = get_default_unique_id("text_sensor", text_sensor);
msg.icon = text_sensor->get_icon();
msg.disabled_by_default = text_sensor->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(text_sensor->get_entity_category());
return this->send_list_entities_text_sensor_response(msg);
}
#endif
@@ -528,6 +542,8 @@ bool APIConnection::send_climate_info(climate::Climate *climate) {
msg.unique_id = get_default_unique_id("climate", climate);
msg.disabled_by_default = climate->is_disabled_by_default();
msg.icon = climate->get_icon();
msg.entity_category = static_cast<enums::EntityCategory>(climate->get_entity_category());
msg.supports_current_temperature = traits.get_supports_current_temperature();
msg.supports_two_point_target_temperature = traits.get_supports_two_point_target_temperature();
@@ -600,8 +616,11 @@ bool APIConnection::send_number_info(number::Number *number) {
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.icon = number->get_icon();
msg.disabled_by_default = number->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(number->get_entity_category());
msg.unit_of_measurement = number->traits.get_unit_of_measurement();
msg.mode = static_cast<enums::NumberMode>(number->traits.get_mode());
msg.min_value = number->traits.get_min_value();
msg.max_value = number->traits.get_max_value();
@@ -637,8 +656,9 @@ bool APIConnection::send_select_info(select::Select *select) {
msg.object_id = select->get_object_id();
msg.name = select->get_name();
msg.unique_id = get_default_unique_id("select", select);
msg.icon = select->traits.get_icon();
msg.icon = select->get_icon();
msg.disabled_by_default = select->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(select->get_entity_category());
for (const auto &option : select->traits.get_options())
msg.options.push_back(option);
@@ -656,13 +676,35 @@ void APIConnection::select_command(const SelectCommandRequest &msg) {
}
#endif
#ifdef USE_BUTTON
bool APIConnection::send_button_info(button::Button *button) {
ListEntitiesButtonResponse msg;
msg.key = button->get_object_id_hash();
msg.object_id = button->get_object_id();
msg.name = button->get_name();
msg.unique_id = get_default_unique_id("button", button);
msg.icon = button->get_icon();
msg.disabled_by_default = button->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(button->get_entity_category());
msg.device_class = button->get_device_class();
return this->send_list_entities_button_response(msg);
}
void APIConnection::button_command(const ButtonCommandRequest &msg) {
button::Button *button = App.get_button_by_key(msg.key);
if (button == nullptr)
return;
button->press();
}
#endif
#ifdef USE_ESP32_CAMERA
void APIConnection::send_camera_state(std::shared_ptr<esp32_camera::CameraImage> image) {
if (!this->state_subscription_)
return;
if (this->image_reader_.available())
return;
this->image_reader_.set_image(image);
this->image_reader_.set_image(std::move(image));
}
bool APIConnection::send_camera_info(esp32_camera::ESP32Camera *camera) {
ListEntitiesCameraResponse msg;
@@ -671,6 +713,8 @@ bool APIConnection::send_camera_info(esp32_camera::ESP32Camera *camera) {
msg.name = camera->get_name();
msg.unique_id = get_default_unique_id("camera", camera);
msg.disabled_by_default = camera->is_disabled_by_default();
msg.icon = camera->get_icon();
msg.entity_category = static_cast<enums::EntityCategory>(camera->get_entity_category());
return this->send_list_entities_camera_response(msg);
}
void APIConnection::camera_image(const CameraImageRequest &msg) {
@@ -749,6 +793,9 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
#ifdef ESPHOME_PROJECT_NAME
resp.project_name = ESPHOME_PROJECT_NAME;
resp.project_version = ESPHOME_PROJECT_VERSION;
#endif
#ifdef USE_WEBSERVER
resp.webserver_port = WEBSERVER_PORT;
#endif
return resp;
}

View File

@@ -73,6 +73,10 @@ class APIConnection : public APIServerConnection {
bool send_select_state(select::Select *select, std::string state);
bool send_select_info(select::Select *select);
void select_command(const SelectCommandRequest &msg) override;
#endif
#ifdef USE_BUTTON
bool send_button_info(button::Button *button);
void button_command(const ButtonCommandRequest &msg) override;
#endif
bool send_log_message(int level, const char *tag, const char *line);
void send_homeassistant_service_call(const HomeassistantServiceResponse &call) {

View File

@@ -3,13 +3,14 @@
#include "esphome/core/log.h"
#include "esphome/core/helpers.h"
#include "proto.h"
#include <cstring>
namespace esphome {
namespace api {
static const char *const TAG = "api.socket";
/// Is the given return value (from read/write syscalls) a wouldblock error?
/// Is the given return value (from write syscalls) a wouldblock error?
bool is_would_block(ssize_t ret) {
if (ret == -1) {
return errno == EWOULDBLOCK || errno == EAGAIN;
@@ -63,6 +64,8 @@ const char *api_error_to_str(APIError err) {
return "HANDSHAKESTATE_SPLIT_FAILED";
} else if (err == APIError::BAD_HANDSHAKE_ERROR_BYTE) {
return "BAD_HANDSHAKE_ERROR_BYTE";
} else if (err == APIError::CONNECTION_CLOSED) {
return "CONNECTION_CLOSED";
}
return "UNKNOWN";
}
@@ -171,9 +174,6 @@ APIError APINoiseFrameHelper::loop() {
* errno API_ERROR_HANDSHAKE_PACKET_LEN: Packet too big for this phase.
*/
APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
int err;
APIError aerr;
if (frame == nullptr) {
HELPER_LOG("Bad argument for try_read_frame_");
return APIError::BAD_ARG;
@@ -184,15 +184,20 @@ APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
// no header information yet
size_t to_read = 3 - rx_header_buf_len_;
ssize_t received = socket_->read(&rx_header_buf_[rx_header_buf_len_], to_read);
if (is_would_block(received)) {
return APIError::WOULD_BLOCK;
} else if (received == -1) {
if (received == -1) {
if (errno == EWOULDBLOCK || errno == EAGAIN) {
return APIError::WOULD_BLOCK;
}
state_ = State::FAILED;
HELPER_LOG("Socket read failed with errno %d", errno);
return APIError::SOCKET_READ_FAILED;
} else if (received == 0) {
state_ = State::FAILED;
HELPER_LOG("Connection closed");
return APIError::CONNECTION_CLOSED;
}
rx_header_buf_len_ += received;
if (received != to_read) {
if ((size_t) received != to_read) {
// not a full read
return APIError::WOULD_BLOCK;
}
@@ -226,15 +231,20 @@ APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
// more data to read
size_t to_read = msg_size - rx_buf_len_;
ssize_t received = socket_->read(&rx_buf_[rx_buf_len_], to_read);
if (is_would_block(received)) {
return APIError::WOULD_BLOCK;
} else if (received == -1) {
if (received == -1) {
if (errno == EWOULDBLOCK || errno == EAGAIN) {
return APIError::WOULD_BLOCK;
}
state_ = State::FAILED;
HELPER_LOG("Socket read failed with errno %d", errno);
return APIError::SOCKET_READ_FAILED;
} else if (received == 0) {
state_ = State::FAILED;
HELPER_LOG("Connection closed");
return APIError::CONNECTION_CLOSED;
}
rx_buf_len_ += received;
if (received != to_read) {
if ((size_t) received != to_read) {
// not all read
return APIError::WOULD_BLOCK;
}
@@ -242,7 +252,7 @@ APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
// uncomment for even more debugging
#ifdef HELPER_LOG_PACKETS
ESP_LOGVV(TAG, "Received frame: %s", hexencode(rx_buf_).c_str());
ESP_LOGVV(TAG, "Received frame: %s", format_hex_pretty(rx_buf_).c_str());
#endif
frame->msg = std::move(rx_buf_);
// consume msg
@@ -531,13 +541,13 @@ APIError APINoiseFrameHelper::try_send_tx_buf_() {
APIError APINoiseFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) {
if (iovcnt == 0)
return APIError::OK;
int err;
APIError aerr;
size_t total_write_len = 0;
for (int i = 0; i < iovcnt; i++) {
#ifdef HELPER_LOG_PACKETS
ESP_LOGVV(TAG, "Sending raw: %s", hexencode(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len).c_str());
ESP_LOGVV(TAG, "Sending raw: %s",
format_hex_pretty(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len).c_str());
#endif
total_write_len += iov[i].iov_len;
}
@@ -571,7 +581,7 @@ APIError APINoiseFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) {
state_ = State::FAILED;
HELPER_LOG("Socket write failed with errno %d", errno);
return APIError::SOCKET_WRITE_FAILED;
} else if (sent != total_write_len) {
} else if ((size_t) sent != total_write_len) {
// partially sent, add end to tx_buf
size_t to_consume = sent;
for (int i = 0; i < iovcnt; i++) {
@@ -765,9 +775,6 @@ APIError APIPlaintextFrameHelper::loop() {
* error API_ERROR_BAD_INDICATOR: Bad indicator byte at start of frame.
*/
APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
int err;
APIError aerr;
if (frame == nullptr) {
HELPER_LOG("Bad argument for try_read_frame_");
return APIError::BAD_ARG;
@@ -777,12 +784,17 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
while (!rx_header_parsed_) {
uint8_t data;
ssize_t received = socket_->read(&data, 1);
if (is_would_block(received)) {
return APIError::WOULD_BLOCK;
} else if (received == -1) {
if (received == -1) {
if (errno == EWOULDBLOCK || errno == EAGAIN) {
return APIError::WOULD_BLOCK;
}
state_ = State::FAILED;
HELPER_LOG("Socket read failed with errno %d", errno);
return APIError::SOCKET_READ_FAILED;
} else if (received == 0) {
state_ = State::FAILED;
HELPER_LOG("Connection closed");
return APIError::CONNECTION_CLOSED;
}
rx_header_buf_.push_back(data);
@@ -823,15 +835,20 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
// more data to read
size_t to_read = rx_header_parsed_len_ - rx_buf_len_;
ssize_t received = socket_->read(&rx_buf_[rx_buf_len_], to_read);
if (is_would_block(received)) {
return APIError::WOULD_BLOCK;
} else if (received == -1) {
if (received == -1) {
if (errno == EWOULDBLOCK || errno == EAGAIN) {
return APIError::WOULD_BLOCK;
}
state_ = State::FAILED;
HELPER_LOG("Socket read failed with errno %d", errno);
return APIError::SOCKET_READ_FAILED;
} else if (received == 0) {
state_ = State::FAILED;
HELPER_LOG("Connection closed");
return APIError::CONNECTION_CLOSED;
}
rx_buf_len_ += received;
if (received != to_read) {
if ((size_t) received != to_read) {
// not all read
return APIError::WOULD_BLOCK;
}
@@ -839,7 +856,7 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
// uncomment for even more debugging
#ifdef HELPER_LOG_PACKETS
ESP_LOGVV(TAG, "Received frame: %s", hexencode(rx_buf_).c_str());
ESP_LOGVV(TAG, "Received frame: %s", format_hex_pretty(rx_buf_).c_str());
#endif
frame->msg = std::move(rx_buf_);
// consume msg
@@ -851,7 +868,6 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
}
APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) {
int err;
APIError aerr;
if (state_ != State::DATA) {
@@ -871,9 +887,6 @@ APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) {
}
bool APIPlaintextFrameHelper::can_write_without_blocking() { return state_ == State::DATA && tx_buf_.empty(); }
APIError APIPlaintextFrameHelper::write_packet(uint16_t type, const uint8_t *payload, size_t payload_len) {
int err;
APIError aerr;
if (state_ != State::DATA) {
return APIError::BAD_STATE;
}
@@ -917,13 +930,13 @@ APIError APIPlaintextFrameHelper::try_send_tx_buf_() {
APIError APIPlaintextFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) {
if (iovcnt == 0)
return APIError::OK;
int err;
APIError aerr;
size_t total_write_len = 0;
for (int i = 0; i < iovcnt; i++) {
#ifdef HELPER_LOG_PACKETS
ESP_LOGVV(TAG, "Sending raw: %s", hexencode(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len).c_str());
ESP_LOGVV(TAG, "Sending raw: %s",
format_hex_pretty(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len).c_str());
#endif
total_write_len += iov[i].iov_len;
}
@@ -957,7 +970,7 @@ APIError APIPlaintextFrameHelper::write_raw_(const struct iovec *iov, int iovcnt
state_ = State::FAILED;
HELPER_LOG("Socket write failed with errno %d", errno);
return APIError::SOCKET_WRITE_FAILED;
} else if (sent != total_write_len) {
} else if ((size_t) sent != total_write_len) {
// partially sent, add end to tx_buf
size_t to_consume = sent;
for (int i = 0; i < iovcnt; i++) {

View File

@@ -1,7 +1,8 @@
#pragma once
#include <cstdint>
#include <vector>
#include <deque>
#include <utility>
#include <vector>
#include "esphome/core/defines.h"
@@ -52,6 +53,7 @@ enum class APIError : int {
HANDSHAKESTATE_SETUP_FAILED = 1019,
HANDSHAKESTATE_SPLIT_FAILED = 1020,
BAD_HANDSHAKE_ERROR_BYTE = 1021,
CONNECTION_CLOSED = 1022,
};
const char *api_error_to_str(APIError err);
@@ -75,8 +77,8 @@ class APIFrameHelper {
class APINoiseFrameHelper : public APIFrameHelper {
public:
APINoiseFrameHelper(std::unique_ptr<socket::Socket> socket, std::shared_ptr<APINoiseContext> ctx)
: socket_(std::move(socket)), ctx_(ctx) {}
~APINoiseFrameHelper();
: socket_(std::move(socket)), ctx_(std::move(std::move(ctx))) {}
~APINoiseFrameHelper() override;
APIError init() override;
APIError loop() override;
APIError read_packet(ReadPacketBuffer *buffer) override;
@@ -136,7 +138,7 @@ class APINoiseFrameHelper : public APIFrameHelper {
class APIPlaintextFrameHelper : public APIFrameHelper {
public:
APIPlaintextFrameHelper(std::unique_ptr<socket::Socket> socket) : socket_(std::move(socket)) {}
~APIPlaintextFrameHelper() = default;
~APIPlaintextFrameHelper() override = default;
APIError init() override;
APIError loop() override;
APIError read_packet(ReadPacketBuffer *buffer) override;

View File

@@ -11,7 +11,7 @@ using psk_t = std::array<uint8_t, 32>;
class APINoiseContext {
public:
void set_psk(psk_t psk) { psk_ = std::move(psk); }
void set_psk(psk_t psk) { psk_ = psk; }
const psk_t &get_psk() const { return psk_; }
protected:

View File

@@ -6,6 +6,18 @@
namespace esphome {
namespace api {
template<> const char *proto_enum_to_string<enums::EntityCategory>(enums::EntityCategory value) {
switch (value) {
case enums::ENTITY_CATEGORY_NONE:
return "ENTITY_CATEGORY_NONE";
case enums::ENTITY_CATEGORY_CONFIG:
return "ENTITY_CATEGORY_CONFIG";
case enums::ENTITY_CATEGORY_DIAGNOSTIC:
return "ENTITY_CATEGORY_DIAGNOSTIC";
default:
return "UNKNOWN";
}
}
template<> const char *proto_enum_to_string<enums::LegacyCoverState>(enums::LegacyCoverState value) {
switch (value) {
case enums::LEGACY_COVER_STATE_OPEN:
@@ -254,6 +266,18 @@ template<> const char *proto_enum_to_string<enums::ClimatePreset>(enums::Climate
return "UNKNOWN";
}
}
template<> const char *proto_enum_to_string<enums::NumberMode>(enums::NumberMode value) {
switch (value) {
case enums::NUMBER_MODE_AUTO:
return "NUMBER_MODE_AUTO";
case enums::NUMBER_MODE_BOX:
return "NUMBER_MODE_BOX";
case enums::NUMBER_MODE_SLIDER:
return "NUMBER_MODE_SLIDER";
default:
return "UNKNOWN";
}
}
bool HelloRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 1: {
@@ -267,7 +291,7 @@ bool HelloRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value)
void HelloRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->client_info); }
#ifdef HAS_PROTO_MESSAGE_DUMP
void HelloRequest::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("HelloRequest {\n");
out.append(" client_info: ");
out.append("'").append(this->client_info).append("'");
@@ -306,7 +330,7 @@ void HelloResponse::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void HelloResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("HelloResponse {\n");
out.append(" api_version_major: ");
sprintf(buffer, "%u", this->api_version_major);
@@ -337,7 +361,7 @@ bool ConnectRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value
void ConnectRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->password); }
#ifdef HAS_PROTO_MESSAGE_DUMP
void ConnectRequest::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("ConnectRequest {\n");
out.append(" password: ");
out.append("'").append(this->password).append("'");
@@ -358,7 +382,7 @@ bool ConnectResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
void ConnectResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(1, this->invalid_password); }
#ifdef HAS_PROTO_MESSAGE_DUMP
void ConnectResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("ConnectResponse {\n");
out.append(" invalid_password: ");
out.append(YESNO(this->invalid_password));
@@ -396,6 +420,10 @@ bool DeviceInfoResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
this->has_deep_sleep = value.as_bool();
return true;
}
case 10: {
this->webserver_port = value.as_uint32();
return true;
}
default:
return false;
}
@@ -444,10 +472,11 @@ void DeviceInfoResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(7, this->has_deep_sleep);
buffer.encode_string(8, this->project_name);
buffer.encode_string(9, this->project_version);
buffer.encode_uint32(10, this->webserver_port);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void DeviceInfoResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("DeviceInfoResponse {\n");
out.append(" uses_password: ");
out.append(YESNO(this->uses_password));
@@ -484,6 +513,11 @@ void DeviceInfoResponse::dump_to(std::string &out) const {
out.append(" project_version: ");
out.append("'").append(this->project_version).append("'");
out.append("\n");
out.append(" webserver_port: ");
sprintf(buffer, "%u", this->webserver_port);
out.append(buffer);
out.append("\n");
out.append("}");
}
#endif
@@ -509,6 +543,10 @@ bool ListEntitiesBinarySensorResponse::decode_varint(uint32_t field_id, ProtoVar
this->disabled_by_default = value.as_bool();
return true;
}
case 9: {
this->entity_category = value.as_enum<enums::EntityCategory>();
return true;
}
default:
return false;
}
@@ -531,6 +569,10 @@ bool ListEntitiesBinarySensorResponse::decode_length(uint32_t field_id, ProtoLen
this->device_class = value.as_string();
return true;
}
case 8: {
this->icon = value.as_string();
return true;
}
default:
return false;
}
@@ -553,10 +595,12 @@ void ListEntitiesBinarySensorResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(5, this->device_class);
buffer.encode_bool(6, this->is_status_binary_sensor);
buffer.encode_bool(7, this->disabled_by_default);
buffer.encode_string(8, this->icon);
buffer.encode_enum<enums::EntityCategory>(9, this->entity_category);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesBinarySensorResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("ListEntitiesBinarySensorResponse {\n");
out.append(" object_id: ");
out.append("'").append(this->object_id).append("'");
@@ -586,6 +630,14 @@ void ListEntitiesBinarySensorResponse::dump_to(std::string &out) const {
out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default));
out.append("\n");
out.append(" icon: ");
out.append("'").append(this->icon).append("'");
out.append("\n");
out.append(" entity_category: ");
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
out.append("\n");
out.append("}");
}
#endif
@@ -620,7 +672,7 @@ void BinarySensorStateResponse::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void BinarySensorStateResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("BinarySensorStateResponse {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
@@ -655,6 +707,10 @@ bool ListEntitiesCoverResponse::decode_varint(uint32_t field_id, ProtoVarInt val
this->disabled_by_default = value.as_bool();
return true;
}
case 11: {
this->entity_category = value.as_enum<enums::EntityCategory>();
return true;
}
default:
return false;
}
@@ -677,6 +733,10 @@ bool ListEntitiesCoverResponse::decode_length(uint32_t field_id, ProtoLengthDeli
this->device_class = value.as_string();
return true;
}
case 10: {
this->icon = value.as_string();
return true;
}
default:
return false;
}
@@ -701,10 +761,12 @@ void ListEntitiesCoverResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(7, this->supports_tilt);
buffer.encode_string(8, this->device_class);
buffer.encode_bool(9, this->disabled_by_default);
buffer.encode_string(10, this->icon);
buffer.encode_enum<enums::EntityCategory>(11, this->entity_category);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesCoverResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("ListEntitiesCoverResponse {\n");
out.append(" object_id: ");
out.append("'").append(this->object_id).append("'");
@@ -742,6 +804,14 @@ void ListEntitiesCoverResponse::dump_to(std::string &out) const {
out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default));
out.append("\n");
out.append(" icon: ");
out.append("'").append(this->icon).append("'");
out.append("\n");
out.append(" entity_category: ");
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
out.append("\n");
out.append("}");
}
#endif
@@ -786,7 +856,7 @@ void CoverStateResponse::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void CoverStateResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("CoverStateResponse {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
@@ -869,7 +939,7 @@ void CoverCommandRequest::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void CoverCommandRequest::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("CoverCommandRequest {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
@@ -930,6 +1000,10 @@ bool ListEntitiesFanResponse::decode_varint(uint32_t field_id, ProtoVarInt value
this->disabled_by_default = value.as_bool();
return true;
}
case 11: {
this->entity_category = value.as_enum<enums::EntityCategory>();
return true;
}
default:
return false;
}
@@ -948,6 +1022,10 @@ bool ListEntitiesFanResponse::decode_length(uint32_t field_id, ProtoLengthDelimi
this->unique_id = value.as_string();
return true;
}
case 10: {
this->icon = value.as_string();
return true;
}
default:
return false;
}
@@ -972,10 +1050,12 @@ void ListEntitiesFanResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(7, this->supports_direction);
buffer.encode_int32(8, this->supported_speed_count);
buffer.encode_bool(9, this->disabled_by_default);
buffer.encode_string(10, this->icon);
buffer.encode_enum<enums::EntityCategory>(11, this->entity_category);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesFanResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("ListEntitiesFanResponse {\n");
out.append(" object_id: ");
out.append("'").append(this->object_id).append("'");
@@ -1014,6 +1094,14 @@ void ListEntitiesFanResponse::dump_to(std::string &out) const {
out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default));
out.append("\n");
out.append(" icon: ");
out.append("'").append(this->icon).append("'");
out.append("\n");
out.append(" entity_category: ");
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
out.append("\n");
out.append("}");
}
#endif
@@ -1063,7 +1151,7 @@ void FanStateResponse::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void FanStateResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("FanStateResponse {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
@@ -1164,7 +1252,7 @@ void FanCommandRequest::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void FanCommandRequest::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("FanCommandRequest {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
@@ -1240,6 +1328,10 @@ bool ListEntitiesLightResponse::decode_varint(uint32_t field_id, ProtoVarInt val
this->disabled_by_default = value.as_bool();
return true;
}
case 15: {
this->entity_category = value.as_enum<enums::EntityCategory>();
return true;
}
default:
return false;
}
@@ -1262,6 +1354,10 @@ bool ListEntitiesLightResponse::decode_length(uint32_t field_id, ProtoLengthDeli
this->effects.push_back(value.as_string());
return true;
}
case 14: {
this->icon = value.as_string();
return true;
}
default:
return false;
}
@@ -1302,10 +1398,12 @@ void ListEntitiesLightResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(11, it, true);
}
buffer.encode_bool(13, this->disabled_by_default);
buffer.encode_string(14, this->icon);
buffer.encode_enum<enums::EntityCategory>(15, this->entity_category);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesLightResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("ListEntitiesLightResponse {\n");
out.append(" object_id: ");
out.append("'").append(this->object_id).append("'");
@@ -1365,6 +1463,14 @@ void ListEntitiesLightResponse::dump_to(std::string &out) const {
out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default));
out.append("\n");
out.append(" icon: ");
out.append("'").append(this->icon).append("'");
out.append("\n");
out.append(" entity_category: ");
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
out.append("\n");
out.append("}");
}
#endif
@@ -1455,7 +1561,7 @@ void LightStateResponse::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void LightStateResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("LightStateResponse {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
@@ -1678,7 +1784,7 @@ void LightCommandRequest::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void LightCommandRequest::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("LightCommandRequest {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
@@ -1824,6 +1930,10 @@ bool ListEntitiesSensorResponse::decode_varint(uint32_t field_id, ProtoVarInt va
this->disabled_by_default = value.as_bool();
return true;
}
case 13: {
this->entity_category = value.as_enum<enums::EntityCategory>();
return true;
}
default:
return false;
}
@@ -1881,10 +1991,11 @@ void ListEntitiesSensorResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_enum<enums::SensorStateClass>(10, this->state_class);
buffer.encode_enum<enums::SensorLastResetType>(11, this->legacy_last_reset_type);
buffer.encode_bool(12, this->disabled_by_default);
buffer.encode_enum<enums::EntityCategory>(13, this->entity_category);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesSensorResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("ListEntitiesSensorResponse {\n");
out.append(" object_id: ");
out.append("'").append(this->object_id).append("'");
@@ -1935,6 +2046,10 @@ void ListEntitiesSensorResponse::dump_to(std::string &out) const {
out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default));
out.append("\n");
out.append(" entity_category: ");
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
out.append("\n");
out.append("}");
}
#endif
@@ -1969,7 +2084,7 @@ void SensorStateResponse::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void SensorStateResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("SensorStateResponse {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
@@ -1997,6 +2112,10 @@ bool ListEntitiesSwitchResponse::decode_varint(uint32_t field_id, ProtoVarInt va
this->disabled_by_default = value.as_bool();
return true;
}
case 8: {
this->entity_category = value.as_enum<enums::EntityCategory>();
return true;
}
default:
return false;
}
@@ -2041,10 +2160,11 @@ void ListEntitiesSwitchResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(5, this->icon);
buffer.encode_bool(6, this->assumed_state);
buffer.encode_bool(7, this->disabled_by_default);
buffer.encode_enum<enums::EntityCategory>(8, this->entity_category);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesSwitchResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("ListEntitiesSwitchResponse {\n");
out.append(" object_id: ");
out.append("'").append(this->object_id).append("'");
@@ -2074,6 +2194,10 @@ void ListEntitiesSwitchResponse::dump_to(std::string &out) const {
out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default));
out.append("\n");
out.append(" entity_category: ");
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
out.append("\n");
out.append("}");
}
#endif
@@ -2103,7 +2227,7 @@ void SwitchStateResponse::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void SwitchStateResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("SwitchStateResponse {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
@@ -2142,7 +2266,7 @@ void SwitchCommandRequest::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void SwitchCommandRequest::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("SwitchCommandRequest {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
@@ -2161,6 +2285,10 @@ bool ListEntitiesTextSensorResponse::decode_varint(uint32_t field_id, ProtoVarIn
this->disabled_by_default = value.as_bool();
return true;
}
case 7: {
this->entity_category = value.as_enum<enums::EntityCategory>();
return true;
}
default:
return false;
}
@@ -2204,10 +2332,11 @@ void ListEntitiesTextSensorResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(4, this->unique_id);
buffer.encode_string(5, this->icon);
buffer.encode_bool(6, this->disabled_by_default);
buffer.encode_enum<enums::EntityCategory>(7, this->entity_category);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesTextSensorResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("ListEntitiesTextSensorResponse {\n");
out.append(" object_id: ");
out.append("'").append(this->object_id).append("'");
@@ -2233,6 +2362,10 @@ void ListEntitiesTextSensorResponse::dump_to(std::string &out) const {
out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default));
out.append("\n");
out.append(" entity_category: ");
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
out.append("\n");
out.append("}");
}
#endif
@@ -2273,7 +2406,7 @@ void TextSensorStateResponse::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void TextSensorStateResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("TextSensorStateResponse {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
@@ -2310,7 +2443,7 @@ void SubscribeLogsRequest::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void SubscribeLogsRequest::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("SubscribeLogsRequest {\n");
out.append(" level: ");
out.append(proto_enum_to_string<enums::LogLevel>(this->level));
@@ -2353,7 +2486,7 @@ void SubscribeLogsResponse::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void SubscribeLogsResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("SubscribeLogsResponse {\n");
out.append(" level: ");
out.append(proto_enum_to_string<enums::LogLevel>(this->level));
@@ -2395,7 +2528,7 @@ void HomeassistantServiceMap::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void HomeassistantServiceMap::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("HomeassistantServiceMap {\n");
out.append(" key: ");
out.append("'").append(this->key).append("'");
@@ -2454,7 +2587,7 @@ void HomeassistantServiceResponse::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void HomeassistantServiceResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("HomeassistantServiceResponse {\n");
out.append(" service: ");
out.append("'").append(this->service).append("'");
@@ -2510,7 +2643,7 @@ void SubscribeHomeAssistantStateResponse::encode(ProtoWriteBuffer buffer) const
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void SubscribeHomeAssistantStateResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("SubscribeHomeAssistantStateResponse {\n");
out.append(" entity_id: ");
out.append("'").append(this->entity_id).append("'");
@@ -2547,7 +2680,7 @@ void HomeAssistantStateResponse::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void HomeAssistantStateResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("HomeAssistantStateResponse {\n");
out.append(" entity_id: ");
out.append("'").append(this->entity_id).append("'");
@@ -2580,7 +2713,7 @@ bool GetTimeResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
void GetTimeResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->epoch_seconds); }
#ifdef HAS_PROTO_MESSAGE_DUMP
void GetTimeResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("GetTimeResponse {\n");
out.append(" epoch_seconds: ");
sprintf(buffer, "%u", this->epoch_seconds);
@@ -2615,7 +2748,7 @@ void ListEntitiesServicesArgument::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesServicesArgument::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("ListEntitiesServicesArgument {\n");
out.append(" name: ");
out.append("'").append(this->name).append("'");
@@ -2660,7 +2793,7 @@ void ListEntitiesServicesResponse::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesServicesResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("ListEntitiesServicesResponse {\n");
out.append(" name: ");
out.append("'").append(this->name).append("'");
@@ -2754,7 +2887,7 @@ void ExecuteServiceArgument::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ExecuteServiceArgument::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("ExecuteServiceArgument {\n");
out.append(" bool_: ");
out.append(YESNO(this->bool_));
@@ -2835,7 +2968,7 @@ void ExecuteServiceRequest::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ExecuteServiceRequest::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("ExecuteServiceRequest {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
@@ -2856,6 +2989,10 @@ bool ListEntitiesCameraResponse::decode_varint(uint32_t field_id, ProtoVarInt va
this->disabled_by_default = value.as_bool();
return true;
}
case 7: {
this->entity_category = value.as_enum<enums::EntityCategory>();
return true;
}
default:
return false;
}
@@ -2874,6 +3011,10 @@ bool ListEntitiesCameraResponse::decode_length(uint32_t field_id, ProtoLengthDel
this->unique_id = value.as_string();
return true;
}
case 6: {
this->icon = value.as_string();
return true;
}
default:
return false;
}
@@ -2894,10 +3035,12 @@ void ListEntitiesCameraResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(3, this->name);
buffer.encode_string(4, this->unique_id);
buffer.encode_bool(5, this->disabled_by_default);
buffer.encode_string(6, this->icon);
buffer.encode_enum<enums::EntityCategory>(7, this->entity_category);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesCameraResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("ListEntitiesCameraResponse {\n");
out.append(" object_id: ");
out.append("'").append(this->object_id).append("'");
@@ -2919,6 +3062,14 @@ void ListEntitiesCameraResponse::dump_to(std::string &out) const {
out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default));
out.append("\n");
out.append(" icon: ");
out.append("'").append(this->icon).append("'");
out.append("\n");
out.append(" entity_category: ");
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
out.append("\n");
out.append("}");
}
#endif
@@ -2959,7 +3110,7 @@ void CameraImageResponse::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void CameraImageResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("CameraImageResponse {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
@@ -2996,7 +3147,7 @@ void CameraImageRequest::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void CameraImageRequest::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("CameraImageRequest {\n");
out.append(" single: ");
out.append(YESNO(this->single));
@@ -3046,6 +3197,10 @@ bool ListEntitiesClimateResponse::decode_varint(uint32_t field_id, ProtoVarInt v
this->disabled_by_default = value.as_bool();
return true;
}
case 20: {
this->entity_category = value.as_enum<enums::EntityCategory>();
return true;
}
default:
return false;
}
@@ -3072,6 +3227,10 @@ bool ListEntitiesClimateResponse::decode_length(uint32_t field_id, ProtoLengthDe
this->supported_custom_presets.push_back(value.as_string());
return true;
}
case 19: {
this->icon = value.as_string();
return true;
}
default:
return false;
}
@@ -3129,10 +3288,12 @@ void ListEntitiesClimateResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(17, it, true);
}
buffer.encode_bool(18, this->disabled_by_default);
buffer.encode_string(19, this->icon);
buffer.encode_enum<enums::EntityCategory>(20, this->entity_category);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesClimateResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("ListEntitiesClimateResponse {\n");
out.append(" object_id: ");
out.append("'").append(this->object_id).append("'");
@@ -3221,6 +3382,14 @@ void ListEntitiesClimateResponse::dump_to(std::string &out) const {
out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default));
out.append("\n");
out.append(" icon: ");
out.append("'").append(this->icon).append("'");
out.append("\n");
out.append(" entity_category: ");
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
out.append("\n");
out.append("}");
}
#endif
@@ -3311,7 +3480,7 @@ void ClimateStateResponse::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ClimateStateResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("ClimateStateResponse {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
@@ -3499,7 +3668,7 @@ void ClimateCommandRequest::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ClimateCommandRequest::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("ClimateCommandRequest {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
@@ -3597,6 +3766,14 @@ bool ListEntitiesNumberResponse::decode_varint(uint32_t field_id, ProtoVarInt va
this->disabled_by_default = value.as_bool();
return true;
}
case 10: {
this->entity_category = value.as_enum<enums::EntityCategory>();
return true;
}
case 12: {
this->mode = value.as_enum<enums::NumberMode>();
return true;
}
default:
return false;
}
@@ -3619,6 +3796,10 @@ bool ListEntitiesNumberResponse::decode_length(uint32_t field_id, ProtoLengthDel
this->icon = value.as_string();
return true;
}
case 11: {
this->unit_of_measurement = value.as_string();
return true;
}
default:
return false;
}
@@ -3655,10 +3836,13 @@ void ListEntitiesNumberResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_float(7, this->max_value);
buffer.encode_float(8, this->step);
buffer.encode_bool(9, this->disabled_by_default);
buffer.encode_enum<enums::EntityCategory>(10, this->entity_category);
buffer.encode_string(11, this->unit_of_measurement);
buffer.encode_enum<enums::NumberMode>(12, this->mode);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesNumberResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("ListEntitiesNumberResponse {\n");
out.append(" object_id: ");
out.append("'").append(this->object_id).append("'");
@@ -3699,6 +3883,18 @@ void ListEntitiesNumberResponse::dump_to(std::string &out) const {
out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default));
out.append("\n");
out.append(" entity_category: ");
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
out.append("\n");
out.append(" unit_of_measurement: ");
out.append("'").append(this->unit_of_measurement).append("'");
out.append("\n");
out.append(" mode: ");
out.append(proto_enum_to_string<enums::NumberMode>(this->mode));
out.append("\n");
out.append("}");
}
#endif
@@ -3733,7 +3929,7 @@ void NumberStateResponse::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void NumberStateResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("NumberStateResponse {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
@@ -3771,7 +3967,7 @@ void NumberCommandRequest::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void NumberCommandRequest::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("NumberCommandRequest {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
@@ -3791,6 +3987,10 @@ bool ListEntitiesSelectResponse::decode_varint(uint32_t field_id, ProtoVarInt va
this->disabled_by_default = value.as_bool();
return true;
}
case 8: {
this->entity_category = value.as_enum<enums::EntityCategory>();
return true;
}
default:
return false;
}
@@ -3841,10 +4041,11 @@ void ListEntitiesSelectResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(6, it, true);
}
buffer.encode_bool(7, this->disabled_by_default);
buffer.encode_enum<enums::EntityCategory>(8, this->entity_category);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesSelectResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("ListEntitiesSelectResponse {\n");
out.append(" object_id: ");
out.append("'").append(this->object_id).append("'");
@@ -3876,6 +4077,10 @@ void ListEntitiesSelectResponse::dump_to(std::string &out) const {
out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default));
out.append("\n");
out.append(" entity_category: ");
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
out.append("\n");
out.append("}");
}
#endif
@@ -3916,7 +4121,7 @@ void SelectStateResponse::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void SelectStateResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("SelectStateResponse {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
@@ -3959,7 +4164,7 @@ void SelectCommandRequest::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void SelectCommandRequest::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("SelectCommandRequest {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
@@ -3972,6 +4177,127 @@ void SelectCommandRequest::dump_to(std::string &out) const {
out.append("}");
}
#endif
bool ListEntitiesButtonResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 6: {
this->disabled_by_default = value.as_bool();
return true;
}
case 7: {
this->entity_category = value.as_enum<enums::EntityCategory>();
return true;
}
default:
return false;
}
}
bool ListEntitiesButtonResponse::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;
}
case 8: {
this->device_class = value.as_string();
return true;
}
default:
return false;
}
}
bool ListEntitiesButtonResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
switch (field_id) {
case 2: {
this->key = value.as_fixed32();
return true;
}
default:
return false;
}
}
void ListEntitiesButtonResponse::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_bool(6, this->disabled_by_default);
buffer.encode_enum<enums::EntityCategory>(7, this->entity_category);
buffer.encode_string(8, this->device_class);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesButtonResponse::dump_to(std::string &out) const {
char buffer[64];
out.append("ListEntitiesButtonResponse {\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(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default));
out.append("\n");
out.append(" entity_category: ");
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
out.append("\n");
out.append(" device_class: ");
out.append("'").append(this->device_class).append("'");
out.append("\n");
out.append("}");
}
#endif
bool ButtonCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
switch (field_id) {
case 1: {
this->key = value.as_fixed32();
return true;
}
default:
return false;
}
}
void ButtonCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); }
#ifdef HAS_PROTO_MESSAGE_DUMP
void ButtonCommandRequest::dump_to(std::string &out) const {
char buffer[64];
out.append("ButtonCommandRequest {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
out.append(buffer);
out.append("\n");
out.append("}");
}
#endif
} // namespace api
} // namespace esphome

View File

@@ -9,6 +9,11 @@ namespace api {
namespace enums {
enum EntityCategory : uint32_t {
ENTITY_CATEGORY_NONE = 0,
ENTITY_CATEGORY_CONFIG = 1,
ENTITY_CATEGORY_DIAGNOSTIC = 2,
};
enum LegacyCoverState : uint32_t {
LEGACY_COVER_STATE_OPEN = 0,
LEGACY_COVER_STATE_CLOSED = 1,
@@ -118,6 +123,11 @@ enum ClimatePreset : uint32_t {
CLIMATE_PRESET_SLEEP = 6,
CLIMATE_PRESET_ACTIVITY = 7,
};
enum NumberMode : uint32_t {
NUMBER_MODE_AUTO = 0,
NUMBER_MODE_BOX = 1,
NUMBER_MODE_SLIDER = 2,
};
} // namespace enums
@@ -224,6 +234,7 @@ class DeviceInfoResponse : public ProtoMessage {
bool has_deep_sleep{false};
std::string project_name{};
std::string project_version{};
uint32_t webserver_port{0};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
@@ -269,6 +280,8 @@ class ListEntitiesBinarySensorResponse : public ProtoMessage {
std::string device_class{};
bool is_status_binary_sensor{false};
bool disabled_by_default{false};
std::string icon{};
enums::EntityCategory entity_category{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
@@ -304,6 +317,8 @@ class ListEntitiesCoverResponse : public ProtoMessage {
bool supports_tilt{false};
std::string device_class{};
bool disabled_by_default{false};
std::string icon{};
enums::EntityCategory entity_category{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
@@ -360,6 +375,8 @@ class ListEntitiesFanResponse : public ProtoMessage {
bool supports_direction{false};
int32_t supported_speed_count{0};
bool disabled_by_default{false};
std::string icon{};
enums::EntityCategory entity_category{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
@@ -424,6 +441,8 @@ class ListEntitiesLightResponse : public ProtoMessage {
float max_mireds{0.0f};
std::vector<std::string> effects{};
bool disabled_by_default{false};
std::string icon{};
enums::EntityCategory entity_category{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
@@ -512,6 +531,7 @@ class ListEntitiesSensorResponse : public ProtoMessage {
enums::SensorStateClass state_class{};
enums::SensorLastResetType legacy_last_reset_type{};
bool disabled_by_default{false};
enums::EntityCategory entity_category{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
@@ -545,6 +565,7 @@ class ListEntitiesSwitchResponse : public ProtoMessage {
std::string icon{};
bool assumed_state{false};
bool disabled_by_default{false};
enums::EntityCategory entity_category{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
@@ -589,6 +610,7 @@ class ListEntitiesTextSensorResponse : public ProtoMessage {
std::string unique_id{};
std::string icon{};
bool disabled_by_default{false};
enums::EntityCategory entity_category{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
@@ -799,6 +821,8 @@ class ListEntitiesCameraResponse : public ProtoMessage {
std::string name{};
std::string unique_id{};
bool disabled_by_default{false};
std::string icon{};
enums::EntityCategory entity_category{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
@@ -856,6 +880,8 @@ class ListEntitiesClimateResponse : public ProtoMessage {
std::vector<enums::ClimatePreset> supported_presets{};
std::vector<std::string> supported_custom_presets{};
bool disabled_by_default{false};
std::string icon{};
enums::EntityCategory entity_category{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
@@ -935,6 +961,9 @@ class ListEntitiesNumberResponse : public ProtoMessage {
float max_value{0.0f};
float step{0.0f};
bool disabled_by_default{false};
enums::EntityCategory entity_category{};
std::string unit_of_measurement{};
enums::NumberMode mode{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
@@ -980,6 +1009,7 @@ class ListEntitiesSelectResponse : public ProtoMessage {
std::string icon{};
std::vector<std::string> options{};
bool disabled_by_default{false};
enums::EntityCategory entity_category{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
@@ -1018,6 +1048,37 @@ class SelectCommandRequest : public ProtoMessage {
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
};
class ListEntitiesButtonResponse : public ProtoMessage {
public:
std::string object_id{};
uint32_t key{0};
std::string name{};
std::string unique_id{};
std::string icon{};
bool disabled_by_default{false};
enums::EntityCategory entity_category{};
std::string device_class{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class ButtonCommandRequest : public ProtoMessage {
public:
uint32_t key{0};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
};
} // namespace api
} // namespace esphome

View File

@@ -282,6 +282,16 @@ bool APIServerConnectionBase::send_select_state_response(const SelectStateRespon
#endif
#ifdef USE_SELECT
#endif
#ifdef USE_BUTTON
bool APIServerConnectionBase::send_list_entities_button_response(const ListEntitiesButtonResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_button_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesButtonResponse>(msg, 61);
}
#endif
#ifdef USE_BUTTON
#endif
bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {
switch (msg_type) {
case 1: {
@@ -513,6 +523,17 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
ESP_LOGVV(TAG, "on_select_command_request: %s", msg.dump().c_str());
#endif
this->on_select_command_request(msg);
#endif
break;
}
case 62: {
#ifdef USE_BUTTON
ButtonCommandRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_button_command_request: %s", msg.dump().c_str());
#endif
this->on_button_command_request(msg);
#endif
break;
}
@@ -737,6 +758,19 @@ void APIServerConnection::on_select_command_request(const SelectCommandRequest &
this->select_command(msg);
}
#endif
#ifdef USE_BUTTON
void APIServerConnection::on_button_command_request(const ButtonCommandRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->button_command(msg);
}
#endif
} // namespace api
} // namespace esphome

View File

@@ -129,6 +129,12 @@ class APIServerConnectionBase : public ProtoService {
#endif
#ifdef USE_SELECT
virtual void on_select_command_request(const SelectCommandRequest &value){};
#endif
#ifdef USE_BUTTON
bool send_list_entities_button_response(const ListEntitiesButtonResponse &msg);
#endif
#ifdef USE_BUTTON
virtual void on_button_command_request(const ButtonCommandRequest &value){};
#endif
protected:
bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;
@@ -171,6 +177,9 @@ class APIServerConnection : public APIServerConnectionBase {
#endif
#ifdef USE_SELECT
virtual void select_command(const SelectCommandRequest &msg) = 0;
#endif
#ifdef USE_BUTTON
virtual void button_command(const ButtonCommandRequest &msg) = 0;
#endif
protected:
void on_hello_request(const HelloRequest &msg) override;
@@ -209,6 +218,9 @@ class APIServerConnection : public APIServerConnectionBase {
#ifdef USE_SELECT
void on_select_command_request(const SelectCommandRequest &msg) override;
#endif
#ifdef USE_BUTTON
void on_button_command_request(const ButtonCommandRequest &msg) override;
#endif
};
} // namespace api

View File

@@ -5,6 +5,8 @@
#include "esphome/core/log.h"
#include "esphome/core/util.h"
#include "esphome/core/version.h"
#include "esphome/core/hal.h"
#include "esphome/components/network/util.h"
#include <cerrno>
#ifdef USE_LOGGER
@@ -64,7 +66,7 @@ void APIServer::setup() {
#ifdef USE_LOGGER
if (logger::global_logger != nullptr) {
logger::global_logger->add_on_log_callback([this](int level, const char *tag, const char *message) {
for (auto *c : this->clients_) {
for (auto &c : this->clients_) {
if (!c->remove_)
c->send_log_message(level, tag, message);
}
@@ -75,12 +77,13 @@ void APIServer::setup() {
this->last_connected_ = millis();
#ifdef USE_ESP32_CAMERA
if (esp32_camera::global_esp32_camera != nullptr) {
esp32_camera::global_esp32_camera->add_image_callback([this](std::shared_ptr<esp32_camera::CameraImage> image) {
for (auto *c : this->clients_)
if (!c->remove_)
c->send_camera_state(image);
});
if (esp32_camera::global_esp32_camera != nullptr && !esp32_camera::global_esp32_camera->is_internal()) {
esp32_camera::global_esp32_camera->add_image_callback(
[this](const std::shared_ptr<esp32_camera::CameraImage> &image) {
for (auto &c : this->clients_)
if (!c->remove_)
c->send_camera_state(image);
});
}
#endif
}
@@ -95,25 +98,21 @@ void APIServer::loop() {
ESP_LOGD(TAG, "Accepted %s", sock->getpeername().c_str());
auto *conn = new APIConnection(std::move(sock), this);
clients_.push_back(conn);
clients_.emplace_back(conn);
conn->start();
}
// Partition clients into remove and active
auto new_end =
std::partition(this->clients_.begin(), this->clients_.end(), [](APIConnection *conn) { return !conn->remove_; });
auto new_end = std::partition(this->clients_.begin(), this->clients_.end(),
[](const std::unique_ptr<APIConnection> &conn) { return !conn->remove_; });
// print disconnection messages
for (auto it = new_end; it != this->clients_.end(); ++it) {
ESP_LOGV(TAG, "Removing connection to %s", (*it)->client_info_.c_str());
}
// only then delete the pointers, otherwise log routine
// would access freed memory
for (auto it = new_end; it != this->clients_.end(); ++it)
delete *it;
// resize vector
this->clients_.erase(new_end, this->clients_.end());
for (auto *client : this->clients_) {
for (auto &client : this->clients_) {
client->loop();
}
@@ -133,7 +132,7 @@ void APIServer::loop() {
}
void APIServer::dump_config() {
ESP_LOGCONFIG(TAG, "API Server:");
ESP_LOGCONFIG(TAG, " Address: %s:%u", network_get_address().c_str(), this->port_);
ESP_LOGCONFIG(TAG, " Address: %s:%u", network::get_use_address().c_str(), this->port_);
#ifdef USE_API_NOISE
ESP_LOGCONFIG(TAG, " Using noise encryption: YES");
#else
@@ -174,7 +173,7 @@ void APIServer::handle_disconnect(APIConnection *conn) {}
void APIServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state) {
if (obj->is_internal())
return;
for (auto *c : this->clients_)
for (auto &c : this->clients_)
c->send_binary_sensor_state(obj, state);
}
#endif
@@ -183,7 +182,7 @@ void APIServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool s
void APIServer::on_cover_update(cover::Cover *obj) {
if (obj->is_internal())
return;
for (auto *c : this->clients_)
for (auto &c : this->clients_)
c->send_cover_state(obj);
}
#endif
@@ -192,7 +191,7 @@ void APIServer::on_cover_update(cover::Cover *obj) {
void APIServer::on_fan_update(fan::FanState *obj) {
if (obj->is_internal())
return;
for (auto *c : this->clients_)
for (auto &c : this->clients_)
c->send_fan_state(obj);
}
#endif
@@ -201,7 +200,7 @@ void APIServer::on_fan_update(fan::FanState *obj) {
void APIServer::on_light_update(light::LightState *obj) {
if (obj->is_internal())
return;
for (auto *c : this->clients_)
for (auto &c : this->clients_)
c->send_light_state(obj);
}
#endif
@@ -210,7 +209,7 @@ void APIServer::on_light_update(light::LightState *obj) {
void APIServer::on_sensor_update(sensor::Sensor *obj, float state) {
if (obj->is_internal())
return;
for (auto *c : this->clients_)
for (auto &c : this->clients_)
c->send_sensor_state(obj, state);
}
#endif
@@ -219,7 +218,7 @@ void APIServer::on_sensor_update(sensor::Sensor *obj, float state) {
void APIServer::on_switch_update(switch_::Switch *obj, bool state) {
if (obj->is_internal())
return;
for (auto *c : this->clients_)
for (auto &c : this->clients_)
c->send_switch_state(obj, state);
}
#endif
@@ -228,7 +227,7 @@ void APIServer::on_switch_update(switch_::Switch *obj, bool state) {
void APIServer::on_text_sensor_update(text_sensor::TextSensor *obj, const std::string &state) {
if (obj->is_internal())
return;
for (auto *c : this->clients_)
for (auto &c : this->clients_)
c->send_text_sensor_state(obj, state);
}
#endif
@@ -237,7 +236,7 @@ void APIServer::on_text_sensor_update(text_sensor::TextSensor *obj, const std::s
void APIServer::on_climate_update(climate::Climate *obj) {
if (obj->is_internal())
return;
for (auto *c : this->clients_)
for (auto &c : this->clients_)
c->send_climate_state(obj);
}
#endif
@@ -246,7 +245,7 @@ void APIServer::on_climate_update(climate::Climate *obj) {
void APIServer::on_number_update(number::Number *obj, float state) {
if (obj->is_internal())
return;
for (auto *c : this->clients_)
for (auto &c : this->clients_)
c->send_number_state(obj, state);
}
#endif
@@ -255,7 +254,7 @@ void APIServer::on_number_update(number::Number *obj, float state) {
void APIServer::on_select_update(select::Select *obj, const std::string &state) {
if (obj->is_internal())
return;
for (auto *c : this->clients_)
for (auto &c : this->clients_)
c->send_select_state(obj, state);
}
#endif
@@ -266,7 +265,7 @@ APIServer *global_api_server = nullptr; // NOLINT(cppcoreguidelines-avoid-non-c
void APIServer::set_password(const std::string &password) { this->password_ = password; }
void APIServer::send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
for (auto *client : this->clients_) {
for (auto &client : this->clients_) {
client->send_homeassistant_service_call(call);
}
}
@@ -286,7 +285,7 @@ uint16_t APIServer::get_port() const { return this->port_; }
void APIServer::set_reboot_timeout(uint32_t reboot_timeout) { this->reboot_timeout_ = reboot_timeout; }
#ifdef USE_HOMEASSISTANT_TIME
void APIServer::request_time() {
for (auto *client : this->clients_) {
for (auto &client : this->clients_) {
if (!client->remove_ && client->connection_state_ == APIConnection::ConnectionState::CONNECTED)
client->send_time_request();
}
@@ -294,7 +293,7 @@ void APIServer::request_time() {
#endif
bool APIServer::is_connected() const { return !this->clients_.empty(); }
void APIServer::on_shutdown() {
for (auto *c : this->clients_) {
for (auto &c : this->clients_) {
c->send_disconnect_request(DisconnectRequest());
}
delay(10);

View File

@@ -32,7 +32,7 @@ class APIServer : public Component, public Controller {
void set_reboot_timeout(uint32_t reboot_timeout);
#ifdef USE_API_NOISE
void set_noise_psk(psk_t psk) { noise_ctx_->set_psk(std::move(psk)); }
void set_noise_psk(psk_t psk) { noise_ctx_->set_psk(psk); }
std::shared_ptr<APINoiseContext> get_noise_ctx() { return noise_ctx_; }
#endif // USE_API_NOISE
@@ -91,7 +91,7 @@ class APIServer : public Component, public Controller {
uint16_t port_{6053};
uint32_t reboot_timeout_{300000};
uint32_t last_connected_{0};
std::vector<APIConnection *> clients_;
std::vector<std::unique_ptr<APIConnection>> clients_;
std::string password_;
std::vector<HomeAssistantStateSubscription> state_subs_;
std::vector<UserServiceDescriptor *> user_services_;

View File

@@ -23,7 +23,6 @@ async def async_run_logs(config, address):
_LOGGER.info("Starting log output from %s using esphome API", address)
zc = zeroconf.Zeroconf()
cli = APIClient(
asyncio.get_event_loop(),
address,
port,
password,

View File

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

View File

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

View File

@@ -27,6 +27,9 @@ bool ListEntitiesIterator::on_sensor(sensor::Sensor *sensor) { return this->clie
#ifdef USE_SWITCH
bool ListEntitiesIterator::on_switch(switch_::Switch *a_switch) { return this->client_->send_switch_info(a_switch); }
#endif
#ifdef USE_BUTTON
bool ListEntitiesIterator::on_button(button::Button *button) { return this->client_->send_button_info(button); }
#endif
#ifdef USE_TEXT_SENSOR
bool ListEntitiesIterator::on_text_sensor(text_sensor::TextSensor *text_sensor) {
return this->client_->send_text_sensor_info(text_sensor);

View File

@@ -30,6 +30,9 @@ class ListEntitiesIterator : public ComponentIterator {
#ifdef USE_SWITCH
bool on_switch(switch_::Switch *a_switch) override;
#endif
#ifdef USE_BUTTON
bool on_button(button::Button *button) override;
#endif
#ifdef USE_TEXT_SENSOR
bool on_text_sensor(text_sensor::TextSensor *text_sensor) override;
#endif

View File

@@ -246,6 +246,7 @@ class ProtoWriteBuffer {
class ProtoMessage {
public:
virtual ~ProtoMessage() = default;
virtual void encode(ProtoWriteBuffer buffer) const = 0;
void decode(const uint8_t *buffer, size_t length);
#ifdef HAS_PROTO_MESSAGE_DUMP

View File

@@ -31,6 +31,9 @@ class InitialStateIterator : public ComponentIterator {
#ifdef USE_SWITCH
bool on_switch(switch_::Switch *a_switch) override;
#endif
#ifdef USE_BUTTON
bool on_button(button::Button *button) override { return true; };
#endif
#ifdef USE_TEXT_SENSOR
bool on_text_sensor(text_sensor::TextSensor *text_sensor) override;
#endif

View File

@@ -116,6 +116,21 @@ void ComponentIterator::advance() {
}
break;
#endif
#ifdef USE_BUTTON
case IteratorState::BUTTON:
if (this->at_ >= App.get_buttons().size()) {
advance_platform = true;
} else {
auto *button = App.get_buttons()[this->at_];
if (button->is_internal()) {
success = true;
break;
} else {
success = this->on_button(button);
}
}
break;
#endif
#ifdef USE_TEXT_SENSOR
case IteratorState::TEXT_SENSOR:
if (this->at_ >= App.get_text_sensors().size()) {

View File

@@ -38,6 +38,9 @@ class ComponentIterator {
#ifdef USE_SWITCH
virtual bool on_switch(switch_::Switch *a_switch) = 0;
#endif
#ifdef USE_BUTTON
virtual bool on_button(button::Button *button) = 0;
#endif
#ifdef USE_TEXT_SENSOR
virtual bool on_text_sensor(text_sensor::TextSensor *text_sensor) = 0;
#endif
@@ -78,6 +81,9 @@ class ComponentIterator {
#ifdef USE_SWITCH
SWITCH,
#endif
#ifdef USE_BUTTON
BUTTON,
#endif
#ifdef USE_TEXT_SENSOR
TEXT_SENSOR,
#endif

View File

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

View File

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

View File

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

View File

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

View File

@@ -4,7 +4,7 @@
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#ifdef ARDUINO_ARCH_ESP32
#ifdef USE_ESP32
namespace esphome {
namespace atc_mithermometer {
@@ -14,7 +14,6 @@ struct ParseResult {
optional<float> humidity;
optional<float> battery_level;
optional<float> battery_voltage;
bool is_duplicate;
int raw_offset;
};
@@ -37,9 +36,9 @@ class ATCMiThermometer : public Component, public esp32_ble_tracker::ESPBTDevice
sensor::Sensor *battery_level_{nullptr};
sensor::Sensor *battery_voltage_{nullptr};
optional<ParseResult> parse_header(const esp32_ble_tracker::ServiceData &service_data);
bool parse_message(const std::vector<uint8_t> &message, ParseResult &result);
bool report_results(const optional<ParseResult> &result, const std::string &address);
optional<ParseResult> parse_header_(const esp32_ble_tracker::ServiceData &service_data);
bool parse_message_(const std::vector<uint8_t> &message, ParseResult &result);
bool report_results_(const optional<ParseResult> &result, const std::string &address);
};
} // namespace atc_mithermometer

View File

@@ -12,6 +12,7 @@ from esphome.const import (
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_VOLTAGE,
ENTITY_CATEGORY_DIAGNOSTIC,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
UNIT_PERCENT,
@@ -49,12 +50,14 @@ CONFIG_SCHEMA = (
accuracy_decimals=0,
device_class=DEVICE_CLASS_BATTERY,
state_class=STATE_CLASS_MEASUREMENT,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
),
cv.Optional(CONF_BATTERY_VOLTAGE): sensor.sensor_schema(
unit_of_measurement=UNIT_VOLT,
accuracy_decimals=3,
device_class=DEVICE_CLASS_VOLTAGE,
state_class=STATE_CLASS_MEASUREMENT,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
),
}
)

View File

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

View File

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

View File

@@ -17,6 +17,7 @@ from esphome.const import (
DEVICE_CLASS_POWER_FACTOR,
DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_VOLTAGE,
ENTITY_CATEGORY_DIAGNOSTIC,
ICON_LIGHTBULB,
ICON_CURRENT_AC,
STATE_CLASS_MEASUREMENT,
@@ -125,6 +126,7 @@ CONFIG_SCHEMA = (
accuracy_decimals=1,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
),
cv.Required(CONF_LINE_FREQUENCY): cv.enum(LINE_FREQS, upper=True),
cv.Optional(CONF_CURRENT_PHASES, default="3"): cv.enum(

View File

@@ -1,7 +1,7 @@
#include "b_parasite.h"
#include "esphome/core/log.h"
#ifdef ARDUINO_ARCH_ESP32
#ifdef USE_ESP32
namespace esphome {
namespace b_parasite {
@@ -14,6 +14,7 @@ void BParasite::dump_config() {
LOG_SENSOR(" ", "Temperature", this->temperature_);
LOG_SENSOR(" ", "Humidity", this->humidity_);
LOG_SENSOR(" ", "Soil Moisture", this->soil_moisture_);
LOG_SENSOR(" ", "Illuminance", this->illuminance_);
}
bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
@@ -36,6 +37,15 @@ bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
const auto &data = service_data.data;
const uint8_t protocol_version = data[0] >> 4;
if (protocol_version != 1) {
ESP_LOGE(TAG, "Unsupported protocol version: %u", protocol_version);
return false;
}
// Some b-parasite versions have an (optional) illuminance sensor.
bool has_illuminance = data[0] & 0x1;
// Counter for deduplicating messages.
uint8_t counter = data[1] & 0x0f;
if (last_processed_counter_ == counter) {
@@ -59,6 +69,9 @@ bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
uint16_t soil_moisture = data[8] << 8 | data[9];
float moisture_percent = (100.0f * soil_moisture) / (1 << 16);
// Ambient light in lux.
float illuminance = has_illuminance ? data[16] << 8 | data[17] : 0.0f;
if (battery_voltage_ != nullptr) {
battery_voltage_->publish_state(battery_voltage);
}
@@ -71,6 +84,13 @@ bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
if (soil_moisture_ != nullptr) {
soil_moisture_->publish_state(moisture_percent);
}
if (illuminance_ != nullptr) {
if (has_illuminance) {
illuminance_->publish_state(illuminance);
} else {
ESP_LOGE(TAG, "No lux information is present in the BLE packet");
}
}
last_processed_counter_ = counter;
return true;
@@ -79,4 +99,4 @@ bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
} // namespace b_parasite
} // namespace esphome
#endif // ARDUINO_ARCH_ESP32
#endif // USE_ESP32

View File

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

View File

@@ -5,14 +5,18 @@ from esphome.const import (
CONF_BATTERY_VOLTAGE,
CONF_HUMIDITY,
CONF_ID,
CONF_ILLUMINANCE,
CONF_MOISTURE,
CONF_MAC_ADDRESS,
CONF_TEMPERATURE,
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_ILLUMINANCE,
DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_VOLTAGE,
ENTITY_CATEGORY_DIAGNOSTIC,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
UNIT_LUX,
UNIT_PERCENT,
UNIT_VOLT,
)
@@ -48,6 +52,7 @@ CONFIG_SCHEMA = (
accuracy_decimals=3,
device_class=DEVICE_CLASS_VOLTAGE,
state_class=STATE_CLASS_MEASUREMENT,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
),
cv.Optional(CONF_MOISTURE): sensor.sensor_schema(
unit_of_measurement=UNIT_PERCENT,
@@ -55,6 +60,12 @@ CONFIG_SCHEMA = (
device_class=DEVICE_CLASS_HUMIDITY,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_ILLUMINANCE): sensor.sensor_schema(
unit_of_measurement=UNIT_LUX,
accuracy_decimals=0,
device_class=DEVICE_CLASS_ILLUMINANCE,
state_class=STATE_CLASS_MEASUREMENT,
),
}
)
.extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
@@ -74,6 +85,7 @@ async def to_code(config):
(CONF_HUMIDITY, var.set_humidity),
(CONF_BATTERY_VOLTAGE, var.set_battery_voltage),
(CONF_MOISTURE, var.set_soil_moisture),
(CONF_ILLUMINANCE, var.set_illuminance),
]:
if config_key in config:
sens = await sensor.new_sensor(config[config_key])

View File

@@ -69,7 +69,8 @@ void BangBangClimate::compute_state_() {
this->switch_to_action_(climate::CLIMATE_ACTION_OFF);
return;
}
if (isnan(this->current_temperature) || isnan(this->target_temperature_low) || isnan(this->target_temperature_high)) {
if (std::isnan(this->current_temperature) || std::isnan(this->target_temperature_low) ||
std::isnan(this->target_temperature_high)) {
// if any control parameters are nan, go to OFF action (not IDLE!)
this->switch_to_action_(climate::CLIMATE_ACTION_OFF);
return;

View File

@@ -71,13 +71,17 @@ void BH1750Sensor::update() {
float BH1750Sensor::get_setup_priority() const { return setup_priority::DATA; }
void BH1750Sensor::read_data_() {
uint16_t raw_value;
if (!this->parent_->raw_receive_16(this->address_, &raw_value, 1)) {
if (this->read(reinterpret_cast<uint8_t *>(&raw_value), 2) != i2c::ERROR_OK) {
this->status_set_warning();
return;
}
raw_value = i2c::i2ctohs(raw_value);
float lx = float(raw_value) / 1.2f;
lx *= 69.0f / this->measurement_duration_;
if (this->resolution_ == BH1750_RESOLUTION_0P5_LX) {
lx /= 2.0f;
}
ESP_LOGD(TAG, "'%s': Got illuminance=%.1flx", this->get_name().c_str(), lx);
this->publish_state(lx);
this->status_clear_warning();

View File

@@ -1,15 +1,14 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.cpp_helpers import setup_entity
from esphome import automation, core
from esphome.automation import Condition, maybe_simple_id
from esphome.components import mqtt
from esphome.const import (
CONF_DELAY,
CONF_DEVICE_CLASS,
CONF_DISABLED_BY_DEFAULT,
CONF_FILTERS,
CONF_ID,
CONF_INTERNAL,
CONF_INVALID_COOLDOWN,
CONF_INVERTED,
CONF_MAX_LENGTH,
@@ -45,9 +44,11 @@ from esphome.const import (
DEVICE_CLASS_POWER,
DEVICE_CLASS_PRESENCE,
DEVICE_CLASS_PROBLEM,
DEVICE_CLASS_RUNNING,
DEVICE_CLASS_SAFETY,
DEVICE_CLASS_SMOKE,
DEVICE_CLASS_SOUND,
DEVICE_CLASS_TAMPER,
DEVICE_CLASS_UPDATE,
DEVICE_CLASS_VIBRATION,
DEVICE_CLASS_WINDOW,
@@ -77,9 +78,11 @@ DEVICE_CLASSES = [
DEVICE_CLASS_POWER,
DEVICE_CLASS_PRESENCE,
DEVICE_CLASS_PROBLEM,
DEVICE_CLASS_RUNNING,
DEVICE_CLASS_SAFETY,
DEVICE_CLASS_SMOKE,
DEVICE_CLASS_SOUND,
DEVICE_CLASS_TAMPER,
DEVICE_CLASS_UPDATE,
DEVICE_CLASS_VIBRATION,
DEVICE_CLASS_WINDOW,
@@ -88,7 +91,7 @@ DEVICE_CLASSES = [
IS_PLATFORM_COMPONENT = True
binary_sensor_ns = cg.esphome_ns.namespace("binary_sensor")
BinarySensor = binary_sensor_ns.class_("BinarySensor", cg.Nameable)
BinarySensor = binary_sensor_ns.class_("BinarySensor", cg.EntityBase)
BinarySensorInitiallyOff = binary_sensor_ns.class_(
"BinarySensorInitiallyOff", BinarySensor
)
@@ -231,17 +234,16 @@ def parse_multi_click_timing_str(value):
parts = value.lower().split(" ")
if len(parts) != 5:
raise cv.Invalid(
"Multi click timing grammar consists of exactly 5 words, not {}"
"".format(len(parts))
f"Multi click timing grammar consists of exactly 5 words, not {len(parts)}"
)
try:
state = cv.boolean(parts[0])
except cv.Invalid:
# pylint: disable=raise-missing-from
raise cv.Invalid("First word must either be ON or OFF, not {}".format(parts[0]))
raise cv.Invalid(f"First word must either be ON or OFF, not {parts[0]}")
if parts[1] != "for":
raise cv.Invalid("Second word must be 'for', got {}".format(parts[1]))
raise cv.Invalid(f"Second word must be 'for', got {parts[1]}")
if parts[2] == "at":
if parts[3] == "least":
@@ -250,8 +252,7 @@ def parse_multi_click_timing_str(value):
key = CONF_MAX_LENGTH
else:
raise cv.Invalid(
"Third word after at must either be 'least' or 'most', got {}"
"".format(parts[3])
f"Third word after at must either be 'least' or 'most', got {parts[3]}"
)
try:
length = cv.positive_time_period_milliseconds(parts[4])
@@ -296,13 +297,11 @@ def validate_multi_click_timing(value):
new_state = v_.get(CONF_STATE, not state)
if new_state == state:
raise cv.Invalid(
"Timings must have alternating state. Indices {} and {} have "
"the same state {}".format(i, i + 1, state)
f"Timings must have alternating state. Indices {i} and {i + 1} have the same state {state}"
)
if max_length is not None and max_length < min_length:
raise cv.Invalid(
"Max length ({}) must be larger than min length ({})."
"".format(max_length, min_length)
f"Max length ({max_length}) must be larger than min length ({min_length})."
)
state = new_state
@@ -318,7 +317,8 @@ def validate_multi_click_timing(value):
device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
BINARY_SENSOR_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend(
BINARY_SENSOR_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend(
{
cv.GenerateID(): cv.declare_id(BinarySensor),
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(
@@ -379,10 +379,8 @@ BINARY_SENSOR_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).exten
async def setup_binary_sensor_core_(var, config):
cg.add(var.set_name(config[CONF_NAME]))
cg.add(var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT]))
if CONF_INTERNAL in config:
cg.add(var.set_internal(config[CONF_INTERNAL]))
await setup_entity(var, config)
if CONF_DEVICE_CLASS in config:
cg.add(var.set_device_class(config[CONF_DEVICE_CLASS]))
if CONF_INVERTED in config:

View File

@@ -4,6 +4,7 @@
#include "esphome/core/component.h"
#include "esphome/core/automation.h"
#include "esphome/core/hal.h"
#include "esphome/components/binary_sensor/binary_sensor.h"
namespace esphome {

View File

@@ -42,7 +42,7 @@ void BinarySensor::send_state_internal(bool state, bool is_initial) {
}
}
std::string BinarySensor::device_class() { return ""; }
BinarySensor::BinarySensor(const std::string &name) : Nameable(name), state(false) {}
BinarySensor::BinarySensor(const std::string &name) : EntityBase(name), state(false) {}
BinarySensor::BinarySensor() : BinarySensor("") {}
void BinarySensor::set_device_class(const std::string &device_class) { this->device_class_ = device_class; }
std::string BinarySensor::get_device_class() {

View File

@@ -1,6 +1,7 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/entity_base.h"
#include "esphome/core/helpers.h"
#include "esphome/components/binary_sensor/filter.h"
@@ -10,7 +11,7 @@ namespace binary_sensor {
#define LOG_BINARY_SENSOR(prefix, type, obj) \
if ((obj) != nullptr) { \
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, (obj)->get_name().c_str()); \
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \
if (!(obj)->get_device_class().empty()) { \
ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, (obj)->get_device_class().c_str()); \
} \
@@ -22,7 +23,7 @@ namespace binary_sensor {
* The sub classes should notify the front-end of new states via the publish_state() method which
* handles inverted inputs for you.
*/
class BinarySensor : public Nameable {
class BinarySensor : public EntityBase {
public:
explicit BinarySensor();
/** Construct a binary sensor with the specified name

View File

@@ -3,7 +3,7 @@
#include "esphome/core/automation.h"
#include "esphome/components/ble_client/ble_client.h"
#ifdef ARDUINO_ARCH_ESP32
#ifdef USE_ESP32
namespace esphome {
namespace ble_client {
@@ -11,11 +11,12 @@ class BLEClientConnectTrigger : public Trigger<>, public BLEClientNode {
public:
explicit BLEClientConnectTrigger(BLEClient *parent) { parent->register_ble_node(this); }
void loop() override {}
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) {
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) override {
if (event == ESP_GATTC_OPEN_EVT && param->open.status == ESP_GATT_OK)
this->trigger();
if (event == ESP_GATTC_SEARCH_CMPL_EVT)
this->node_state = espbt::ClientState::Established;
this->node_state = espbt::ClientState::ESTABLISHED;
}
};
@@ -23,11 +24,12 @@ class BLEClientDisconnectTrigger : public Trigger<>, public BLEClientNode {
public:
explicit BLEClientDisconnectTrigger(BLEClient *parent) { parent->register_ble_node(this); }
void loop() override {}
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) {
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) override {
if (event == ESP_GATTC_DISCONNECT_EVT && memcmp(param->disconnect.remote_bda, this->parent_->remote_bda, 6) == 0)
this->trigger();
if (event == ESP_GATTC_SEARCH_CMPL_EVT)
this->node_state = espbt::ClientState::Established;
this->node_state = espbt::ClientState::ESTABLISHED;
}
};

View File

@@ -4,25 +4,27 @@
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include "ble_client.h"
#ifdef ARDUINO_ARCH_ESP32
#ifdef USE_ESP32
namespace esphome {
namespace ble_client {
static const char *const TAG = "ble_client";
float BLEClient::get_setup_priority() const { return setup_priority::AFTER_BLUETOOTH; }
void BLEClient::setup() {
auto ret = esp_ble_gattc_app_register(this->app_id);
if (ret) {
ESP_LOGE(TAG, "gattc app register failed. app_id=%d code=%d", this->app_id, ret);
this->mark_failed();
}
this->set_states(espbt::ClientState::Idle);
this->set_states_(espbt::ClientState::IDLE);
this->enabled = true;
}
void BLEClient::loop() {
if (this->state() == espbt::ClientState::Discovered) {
if (this->state() == espbt::ClientState::DISCOVERED) {
this->connect();
}
for (auto *node : this->nodes_)
@@ -39,11 +41,11 @@ bool BLEClient::parse_device(const espbt::ESPBTDevice &device) {
return false;
if (device.address_uint64() != this->address)
return false;
if (this->state() != espbt::ClientState::Idle)
if (this->state() != espbt::ClientState::IDLE)
return false;
ESP_LOGD(TAG, "Found device at MAC address [%s]", device.address_str().c_str());
this->set_states(espbt::ClientState::Discovered);
this->set_states_(espbt::ClientState::DISCOVERED);
auto addr = device.address_uint64();
this->remote_bda[0] = (addr >> 40) & 0xFF;
@@ -69,7 +71,7 @@ std::string BLEClient::address_str() const {
void BLEClient::set_enabled(bool enabled) {
if (enabled == this->enabled)
return;
if (!enabled && this->state() != espbt::ClientState::Idle) {
if (!enabled && this->state() != espbt::ClientState::IDLE) {
ESP_LOGI(TAG, "[%s] Disabling BLE client.", this->address_str().c_str());
auto ret = esp_ble_gattc_close(this->gattc_if, this->conn_id);
if (ret) {
@@ -84,9 +86,9 @@ void BLEClient::connect() {
auto ret = esp_ble_gattc_open(this->gattc_if, this->remote_bda, BLE_ADDR_TYPE_PUBLIC, true);
if (ret) {
ESP_LOGW(TAG, "esp_ble_gattc_open error, address=%s status=%d", this->address_str().c_str(), ret);
this->set_states(espbt::ClientState::Idle);
this->set_states_(espbt::ClientState::IDLE);
} else {
this->set_states(espbt::ClientState::Connecting);
this->set_states_(espbt::ClientState::CONNECTING);
}
}
@@ -97,7 +99,7 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
if (event != ESP_GATTC_REG_EVT && esp_gattc_if != ESP_GATT_IF_NONE && esp_gattc_if != this->gattc_if)
return;
bool all_established = this->all_nodes_established();
bool all_established = this->all_nodes_established_();
switch (event) {
case ESP_GATTC_REG_EVT: {
@@ -113,7 +115,7 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
ESP_LOGV(TAG, "[%s] ESP_GATTC_OPEN_EVT", this->address_str().c_str());
if (param->open.status != ESP_GATT_OK) {
ESP_LOGW(TAG, "connect to %s failed, status=%d", this->address_str().c_str(), param->open.status);
this->set_states(espbt::ClientState::Idle);
this->set_states_(espbt::ClientState::IDLE);
break;
}
this->conn_id = param->open.conn_id;
@@ -126,11 +128,11 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
case ESP_GATTC_CFG_MTU_EVT: {
if (param->cfg_mtu.status != ESP_GATT_OK) {
ESP_LOGW(TAG, "cfg_mtu to %s failed, status %d", this->address_str().c_str(), param->cfg_mtu.status);
this->set_states(espbt::ClientState::Idle);
this->set_states_(espbt::ClientState::IDLE);
break;
}
ESP_LOGV(TAG, "cfg_mtu status %d, mtu %d", param->cfg_mtu.status, param->cfg_mtu.mtu);
esp_ble_gattc_search_service(esp_gattc_if, param->cfg_mtu.conn_id, NULL);
esp_ble_gattc_search_service(esp_gattc_if, param->cfg_mtu.conn_id, nullptr);
break;
}
case ESP_GATTC_DISCONNECT_EVT: {
@@ -139,13 +141,13 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
}
ESP_LOGV(TAG, "[%s] ESP_GATTC_DISCONNECT_EVT", this->address_str().c_str());
for (auto &svc : this->services_)
delete svc;
delete svc; // NOLINT(cppcoreguidelines-owning-memory)
this->services_.clear();
this->set_states(espbt::ClientState::Idle);
this->set_states_(espbt::ClientState::IDLE);
break;
}
case ESP_GATTC_SEARCH_RES_EVT: {
BLEService *ble_service = new BLEService();
BLEService *ble_service = new BLEService(); // NOLINT(cppcoreguidelines-owning-memory)
ble_service->uuid = espbt::ESPBTUUID::from_uuid(param->search_res.srvc_id.uuid);
ble_service->start_handle = param->search_res.start_handle;
ble_service->end_handle = param->search_res.end_handle;
@@ -160,8 +162,8 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
ESP_LOGI(TAG, " start_handle: 0x%x end_handle: 0x%x", svc->start_handle, svc->end_handle);
svc->parse_characteristics();
}
this->set_states(espbt::ClientState::Connected);
this->set_state(espbt::ClientState::Established);
this->set_states_(espbt::ClientState::CONNECTED);
this->set_state(espbt::ClientState::ESTABLISHED);
break;
}
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
@@ -192,9 +194,9 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
node->gattc_event_handler(event, esp_gattc_if, param);
// Delete characteristics after clients have used them to save RAM.
if (!all_established && this->all_nodes_established()) {
if (!all_established && this->all_nodes_established_()) {
for (auto &svc : this->services_)
delete svc;
delete svc; // NOLINT(cppcoreguidelines-owning-memory)
this->services_.clear();
}
}
@@ -307,7 +309,7 @@ BLEDescriptor *BLEClient::get_descriptor(uint16_t service, uint16_t chr, uint16_
BLEService::~BLEService() {
for (auto &chr : this->characteristics)
delete chr;
delete chr; // NOLINT(cppcoreguidelines-owning-memory)
}
void BLEService::parse_characteristics() {
@@ -329,7 +331,7 @@ void BLEService::parse_characteristics() {
break;
}
BLECharacteristic *characteristic = new BLECharacteristic();
BLECharacteristic *characteristic = new BLECharacteristic(); // NOLINT(cppcoreguidelines-owning-memory)
characteristic->uuid = espbt::ESPBTUUID::from_uuid(result.uuid);
characteristic->properties = result.properties;
characteristic->handle = result.char_handle;
@@ -344,7 +346,7 @@ void BLEService::parse_characteristics() {
BLECharacteristic::~BLECharacteristic() {
for (auto &desc : this->descriptors)
delete desc;
delete desc; // NOLINT(cppcoreguidelines-owning-memory)
}
void BLECharacteristic::parse_descriptors() {
@@ -366,7 +368,7 @@ void BLECharacteristic::parse_descriptors() {
break;
}
BLEDescriptor *desc = new BLEDescriptor();
BLEDescriptor *desc = new BLEDescriptor(); // NOLINT(cppcoreguidelines-owning-memory)
desc->uuid = espbt::ESPBTUUID::from_uuid(result.uuid);
desc->handle = result.handle;
desc->characteristic = this;
@@ -386,6 +388,15 @@ BLEDescriptor *BLECharacteristic::get_descriptor(uint16_t uuid) {
return this->get_descriptor(espbt::ESPBTUUID::from_uint16(uuid));
}
void BLECharacteristic::write_value(uint8_t *new_val, int16_t new_val_size) {
auto client = this->service->client;
auto status = esp_ble_gattc_write_char(client->gattc_if, client->conn_id, this->handle, new_val_size, new_val,
ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
if (status) {
ESP_LOGW(TAG, "Error sending write value to BLE gattc server, status=%d", status);
}
}
} // namespace ble_client
} // namespace esphome

View File

@@ -4,7 +4,7 @@
#include "esphome/core/helpers.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#ifdef ARDUINO_ARCH_ESP32
#ifdef USE_ESP32
#include <string>
#include <array>
@@ -59,7 +59,7 @@ class BLECharacteristic {
void parse_descriptors();
BLEDescriptor *get_descriptor(espbt::ESPBTUUID uuid);
BLEDescriptor *get_descriptor(uint16_t uuid);
void write_value(uint8_t *new_val, int16_t new_val_size);
BLEService *service;
};
@@ -81,11 +81,13 @@ class BLEClient : public espbt::ESPBTClient, public Component {
void setup() override;
void dump_config() override;
void loop() override;
float get_setup_priority() const override;
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param);
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) override;
bool parse_device(const espbt::ESPBTDevice &device) override;
void on_scan_end() override {}
void connect();
void connect() override;
void set_address(uint64_t address) { this->address = address; }
@@ -116,16 +118,16 @@ class BLEClient : public espbt::ESPBTClient, public Component {
std::string address_str() const;
protected:
void set_states(espbt::ClientState st) {
void set_states_(espbt::ClientState st) {
this->set_state(st);
for (auto &node : nodes_)
node->node_state = st;
}
bool all_nodes_established() {
if (this->state() != espbt::ClientState::Established)
bool all_nodes_established_() {
if (this->state() != espbt::ClientState::ESTABLISHED)
return false;
for (auto &node : nodes_)
if (node->node_state != espbt::ClientState::Established)
if (node->node_state != espbt::ClientState::ESTABLISHED)
return false;
return true;
}

View File

@@ -0,0 +1,67 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import output, ble_client, esp32_ble_tracker
from esphome.const import CONF_ID, CONF_SERVICE_UUID
from .. import ble_client_ns
DEPENDENCIES = ["ble_client"]
CONF_CHARACTERISTIC_UUID = "characteristic_uuid"
BLEBinaryOutput = ble_client_ns.class_(
"BLEBinaryOutput", output.BinaryOutput, ble_client.BLEClientNode, cg.Component
)
CONFIG_SCHEMA = cv.All(
output.BINARY_OUTPUT_SCHEMA.extend(
{
cv.Required(CONF_ID): cv.declare_id(BLEBinaryOutput),
cv.Required(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid,
cv.Required(CONF_CHARACTERISTIC_UUID): esp32_ble_tracker.bt_uuid,
}
)
.extend(cv.COMPONENT_SCHEMA)
.extend(ble_client.BLE_CLIENT_SCHEMA)
)
def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
if len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
cg.add(
var.set_service_uuid16(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]))
)
elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid32_format):
cg.add(
var.set_service_uuid32(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]))
)
elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid128_format):
uuid128 = esp32_ble_tracker.as_reversed_hex_array(config[CONF_SERVICE_UUID])
cg.add(var.set_service_uuid128(uuid128))
if len(config[CONF_CHARACTERISTIC_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
cg.add(
var.set_char_uuid16(
esp32_ble_tracker.as_hex(config[CONF_CHARACTERISTIC_UUID])
)
)
elif len(config[CONF_CHARACTERISTIC_UUID]) == len(
esp32_ble_tracker.bt_uuid32_format
):
cg.add(
var.set_char_uuid32(
esp32_ble_tracker.as_hex(config[CONF_CHARACTERISTIC_UUID])
)
)
elif len(config[CONF_CHARACTERISTIC_UUID]) == len(
esp32_ble_tracker.bt_uuid128_format
):
uuid128 = esp32_ble_tracker.as_reversed_hex_array(
config[CONF_CHARACTERISTIC_UUID]
)
cg.add(var.set_char_uuid128(uuid128))
yield output.register_output(var, config)
yield ble_client.register_ble_node(var, config)
yield cg.register_component(var, config)

View File

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

View File

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

View File

@@ -67,7 +67,7 @@ async def to_code(config):
var.set_service_uuid32(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]))
)
elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid128_format):
uuid128 = esp32_ble_tracker.as_hex_array(config[CONF_SERVICE_UUID])
uuid128 = esp32_ble_tracker.as_reversed_hex_array(config[CONF_SERVICE_UUID])
cg.add(var.set_service_uuid128(uuid128))
if len(config[CONF_CHARACTERISTIC_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
@@ -87,7 +87,9 @@ async def to_code(config):
elif len(config[CONF_CHARACTERISTIC_UUID]) == len(
esp32_ble_tracker.bt_uuid128_format
):
uuid128 = esp32_ble_tracker.as_hex_array(config[CONF_CHARACTERISTIC_UUID])
uuid128 = esp32_ble_tracker.as_reversed_hex_array(
config[CONF_CHARACTERISTIC_UUID]
)
cg.add(var.set_char_uuid128(uuid128))
if CONF_DESCRIPTOR_UUID in config:
@@ -108,7 +110,9 @@ async def to_code(config):
elif len(config[CONF_DESCRIPTOR_UUID]) == len(
esp32_ble_tracker.bt_uuid128_format
):
uuid128 = esp32_ble_tracker.as_hex_array(config[CONF_DESCRIPTOR_UUID])
uuid128 = esp32_ble_tracker.as_reversed_hex_array(
config[CONF_DESCRIPTOR_UUID]
)
cg.add(var.set_descr_uuid128(uuid128))
if CONF_LAMBDA in config:

View File

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

View File

@@ -4,7 +4,7 @@
#include "esphome/core/helpers.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#ifdef ARDUINO_ARCH_ESP32
#ifdef USE_ESP32
namespace esphome {
namespace ble_client {
@@ -71,7 +71,7 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
ESP_LOGW(TAG, "esp_ble_gattc_register_for_notify failed, status=%d", status);
}
} else {
this->node_state = espbt::ClientState::Established;
this->node_state = espbt::ClientState::ESTABLISHED;
}
break;
}
@@ -84,7 +84,7 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
}
if (param->read.handle == this->handle) {
this->status_clear_warning();
this->publish_state(this->parse_data(param->read.value, param->read.value_len));
this->publish_state(this->parse_data_(param->read.value, param->read.value_len));
}
break;
}
@@ -93,11 +93,11 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
break;
ESP_LOGV(TAG, "[%s] ESP_GATTC_NOTIFY_EVT: handle=0x%x, value=0x%x", this->get_name().c_str(),
param->notify.handle, param->notify.value[0]);
this->publish_state(this->parse_data(param->notify.value, param->notify.value_len));
this->publish_state(this->parse_data_(param->notify.value, param->notify.value_len));
break;
}
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
this->node_state = espbt::ClientState::Established;
this->node_state = espbt::ClientState::ESTABLISHED;
break;
}
default:
@@ -105,7 +105,7 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
}
}
float BLESensor::parse_data(uint8_t *value, uint16_t value_len) {
float BLESensor::parse_data_(uint8_t *value, uint16_t value_len) {
if (this->data_to_value_func_.has_value()) {
std::vector<uint8_t> data(value, value + value_len);
return (*this->data_to_value_func_)(data);
@@ -115,7 +115,7 @@ float BLESensor::parse_data(uint8_t *value, uint16_t value_len) {
}
void BLESensor::update() {
if (this->node_state != espbt::ClientState::Established) {
if (this->node_state != espbt::ClientState::ESTABLISHED) {
ESP_LOGW(TAG, "[%s] Cannot poll, not connected", this->get_name().c_str());
return;
}

View File

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

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
#include "ble_presence_device.h"
#include "esphome/core/log.h"
#ifdef ARDUINO_ARCH_ESP32
#ifdef USE_ESP32
namespace esphome {
namespace ble_presence {

View File

@@ -4,7 +4,7 @@
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include "esphome/components/binary_sensor/binary_sensor.h"
#ifdef ARDUINO_ARCH_ESP32
#ifdef USE_ESP32
namespace esphome {
namespace ble_presence {
@@ -93,8 +93,8 @@ class BLEPresenceDevice : public binary_sensor::BinarySensorInitiallyOff,
float get_setup_priority() const override { return setup_priority::DATA; }
protected:
enum MATCH_TYPE { MATCH_BY_MAC_ADDRESS, MATCH_BY_SERVICE_UUID, MATCH_BY_IBEACON_UUID };
MATCH_TYPE match_by_;
enum MatchType { MATCH_BY_MAC_ADDRESS, MATCH_BY_SERVICE_UUID, MATCH_BY_IBEACON_UUID };
MatchType match_by_;
bool found_{false};

View File

@@ -1,7 +1,7 @@
#include "ble_rssi_sensor.h"
#include "esphome/core/log.h"
#ifdef ARDUINO_ARCH_ESP32
#ifdef USE_ESP32
namespace esphome {
namespace ble_rssi {

View File

@@ -4,7 +4,7 @@
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include "esphome/components/sensor/sensor.h"
#ifdef ARDUINO_ARCH_ESP32
#ifdef USE_ESP32
namespace esphome {
namespace ble_rssi {

View File

@@ -1,7 +1,7 @@
#include "ble_scanner.h"
#include "esphome/core/log.h"
#ifdef ARDUINO_ARCH_ESP32
#ifdef USE_ESP32
namespace esphome {
namespace ble_scanner {

View File

@@ -7,7 +7,7 @@
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include "esphome/components/text_sensor/text_sensor.h"
#ifdef ARDUINO_ARCH_ESP32
#ifdef USE_ESP32
namespace esphome {
namespace ble_scanner {
@@ -15,7 +15,7 @@ namespace ble_scanner {
class BLEScanner : public text_sensor::TextSensor, public esp32_ble_tracker::ESPBTDeviceListener, public Component {
public:
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override {
this->publish_state("{\"timestamp\":" + to_string(::time(NULL)) +
this->publish_state("{\"timestamp\":" + to_string(::time(nullptr)) +
","
"\"address\":\"" +
device.address_str() +

View File

@@ -33,6 +33,7 @@ static const uint8_t BME280_REGISTER_CONTROLHUMID = 0xF2;
static const uint8_t BME280_REGISTER_STATUS = 0xF3;
static const uint8_t BME280_REGISTER_CONTROL = 0xF4;
static const uint8_t BME280_REGISTER_CONFIG = 0xF5;
static const uint8_t BME280_REGISTER_MEASUREMENTS = 0xF7;
static const uint8_t BME280_REGISTER_PRESSUREDATA = 0xF7;
static const uint8_t BME280_REGISTER_TEMPDATA = 0xFA;
static const uint8_t BME280_REGISTER_HUMIDDATA = 0xFD;
@@ -113,14 +114,14 @@ void BME280Component::setup() {
this->calibration_.h5 = read_u8_(BME280_REGISTER_DIG_H5 + 1) << 4 | (read_u8_(BME280_REGISTER_DIG_H5) >> 4);
this->calibration_.h6 = read_u8_(BME280_REGISTER_DIG_H6);
uint8_t humid_register = 0;
if (!this->read_byte(BME280_REGISTER_CONTROLHUMID, &humid_register)) {
uint8_t humid_control_val = 0;
if (!this->read_byte(BME280_REGISTER_CONTROLHUMID, &humid_control_val)) {
this->mark_failed();
return;
}
humid_register &= ~0b00000111;
humid_register |= this->humidity_oversampling_ & 0b111;
if (!this->write_byte(BME280_REGISTER_CONTROLHUMID, humid_register)) {
humid_control_val &= ~0b00000111;
humid_control_val |= this->humidity_oversampling_ & 0b111;
if (!this->write_byte(BME280_REGISTER_CONTROLHUMID, humid_control_val)) {
this->mark_failed();
return;
}
@@ -169,32 +170,38 @@ inline uint8_t oversampling_to_time(BME280Oversampling over_sampling) { return (
void BME280Component::update() {
// Enable sensor
ESP_LOGV(TAG, "Sending conversion request...");
uint8_t meas_register = 0;
meas_register |= (this->temperature_oversampling_ & 0b111) << 5;
meas_register |= (this->pressure_oversampling_ & 0b111) << 2;
meas_register |= BME280_MODE_FORCED;
if (!this->write_byte(BME280_REGISTER_CONTROL, meas_register)) {
uint8_t meas_value = 0;
meas_value |= (this->temperature_oversampling_ & 0b111) << 5;
meas_value |= (this->pressure_oversampling_ & 0b111) << 2;
meas_value |= BME280_MODE_FORCED;
if (!this->write_byte(BME280_REGISTER_CONTROL, meas_value)) {
this->status_set_warning();
return;
}
float meas_time = 1.5;
float meas_time = 1.5f;
meas_time += 2.3f * oversampling_to_time(this->temperature_oversampling_);
meas_time += 2.3f * oversampling_to_time(this->pressure_oversampling_) + 0.575f;
meas_time += 2.3f * oversampling_to_time(this->humidity_oversampling_) + 0.575f;
this->set_timeout("data", uint32_t(ceilf(meas_time)), [this]() {
uint8_t data[8];
if (!this->read_bytes(BME280_REGISTER_MEASUREMENTS, data, 8)) {
ESP_LOGW(TAG, "Error reading registers.");
this->status_set_warning();
return;
}
int32_t t_fine = 0;
float temperature = this->read_temperature_(&t_fine);
if (isnan(temperature)) {
float temperature = this->read_temperature_(data, &t_fine);
if (std::isnan(temperature)) {
ESP_LOGW(TAG, "Invalid temperature, cannot read pressure & humidity values.");
this->status_set_warning();
return;
}
float pressure = this->read_pressure_(t_fine);
float humidity = this->read_humidity_(t_fine);
float pressure = this->read_pressure_(data, t_fine);
float humidity = this->read_humidity_(data, t_fine);
ESP_LOGD(TAG, "Got temperature=%.1f°C pressure=%.1fhPa humidity=%.1f%%", temperature, pressure, humidity);
ESP_LOGV(TAG, "Got temperature=%.1f°C pressure=%.1fhPa humidity=%.1f%%", temperature, pressure, humidity);
if (this->temperature_sensor_ != nullptr)
this->temperature_sensor_->publish_state(temperature);
if (this->pressure_sensor_ != nullptr)
@@ -204,11 +211,8 @@ void BME280Component::update() {
this->status_clear_warning();
});
}
float BME280Component::read_temperature_(int32_t *t_fine) {
uint8_t data[3];
if (!this->read_bytes(BME280_REGISTER_TEMPDATA, data, 3))
return NAN;
int32_t adc = ((data[0] & 0xFF) << 16) | ((data[1] & 0xFF) << 8) | (data[2] & 0xFF);
float BME280Component::read_temperature_(const uint8_t *data, int32_t *t_fine) {
int32_t adc = ((data[3] & 0xFF) << 16) | ((data[4] & 0xFF) << 8) | (data[5] & 0xFF);
adc >>= 4;
if (adc == 0x80000)
// temperature was disabled
@@ -226,10 +230,7 @@ float BME280Component::read_temperature_(int32_t *t_fine) {
return temperature / 100.0f;
}
float BME280Component::read_pressure_(int32_t t_fine) {
uint8_t data[3];
if (!this->read_bytes(BME280_REGISTER_PRESSUREDATA, data, 3))
return NAN;
float BME280Component::read_pressure_(const uint8_t *data, int32_t t_fine) {
int32_t adc = ((data[0] & 0xFF) << 16) | ((data[1] & 0xFF) << 8) | (data[2] & 0xFF);
adc >>= 4;
if (adc == 0x80000)
@@ -265,9 +266,9 @@ float BME280Component::read_pressure_(int32_t t_fine) {
return (p / 256.0f) / 100.0f;
}
float BME280Component::read_humidity_(int32_t t_fine) {
uint16_t raw_adc;
if (!this->read_byte_16(BME280_REGISTER_HUMIDDATA, &raw_adc) || raw_adc == 0x8000)
float BME280Component::read_humidity_(const uint8_t *data, int32_t t_fine) {
uint16_t raw_adc = ((data[6] & 0xFF) << 8) | (data[7] & 0xFF);
if (raw_adc == 0x8000)
return NAN;
int32_t adc = raw_adc;

View File

@@ -82,11 +82,11 @@ class BME280Component : public PollingComponent, public i2c::I2CDevice {
protected:
/// Read the temperature value and store the calculated ambient temperature in t_fine.
float read_temperature_(int32_t *t_fine);
float read_temperature_(const uint8_t *data, int32_t *t_fine);
/// Read the pressure value in hPa using the provided t_fine value.
float read_pressure_(int32_t t_fine);
float read_pressure_(const uint8_t *data, int32_t t_fine);
/// Read the humidity value in % using the provided t_fine value.
float read_humidity_(int32_t t_fine);
float read_humidity_(const uint8_t *data, int32_t t_fine);
uint8_t read_u8_(uint8_t a_register);
uint16_t read_u16_le_(uint8_t a_register);
int16_t read_s16_le_(uint8_t a_register);

View File

@@ -1,5 +1,6 @@
#include "bme680.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
namespace esphome {
namespace bme680 {

View File

@@ -44,7 +44,8 @@ CONFIG_SCHEMA = cv.Schema(
cv.Optional(
CONF_STATE_SAVE_INTERVAL, default="6hours"
): cv.positive_time_period_minutes,
}
},
cv.only_with_arduino,
).extend(i2c.i2c_device_schema(0x76))
@@ -60,5 +61,8 @@ async def to_code(config):
var.set_state_save_interval(config[CONF_STATE_SAVE_INTERVAL].total_milliseconds)
)
# Although this component does not use SPI, the BSEC library requires the SPI library
cg.add_library("SPI", None)
cg.add_define("USE_BSEC")
cg.add_library("BSEC Software Library", "1.6.1480")
cg.add_library("boschsensortec/BSEC Software Library", "1.6.1480")

View File

@@ -381,7 +381,7 @@ void BME680BSECComponent::delay_ms(uint32_t period) {
void BME680BSECComponent::load_state_() {
uint32_t hash = fnv1_hash("bme680_bsec_state_" + to_string(this->address_));
this->bsec_state_ = global_preferences.make_preference<uint8_t[BSEC_MAX_STATE_BLOB_SIZE]>(hash, true);
this->bsec_state_ = global_preferences->make_preference<uint8_t[BSEC_MAX_STATE_BLOB_SIZE]>(hash, true);
uint8_t state[BSEC_MAX_STATE_BLOB_SIZE];
if (this->bsec_state_.load(&state)) {

View File

@@ -5,6 +5,7 @@
#include "esphome/components/text_sensor/text_sensor.h"
#include "esphome/components/i2c/i2c.h"
#include "esphome/core/preferences.h"
#include "esphome/core/defines.h"
#include <map>
#ifdef USE_BSEC

View File

@@ -123,11 +123,11 @@ inline uint8_t oversampling_to_time(BMP280Oversampling over_sampling) { return (
void BMP280Component::update() {
// Enable sensor
ESP_LOGV(TAG, "Sending conversion request...");
uint8_t meas_register = 0;
meas_register |= (this->temperature_oversampling_ & 0b111) << 5;
meas_register |= (this->pressure_oversampling_ & 0b111) << 2;
meas_register |= 0b01; // Forced mode
if (!this->write_byte(BMP280_REGISTER_CONTROL, meas_register)) {
uint8_t meas_value = 0;
meas_value |= (this->temperature_oversampling_ & 0b111) << 5;
meas_value |= (this->pressure_oversampling_ & 0b111) << 2;
meas_value |= 0b01; // Forced mode
if (!this->write_byte(BMP280_REGISTER_CONTROL, meas_value)) {
this->status_set_warning();
return;
}
@@ -139,7 +139,7 @@ void BMP280Component::update() {
this->set_timeout("data", uint32_t(ceilf(meas_time)), [this]() {
int32_t t_fine = 0;
float temperature = this->read_temperature_(&t_fine);
if (isnan(temperature)) {
if (std::isnan(temperature)) {
ESP_LOGW(TAG, "Invalid temperature, cannot read pressure values.");
this->status_set_warning();
return;

View File

@@ -0,0 +1,127 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
from esphome.automation import maybe_simple_id
from esphome.components import mqtt
from esphome.const import (
CONF_DEVICE_CLASS,
CONF_ENTITY_CATEGORY,
CONF_ICON,
CONF_ID,
CONF_ON_PRESS,
CONF_TRIGGER_ID,
CONF_MQTT_ID,
DEVICE_CLASS_RESTART,
DEVICE_CLASS_UPDATE,
)
from esphome.core import CORE, coroutine_with_priority
from esphome.cpp_helpers import setup_entity
CODEOWNERS = ["@esphome/core"]
IS_PLATFORM_COMPONENT = True
DEVICE_CLASSES = [
DEVICE_CLASS_RESTART,
DEVICE_CLASS_UPDATE,
]
button_ns = cg.esphome_ns.namespace("button")
Button = button_ns.class_("Button", cg.EntityBase)
ButtonPtr = Button.operator("ptr")
PressAction = button_ns.class_("PressAction", automation.Action)
ButtonPressTrigger = button_ns.class_(
"ButtonPressTrigger", automation.Trigger.template()
)
validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
BUTTON_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
{
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTButtonComponent),
cv.Optional(CONF_DEVICE_CLASS): validate_device_class,
cv.Optional(CONF_ON_PRESS): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ButtonPressTrigger),
}
),
}
)
_UNDEF = object()
def button_schema(
icon: str = _UNDEF,
entity_category: str = _UNDEF,
device_class: str = _UNDEF,
) -> cv.Schema:
schema = BUTTON_SCHEMA
if icon is not _UNDEF:
schema = schema.extend({cv.Optional(CONF_ICON, default=icon): cv.icon})
if entity_category is not _UNDEF:
schema = schema.extend(
{
cv.Optional(
CONF_ENTITY_CATEGORY, default=entity_category
): cv.entity_category
}
)
if device_class is not _UNDEF:
schema = schema.extend(
{
cv.Optional(
CONF_DEVICE_CLASS, default=device_class
): validate_device_class
}
)
return schema
async def setup_button_core_(var, config):
await setup_entity(var, config)
for conf in config.get(CONF_ON_PRESS, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf)
if CONF_DEVICE_CLASS in config:
cg.add(var.set_device_class(config[CONF_DEVICE_CLASS]))
if CONF_MQTT_ID in config:
mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var)
await mqtt.register_mqtt_component(mqtt_, config)
async def register_button(var, config):
if not CORE.has_id(config[CONF_ID]):
var = cg.Pvariable(config[CONF_ID], var)
cg.add(cg.App.register_button(var))
await setup_button_core_(var, config)
async def new_button(config):
var = cg.new_Pvariable(config[CONF_ID])
await register_button(var, config)
return var
BUTTON_PRESS_SCHEMA = maybe_simple_id(
{
cv.Required(CONF_ID): cv.use_id(Button),
}
)
@automation.register_action("button.press", PressAction, BUTTON_PRESS_SCHEMA)
async def button_press_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
return cg.new_Pvariable(action_id, template_arg, paren)
@coroutine_with_priority(100.0)
async def to_code(config):
cg.add_global(button_ns.using)
cg.add_define("USE_BUTTON")

View File

@@ -0,0 +1,28 @@
#pragma once
#include "esphome/components/button/button.h"
#include "esphome/core/automation.h"
#include "esphome/core/component.h"
namespace esphome {
namespace button {
template<typename... Ts> class PressAction : public Action<Ts...> {
public:
explicit PressAction(Button *button) : button_(button) {}
void play(Ts... x) override { this->button_->press(); }
protected:
Button *button_;
};
class ButtonPressTrigger : public Trigger<> {
public:
ButtonPressTrigger(Button *button) {
button->add_on_press_callback([this]() { this->trigger(); });
}
};
} // namespace button
} // namespace esphome

View File

@@ -0,0 +1,28 @@
#include "button.h"
#include "esphome/core/log.h"
namespace esphome {
namespace button {
static const char *const TAG = "button";
Button::Button(const std::string &name) : EntityBase(name) {}
Button::Button() : Button("") {}
void Button::press() {
ESP_LOGD(TAG, "'%s' Pressed.", this->get_name().c_str());
this->press_action();
this->press_callback_.call();
}
void Button::add_on_press_callback(std::function<void()> &&callback) { this->press_callback_.add(std::move(callback)); }
uint32_t Button::hash_base() { return 1495763804UL; }
void Button::set_device_class(const std::string &device_class) { this->device_class_ = device_class; }
std::string Button::get_device_class() {
if (this->device_class_.has_value())
return *this->device_class_;
return "";
}
} // namespace button
} // namespace esphome

View File

@@ -0,0 +1,57 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/entity_base.h"
#include "esphome/core/helpers.h"
namespace esphome {
namespace button {
#define LOG_BUTTON(prefix, type, obj) \
if ((obj) != nullptr) { \
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \
if (!(obj)->get_icon().empty()) { \
ESP_LOGCONFIG(TAG, "%s Icon: '%s'", prefix, (obj)->get_icon().c_str()); \
} \
}
/** Base class for all buttons.
*
* A button is just a momentary switch that does not have a state, only a trigger.
*/
class Button : public EntityBase {
public:
explicit Button();
explicit Button(const std::string &name);
/** Press this button. This is called by the front-end.
*
* For implementing buttons, please override press_action.
*/
void press();
/** Set callback for state changes.
*
* @param callback The void() callback.
*/
void add_on_press_callback(std::function<void()> &&callback);
/// Set the Home Assistant device class (see button::device_class).
void set_device_class(const std::string &device_class);
/// Get the device class for this button.
std::string get_device_class();
protected:
/** You should implement this virtual method if you want to create your own button.
*/
virtual void press_action(){};
uint32_t hash_base() override;
CallbackManager<void()> press_callback_{};
optional<std::string> device_class_{};
};
} // namespace button
} // namespace esphome

View File

@@ -0,0 +1,45 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c
from esphome.const import CONF_ID, CONF_RESET_PIN
from esphome import pins
CONF_TOUCH_THRESHOLD = "touch_threshold"
CONF_ALLOW_MULTIPLE_TOUCHES = "allow_multiple_touches"
DEPENDENCIES = ["i2c"]
AUTO_LOAD = ["binary_sensor", "output"]
CODEOWNERS = ["@MrEditor97"]
cap1188_ns = cg.esphome_ns.namespace("cap1188")
CONF_CAP1188_ID = "cap1188_id"
CAP1188Component = cap1188_ns.class_("CAP1188Component", cg.Component, i2c.I2CDevice)
MULTI_CONF = True
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(CAP1188Component),
cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema,
cv.Optional(CONF_TOUCH_THRESHOLD, default=0x20): cv.int_range(
min=0x01, max=0x80
),
cv.Optional(CONF_ALLOW_MULTIPLE_TOUCHES, default=False): cv.boolean,
}
)
.extend(cv.COMPONENT_SCHEMA)
.extend(i2c.i2c_device_schema(0x29))
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
cg.add(var.set_touch_threshold(config[CONF_TOUCH_THRESHOLD]))
cg.add(var.set_allow_multiple_touches(config[CONF_ALLOW_MULTIPLE_TOUCHES]))
if CONF_RESET_PIN in config:
pin = await cg.gpio_pin_expression(config[CONF_RESET_PIN])
cg.add(var.set_reset_pin(pin))
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)

View File

@@ -0,0 +1,25 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import binary_sensor
from esphome.const import CONF_CHANNEL, CONF_ID
from . import cap1188_ns, CAP1188Component, CONF_CAP1188_ID
DEPENDENCIES = ["cap1188"]
CAP1188Channel = cap1188_ns.class_("CAP1188Channel", binary_sensor.BinarySensor)
CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(CAP1188Channel),
cv.GenerateID(CONF_CAP1188_ID): cv.use_id(CAP1188Component),
cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=7),
}
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await binary_sensor.register_binary_sensor(var, config)
hub = await cg.get_variable(config[CONF_CAP1188_ID])
cg.add(var.set_channel(config[CONF_CHANNEL]))
cg.add(hub.register_channel(var))

View File

@@ -0,0 +1,88 @@
#include "cap1188.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
namespace esphome {
namespace cap1188 {
static const char *const TAG = "cap1188";
void CAP1188Component::setup() {
ESP_LOGCONFIG(TAG, "Setting up CAP1188...");
// Reset device using the reset pin
if (this->reset_pin_ != nullptr) {
this->reset_pin_->setup();
this->reset_pin_->digital_write(false);
delay(100); // NOLINT
this->reset_pin_->digital_write(true);
delay(100); // NOLINT
this->reset_pin_->digital_write(false);
delay(100); // NOLINT
}
// Check if CAP1188 is actually connected
this->read_byte(CAP1188_PRODUCT_ID, &this->cap1188_product_id_);
this->read_byte(CAP1188_MANUFACTURE_ID, &this->cap1188_manufacture_id_);
this->read_byte(CAP1188_REVISION, &this->cap1188_revision_);
if ((this->cap1188_product_id_ != 0x50) || (this->cap1188_manufacture_id_ != 0x5D)) {
this->error_code_ = COMMUNICATION_FAILED;
this->mark_failed();
return;
}
// Set sensitivity
uint8_t sensitivity = 0;
this->read_byte(CAP1188_SENSITVITY, &sensitivity);
sensitivity = sensitivity & 0x0f;
this->write_byte(CAP1188_SENSITVITY, sensitivity | this->touch_threshold_);
// Allow multiple touches
this->write_byte(CAP1188_MULTI_TOUCH, this->allow_multiple_touches_);
// Have LEDs follow touches
this->write_byte(CAP1188_LED_LINK, 0xFF);
// Speed up a bit
this->write_byte(CAP1188_STAND_BY_CONFIGURATION, 0x30);
}
void CAP1188Component::dump_config() {
ESP_LOGCONFIG(TAG, "CAP1188:");
LOG_I2C_DEVICE(this);
LOG_PIN(" Reset Pin: ", this->reset_pin_);
ESP_LOGCONFIG(TAG, " Product ID: 0x%x", this->cap1188_product_id_);
ESP_LOGCONFIG(TAG, " Manufacture ID: 0x%x", this->cap1188_manufacture_id_);
ESP_LOGCONFIG(TAG, " Revision ID: 0x%x", this->cap1188_revision_);
switch (this->error_code_) {
case COMMUNICATION_FAILED:
ESP_LOGE(TAG, "Product ID or Manufacture ID of the connected device does not match a known CAP1188.");
break;
case NONE:
default:
break;
}
}
void CAP1188Component::loop() {
uint8_t touched = 0;
this->read_register(CAP1188_SENSOR_INPUT_STATUS, &touched, 1);
if (touched) {
uint8_t data = 0;
this->read_register(CAP1188_MAIN, &data, 1);
data = data & ~CAP1188_MAIN_INT;
this->write_register(CAP1188_MAIN, &data, 2);
}
for (auto *channel : this->channels_) {
channel->process(touched);
}
}
} // namespace cap1188
} // namespace esphome

View File

@@ -0,0 +1,68 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/hal.h"
#include "esphome/components/i2c/i2c.h"
#include "esphome/components/output/binary_output.h"
#include "esphome/components/binary_sensor/binary_sensor.h"
namespace esphome {
namespace cap1188 {
enum {
CAP1188_I2CADDR = 0x29,
CAP1188_SENSOR_INPUT_STATUS = 0x3,
CAP1188_MULTI_TOUCH = 0x2A,
CAP1188_LED_LINK = 0x72,
CAP1188_PRODUCT_ID = 0xFD,
CAP1188_MANUFACTURE_ID = 0xFE,
CAP1188_STAND_BY_CONFIGURATION = 0x41,
CAP1188_REVISION = 0xFF,
CAP1188_MAIN = 0x00,
CAP1188_MAIN_INT = 0x01,
CAP1188_LEDPOL = 0x73,
CAP1188_INTERUPT_REPEAT = 0x28,
CAP1188_SENSITVITY = 0x1f,
};
class CAP1188Channel : public binary_sensor::BinarySensor {
public:
void set_channel(uint8_t channel) { channel_ = channel; }
void process(uint8_t data) { this->publish_state(static_cast<bool>(data & (1 << this->channel_))); }
protected:
uint8_t channel_{0};
};
class CAP1188Component : public Component, public i2c::I2CDevice {
public:
void register_channel(CAP1188Channel *channel) { this->channels_.push_back(channel); }
void set_touch_threshold(uint8_t touch_threshold) { this->touch_threshold_ = touch_threshold; };
void set_allow_multiple_touches(bool allow_multiple_touches) {
this->allow_multiple_touches_ = allow_multiple_touches ? 0x41 : 0x80;
};
void set_reset_pin(GPIOPin *reset_pin) { this->reset_pin_ = reset_pin; }
void setup() override;
void dump_config() override;
float get_setup_priority() const override { return setup_priority::DATA; }
void loop() override;
protected:
std::vector<CAP1188Channel *> channels_{};
uint8_t touch_threshold_{0x20};
uint8_t allow_multiple_touches_{0x80};
GPIOPin *reset_pin_{nullptr};
uint8_t cap1188_product_id_{0};
uint8_t cap1188_manufacture_id_{0};
uint8_t cap1188_revision_{0};
enum ErrorCode {
NONE = 0,
COMMUNICATION_FAILED,
} error_code_{NONE};
};
} // namespace cap1188
} // namespace esphome

View File

@@ -3,7 +3,7 @@ import esphome.config_validation as cv
from esphome.components import web_server_base
from esphome.components.web_server_base import CONF_WEB_SERVER_BASE_ID
from esphome.const import CONF_ID
from esphome.core import coroutine_with_priority
from esphome.core import coroutine_with_priority, CORE
AUTO_LOAD = ["web_server_base"]
DEPENDENCIES = ["wifi"]
@@ -12,14 +12,17 @@ CODEOWNERS = ["@OttoWinter"]
captive_portal_ns = cg.esphome_ns.namespace("captive_portal")
CaptivePortal = captive_portal_ns.class_("CaptivePortal", cg.Component)
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(CaptivePortal),
cv.GenerateID(CONF_WEB_SERVER_BASE_ID): cv.use_id(
web_server_base.WebServerBase
),
}
).extend(cv.COMPONENT_SCHEMA)
CONFIG_SCHEMA = cv.All(
cv.Schema(
{
cv.GenerateID(): cv.declare_id(CaptivePortal),
cv.GenerateID(CONF_WEB_SERVER_BASE_ID): cv.use_id(
web_server_base.WebServerBase
),
}
).extend(cv.COMPONENT_SCHEMA),
cv.only_with_arduino,
)
@coroutine_with_priority(64.0)
@@ -29,3 +32,9 @@ async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID], paren)
await cg.register_component(var, config)
cg.add_define("USE_CAPTIVE_PORTAL")
if CORE.is_esp32:
cg.add_library("DNSServer", None)
cg.add_library("WiFi", None)
if CORE.is_esp8266:
cg.add_library("DNSServer", None)

View File

@@ -1,3 +1,5 @@
#ifdef USE_ARDUINO
#include "captive_portal.h"
#include "esphome/core/log.h"
#include "esphome/core/application.h"
@@ -65,6 +67,7 @@ void CaptivePortal::handle_wifisave(AsyncWebServerRequest *request) {
ESP_LOGI(TAG, " SSID='%s'", ssid.c_str());
ESP_LOGI(TAG, " Password=" LOG_SECRET("'%s'"), psk.c_str());
wifi::global_wifi_component->save_wifi_sta(ssid, psk);
wifi::global_wifi_component->start_scanning();
request->redirect("/?save=true");
}
@@ -76,26 +79,19 @@ void CaptivePortal::start() {
this->base_->add_ota_handler();
}
this->dns_server_ = new DNSServer();
this->dns_server_ = make_unique<DNSServer>();
this->dns_server_->setErrorReplyCode(DNSReplyCode::NoError);
IPAddress ip = wifi::global_wifi_component->wifi_soft_ap_ip();
this->dns_server_->start(53, "*", ip);
network::IPAddress ip = wifi::global_wifi_component->wifi_soft_ap_ip();
this->dns_server_->start(53, "*", (uint32_t) ip);
this->base_->get_server()->onNotFound([this](AsyncWebServerRequest *req) {
bool not_found = false;
if (!this->active_) {
not_found = true;
} else if (req->host() == wifi::global_wifi_component->wifi_soft_ap_ip().toString()) {
not_found = true;
}
if (not_found) {
if (!this->active_ || req->host().c_str() == wifi::global_wifi_component->wifi_soft_ap_ip().str()) {
req->send(404, "text/html", "File not found");
return;
}
auto url = "http://" + wifi::global_wifi_component->wifi_soft_ap_ip().toString();
req->redirect(url);
auto url = "http://" + wifi::global_wifi_component->wifi_soft_ap_ip().str();
req->redirect(url.c_str());
});
this->initialized_ = true;
@@ -151,3 +147,5 @@ CaptivePortal *global_captive_portal = nullptr; // NOLINT(cppcoreguidelines-avo
} // namespace captive_portal
} // namespace esphome
#endif // USE_ARDUINO

View File

@@ -1,5 +1,8 @@
#pragma once
#ifdef USE_ARDUINO
#include <memory>
#include <DNSServer.h>
#include "esphome/core/component.h"
#include "esphome/core/helpers.h"
@@ -26,7 +29,7 @@ class CaptivePortal : public AsyncWebHandler, public Component {
this->active_ = false;
this->base_->deinit();
this->dns_server_->stop();
delete this->dns_server_;
this->dns_server_ = nullptr;
}
bool canHandle(AsyncWebServerRequest *request) override {
@@ -65,10 +68,12 @@ class CaptivePortal : public AsyncWebHandler, public Component {
web_server_base::WebServerBase *base_;
bool initialized_{false};
bool active_{false};
DNSServer *dns_server_{nullptr};
std::unique_ptr<DNSServer> dns_server_{nullptr};
};
extern CaptivePortal *global_captive_portal; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
} // namespace captive_portal
} // namespace esphome
#endif // USE_ARDUINO

View File

@@ -1,5 +1,6 @@
#include "ccs811.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
namespace esphome {
namespace ccs811 {
@@ -51,7 +52,7 @@ void CCS811Component::setup() {
if (this->baseline_.has_value()) {
// baseline available, write to sensor
this->write_bytes(0x11, decode_uint16(*this->baseline_));
this->write_bytes(0x11, decode_value(*this->baseline_));
}
auto hardware_version_data = this->read_bytes<1>(0x21);
@@ -85,8 +86,11 @@ void CCS811Component::setup() {
}
}
void CCS811Component::update() {
if (!this->status_has_data_())
if (!this->status_has_data_()) {
ESP_LOGD(TAG, "Status indicates no data ready!");
this->status_set_warning();
return;
}
// page 12 - alg result data
auto alg_data = this->read_bytes<4>(0x02);
@@ -124,12 +128,12 @@ void CCS811Component::send_env_data_() {
float humidity = NAN;
if (this->humidity_ != nullptr)
humidity = this->humidity_->state;
if (isnan(humidity) || humidity < 0 || humidity > 100)
if (std::isnan(humidity) || humidity < 0 || humidity > 100)
humidity = 50;
float temperature = NAN;
if (this->temperature_ != nullptr)
temperature = this->temperature_->state;
if (isnan(temperature) || temperature < -25 || temperature > 50)
if (std::isnan(temperature) || temperature < -25 || temperature > 50)
temperature = 25;
// temperature has a 25° offset to allow negative temperatures
temperature += 25;

View File

@@ -1,27 +1,43 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.cpp_helpers import setup_entity
from esphome import automation
from esphome.components import mqtt
from esphome.const import (
CONF_ACTION_STATE_TOPIC,
CONF_AWAY,
CONF_AWAY_COMMAND_TOPIC,
CONF_AWAY_STATE_TOPIC,
CONF_CURRENT_TEMPERATURE_STATE_TOPIC,
CONF_CUSTOM_FAN_MODE,
CONF_CUSTOM_PRESET,
CONF_DISABLED_BY_DEFAULT,
CONF_FAN_MODE,
CONF_FAN_MODE_COMMAND_TOPIC,
CONF_FAN_MODE_STATE_TOPIC,
CONF_ID,
CONF_INTERNAL,
CONF_MAX_TEMPERATURE,
CONF_MIN_TEMPERATURE,
CONF_MODE,
CONF_MODE_COMMAND_TOPIC,
CONF_MODE_STATE_TOPIC,
CONF_ON_STATE,
CONF_PRESET,
CONF_SWING_MODE,
CONF_SWING_MODE_COMMAND_TOPIC,
CONF_SWING_MODE_STATE_TOPIC,
CONF_TARGET_TEMPERATURE,
CONF_TARGET_TEMPERATURE_COMMAND_TOPIC,
CONF_TARGET_TEMPERATURE_STATE_TOPIC,
CONF_TARGET_TEMPERATURE_HIGH,
CONF_TARGET_TEMPERATURE_HIGH_COMMAND_TOPIC,
CONF_TARGET_TEMPERATURE_HIGH_STATE_TOPIC,
CONF_TARGET_TEMPERATURE_LOW,
CONF_TARGET_TEMPERATURE_LOW_COMMAND_TOPIC,
CONF_TARGET_TEMPERATURE_LOW_STATE_TOPIC,
CONF_TEMPERATURE_STEP,
CONF_TRIGGER_ID,
CONF_VISUAL,
CONF_MQTT_ID,
CONF_NAME,
CONF_FAN_MODE,
CONF_SWING_MODE,
)
from esphome.core import CORE, coroutine_with_priority
@@ -30,7 +46,7 @@ IS_PLATFORM_COMPONENT = True
CODEOWNERS = ["@esphome/core"]
climate_ns = cg.esphome_ns.namespace("climate")
Climate = climate_ns.class_("Climate", cg.Nameable)
Climate = climate_ns.class_("Climate", cg.EntityBase)
ClimateCall = climate_ns.class_("ClimateCall")
ClimateTraits = climate_ns.class_("ClimateTraits")
@@ -87,8 +103,9 @@ validate_climate_swing_mode = cv.enum(CLIMATE_SWING_MODES, upper=True)
# Actions
ControlAction = climate_ns.class_("ControlAction", automation.Action)
StateTrigger = climate_ns.class_("StateTrigger", automation.Trigger.template())
CLIMATE_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
CLIMATE_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
{
cv.GenerateID(): cv.declare_id(Climate),
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTClimateComponent),
@@ -99,16 +116,66 @@ CLIMATE_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).ext
cv.Optional(CONF_TEMPERATURE_STEP): cv.temperature,
}
),
# TODO: MQTT topic options
cv.Optional(CONF_ACTION_STATE_TOPIC): cv.All(
cv.requires_component("mqtt"), cv.publish_topic
),
cv.Optional(CONF_AWAY_COMMAND_TOPIC): cv.All(
cv.requires_component("mqtt"), cv.publish_topic
),
cv.Optional(CONF_AWAY_STATE_TOPIC): cv.All(
cv.requires_component("mqtt"), cv.publish_topic
),
cv.Optional(CONF_CURRENT_TEMPERATURE_STATE_TOPIC): cv.All(
cv.requires_component("mqtt"), cv.publish_topic
),
cv.Optional(CONF_FAN_MODE_COMMAND_TOPIC): cv.All(
cv.requires_component("mqtt"), cv.publish_topic
),
cv.Optional(CONF_FAN_MODE_STATE_TOPIC): cv.All(
cv.requires_component("mqtt"), cv.publish_topic
),
cv.Optional(CONF_MODE_COMMAND_TOPIC): cv.All(
cv.requires_component("mqtt"), cv.publish_topic
),
cv.Optional(CONF_MODE_STATE_TOPIC): cv.All(
cv.requires_component("mqtt"), cv.publish_topic
),
cv.Optional(CONF_SWING_MODE_COMMAND_TOPIC): cv.All(
cv.requires_component("mqtt"), cv.publish_topic
),
cv.Optional(CONF_SWING_MODE_STATE_TOPIC): cv.All(
cv.requires_component("mqtt"), cv.publish_topic
),
cv.Optional(CONF_TARGET_TEMPERATURE_COMMAND_TOPIC): cv.All(
cv.requires_component("mqtt"), cv.publish_topic
),
cv.Optional(CONF_TARGET_TEMPERATURE_STATE_TOPIC): cv.All(
cv.requires_component("mqtt"), cv.publish_topic
),
cv.Optional(CONF_TARGET_TEMPERATURE_HIGH_COMMAND_TOPIC): cv.All(
cv.requires_component("mqtt"), cv.publish_topic
),
cv.Optional(CONF_TARGET_TEMPERATURE_HIGH_STATE_TOPIC): cv.All(
cv.requires_component("mqtt"), cv.publish_topic
),
cv.Optional(CONF_TARGET_TEMPERATURE_LOW_COMMAND_TOPIC): cv.All(
cv.requires_component("mqtt"), cv.publish_topic
),
cv.Optional(CONF_TARGET_TEMPERATURE_LOW_STATE_TOPIC): cv.All(
cv.requires_component("mqtt"), cv.publish_topic
),
cv.Optional(CONF_ON_STATE): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateTrigger),
}
),
}
)
async def setup_climate_core_(var, config):
cg.add(var.set_name(config[CONF_NAME]))
cg.add(var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT]))
if CONF_INTERNAL in config:
cg.add(var.set_internal(config[CONF_INTERNAL]))
await setup_entity(var, config)
visual = config[CONF_VISUAL]
if CONF_MIN_TEMPERATURE in visual:
cg.add(var.set_visual_min_temperature_override(visual[CONF_MIN_TEMPERATURE]))
@@ -121,6 +188,86 @@ async def setup_climate_core_(var, config):
mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var)
await mqtt.register_mqtt_component(mqtt_, config)
if CONF_ACTION_STATE_TOPIC in config:
cg.add(mqtt_.set_custom_action_state_topic(config[CONF_ACTION_STATE_TOPIC]))
if CONF_AWAY_COMMAND_TOPIC in config:
cg.add(mqtt_.set_custom_away_command_topic(config[CONF_AWAY_COMMAND_TOPIC]))
if CONF_AWAY_STATE_TOPIC in config:
cg.add(mqtt_.set_custom_away_state_topic(config[CONF_AWAY_STATE_TOPIC]))
if CONF_CURRENT_TEMPERATURE_STATE_TOPIC in config:
cg.add(
mqtt_.set_custom_current_temperature_state_topic(
config[CONF_CURRENT_TEMPERATURE_STATE_TOPIC]
)
)
if CONF_FAN_MODE_COMMAND_TOPIC in config:
cg.add(
mqtt_.set_custom_fan_mode_command_topic(
config[CONF_FAN_MODE_COMMAND_TOPIC]
)
)
if CONF_FAN_MODE_STATE_TOPIC in config:
cg.add(
mqtt_.set_custom_fan_mode_state_topic(config[CONF_FAN_MODE_STATE_TOPIC])
)
if CONF_MODE_COMMAND_TOPIC in config:
cg.add(mqtt_.set_custom_mode_command_topic(config[CONF_MODE_COMMAND_TOPIC]))
if CONF_MODE_STATE_TOPIC in config:
cg.add(mqtt_.set_custom_mode_state_topic(config[CONF_MODE_STATE_TOPIC]))
if CONF_SWING_MODE_COMMAND_TOPIC in config:
cg.add(
mqtt_.set_custom_swing_mode_command_topic(
config[CONF_SWING_MODE_COMMAND_TOPIC]
)
)
if CONF_SWING_MODE_STATE_TOPIC in config:
cg.add(
mqtt_.set_custom_swing_mode_state_topic(
config[CONF_SWING_MODE_STATE_TOPIC]
)
)
if CONF_TARGET_TEMPERATURE_COMMAND_TOPIC in config:
cg.add(
mqtt_.set_custom_target_temperature_command_topic(
config[CONF_TARGET_TEMPERATURE_COMMAND_TOPIC]
)
)
if CONF_TARGET_TEMPERATURE_STATE_TOPIC in config:
cg.add(
mqtt_.set_custom_target_temperature_state_topic(
config[CONF_TARGET_TEMPERATURE_STATE_TOPIC]
)
)
if CONF_TARGET_TEMPERATURE_HIGH_COMMAND_TOPIC in config:
cg.add(
mqtt_.set_custom_target_temperature_high_command_topic(
config[CONF_TARGET_TEMPERATURE_HIGH_COMMAND_TOPIC]
)
)
if CONF_TARGET_TEMPERATURE_HIGH_STATE_TOPIC in config:
cg.add(
mqtt_.set_custom_target_temperature_high_state_topic(
config[CONF_TARGET_TEMPERATURE_HIGH_STATE_TOPIC]
)
)
if CONF_TARGET_TEMPERATURE_LOW_COMMAND_TOPIC in config:
cg.add(
mqtt_.set_custom_target_temperature_low_command_topic(
config[CONF_TARGET_TEMPERATURE_LOW_COMMAND_TOPIC]
)
)
if CONF_TARGET_TEMPERATURE_LOW_STATE_TOPIC in config:
cg.add(
mqtt_.set_custom_target_temperature_state_topic(
config[CONF_TARGET_TEMPERATURE_LOW_STATE_TOPIC]
)
)
for conf in config.get(CONF_ON_STATE, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf)
async def register_climate(var, config):
if not CORE.has_id(config[CONF_ID]):

View File

@@ -42,5 +42,12 @@ template<typename... Ts> class ControlAction : public Action<Ts...> {
Climate *climate_;
};
class StateTrigger : public Trigger<> {
public:
StateTrigger(Climate *climate) {
climate->add_on_state_callback([this]() { this->trigger(); });
}
};
} // namespace climate
} // namespace esphome

View File

@@ -9,8 +9,8 @@ void ClimateCall::perform() {
ESP_LOGD(TAG, "'%s' - Setting", this->parent_->get_name().c_str());
this->validate_();
if (this->mode_.has_value()) {
const char *mode_s = climate_mode_to_string(*this->mode_);
ESP_LOGD(TAG, " Mode: %s", mode_s);
const LogString *mode_s = climate_mode_to_string(*this->mode_);
ESP_LOGD(TAG, " Mode: %s", LOG_STR_ARG(mode_s));
}
if (this->custom_fan_mode_.has_value()) {
this->fan_mode_.reset();
@@ -18,8 +18,8 @@ void ClimateCall::perform() {
}
if (this->fan_mode_.has_value()) {
this->custom_fan_mode_.reset();
const char *fan_mode_s = climate_fan_mode_to_string(*this->fan_mode_);
ESP_LOGD(TAG, " Fan: %s", fan_mode_s);
const LogString *fan_mode_s = climate_fan_mode_to_string(*this->fan_mode_);
ESP_LOGD(TAG, " Fan: %s", LOG_STR_ARG(fan_mode_s));
}
if (this->custom_preset_.has_value()) {
this->preset_.reset();
@@ -27,12 +27,12 @@ void ClimateCall::perform() {
}
if (this->preset_.has_value()) {
this->custom_preset_.reset();
const char *preset_s = climate_preset_to_string(*this->preset_);
ESP_LOGD(TAG, " Preset: %s", preset_s);
const LogString *preset_s = climate_preset_to_string(*this->preset_);
ESP_LOGD(TAG, " Preset: %s", LOG_STR_ARG(preset_s));
}
if (this->swing_mode_.has_value()) {
const char *swing_mode_s = climate_swing_mode_to_string(*this->swing_mode_);
ESP_LOGD(TAG, " Swing: %s", swing_mode_s);
const LogString *swing_mode_s = climate_swing_mode_to_string(*this->swing_mode_);
ESP_LOGD(TAG, " Swing: %s", LOG_STR_ARG(swing_mode_s));
}
if (this->target_temperature_.has_value()) {
ESP_LOGD(TAG, " Target Temperature: %.2f", *this->target_temperature_);
@@ -50,7 +50,7 @@ void ClimateCall::validate_() {
if (this->mode_.has_value()) {
auto mode = *this->mode_;
if (!traits.supports_mode(mode)) {
ESP_LOGW(TAG, " Mode %s is not supported by this device!", climate_mode_to_string(mode));
ESP_LOGW(TAG, " Mode %s is not supported by this device!", LOG_STR_ARG(climate_mode_to_string(mode)));
this->mode_.reset();
}
}
@@ -63,7 +63,8 @@ void ClimateCall::validate_() {
} else if (this->fan_mode_.has_value()) {
auto fan_mode = *this->fan_mode_;
if (!traits.supports_fan_mode(fan_mode)) {
ESP_LOGW(TAG, " Fan Mode %s is not supported by this device!", climate_fan_mode_to_string(fan_mode));
ESP_LOGW(TAG, " Fan Mode %s is not supported by this device!",
LOG_STR_ARG(climate_fan_mode_to_string(fan_mode)));
this->fan_mode_.reset();
}
}
@@ -76,14 +77,15 @@ void ClimateCall::validate_() {
} else if (this->preset_.has_value()) {
auto preset = *this->preset_;
if (!traits.supports_preset(preset)) {
ESP_LOGW(TAG, " Preset %s is not supported by this device!", climate_preset_to_string(preset));
ESP_LOGW(TAG, " Preset %s is not supported by this device!", LOG_STR_ARG(climate_preset_to_string(preset)));
this->preset_.reset();
}
}
if (this->swing_mode_.has_value()) {
auto swing_mode = *this->swing_mode_;
if (!traits.supports_swing_mode(swing_mode)) {
ESP_LOGW(TAG, " Swing Mode %s is not supported by this device!", climate_swing_mode_to_string(swing_mode));
ESP_LOGW(TAG, " Swing Mode %s is not supported by this device!",
LOG_STR_ARG(climate_swing_mode_to_string(swing_mode)));
this->swing_mode_.reset();
}
}
@@ -93,7 +95,7 @@ void ClimateCall::validate_() {
ESP_LOGW(TAG, " Cannot set target temperature for climate device "
"with two-point target temperature!");
this->target_temperature_.reset();
} else if (isnan(target)) {
} else if (std::isnan(target)) {
ESP_LOGW(TAG, " Target temperature must not be NAN!");
this->target_temperature_.reset();
}
@@ -105,11 +107,11 @@ void ClimateCall::validate_() {
this->target_temperature_high_.reset();
}
}
if (this->target_temperature_low_.has_value() && isnan(*this->target_temperature_low_)) {
if (this->target_temperature_low_.has_value() && std::isnan(*this->target_temperature_low_)) {
ESP_LOGW(TAG, " Target temperature low must not be NAN!");
this->target_temperature_low_.reset();
}
if (this->target_temperature_high_.has_value() && isnan(*this->target_temperature_high_)) {
if (this->target_temperature_high_.has_value() && std::isnan(*this->target_temperature_high_)) {
ESP_LOGW(TAG, " Target temperature low must not be NAN!");
this->target_temperature_high_.reset();
}
@@ -316,17 +318,23 @@ void Climate::add_on_state_callback(std::function<void()> &&callback) {
static const uint32_t RESTORE_STATE_VERSION = 0x848EA6ADUL;
optional<ClimateDeviceRestoreState> Climate::restore_state_() {
this->rtc_ =
global_preferences.make_preference<ClimateDeviceRestoreState>(this->get_object_id_hash() ^ RESTORE_STATE_VERSION);
this->rtc_ = global_preferences->make_preference<ClimateDeviceRestoreState>(this->get_object_id_hash() ^
RESTORE_STATE_VERSION);
ClimateDeviceRestoreState recovered{};
if (!this->rtc_.load(&recovered))
return {};
return recovered;
}
void Climate::save_state_() {
#if defined(USE_ESP_IDF) && !defined(CLANG_TIDY)
#pragma GCC diagnostic ignored "-Wclass-memaccess"
#endif
ClimateDeviceRestoreState state{};
// initialize as zero to prevent random data on stack triggering erase
memset(&state, 0, sizeof(ClimateDeviceRestoreState));
#if USE_ESP_IDF && !defined(CLANG_TIDY)
#pragma GCC diagnostic pop
#endif
state.mode = this->mode;
auto traits = this->get_traits();
@@ -373,24 +381,24 @@ void Climate::publish_state() {
ESP_LOGD(TAG, "'%s' - Sending state:", this->name_.c_str());
auto traits = this->get_traits();
ESP_LOGD(TAG, " Mode: %s", climate_mode_to_string(this->mode));
ESP_LOGD(TAG, " Mode: %s", LOG_STR_ARG(climate_mode_to_string(this->mode)));
if (traits.get_supports_action()) {
ESP_LOGD(TAG, " Action: %s", climate_action_to_string(this->action));
ESP_LOGD(TAG, " Action: %s", LOG_STR_ARG(climate_action_to_string(this->action)));
}
if (traits.get_supports_fan_modes() && this->fan_mode.has_value()) {
ESP_LOGD(TAG, " Fan Mode: %s", climate_fan_mode_to_string(this->fan_mode.value()));
ESP_LOGD(TAG, " Fan Mode: %s", LOG_STR_ARG(climate_fan_mode_to_string(this->fan_mode.value())));
}
if (!traits.get_supported_custom_fan_modes().empty() && this->custom_fan_mode.has_value()) {
ESP_LOGD(TAG, " Custom Fan Mode: %s", this->custom_fan_mode.value().c_str());
}
if (traits.get_supports_presets() && this->preset.has_value()) {
ESP_LOGD(TAG, " Preset: %s", climate_preset_to_string(this->preset.value()));
ESP_LOGD(TAG, " Preset: %s", LOG_STR_ARG(climate_preset_to_string(this->preset.value())));
}
if (!traits.get_supported_custom_presets().empty() && this->custom_preset.has_value()) {
ESP_LOGD(TAG, " Custom Preset: %s", this->custom_preset.value().c_str());
}
if (traits.get_supports_swing_modes()) {
ESP_LOGD(TAG, " Swing Mode: %s", climate_swing_mode_to_string(this->swing_mode));
ESP_LOGD(TAG, " Swing Mode: %s", LOG_STR_ARG(climate_swing_mode_to_string(this->swing_mode)));
}
if (traits.get_supports_current_temperature()) {
ESP_LOGD(TAG, " Current Temperature: %.2f°C", this->current_temperature);
@@ -432,7 +440,11 @@ void Climate::set_visual_max_temperature_override(float visual_max_temperature_o
void Climate::set_visual_temperature_step_override(float visual_temperature_step_override) {
this->visual_temperature_step_override_ = visual_temperature_step_override;
}
Climate::Climate(const std::string &name) : Nameable(name) {}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
Climate::Climate(const std::string &name) : EntityBase(name) {}
#pragma GCC diagnostic pop
Climate::Climate() : Climate("") {}
ClimateCall Climate::make_call() { return ClimateCall(this); }
@@ -534,12 +546,12 @@ void Climate::dump_traits_(const char *tag) {
if (!traits.get_supported_modes().empty()) {
ESP_LOGCONFIG(tag, " [x] Supported modes:");
for (ClimateMode m : traits.get_supported_modes())
ESP_LOGCONFIG(tag, " - %s", climate_mode_to_string(m));
ESP_LOGCONFIG(tag, " - %s", LOG_STR_ARG(climate_mode_to_string(m)));
}
if (!traits.get_supported_fan_modes().empty()) {
ESP_LOGCONFIG(tag, " [x] Supported fan modes:");
for (ClimateFanMode m : traits.get_supported_fan_modes())
ESP_LOGCONFIG(tag, " - %s", climate_fan_mode_to_string(m));
ESP_LOGCONFIG(tag, " - %s", LOG_STR_ARG(climate_fan_mode_to_string(m)));
}
if (!traits.get_supported_custom_fan_modes().empty()) {
ESP_LOGCONFIG(tag, " [x] Supported custom fan modes:");
@@ -549,7 +561,7 @@ void Climate::dump_traits_(const char *tag) {
if (!traits.get_supported_presets().empty()) {
ESP_LOGCONFIG(tag, " [x] Supported presets:");
for (ClimatePreset p : traits.get_supported_presets())
ESP_LOGCONFIG(tag, " - %s", climate_preset_to_string(p));
ESP_LOGCONFIG(tag, " - %s", LOG_STR_ARG(climate_preset_to_string(p)));
}
if (!traits.get_supported_custom_presets().empty()) {
ESP_LOGCONFIG(tag, " [x] Supported custom presets:");
@@ -559,7 +571,7 @@ void Climate::dump_traits_(const char *tag) {
if (!traits.get_supported_swing_modes().empty()) {
ESP_LOGCONFIG(tag, " [x] Supported swing modes:");
for (ClimateSwingMode m : traits.get_supported_swing_modes())
ESP_LOGCONFIG(tag, " - %s", climate_swing_mode_to_string(m));
ESP_LOGCONFIG(tag, " - %s", LOG_STR_ARG(climate_swing_mode_to_string(m)));
}
}

View File

@@ -1,6 +1,7 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/entity_base.h"
#include "esphome/core/helpers.h"
#include "esphome/core/preferences.h"
#include "esphome/core/log.h"
@@ -12,7 +13,7 @@ namespace climate {
#define LOG_CLIMATE(prefix, type, obj) \
if ((obj) != nullptr) { \
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, (obj)->get_name().c_str()); \
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \
}
class Climate;
@@ -163,7 +164,7 @@ struct ClimateDeviceRestoreState {
* 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_HEAT_COOL) ...`
*/
class Climate : public Nameable {
class Climate : public EntityBase {
public:
/// Construct a climate device with empty name (will be set later).
Climate();

View File

@@ -3,105 +3,105 @@
namespace esphome {
namespace climate {
const char *climate_mode_to_string(ClimateMode mode) {
const LogString *climate_mode_to_string(ClimateMode mode) {
switch (mode) {
case CLIMATE_MODE_OFF:
return "OFF";
case CLIMATE_MODE_AUTO:
return "AUTO";
case CLIMATE_MODE_COOL:
return "COOL";
case CLIMATE_MODE_HEAT:
return "HEAT";
case CLIMATE_MODE_FAN_ONLY:
return "FAN_ONLY";
case CLIMATE_MODE_DRY:
return "DRY";
return LOG_STR("OFF");
case CLIMATE_MODE_HEAT_COOL:
return "HEAT_COOL";
return LOG_STR("HEAT_COOL");
case CLIMATE_MODE_AUTO:
return LOG_STR("AUTO");
case CLIMATE_MODE_COOL:
return LOG_STR("COOL");
case CLIMATE_MODE_HEAT:
return LOG_STR("HEAT");
case CLIMATE_MODE_FAN_ONLY:
return LOG_STR("FAN_ONLY");
case CLIMATE_MODE_DRY:
return LOG_STR("DRY");
default:
return "UNKNOWN";
return LOG_STR("UNKNOWN");
}
}
const char *climate_action_to_string(ClimateAction action) {
const LogString *climate_action_to_string(ClimateAction action) {
switch (action) {
case CLIMATE_ACTION_OFF:
return "OFF";
return LOG_STR("OFF");
case CLIMATE_ACTION_COOLING:
return "COOLING";
return LOG_STR("COOLING");
case CLIMATE_ACTION_HEATING:
return "HEATING";
return LOG_STR("HEATING");
case CLIMATE_ACTION_IDLE:
return "IDLE";
return LOG_STR("IDLE");
case CLIMATE_ACTION_DRYING:
return "DRYING";
return LOG_STR("DRYING");
case CLIMATE_ACTION_FAN:
return "FAN";
return LOG_STR("FAN");
default:
return "UNKNOWN";
return LOG_STR("UNKNOWN");
}
}
const char *climate_fan_mode_to_string(ClimateFanMode fan_mode) {
const LogString *climate_fan_mode_to_string(ClimateFanMode fan_mode) {
switch (fan_mode) {
case climate::CLIMATE_FAN_ON:
return "ON";
return LOG_STR("ON");
case climate::CLIMATE_FAN_OFF:
return "OFF";
return LOG_STR("OFF");
case climate::CLIMATE_FAN_AUTO:
return "AUTO";
return LOG_STR("AUTO");
case climate::CLIMATE_FAN_LOW:
return "LOW";
return LOG_STR("LOW");
case climate::CLIMATE_FAN_MEDIUM:
return "MEDIUM";
return LOG_STR("MEDIUM");
case climate::CLIMATE_FAN_HIGH:
return "HIGH";
return LOG_STR("HIGH");
case climate::CLIMATE_FAN_MIDDLE:
return "MIDDLE";
return LOG_STR("MIDDLE");
case climate::CLIMATE_FAN_FOCUS:
return "FOCUS";
return LOG_STR("FOCUS");
case climate::CLIMATE_FAN_DIFFUSE:
return "DIFFUSE";
return LOG_STR("DIFFUSE");
default:
return "UNKNOWN";
return LOG_STR("UNKNOWN");
}
}
const char *climate_swing_mode_to_string(ClimateSwingMode swing_mode) {
const LogString *climate_swing_mode_to_string(ClimateSwingMode swing_mode) {
switch (swing_mode) {
case climate::CLIMATE_SWING_OFF:
return "OFF";
return LOG_STR("OFF");
case climate::CLIMATE_SWING_BOTH:
return "BOTH";
return LOG_STR("BOTH");
case climate::CLIMATE_SWING_VERTICAL:
return "VERTICAL";
return LOG_STR("VERTICAL");
case climate::CLIMATE_SWING_HORIZONTAL:
return "HORIZONTAL";
return LOG_STR("HORIZONTAL");
default:
return "UNKNOWN";
return LOG_STR("UNKNOWN");
}
}
const char *climate_preset_to_string(ClimatePreset preset) {
const LogString *climate_preset_to_string(ClimatePreset preset) {
switch (preset) {
case climate::CLIMATE_PRESET_NONE:
return "NONE";
return LOG_STR("NONE");
case climate::CLIMATE_PRESET_HOME:
return "HOME";
return LOG_STR("HOME");
case climate::CLIMATE_PRESET_ECO:
return "ECO";
return LOG_STR("ECO");
case climate::CLIMATE_PRESET_AWAY:
return "AWAY";
return LOG_STR("AWAY");
case climate::CLIMATE_PRESET_BOOST:
return "BOOST";
return LOG_STR("BOOST");
case climate::CLIMATE_PRESET_COMFORT:
return "COMFORT";
return LOG_STR("COMFORT");
case climate::CLIMATE_PRESET_SLEEP:
return "SLEEP";
return LOG_STR("SLEEP");
case climate::CLIMATE_PRESET_ACTIVITY:
return "ACTIVITY";
return LOG_STR("ACTIVITY");
default:
return "UNKNOWN";
return LOG_STR("UNKNOWN");
}
}

View File

@@ -1,6 +1,7 @@
#pragma once
#include <cstdint>
#include "esphome/core/log.h"
namespace esphome {
namespace climate {
@@ -96,19 +97,19 @@ enum ClimatePreset : uint8_t {
};
/// Convert the given ClimateMode to a human-readable string.
const char *climate_mode_to_string(ClimateMode mode);
const LogString *climate_mode_to_string(ClimateMode mode);
/// Convert the given ClimateAction to a human-readable string.
const char *climate_action_to_string(ClimateAction action);
const LogString *climate_action_to_string(ClimateAction action);
/// Convert the given ClimateFanMode to a human-readable string.
const char *climate_fan_mode_to_string(ClimateFanMode mode);
const LogString *climate_fan_mode_to_string(ClimateFanMode mode);
/// Convert the given ClimateSwingMode to a human-readable string.
const char *climate_swing_mode_to_string(ClimateSwingMode mode);
const LogString *climate_swing_mode_to_string(ClimateSwingMode mode);
/// Convert the given ClimateSwingMode to a human-readable string.
const char *climate_preset_to_string(ClimatePreset preset);
const LogString *climate_preset_to_string(ClimatePreset preset);
} // namespace climate
} // namespace esphome

View File

@@ -1,5 +1,5 @@
#include "climate_traits.h"
#include "esphome/core/log.h"
#include <cstdio>
namespace esphome {
namespace climate {

View File

@@ -52,7 +52,7 @@ void ClimateIR::setup() {
this->swing_mode = climate::CLIMATE_SWING_OFF;
}
// Never send nan to HA
if (isnan(this->target_temperature))
if (std::isnan(this->target_temperature))
this->target_temperature = 24;
}

View File

@@ -4,18 +4,21 @@ from esphome import automation
from esphome.automation import maybe_simple_id, Condition
from esphome.components import mqtt
from esphome.const import (
CONF_DISABLED_BY_DEFAULT,
CONF_ID,
CONF_INTERNAL,
CONF_DEVICE_CLASS,
CONF_STATE,
CONF_POSITION,
CONF_POSITION_COMMAND_TOPIC,
CONF_POSITION_STATE_TOPIC,
CONF_TILT,
CONF_TILT_COMMAND_TOPIC,
CONF_TILT_STATE_TOPIC,
CONF_STOP,
CONF_MQTT_ID,
CONF_NAME,
CONF_TRIGGER_ID,
)
from esphome.core import CORE, coroutine_with_priority
from esphome.cpp_helpers import setup_entity
IS_PLATFORM_COMPONENT = True
@@ -36,7 +39,7 @@ DEVICE_CLASSES = [
cover_ns = cg.esphome_ns.namespace("cover")
Cover = cover_ns.class_("Cover", cg.Nameable)
Cover = cover_ns.class_("Cover", cg.EntityBase)
COVER_OPEN = cover_ns.COVER_OPEN
COVER_CLOSED = cover_ns.COVER_CLOSED
@@ -59,33 +62,84 @@ validate_cover_operation = cv.enum(COVER_OPERATIONS, upper=True)
OpenAction = cover_ns.class_("OpenAction", automation.Action)
CloseAction = cover_ns.class_("CloseAction", automation.Action)
StopAction = cover_ns.class_("StopAction", automation.Action)
ToggleAction = cover_ns.class_("ToggleAction", automation.Action)
ControlAction = cover_ns.class_("ControlAction", automation.Action)
CoverPublishAction = cover_ns.class_("CoverPublishAction", automation.Action)
CoverIsOpenCondition = cover_ns.class_("CoverIsOpenCondition", Condition)
CoverIsClosedCondition = cover_ns.class_("CoverIsClosedCondition", Condition)
COVER_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
# Triggers
CoverOpenTrigger = cover_ns.class_("CoverOpenTrigger", automation.Trigger.template())
CoverClosedTrigger = cover_ns.class_(
"CoverClosedTrigger", automation.Trigger.template()
)
CONF_ON_OPEN = "on_open"
CONF_ON_CLOSED = "on_closed"
COVER_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
{
cv.GenerateID(): cv.declare_id(Cover),
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTCoverComponent),
cv.Optional(CONF_DEVICE_CLASS): cv.one_of(*DEVICE_CLASSES, lower=True),
# TODO: MQTT topic options
cv.Optional(CONF_POSITION_COMMAND_TOPIC): cv.All(
cv.requires_component("mqtt"), cv.subscribe_topic
),
cv.Optional(CONF_POSITION_STATE_TOPIC): cv.All(
cv.requires_component("mqtt"), cv.subscribe_topic
),
cv.Optional(CONF_TILT_COMMAND_TOPIC): cv.All(
cv.requires_component("mqtt"), cv.subscribe_topic
),
cv.Optional(CONF_TILT_STATE_TOPIC): cv.All(
cv.requires_component("mqtt"), cv.subscribe_topic
),
cv.Optional(CONF_ON_OPEN): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CoverOpenTrigger),
}
),
cv.Optional(CONF_ON_CLOSED): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CoverClosedTrigger),
}
),
}
)
async def setup_cover_core_(var, config):
cg.add(var.set_name(config[CONF_NAME]))
cg.add(var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT]))
if CONF_INTERNAL in config:
cg.add(var.set_internal(config[CONF_INTERNAL]))
await setup_entity(var, config)
if CONF_DEVICE_CLASS in config:
cg.add(var.set_device_class(config[CONF_DEVICE_CLASS]))
for conf in config.get(CONF_ON_OPEN, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf)
for conf in config.get(CONF_ON_CLOSED, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf)
if CONF_MQTT_ID in config:
mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var)
await mqtt.register_mqtt_component(mqtt_, config)
if CONF_POSITION_STATE_TOPIC in config:
cg.add(
mqtt_.set_custom_position_state_topic(config[CONF_POSITION_STATE_TOPIC])
)
if CONF_POSITION_COMMAND_TOPIC in config:
cg.add(
mqtt_.set_custom_position_command_topic(
config[CONF_POSITION_COMMAND_TOPIC]
)
)
if CONF_TILT_STATE_TOPIC in config:
cg.add(mqtt_.set_custom_tilt_state_topic(config[CONF_TILT_STATE_TOPIC]))
if CONF_TILT_COMMAND_TOPIC in config:
cg.add(mqtt_.set_custom_tilt_command_topic(config[CONF_TILT_COMMAND_TOPIC]))
async def register_cover(var, config):
if not CORE.has_id(config[CONF_ID]):
@@ -119,6 +173,12 @@ async def cover_stop_to_code(config, action_id, template_arg, args):
return cg.new_Pvariable(action_id, template_arg, paren)
@automation.register_action("cover.toggle", ToggleAction, COVER_ACTION_SCHEMA)
def cover_toggle_to_code(config, action_id, template_arg, args):
paren = yield cg.get_variable(config[CONF_ID])
yield cg.new_Pvariable(action_id, template_arg, paren)
COVER_CONTROL_ACTION_SCHEMA = cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(Cover),

View File

@@ -37,6 +37,16 @@ template<typename... Ts> class StopAction : public Action<Ts...> {
Cover *cover_;
};
template<typename... Ts> class ToggleAction : public Action<Ts...> {
public:
explicit ToggleAction(Cover *cover) : cover_(cover) {}
void play(Ts... x) override { this->cover_->make_call().set_command_toggle().perform(); }
protected:
Cover *cover_;
};
template<typename... Ts> class ControlAction : public Action<Ts...> {
public:
explicit ControlAction(Cover *cover) : cover_(cover) {}
@@ -89,6 +99,7 @@ template<typename... Ts> class CoverIsOpenCondition : public Condition<Ts...> {
protected:
Cover *cover_;
};
template<typename... Ts> class CoverIsClosedCondition : public Condition<Ts...> {
public:
CoverIsClosedCondition(Cover *cover) : cover_(cover) {}
@@ -98,5 +109,27 @@ template<typename... Ts> class CoverIsClosedCondition : public Condition<Ts...>
Cover *cover_;
};
class CoverOpenTrigger : public Trigger<> {
public:
CoverOpenTrigger(Cover *a_cover) {
a_cover->add_on_state_callback([this, a_cover]() {
if (a_cover->is_fully_open()) {
this->trigger();
}
});
}
};
class CoverClosedTrigger : public Trigger<> {
public:
CoverClosedTrigger(Cover *a_cover) {
a_cover->add_on_state_callback([this, a_cover]() {
if (a_cover->is_fully_closed()) {
this->trigger();
}
});
}
};
} // namespace cover
} // namespace esphome

View File

@@ -31,7 +31,7 @@ const char *cover_operation_to_str(CoverOperation op) {
}
}
Cover::Cover(const std::string &name) : Nameable(name), position{COVER_OPEN} {}
Cover::Cover(const std::string &name) : EntityBase(name), position{COVER_OPEN} {}
uint32_t Cover::hash_base() { return 1727367479UL; }
@@ -43,6 +43,8 @@ CoverCall &CoverCall::set_command(const char *command) {
this->set_command_close();
} else if (strcasecmp(command, "STOP") == 0) {
this->set_command_stop();
} else if (strcasecmp(command, "TOGGLE") == 0) {
this->set_command_toggle();
} else {
ESP_LOGW(TAG, "'%s' - Unrecognized command %s", this->parent_->get_name().c_str(), command);
}
@@ -60,6 +62,10 @@ CoverCall &CoverCall::set_command_stop() {
this->stop_ = true;
return *this;
}
CoverCall &CoverCall::set_command_toggle() {
this->toggle_ = true;
return *this;
}
CoverCall &CoverCall::set_position(float position) {
this->position_ = position;
return *this;
@@ -85,10 +91,14 @@ void CoverCall::perform() {
if (this->tilt_.has_value()) {
ESP_LOGD(TAG, " Tilt: %.0f%%", *this->tilt_ * 100.0f);
}
if (this->toggle_.has_value()) {
ESP_LOGD(TAG, " Command: TOGGLE");
}
this->parent_->control(*this);
}
const optional<float> &CoverCall::get_position() const { return this->position_; }
const optional<float> &CoverCall::get_tilt() const { return this->tilt_; }
const optional<bool> &CoverCall::get_toggle() const { return this->toggle_; }
void CoverCall::validate_() {
auto traits = this->parent_->get_traits();
if (this->position_.has_value()) {
@@ -111,6 +121,12 @@ void CoverCall::validate_() {
this->tilt_ = clamp(tilt, 0.0f, 1.0f);
}
}
if (this->toggle_.has_value()) {
if (!traits.get_supports_toggle()) {
ESP_LOGW(TAG, "'%s' - This cover device does not support toggle!", this->parent_->get_name().c_str());
this->toggle_.reset();
}
}
if (this->stop_) {
if (this->position_.has_value()) {
ESP_LOGW(TAG, "Cannot set position when stopping a cover!");
@@ -120,6 +136,10 @@ void CoverCall::validate_() {
ESP_LOGW(TAG, "Cannot set tilt when stopping a cover!");
this->tilt_.reset();
}
if (this->toggle_.has_value()) {
ESP_LOGW(TAG, "Cannot set toggle when stopping a cover!");
this->toggle_.reset();
}
}
}
CoverCall &CoverCall::set_stop(bool stop) {
@@ -180,7 +200,7 @@ void Cover::publish_state(bool save) {
}
}
optional<CoverRestoreState> Cover::restore_state_() {
this->rtc_ = global_preferences.make_preference<CoverRestoreState>(this->get_object_id_hash());
this->rtc_ = global_preferences->make_preference<CoverRestoreState>(this->get_object_id_hash());
CoverRestoreState recovered{};
if (!this->rtc_.load(&recovered))
return {};

View File

@@ -1,6 +1,7 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/entity_base.h"
#include "esphome/core/helpers.h"
#include "esphome/core/preferences.h"
#include "cover_traits.h"
@@ -13,7 +14,7 @@ const extern float COVER_CLOSED;
#define LOG_COVER(prefix, type, obj) \
if ((obj) != nullptr) { \
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, (obj)->get_name().c_str()); \
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \
auto traits_ = (obj)->get_traits(); \
if (traits_.get_is_assumed_state()) { \
ESP_LOGCONFIG(TAG, "%s Assumed State: YES", prefix); \
@@ -29,7 +30,7 @@ class CoverCall {
public:
CoverCall(Cover *parent);
/// Set the command as a string, "STOP", "OPEN", "CLOSE".
/// Set the command as a string, "STOP", "OPEN", "CLOSE", "TOGGLE".
CoverCall &set_command(const char *command);
/// Set the command to open the cover.
CoverCall &set_command_open();
@@ -37,6 +38,8 @@ class CoverCall {
CoverCall &set_command_close();
/// Set the command to stop the cover.
CoverCall &set_command_stop();
/// Set the command to toggle the cover.
CoverCall &set_command_toggle();
/// Set the call to a certain target position.
CoverCall &set_position(float position);
/// Set the call to a certain target tilt.
@@ -50,6 +53,7 @@ class CoverCall {
const optional<float> &get_position() const;
bool get_stop() const;
const optional<float> &get_tilt() const;
const optional<bool> &get_toggle() const;
protected:
void validate_();
@@ -58,6 +62,7 @@ class CoverCall {
bool stop_{false};
optional<float> position_{};
optional<float> tilt_{};
optional<bool> toggle_{};
};
/// Struct used to store the restored state of a cover
@@ -103,7 +108,7 @@ const char *cover_operation_to_str(CoverOperation op);
* to control all values of the cover. Also implement get_traits() to return what operations
* the cover supports.
*/
class Cover : public Nameable {
class Cover : public EntityBase {
public:
explicit Cover();
explicit Cover(const std::string &name);

View File

@@ -13,11 +13,14 @@ class CoverTraits {
void set_supports_position(bool supports_position) { this->supports_position_ = supports_position; }
bool get_supports_tilt() const { return this->supports_tilt_; }
void set_supports_tilt(bool supports_tilt) { this->supports_tilt_ = supports_tilt; }
bool get_supports_toggle() const { return this->supports_toggle_; }
void set_supports_toggle(bool supports_toggle) { this->supports_toggle_ = supports_toggle; }
protected:
bool is_assumed_state_{false};
bool supports_position_{false};
bool supports_tilt_{false};
bool supports_toggle_{false};
};
} // namespace cover

View File

@@ -102,8 +102,6 @@ void CS5460AComponent::hw_init_() {
/* Doesn't reset the register values etc., just restarts the "computation cycle" */
void CS5460AComponent::restart_() {
int cnt;
this->enable();
/* Stop running conversion, wake up if needed */
this->write_byte(CMD_POWER_UP);

View File

View File

@@ -0,0 +1,244 @@
#include "cse7761.h"
#include "esphome/core/log.h"
namespace esphome {
namespace cse7761 {
static const char *const TAG = "cse7761";
/*********************************************************************************************\
* CSE7761 - Energy (Sonoff Dual R3 Pow v1.x)
*
* Based on Tasmota source code
* See https://github.com/arendst/Tasmota/discussions/10793
* https://github.com/arendst/Tasmota/blob/development/tasmota/xnrg_19_cse7761.ino
\*********************************************************************************************/
static const int CSE7761_UREF = 42563; // RmsUc
static const int CSE7761_IREF = 52241; // RmsIAC
static const int CSE7761_PREF = 44513; // PowerPAC
static const uint8_t CSE7761_REG_SYSCON = 0x00; // (2) System Control Register (0x0A04)
static const uint8_t CSE7761_REG_EMUCON = 0x01; // (2) Metering control register (0x0000)
static const uint8_t CSE7761_REG_EMUCON2 = 0x13; // (2) Metering control register 2 (0x0001)
static const uint8_t CSE7761_REG_PULSE1SEL = 0x1D; // (2) Pin function output select register (0x3210)
static const uint8_t CSE7761_REG_RMSIA = 0x24; // (3) The effective value of channel A current (0x000000)
static const uint8_t CSE7761_REG_RMSIB = 0x25; // (3) The effective value of channel B current (0x000000)
static const uint8_t CSE7761_REG_RMSU = 0x26; // (3) Voltage RMS (0x000000)
static const uint8_t CSE7761_REG_POWERPA = 0x2C; // (4) Channel A active power, update rate 27.2Hz (0x00000000)
static const uint8_t CSE7761_REG_POWERPB = 0x2D; // (4) Channel B active power, update rate 27.2Hz (0x00000000)
static const uint8_t CSE7761_REG_SYSSTATUS = 0x43; // (1) System status register
static const uint8_t CSE7761_REG_COEFFCHKSUM = 0x6F; // (2) Coefficient checksum
static const uint8_t CSE7761_REG_RMSIAC = 0x70; // (2) Channel A effective current conversion coefficient
static const uint8_t CSE7761_SPECIAL_COMMAND = 0xEA; // Start special command
static const uint8_t CSE7761_CMD_RESET = 0x96; // Reset command, after receiving the command, the chip resets
static const uint8_t CSE7761_CMD_CLOSE_WRITE = 0xDC; // Close write operation
static const uint8_t CSE7761_CMD_ENABLE_WRITE = 0xE5; // Enable write operation
enum CSE7761 { RMS_IAC, RMS_IBC, RMS_UC, POWER_PAC, POWER_PBC, POWER_SC, ENERGY_AC, ENERGY_BC };
void CSE7761Component::setup() {
ESP_LOGCONFIG(TAG, "Setting up CSE7761...");
this->write_(CSE7761_SPECIAL_COMMAND, CSE7761_CMD_RESET);
uint16_t syscon = this->read_(0x00, 2); // Default 0x0A04
if ((0x0A04 == syscon) && this->chip_init_()) {
this->write_(CSE7761_SPECIAL_COMMAND, CSE7761_CMD_CLOSE_WRITE);
ESP_LOGD(TAG, "CSE7761 found");
this->data_.ready = true;
} else {
this->mark_failed();
}
}
void CSE7761Component::dump_config() {
ESP_LOGCONFIG(TAG, "CSE7761:");
if (this->is_failed()) {
ESP_LOGE(TAG, "Communication with CSE7761 failed!");
}
LOG_UPDATE_INTERVAL(this);
this->check_uart_settings(38400, 1, uart::UART_CONFIG_PARITY_EVEN, 8);
}
float CSE7761Component::get_setup_priority() const { return setup_priority::DATA; }
void CSE7761Component::update() {
if (this->data_.ready) {
this->get_data_();
}
}
void CSE7761Component::write_(uint8_t reg, uint16_t data) {
uint8_t buffer[5];
buffer[0] = 0xA5;
buffer[1] = reg;
uint32_t len = 2;
if (data) {
if (data < 0xFF) {
buffer[2] = data & 0xFF;
len = 3;
} else {
buffer[2] = (data >> 8) & 0xFF;
buffer[3] = data & 0xFF;
len = 4;
}
uint8_t crc = 0;
for (uint32_t i = 0; i < len; i++) {
crc += buffer[i];
}
buffer[len] = ~crc;
len++;
}
this->write_array(buffer, len);
}
bool CSE7761Component::read_once_(uint8_t reg, uint8_t size, uint32_t *value) {
while (this->available()) {
this->read();
}
this->write_(reg, 0);
uint8_t buffer[8] = {0};
uint32_t rcvd = 0;
for (uint32_t i = 0; i <= size; i++) {
int value = this->read();
if (value > -1 && rcvd < sizeof(buffer) - 1) {
buffer[rcvd++] = value;
}
}
if (!rcvd) {
ESP_LOGD(TAG, "Received 0 bytes for register %hhu", reg);
return false;
}
rcvd--;
uint32_t result = 0;
// CRC check
uint8_t crc = 0xA5 + reg;
for (uint32_t i = 0; i < rcvd; i++) {
result = (result << 8) | buffer[i];
crc += buffer[i];
}
crc = ~crc;
if (crc != buffer[rcvd]) {
return false;
}
*value = result;
return true;
}
uint32_t CSE7761Component::read_(uint8_t reg, uint8_t size) {
bool result = false; // Start loop
uint8_t retry = 3; // Retry up to three times
uint32_t value = 0; // Default no value
while (!result && retry > 0) {
retry--;
if (this->read_once_(reg, size, &value))
return value;
}
ESP_LOGE(TAG, "Reading register %hhu failed!", reg);
return value;
}
uint32_t CSE7761Component::coefficient_by_unit_(uint32_t unit) {
switch (unit) {
case RMS_UC:
return 0x400000 * 100 / this->data_.coefficient[RMS_UC];
case RMS_IAC:
return (0x800000 * 100 / this->data_.coefficient[RMS_IAC]) * 10; // Stay within 32 bits
case POWER_PAC:
return 0x80000000 / this->data_.coefficient[POWER_PAC];
}
return 0;
}
bool CSE7761Component::chip_init_() {
uint16_t calc_chksum = 0xFFFF;
for (uint32_t i = 0; i < 8; i++) {
this->data_.coefficient[i] = this->read_(CSE7761_REG_RMSIAC + i, 2);
calc_chksum += this->data_.coefficient[i];
}
calc_chksum = ~calc_chksum;
uint16_t coeff_chksum = this->read_(CSE7761_REG_COEFFCHKSUM, 2);
if ((calc_chksum != coeff_chksum) || (!calc_chksum)) {
ESP_LOGD(TAG, "Default calibration");
this->data_.coefficient[RMS_IAC] = CSE7761_IREF;
this->data_.coefficient[RMS_UC] = CSE7761_UREF;
this->data_.coefficient[POWER_PAC] = CSE7761_PREF;
}
this->write_(CSE7761_SPECIAL_COMMAND, CSE7761_CMD_ENABLE_WRITE);
uint8_t sys_status = this->read_(CSE7761_REG_SYSSTATUS, 1);
if (sys_status & 0x10) { // Write enable to protected registers (WREN)
this->write_(CSE7761_REG_SYSCON | 0x80, 0xFF04);
this->write_(CSE7761_REG_EMUCON | 0x80, 0x1183);
this->write_(CSE7761_REG_EMUCON2 | 0x80, 0x0FC1);
this->write_(CSE7761_REG_PULSE1SEL | 0x80, 0x3290);
} else {
ESP_LOGD(TAG, "Write failed at chip_init");
return false;
}
return true;
}
void CSE7761Component::get_data_() {
// The effective value of current and voltage Rms is a 24-bit signed number,
// the highest bit is 0 for valid data,
// and when the highest bit is 1, the reading will be processed as zero
// The active power parameter PowerA/B is in twos complement format, 32-bit
// data, the highest bit is Sign bit.
uint32_t value = this->read_(CSE7761_REG_RMSU, 3);
this->data_.voltage_rms = (value >= 0x800000) ? 0 : value;
value = this->read_(CSE7761_REG_RMSIA, 3);
this->data_.current_rms[0] = ((value >= 0x800000) || (value < 1600)) ? 0 : value; // No load threshold of 10mA
value = this->read_(CSE7761_REG_POWERPA, 4);
this->data_.active_power[0] = (0 == this->data_.current_rms[0]) ? 0 : ((uint32_t) abs((int) value));
value = this->read_(CSE7761_REG_RMSIB, 3);
this->data_.current_rms[1] = ((value >= 0x800000) || (value < 1600)) ? 0 : value; // No load threshold of 10mA
value = this->read_(CSE7761_REG_POWERPB, 4);
this->data_.active_power[1] = (0 == this->data_.current_rms[1]) ? 0 : ((uint32_t) abs((int) value));
// convert values and publish to sensors
float voltage = (float) this->data_.voltage_rms / this->coefficient_by_unit_(RMS_UC);
if (this->voltage_sensor_ != nullptr) {
this->voltage_sensor_->publish_state(voltage);
}
for (uint32_t channel = 0; channel < 2; channel++) {
// Active power = PowerPA * PowerPAC * 1000 / 0x80000000
float active_power = (float) this->data_.active_power[channel] / this->coefficient_by_unit_(POWER_PAC); // W
float amps = (float) this->data_.current_rms[channel] / this->coefficient_by_unit_(RMS_IAC); // A
ESP_LOGD(TAG, "Channel %d power %f W, current %f A", channel + 1, active_power, amps);
if (channel == 0) {
if (this->power_sensor_1_ != nullptr) {
this->power_sensor_1_->publish_state(active_power);
}
if (this->current_sensor_1_ != nullptr) {
this->current_sensor_1_->publish_state(amps);
}
} else if (channel == 1) {
if (this->power_sensor_2_ != nullptr) {
this->power_sensor_2_->publish_state(active_power);
}
if (this->current_sensor_2_ != nullptr) {
this->current_sensor_2_->publish_state(amps);
}
}
}
}
} // namespace cse7761
} // namespace esphome

View File

@@ -0,0 +1,52 @@
#pragma once
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/uart/uart.h"
#include "esphome/core/component.h"
namespace esphome {
namespace cse7761 {
struct CSE7761DataStruct {
uint32_t frequency = 0;
uint32_t voltage_rms = 0;
uint32_t current_rms[2] = {0};
uint32_t energy[2] = {0};
uint32_t active_power[2] = {0};
uint16_t coefficient[8] = {0};
uint8_t energy_update = 0;
bool ready = false;
};
/// This class implements support for the CSE7761 UART power sensor.
class CSE7761Component : public PollingComponent, public uart::UARTDevice {
public:
void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; }
void set_active_power_1_sensor(sensor::Sensor *power_sensor_1) { power_sensor_1_ = power_sensor_1; }
void set_current_1_sensor(sensor::Sensor *current_sensor_1) { current_sensor_1_ = current_sensor_1; }
void set_active_power_2_sensor(sensor::Sensor *power_sensor_2) { power_sensor_2_ = power_sensor_2; }
void set_current_2_sensor(sensor::Sensor *current_sensor_2) { current_sensor_2_ = current_sensor_2; }
void setup() override;
void dump_config() override;
float get_setup_priority() const override;
void update() override;
protected:
// Sensors
sensor::Sensor *voltage_sensor_{nullptr};
sensor::Sensor *power_sensor_1_{nullptr};
sensor::Sensor *current_sensor_1_{nullptr};
sensor::Sensor *power_sensor_2_{nullptr};
sensor::Sensor *current_sensor_2_{nullptr};
CSE7761DataStruct data_;
void write_(uint8_t reg, uint16_t data);
bool read_once_(uint8_t reg, uint8_t size, uint32_t *value);
uint32_t read_(uint8_t reg, uint8_t size);
uint32_t coefficient_by_unit_(uint32_t unit);
bool chip_init_();
void get_data_();
};
} // namespace cse7761
} // namespace esphome

View File

@@ -0,0 +1,90 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor, uart
from esphome.const import (
CONF_ID,
CONF_VOLTAGE,
DEVICE_CLASS_CURRENT,
DEVICE_CLASS_POWER,
DEVICE_CLASS_VOLTAGE,
STATE_CLASS_MEASUREMENT,
UNIT_VOLT,
UNIT_AMPERE,
UNIT_WATT,
)
CODEOWNERS = ["@berfenger"]
DEPENDENCIES = ["uart"]
cse7761_ns = cg.esphome_ns.namespace("cse7761")
CSE7761Component = cse7761_ns.class_(
"CSE7761Component", cg.PollingComponent, uart.UARTDevice
)
CONF_CURRENT_1 = "current_1"
CONF_CURRENT_2 = "current_2"
CONF_ACTIVE_POWER_1 = "active_power_1"
CONF_ACTIVE_POWER_2 = "active_power_2"
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(CSE7761Component),
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
unit_of_measurement=UNIT_VOLT,
accuracy_decimals=1,
device_class=DEVICE_CLASS_VOLTAGE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_CURRENT_1): sensor.sensor_schema(
unit_of_measurement=UNIT_AMPERE,
accuracy_decimals=2,
device_class=DEVICE_CLASS_CURRENT,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_CURRENT_2): sensor.sensor_schema(
unit_of_measurement=UNIT_AMPERE,
accuracy_decimals=2,
device_class=DEVICE_CLASS_CURRENT,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_ACTIVE_POWER_1): sensor.sensor_schema(
unit_of_measurement=UNIT_WATT,
accuracy_decimals=1,
device_class=DEVICE_CLASS_POWER,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_ACTIVE_POWER_2): sensor.sensor_schema(
unit_of_measurement=UNIT_WATT,
accuracy_decimals=1,
device_class=DEVICE_CLASS_POWER,
state_class=STATE_CLASS_MEASUREMENT,
),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(uart.UART_DEVICE_SCHEMA)
)
FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema(
"cse7761", baud_rate=38400, require_rx=True, require_tx=True
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await uart.register_uart_device(var, config)
for key in [
CONF_VOLTAGE,
CONF_CURRENT_1,
CONF_CURRENT_2,
CONF_ACTIVE_POWER_1,
CONF_ACTIVE_POWER_2,
]:
if key not in config:
continue
conf = config[key]
sens = await sensor.new_sensor(conf)
cg.add(getattr(var, f"set_{key}_sensor")(sens))

View File

@@ -90,6 +90,7 @@ void CSE7766Component::parse_data_() {
uint32_t power_cycle = this->get_24_bit_uint_(17);
uint8_t adj = this->raw_data_[20];
uint32_t cf_pulses = (this->raw_data_[21] << 8) + this->raw_data_[22];
bool power_ok = true;
bool voltage_ok = true;
@@ -127,6 +128,18 @@ void CSE7766Component::parse_data_() {
power = power_calib / float(power_cycle);
this->power_acc_ += power;
this->power_counts_ += 1;
uint32_t difference;
if (this->cf_pulses_last_ == 0)
this->cf_pulses_last_ = cf_pulses;
if (cf_pulses < this->cf_pulses_last_) {
difference = cf_pulses + (0x10000 - this->cf_pulses_last_);
} else {
difference = cf_pulses - this->cf_pulses_last_;
}
this->cf_pulses_last_ = cf_pulses;
this->energy_total_ += difference * float(power_calib) / 1000000.0 / 3600.0;
}
if ((adj & 0x20) == 0x20 && current_ok && voltage_ok && power != 0.0) {
@@ -136,9 +149,9 @@ void CSE7766Component::parse_data_() {
}
}
void CSE7766Component::update() {
float voltage = this->voltage_counts_ > 0 ? this->voltage_acc_ / this->voltage_counts_ : 0.0;
float current = this->current_counts_ > 0 ? this->current_acc_ / this->current_counts_ : 0.0;
float power = this->power_counts_ > 0 ? this->power_acc_ / this->power_counts_ : 0.0;
float voltage = this->voltage_counts_ > 0 ? this->voltage_acc_ / this->voltage_counts_ : 0.0f;
float current = this->current_counts_ > 0 ? this->current_acc_ / this->current_counts_ : 0.0f;
float power = this->power_counts_ > 0 ? this->power_acc_ / this->power_counts_ : 0.0f;
ESP_LOGV(TAG, "Got voltage_acc=%.2f current_acc=%.2f power_acc=%.2f", this->voltage_acc_, this->current_acc_,
this->power_acc_);
@@ -152,6 +165,8 @@ void CSE7766Component::update() {
this->current_sensor_->publish_state(current);
if (this->power_sensor_ != nullptr)
this->power_sensor_->publish_state(power);
if (this->energy_sensor_ != nullptr)
this->energy_sensor_->publish_state(this->energy_total_);
this->voltage_acc_ = 0.0f;
this->current_acc_ = 0.0f;
@@ -172,6 +187,7 @@ void CSE7766Component::dump_config() {
LOG_SENSOR(" ", "Voltage", this->voltage_sensor_);
LOG_SENSOR(" ", "Current", this->current_sensor_);
LOG_SENSOR(" ", "Power", this->power_sensor_);
LOG_SENSOR(" ", "Energy", this->energy_sensor_);
this->check_uart_settings(4800);
}

View File

@@ -12,6 +12,7 @@ class CSE7766Component : public PollingComponent, public uart::UARTDevice {
void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; }
void set_current_sensor(sensor::Sensor *current_sensor) { current_sensor_ = current_sensor; }
void set_power_sensor(sensor::Sensor *power_sensor) { power_sensor_ = power_sensor; }
void set_energy_sensor(sensor::Sensor *energy_sensor) { energy_sensor_ = energy_sensor; }
void loop() override;
float get_setup_priority() const override;
@@ -29,9 +30,12 @@ class CSE7766Component : public PollingComponent, public uart::UARTDevice {
sensor::Sensor *voltage_sensor_{nullptr};
sensor::Sensor *current_sensor_{nullptr};
sensor::Sensor *power_sensor_{nullptr};
sensor::Sensor *energy_sensor_{nullptr};
float voltage_acc_{0.0f};
float current_acc_{0.0f};
float power_acc_{0.0f};
float energy_total_{0.0f};
uint32_t cf_pulses_last_{0};
uint32_t voltage_counts_{0};
uint32_t current_counts_{0};
uint32_t power_counts_{0};

View File

@@ -3,16 +3,20 @@ import esphome.config_validation as cv
from esphome.components import sensor, uart
from esphome.const import (
CONF_CURRENT,
CONF_ENERGY,
CONF_ID,
CONF_POWER,
CONF_VOLTAGE,
DEVICE_CLASS_CURRENT,
DEVICE_CLASS_ENERGY,
DEVICE_CLASS_POWER,
DEVICE_CLASS_VOLTAGE,
STATE_CLASS_MEASUREMENT,
STATE_CLASS_TOTAL_INCREASING,
UNIT_VOLT,
UNIT_AMPERE,
UNIT_WATT,
UNIT_WATT_HOURS,
)
DEPENDENCIES = ["uart"]
@@ -44,6 +48,12 @@ CONFIG_SCHEMA = (
device_class=DEVICE_CLASS_POWER,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_ENERGY): sensor.sensor_schema(
unit_of_measurement=UNIT_WATT_HOURS,
accuracy_decimals=3,
device_class=DEVICE_CLASS_ENERGY,
state_class=STATE_CLASS_TOTAL_INCREASING,
),
}
)
.extend(cv.polling_component_schema("60s"))
@@ -71,3 +81,7 @@ async def to_code(config):
conf = config[CONF_POWER]
sens = await sensor.new_sensor(conf)
cg.add(var.set_power_sensor(sens))
if CONF_ENERGY in config:
conf = config[CONF_ENERGY]
sens = await sensor.new_sensor(conf)
cg.add(var.set_energy_sensor(sens))

View File

@@ -8,18 +8,6 @@ namespace ct_clamp {
static const char *const TAG = "ct_clamp";
void CTClampSensor::setup() {
this->is_calibrating_offset_ = true;
this->high_freq_.start();
this->set_timeout("calibrate_offset", this->sample_duration_, [this]() {
this->high_freq_.stop();
this->is_calibrating_offset_ = false;
if (this->num_samples_ != 0) {
this->offset_ = this->sample_sum_ / this->num_samples_;
}
});
}
void CTClampSensor::dump_config() {
LOG_SENSOR("", "CT Clamp Sensor", this);
ESP_LOGCONFIG(TAG, " Sample Duration: %.2fs", this->sample_duration_ / 1e3f);
@@ -27,9 +15,6 @@ void CTClampSensor::dump_config() {
}
void CTClampSensor::update() {
if (this->is_calibrating_offset_)
return;
// Update only starts the sampling phase, in loop() the actual sampling is happening.
// Request a high loop() execution interval during sampling phase.
@@ -46,44 +31,39 @@ void CTClampSensor::update() {
return;
}
float raw = this->sample_sum_ / this->num_samples_;
float irms = std::sqrt(raw);
ESP_LOGD(TAG, "'%s' - Raw Value: %.2fA", this->name_.c_str(), irms);
this->publish_state(irms);
const float rms_ac_dc_squared = this->sample_squared_sum_ / this->num_samples_;
const float rms_dc = this->sample_sum_ / this->num_samples_;
const float rms_ac = std::sqrt(rms_ac_dc_squared - rms_dc * rms_dc);
ESP_LOGD(TAG, "'%s' - Raw AC Value: %.3fA after %d different samples (%d SPS)", this->name_.c_str(), rms_ac,
this->num_samples_, 1000 * this->num_samples_ / this->sample_duration_);
this->publish_state(rms_ac);
});
// Set sampling values
this->is_sampling_ = true;
this->last_value_ = 0.0;
this->num_samples_ = 0;
this->sample_sum_ = 0.0f;
this->sample_squared_sum_ = 0.0f;
this->is_sampling_ = true;
}
void CTClampSensor::loop() {
if (!this->is_sampling_ && !this->is_calibrating_offset_)
if (!this->is_sampling_)
return;
// Perform a single sample
float value = this->source_->sample();
if (isnan(value))
if (std::isnan(value))
return;
if (this->is_calibrating_offset_) {
this->sample_sum_ += value;
this->num_samples_++;
// Assuming a sine wave, avoid requesting values faster than the ADC can provide them
if (this->last_value_ == value)
return;
}
this->last_value_ = value;
// Adjust DC offset via low pass filter (exponential moving average)
const float alpha = 0.001f;
this->offset_ = this->offset_ * (1 - alpha) + value * alpha;
// Filtered value centered around the mid-point (0V)
float filtered = value - this->offset_;
// IRMS is sqrt(∑v_i²)
float sq = filtered * filtered;
this->sample_sum_ += sq;
this->num_samples_++;
this->sample_sum_ += value;
this->sample_squared_sum_ += value * value;
}
} // namespace ct_clamp

View File

@@ -1,7 +1,7 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/esphal.h"
#include "esphome/core/hal.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/voltage_sampler/voltage_sampler.h"
@@ -10,7 +10,6 @@ namespace ct_clamp {
class CTClampSensor : public sensor::Sensor, public PollingComponent {
public:
void setup() override;
void update() override;
void loop() override;
void dump_config() override;
@@ -35,17 +34,20 @@ class CTClampSensor : public sensor::Sensor, public PollingComponent {
*
* Diagram: https://learn.openenergymonitor.org/electricity-monitoring/ct-sensors/interface-with-arduino
*
* This is automatically calculated with an exponential moving average/digital low pass filter.
*
* 0.5 is a good initial approximation to start with for most ESP8266 setups.
* The current clamp only measures AC, so any DC component is an unwanted artifact from the
* sampling circuit. The AC component is essentially the same as the calculating the Standard-Deviation,
* which can be done by cumulating 3 values per sample:
* 1) Number of samples
* 2) Sum of samples
* 3) Sum of sample squared
* https://en.wikipedia.org/wiki/Root_mean_square
*/
float offset_ = 0.5f;
float last_value_ = 0.0f;
float sample_sum_ = 0.0f;
float sample_squared_sum_ = 0.0f;
uint32_t num_samples_ = 0;
bool is_sampling_ = false;
/// Calibrate offset value once at boot
bool is_calibrating_offset_ = false;
};
} // namespace ct_clamp

View File

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

View File

@@ -0,0 +1,124 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
from esphome.components import sensor, cover
from esphome.const import (
CONF_CLOSE_ACTION,
CONF_CLOSE_DURATION,
CONF_ID,
CONF_OPEN_ACTION,
CONF_OPEN_DURATION,
CONF_STOP_ACTION,
CONF_MAX_DURATION,
)
CONF_OPEN_SENSOR = "open_sensor"
CONF_OPEN_MOVING_CURRENT_THRESHOLD = "open_moving_current_threshold"
CONF_OPEN_OBSTACLE_CURRENT_THRESHOLD = "open_obstacle_current_threshold"
CONF_CLOSE_SENSOR = "close_sensor"
CONF_CLOSE_MOVING_CURRENT_THRESHOLD = "close_moving_current_threshold"
CONF_CLOSE_OBSTACLE_CURRENT_THRESHOLD = "close_obstacle_current_threshold"
CONF_OBSTACLE_ROLLBACK = "obstacle_rollback"
CONF_MALFUNCTION_DETECTION = "malfunction_detection"
CONF_MALFUNCTION_ACTION = "malfunction_action"
CONF_START_SENSING_DELAY = "start_sensing_delay"
current_based_ns = cg.esphome_ns.namespace("current_based")
CurrentBasedCover = current_based_ns.class_(
"CurrentBasedCover", cover.Cover, cg.Component
)
CONFIG_SCHEMA = cover.COVER_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(CurrentBasedCover),
cv.Required(CONF_STOP_ACTION): automation.validate_automation(single=True),
cv.Required(CONF_OPEN_SENSOR): cv.use_id(sensor.Sensor),
cv.Required(CONF_OPEN_MOVING_CURRENT_THRESHOLD): cv.float_range(
min=0, min_included=False
),
cv.Optional(CONF_OPEN_OBSTACLE_CURRENT_THRESHOLD): cv.float_range(
min=0, min_included=False
),
cv.Required(CONF_OPEN_ACTION): automation.validate_automation(single=True),
cv.Required(CONF_OPEN_DURATION): cv.positive_time_period_milliseconds,
cv.Required(CONF_CLOSE_SENSOR): cv.use_id(sensor.Sensor),
cv.Required(CONF_CLOSE_MOVING_CURRENT_THRESHOLD): cv.float_range(
min=0, min_included=False
),
cv.Optional(CONF_CLOSE_OBSTACLE_CURRENT_THRESHOLD): cv.float_range(
min=0, min_included=False
),
cv.Required(CONF_CLOSE_ACTION): automation.validate_automation(single=True),
cv.Required(CONF_CLOSE_DURATION): cv.positive_time_period_milliseconds,
cv.Optional(CONF_OBSTACLE_ROLLBACK, default="10%"): cv.percentage,
cv.Optional(CONF_MAX_DURATION): cv.positive_time_period_milliseconds,
cv.Optional(CONF_MALFUNCTION_DETECTION, default=True): cv.boolean,
cv.Optional(CONF_MALFUNCTION_ACTION): automation.validate_automation(
single=True
),
cv.Optional(
CONF_START_SENSING_DELAY, default="500ms"
): cv.positive_time_period_milliseconds,
}
).extend(cv.COMPONENT_SCHEMA)
def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config)
yield cover.register_cover(var, config)
yield automation.build_automation(
var.get_stop_trigger(), [], config[CONF_STOP_ACTION]
)
# OPEN
bin = yield cg.get_variable(config[CONF_OPEN_SENSOR])
cg.add(var.set_open_sensor(bin))
cg.add(
var.set_open_moving_current_threshold(
config[CONF_OPEN_MOVING_CURRENT_THRESHOLD]
)
)
if CONF_OPEN_OBSTACLE_CURRENT_THRESHOLD in config:
cg.add(
var.set_open_obstacle_current_threshold(
config[CONF_OPEN_OBSTACLE_CURRENT_THRESHOLD]
)
)
cg.add(var.set_open_duration(config[CONF_OPEN_DURATION]))
yield automation.build_automation(
var.get_open_trigger(), [], config[CONF_OPEN_ACTION]
)
# CLOSE
bin = yield cg.get_variable(config[CONF_CLOSE_SENSOR])
cg.add(var.set_close_sensor(bin))
cg.add(
var.set_close_moving_current_threshold(
config[CONF_CLOSE_MOVING_CURRENT_THRESHOLD]
)
)
if CONF_CLOSE_OBSTACLE_CURRENT_THRESHOLD in config:
cg.add(
var.set_close_obstacle_current_threshold(
config[CONF_CLOSE_OBSTACLE_CURRENT_THRESHOLD]
)
)
cg.add(var.set_close_duration(config[CONF_CLOSE_DURATION]))
yield automation.build_automation(
var.get_close_trigger(), [], config[CONF_CLOSE_ACTION]
)
cg.add(var.set_obstacle_rollback(config[CONF_OBSTACLE_ROLLBACK]))
if CONF_MAX_DURATION in config:
cg.add(var.set_max_duration(config[CONF_MAX_DURATION]))
cg.add(var.set_malfunction_detection(config[CONF_MALFUNCTION_DETECTION]))
if CONF_MALFUNCTION_ACTION in config:
yield automation.build_automation(
var.get_malfunction_trigger(), [], config[CONF_MALFUNCTION_ACTION]
)
cg.add(var.set_start_sensing_delay(config[CONF_START_SENSING_DELAY]))

View File

@@ -0,0 +1,251 @@
#include "current_based_cover.h"
#include "esphome/core/hal.h"
#include "esphome/core/log.h"
#include <cfloat>
namespace esphome {
namespace current_based {
static const char *const TAG = "current_based.cover";
using namespace esphome::cover;
CoverTraits CurrentBasedCover::get_traits() {
auto traits = CoverTraits();
traits.set_supports_position(true);
traits.set_is_assumed_state(false);
return traits;
}
void CurrentBasedCover::control(const CoverCall &call) {
if (call.get_stop()) {
this->direction_idle_();
}
if (call.get_position().has_value()) {
auto pos = *call.get_position();
if (pos == this->position) {
// already at target
} else {
auto op = pos < this->position ? COVER_OPERATION_CLOSING : COVER_OPERATION_OPENING;
this->target_position_ = pos;
this->start_direction_(op);
}
}
}
void CurrentBasedCover::setup() {
auto restore = this->restore_state_();
if (restore.has_value()) {
restore->apply(this);
} else {
this->position = 0.5f;
}
}
void CurrentBasedCover::loop() {
if (this->current_operation == COVER_OPERATION_IDLE)
return;
const uint32_t now = millis();
if (this->current_operation == COVER_OPERATION_OPENING) {
if (this->malfunction_detection_ && this->is_closing_()) { // Malfunction
this->direction_idle_();
this->malfunction_trigger_->trigger();
ESP_LOGI(TAG, "'%s' - Malfunction detected during opening. Current flow detected in close circuit",
this->name_.c_str());
} else if (this->is_opening_blocked_()) { // Blocked
ESP_LOGD(TAG, "'%s' - Obstacle detected during opening.", this->name_.c_str());
this->direction_idle_();
if (this->obstacle_rollback_ != 0) {
this->set_timeout("rollback", 300, [this]() {
ESP_LOGD(TAG, "'%s' - Rollback.", this->name_.c_str());
this->target_position_ = clamp(this->position - this->obstacle_rollback_, 0.0F, 1.0F);
this->start_direction_(COVER_OPERATION_CLOSING);
});
}
} else if (this->is_initial_delay_finished_() && !this->is_opening_()) { // End reached
auto dur = (now - this->start_dir_time_) / 1e3f;
ESP_LOGD(TAG, "'%s' - Open position reached. Took %.1fs.", this->name_.c_str(), dur);
this->direction_idle_(COVER_OPEN);
}
} else if (this->current_operation == COVER_OPERATION_CLOSING) {
if (this->malfunction_detection_ && this->is_opening_()) { // Malfunction
this->direction_idle_();
this->malfunction_trigger_->trigger();
ESP_LOGI(TAG, "'%s' - Malfunction detected during closing. Current flow detected in open circuit",
this->name_.c_str());
} else if (this->is_closing_blocked_()) { // Blocked
ESP_LOGD(TAG, "'%s' - Obstacle detected during closing.", this->name_.c_str());
this->direction_idle_();
if (this->obstacle_rollback_ != 0) {
this->set_timeout("rollback", 300, [this]() {
ESP_LOGD(TAG, "'%s' - Rollback.", this->name_.c_str());
this->target_position_ = clamp(this->position + this->obstacle_rollback_, 0.0F, 1.0F);
this->start_direction_(COVER_OPERATION_OPENING);
});
}
} else if (this->is_initial_delay_finished_() && !this->is_closing_()) { // End reached
auto dur = (now - this->start_dir_time_) / 1e3f;
ESP_LOGD(TAG, "'%s' - Close position reached. Took %.1fs.", this->name_.c_str(), dur);
this->direction_idle_(COVER_CLOSED);
}
} else if (now - this->start_dir_time_ > this->max_duration_) {
ESP_LOGD(TAG, "'%s' - Max duration reached. Stopping cover.", this->name_.c_str());
this->direction_idle_();
}
// Recompute position every loop cycle
this->recompute_position_();
if (this->current_operation != COVER_OPERATION_IDLE && this->is_at_target_()) {
this->direction_idle_();
}
// Send current position every second
if (this->current_operation != COVER_OPERATION_IDLE && now - this->last_publish_time_ > 1000) {
this->publish_state(false);
this->last_publish_time_ = now;
}
}
void CurrentBasedCover::direction_idle_(float new_position) {
this->start_direction_(COVER_OPERATION_IDLE);
if (new_position != FLT_MAX) {
this->position = new_position;
}
this->publish_state();
}
void CurrentBasedCover::dump_config() {
LOG_COVER("", "Endstop Cover", this);
LOG_SENSOR(" ", "Open Sensor", this->open_sensor_);
ESP_LOGCONFIG(TAG, " Open moving current threshold: %.11fA", this->open_moving_current_threshold_);
if (this->open_obstacle_current_threshold_ != FLT_MAX) {
ESP_LOGCONFIG(TAG, " Open obstacle current threshold: %.11fA", this->open_obstacle_current_threshold_);
}
ESP_LOGCONFIG(TAG, " Open Duration: %.1fs", this->open_duration_ / 1e3f);
LOG_SENSOR(" ", "Close Sensor", this->close_sensor_);
ESP_LOGCONFIG(TAG, " Close moving current threshold: %.11fA", this->close_moving_current_threshold_);
if (this->close_obstacle_current_threshold_ != FLT_MAX) {
ESP_LOGCONFIG(TAG, " Close obstacle current threshold: %.11fA", this->close_obstacle_current_threshold_);
}
ESP_LOGCONFIG(TAG, " Close Duration: %.1fs", this->close_duration_ / 1e3f);
ESP_LOGCONFIG(TAG, "Obstacle Rollback: %.1f%%", this->obstacle_rollback_ * 100);
if (this->max_duration_ != UINT32_MAX) {
ESP_LOGCONFIG(TAG, "Maximun duration: %.1fs", this->max_duration_ / 1e3f);
}
ESP_LOGCONFIG(TAG, "Start sensing delay: %.1fs", this->start_sensing_delay_ / 1e3f);
ESP_LOGCONFIG(TAG, "Malfunction detection: %s", YESNO(this->malfunction_detection_));
}
float CurrentBasedCover::get_setup_priority() const { return setup_priority::DATA; }
void CurrentBasedCover::stop_prev_trigger_() {
if (this->prev_command_trigger_ != nullptr) {
this->prev_command_trigger_->stop_action();
this->prev_command_trigger_ = nullptr;
}
}
bool CurrentBasedCover::is_opening_() const {
return this->open_sensor_->get_state() > this->open_moving_current_threshold_;
}
bool CurrentBasedCover::is_opening_blocked_() const {
if (this->open_obstacle_current_threshold_ == FLT_MAX) {
return false;
}
return this->open_sensor_->get_state() > this->open_obstacle_current_threshold_;
}
bool CurrentBasedCover::is_closing_() const {
return this->close_sensor_->get_state() > this->close_moving_current_threshold_;
}
bool CurrentBasedCover::is_closing_blocked_() const {
if (this->close_obstacle_current_threshold_ == FLT_MAX) {
return false;
}
return this->open_sensor_->get_state() > this->open_obstacle_current_threshold_;
}
bool CurrentBasedCover::is_initial_delay_finished_() const {
return millis() - this->start_dir_time_ > this->start_sensing_delay_;
}
bool CurrentBasedCover::is_at_target_() const {
switch (this->current_operation) {
case COVER_OPERATION_OPENING:
if (this->target_position_ == COVER_OPEN) {
if (!this->is_initial_delay_finished_()) // During initial delay, state is assumed
return false;
return !this->is_opening_();
}
return this->position >= this->target_position_;
case COVER_OPERATION_CLOSING:
if (this->target_position_ == COVER_CLOSED) {
if (!this->is_initial_delay_finished_()) // During initial delay, state is assumed
return false;
return !this->is_closing_();
}
return this->position <= this->target_position_;
case COVER_OPERATION_IDLE:
default:
return true;
}
}
void CurrentBasedCover::start_direction_(CoverOperation dir) {
if (dir == this->current_operation)
return;
this->recompute_position_();
Trigger<> *trig;
switch (dir) {
case COVER_OPERATION_IDLE:
trig = this->stop_trigger_;
break;
case COVER_OPERATION_OPENING:
trig = this->open_trigger_;
break;
case COVER_OPERATION_CLOSING:
trig = this->close_trigger_;
break;
default:
return;
}
this->current_operation = dir;
this->stop_prev_trigger_();
trig->trigger();
this->prev_command_trigger_ = trig;
const auto now = millis();
this->start_dir_time_ = now;
this->last_recompute_time_ = now;
}
void CurrentBasedCover::recompute_position_() {
if (this->current_operation == COVER_OPERATION_IDLE)
return;
float dir;
float action_dur;
switch (this->current_operation) {
case COVER_OPERATION_OPENING:
dir = 1.0F;
action_dur = this->open_duration_;
break;
case COVER_OPERATION_CLOSING:
dir = -1.0F;
action_dur = this->close_duration_;
break;
default:
return;
}
const auto now = millis();
this->position += dir * (now - this->last_recompute_time_) / action_dur;
this->position = clamp(this->position, 0.0F, 1.0F);
this->last_recompute_time_ = now;
}
} // namespace current_based
} // namespace esphome

View File

@@ -0,0 +1,95 @@
#pragma once
#include "esphome/components/cover/cover.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/core/automation.h"
#include "esphome/core/component.h"
#include <cfloat>
namespace esphome {
namespace current_based {
class CurrentBasedCover : public cover::Cover, public Component {
public:
void setup() override;
void loop() override;
void dump_config() override;
float get_setup_priority() const override;
Trigger<> *get_stop_trigger() const { return this->stop_trigger_; }
Trigger<> *get_open_trigger() const { return this->open_trigger_; }
void set_open_sensor(sensor::Sensor *open_sensor) { this->open_sensor_ = open_sensor; }
void set_open_moving_current_threshold(float open_moving_current_threshold) {
this->open_moving_current_threshold_ = open_moving_current_threshold;
}
void set_open_obstacle_current_threshold(float open_obstacle_current_threshold) {
this->open_obstacle_current_threshold_ = open_obstacle_current_threshold;
}
void set_open_duration(uint32_t open_duration) { this->open_duration_ = open_duration; }
Trigger<> *get_close_trigger() const { return this->close_trigger_; }
void set_close_sensor(sensor::Sensor *close_sensor) { this->close_sensor_ = close_sensor; }
void set_close_moving_current_threshold(float close_moving_current_threshold) {
this->close_moving_current_threshold_ = close_moving_current_threshold;
}
void set_close_obstacle_current_threshold(float close_obstacle_current_threshold) {
this->close_obstacle_current_threshold_ = close_obstacle_current_threshold;
}
void set_close_duration(uint32_t close_duration) { this->close_duration_ = close_duration; }
void set_max_duration(uint32_t max_duration) { this->max_duration_ = max_duration; }
void set_obstacle_rollback(float obstacle_rollback) { this->obstacle_rollback_ = obstacle_rollback; }
void set_malfunction_detection(bool malfunction_detection) { this->malfunction_detection_ = malfunction_detection; }
void set_start_sensing_delay(uint32_t start_sensing_delay) { this->start_sensing_delay_ = start_sensing_delay; }
Trigger<> *get_malfunction_trigger() const { return this->malfunction_trigger_; }
cover::CoverTraits get_traits() override;
protected:
void control(const cover::CoverCall &call) override;
void stop_prev_trigger_();
bool is_at_target_() const;
bool is_opening_() const;
bool is_opening_blocked_() const;
bool is_closing_() const;
bool is_closing_blocked_() const;
bool is_initial_delay_finished_() const;
void direction_idle_(float new_position = FLT_MAX);
void start_direction_(cover::CoverOperation dir);
void recompute_position_();
Trigger<> *stop_trigger_{new Trigger<>()};
sensor::Sensor *open_sensor_{nullptr};
Trigger<> *open_trigger_{new Trigger<>()};
float open_moving_current_threshold_;
float open_obstacle_current_threshold_{FLT_MAX};
uint32_t open_duration_;
sensor::Sensor *close_sensor_{nullptr};
Trigger<> *close_trigger_{new Trigger<>()};
float close_moving_current_threshold_;
float close_obstacle_current_threshold_{FLT_MAX};
uint32_t close_duration_;
uint32_t max_duration_{UINT32_MAX};
bool malfunction_detection_{true};
Trigger<> *malfunction_trigger_{new Trigger<>()};
uint32_t start_sensing_delay_;
float obstacle_rollback_;
Trigger<> *prev_command_trigger_{nullptr};
uint32_t last_recompute_time_{0};
uint32_t start_dir_time_{0};
uint32_t last_publish_time_{0};
float target_position_{0};
};
} // namespace current_based
} // namespace esphome

View File

@@ -231,7 +231,7 @@ bool DaikinClimate::on_receive(remote_base::RemoteReceiveData data) {
// frame header
if (byte != 0x27)
return false;
} else if (pos == 3) {
} else if (pos == 3) { // NOLINT(bugprone-branch-clone)
// frame header
if (byte != 0x00)
return false;

View File

@@ -6,22 +6,20 @@ from esphome.const import CONF_ID, CONF_PIN
MULTI_CONF = True
AUTO_LOAD = ["sensor"]
CONF_ONE_WIRE_ID = "one_wire_id"
dallas_ns = cg.esphome_ns.namespace("dallas")
DallasComponent = dallas_ns.class_("DallasComponent", cg.PollingComponent)
ESPOneWire = dallas_ns.class_("ESPOneWire")
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(DallasComponent),
cv.GenerateID(CONF_ONE_WIRE_ID): cv.declare_id(ESPOneWire),
cv.Required(CONF_PIN): pins.internal_gpio_output_pin_schema,
}
).extend(cv.polling_component_schema("60s"))
async def to_code(config):
pin = await cg.gpio_pin_expression(config[CONF_PIN])
one_wire = cg.new_Pvariable(config[CONF_ONE_WIRE_ID], pin)
var = cg.new_Pvariable(config[CONF_ID], one_wire)
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
pin = await cg.gpio_pin_expression(config[CONF_PIN])
cg.add(var.set_pin(pin))

View File

@@ -31,12 +31,11 @@ uint16_t DallasTemperatureSensor::millis_to_wait_for_conversion() const {
void DallasComponent::setup() {
ESP_LOGCONFIG(TAG, "Setting up DallasComponent...");
yield();
pin_->setup();
one_wire_ = new ESPOneWire(pin_); // NOLINT(cppcoreguidelines-owning-memory)
std::vector<uint64_t> raw_sensors;
{
InterruptLock lock;
raw_sensors = this->one_wire_->search_vec();
}
raw_sensors = this->one_wire_->search_vec();
for (auto &address : raw_sensors) {
std::string s = uint64_to_string(address);
@@ -70,7 +69,7 @@ void DallasComponent::setup() {
}
void DallasComponent::dump_config() {
ESP_LOGCONFIG(TAG, "DallasComponent:");
LOG_PIN(" Pin: ", this->one_wire_->get_pin());
LOG_PIN(" Pin: ", this->pin_);
LOG_UPDATE_INTERVAL(this);
if (this->found_sensors_.empty()) {
@@ -97,29 +96,17 @@ void DallasComponent::dump_config() {
}
}
DallasTemperatureSensor *DallasComponent::get_sensor_by_address(uint64_t address, uint8_t resolution) {
auto s = new DallasTemperatureSensor(address, resolution, this);
this->sensors_.push_back(s);
return s;
}
DallasTemperatureSensor *DallasComponent::get_sensor_by_index(uint8_t index, uint8_t resolution) {
auto s = this->get_sensor_by_address(0, resolution);
s->set_index(index);
return s;
}
void DallasComponent::register_sensor(DallasTemperatureSensor *sensor) { this->sensors_.push_back(sensor); }
void DallasComponent::update() {
this->status_clear_warning();
bool result;
{
InterruptLock lock;
if (!this->one_wire_->reset()) {
result = false;
} else {
result = true;
this->one_wire_->skip();
this->one_wire_->write8(DALLAS_COMMAND_START_CONVERSION);
}
if (!this->one_wire_->reset()) {
result = false;
} else {
result = true;
this->one_wire_->skip();
this->one_wire_->write8(DALLAS_COMMAND_START_CONVERSION);
}
if (!result) {
@@ -130,11 +117,7 @@ void DallasComponent::update() {
for (auto *sensor : this->sensors_) {
this->set_timeout(sensor->get_address_name(), sensor->millis_to_wait_for_conversion(), [this, sensor] {
bool res;
{
InterruptLock lock;
res = sensor->read_scratch_pad();
}
bool res = sensor->read_scratch_pad();
if (!res) {
ESP_LOGW(TAG, "'%s' - Resetting bus for read failed!", sensor->get_name().c_str());
@@ -155,13 +138,7 @@ void DallasComponent::update() {
});
}
}
DallasComponent::DallasComponent(ESPOneWire *one_wire) : one_wire_(one_wire) {}
DallasTemperatureSensor::DallasTemperatureSensor(uint64_t address, uint8_t resolution, DallasComponent *parent)
: parent_(parent) {
this->set_address(address);
this->set_resolution(resolution);
}
void DallasTemperatureSensor::set_address(uint64_t address) { this->address_ = address; }
uint8_t DallasTemperatureSensor::get_resolution() const { return this->resolution_; }
void DallasTemperatureSensor::set_resolution(uint8_t resolution) { this->resolution_ = resolution; }
@@ -175,8 +152,8 @@ const std::string &DallasTemperatureSensor::get_address_name() {
return this->address_name_;
}
bool ICACHE_RAM_ATTR DallasTemperatureSensor::read_scratch_pad() {
ESPOneWire *wire = this->parent_->one_wire_;
bool IRAM_ATTR DallasTemperatureSensor::read_scratch_pad() {
auto *wire = this->parent_->one_wire_;
if (!wire->reset()) {
return false;
}
@@ -190,11 +167,7 @@ bool ICACHE_RAM_ATTR DallasTemperatureSensor::read_scratch_pad() {
return true;
}
bool DallasTemperatureSensor::setup_sensor() {
bool r;
{
InterruptLock lock;
r = this->read_scratch_pad();
}
bool r = this->read_scratch_pad();
if (!r) {
ESP_LOGE(TAG, "Reading scratchpad failed: reset");
@@ -228,21 +201,18 @@ bool DallasTemperatureSensor::setup_sensor() {
break;
}
ESPOneWire *wire = this->parent_->one_wire_;
{
InterruptLock lock;
if (wire->reset()) {
wire->select(this->address_);
wire->write8(DALLAS_COMMAND_WRITE_SCRATCH_PAD);
wire->write8(this->scratch_pad_[2]); // high alarm temp
wire->write8(this->scratch_pad_[3]); // low alarm temp
wire->write8(this->scratch_pad_[4]); // resolution
wire->reset();
auto *wire = this->parent_->one_wire_;
if (wire->reset()) {
wire->select(this->address_);
wire->write8(DALLAS_COMMAND_WRITE_SCRATCH_PAD);
wire->write8(this->scratch_pad_[2]); // high alarm temp
wire->write8(this->scratch_pad_[3]); // low alarm temp
wire->write8(this->scratch_pad_[4]); // resolution
wire->reset();
// write value to EEPROM
wire->select(this->address_);
wire->write8(0x48);
}
// write value to EEPROM
wire->select(this->address_);
wire->write8(0x48);
}
delay(20); // allow it to finish operation

View File

@@ -11,10 +11,8 @@ class DallasTemperatureSensor;
class DallasComponent : public PollingComponent {
public:
explicit DallasComponent(ESPOneWire *one_wire);
DallasTemperatureSensor *get_sensor_by_address(uint64_t address, uint8_t resolution);
DallasTemperatureSensor *get_sensor_by_index(uint8_t index, uint8_t resolution);
void set_pin(InternalGPIOPin *pin) { pin_ = pin; }
void register_sensor(DallasTemperatureSensor *sensor);
void setup() override;
void dump_config() override;
@@ -25,6 +23,7 @@ class DallasComponent : public PollingComponent {
protected:
friend DallasTemperatureSensor;
InternalGPIOPin *pin_;
ESPOneWire *one_wire_;
std::vector<DallasTemperatureSensor *> sensors_;
std::vector<uint64_t> found_sensors_;
@@ -33,8 +32,7 @@ class DallasComponent : public PollingComponent {
/// Internal class that helps us create multiple sensors for one Dallas hub.
class DallasTemperatureSensor : public sensor::Sensor {
public:
DallasTemperatureSensor(uint64_t address, uint8_t resolution, DallasComponent *parent);
void set_parent(DallasComponent *parent) { parent_ = parent; }
/// Helper to get a pointer to the address as uint8_t.
uint8_t *get_address8();
/// Helper to create (and cache) the name for this sensor. For example "0xfe0000031f1eaf29".

View File

@@ -10,115 +10,123 @@ static const char *const TAG = "dallas.one_wire";
const uint8_t ONE_WIRE_ROM_SELECT = 0x55;
const int ONE_WIRE_ROM_SEARCH = 0xF0;
ESPOneWire::ESPOneWire(GPIOPin *pin) : pin_(pin) {}
ESPOneWire::ESPOneWire(InternalGPIOPin *pin) { pin_ = pin->to_isr(); }
bool HOT ICACHE_RAM_ATTR ESPOneWire::reset() {
bool HOT IRAM_ATTR ESPOneWire::reset() {
// See reset here:
// https://www.maximintegrated.com/en/design/technical-documents/app-notes/1/126.html
InterruptLock lock;
// Wait for communication to clear (delay G)
pin_.pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP);
uint8_t retries = 125;
// Wait for communication to clear
this->pin_->pin_mode(INPUT_PULLUP);
do {
if (--retries == 0)
return false;
delayMicroseconds(2);
} while (!this->pin_->digital_read());
} while (!pin_.digital_read());
// Send 480µs LOW TX reset pulse
this->pin_->pin_mode(OUTPUT);
this->pin_->digital_write(false);
// Send 480µs LOW TX reset pulse (drive bus low, delay H)
pin_.pin_mode(gpio::FLAG_OUTPUT);
pin_.digital_write(false);
delayMicroseconds(480);
// Switch into RX mode, letting the pin float
this->pin_->pin_mode(INPUT_PULLUP);
// after 15µs-60µs wait time, responder pulls low for 60µs-240µs
// let's have 70µs just in case
// Release the bus, delay I
pin_.pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP);
delayMicroseconds(70);
bool r = !this->pin_->digital_read();
// sample bus, 0=device(s) present, 1=no device present
bool r = !pin_.digital_read();
// delay J
delayMicroseconds(410);
return r;
}
void HOT ICACHE_RAM_ATTR ESPOneWire::write_bit(bool bit) {
// Initiate write/read by pulling low.
this->pin_->pin_mode(OUTPUT);
this->pin_->digital_write(false);
void HOT IRAM_ATTR ESPOneWire::write_bit(bool bit) {
// See write 1/0 bit here:
// https://www.maximintegrated.com/en/design/technical-documents/app-notes/1/126.html
InterruptLock lock;
// bus sampled within 15µs and 60µs after pulling LOW.
if (bit) {
// pull high/release within 15µs
delayMicroseconds(10);
this->pin_->digital_write(true);
// in total minimum of 60µs long
delayMicroseconds(55);
} else {
// continue pulling LOW for at least 60µs
delayMicroseconds(65);
this->pin_->digital_write(true);
// grace period, 1µs recovery time
delayMicroseconds(5);
}
// drive bus low
pin_.pin_mode(gpio::FLAG_OUTPUT);
pin_.digital_write(false);
uint32_t delay0 = bit ? 10 : 65;
uint32_t delay1 = bit ? 55 : 5;
// delay A/C
delayMicroseconds(delay0);
// release bus
pin_.digital_write(true);
// delay B/D
delayMicroseconds(delay1);
}
bool HOT ICACHE_RAM_ATTR ESPOneWire::read_bit() {
// Initiate read slot by pulling LOW for at least 1µs
this->pin_->pin_mode(OUTPUT);
this->pin_->digital_write(false);
bool HOT IRAM_ATTR ESPOneWire::read_bit() {
// See read bit here:
// https://www.maximintegrated.com/en/design/technical-documents/app-notes/1/126.html
InterruptLock lock;
// drive bus low, delay A
pin_.pin_mode(gpio::FLAG_OUTPUT);
pin_.digital_write(false);
delayMicroseconds(3);
// release bus, we have to sample within 15µs of pulling low
this->pin_->pin_mode(INPUT_PULLUP);
// release bus, delay E
pin_.pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP);
delayMicroseconds(10);
bool r = this->pin_->digital_read();
// read time slot at least 60µs long + 1µs recovery time between slots
// sample bus to read bit from peer
bool r = pin_.digital_read();
// delay F
delayMicroseconds(53);
return r;
}
void ICACHE_RAM_ATTR ESPOneWire::write8(uint8_t val) {
void ESPOneWire::write8(uint8_t val) {
for (uint8_t i = 0; i < 8; i++) {
this->write_bit(bool((1u << i) & val));
}
}
void ICACHE_RAM_ATTR ESPOneWire::write64(uint64_t val) {
void ESPOneWire::write64(uint64_t val) {
for (uint8_t i = 0; i < 64; i++) {
this->write_bit(bool((1ULL << i) & val));
}
}
uint8_t ICACHE_RAM_ATTR ESPOneWire::read8() {
uint8_t ESPOneWire::read8() {
uint8_t ret = 0;
for (uint8_t i = 0; i < 8; i++) {
ret |= (uint8_t(this->read_bit()) << i);
}
return ret;
}
uint64_t ICACHE_RAM_ATTR ESPOneWire::read64() {
uint64_t ESPOneWire::read64() {
uint64_t ret = 0;
for (uint8_t i = 0; i < 8; i++) {
ret |= (uint64_t(this->read_bit()) << i);
}
return ret;
}
void ICACHE_RAM_ATTR ESPOneWire::select(uint64_t address) {
void ESPOneWire::select(uint64_t address) {
this->write8(ONE_WIRE_ROM_SELECT);
this->write64(address);
}
void ICACHE_RAM_ATTR ESPOneWire::reset_search() {
void ESPOneWire::reset_search() {
this->last_discrepancy_ = 0;
this->last_device_flag_ = false;
this->last_family_discrepancy_ = 0;
this->rom_number_ = 0;
}
uint64_t HOT ICACHE_RAM_ATTR ESPOneWire::search() {
uint64_t ESPOneWire::search() {
if (this->last_device_flag_) {
return 0u;
}
if (!this->reset()) {
// Reset failed
// Reset failed or no devices present
this->reset_search();
return 0u;
}
@@ -196,7 +204,7 @@ uint64_t HOT ICACHE_RAM_ATTR ESPOneWire::search() {
return this->rom_number_;
}
std::vector<uint64_t> ICACHE_RAM_ATTR ESPOneWire::search_vec() {
std::vector<uint64_t> ESPOneWire::search_vec() {
std::vector<uint64_t> res;
this->reset_search();
@@ -206,12 +214,11 @@ std::vector<uint64_t> ICACHE_RAM_ATTR ESPOneWire::search_vec() {
return res;
}
void ICACHE_RAM_ATTR ESPOneWire::skip() {
void ESPOneWire::skip() {
this->write8(0xCC); // skip ROM
}
GPIOPin *ESPOneWire::get_pin() { return this->pin_; }
uint8_t ICACHE_RAM_ATTR *ESPOneWire::rom_number8_() { return reinterpret_cast<uint8_t *>(&this->rom_number_); }
uint8_t IRAM_ATTR *ESPOneWire::rom_number8_() { return reinterpret_cast<uint8_t *>(&this->rom_number_); }
} // namespace dallas
} // namespace esphome

View File

@@ -1,7 +1,7 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/esphal.h"
#include "esphome/core/hal.h"
#include <vector>
namespace esphome {
namespace dallas {
@@ -11,7 +11,7 @@ extern const int ONE_WIRE_ROM_SEARCH;
class ESPOneWire {
public:
explicit ESPOneWire(GPIOPin *pin);
explicit ESPOneWire(InternalGPIOPin *pin);
/** Reset the bus, should be done before all write operations.
*
@@ -54,13 +54,11 @@ class ESPOneWire {
/// Helper that wraps search in a std::vector.
std::vector<uint64_t> search_vec();
GPIOPin *get_pin();
protected:
/// Helper to get the internal 64-bit unsigned rom number as a 8-bit integer pointer.
inline uint8_t *rom_number8_();
GPIOPin *pin_;
ISRInternalGPIOPin pin_;
uint8_t last_discrepancy_{0};
uint8_t last_family_discrepancy_{0};
bool last_device_flag_{false};

View File

@@ -36,10 +36,17 @@ CONFIG_SCHEMA = cv.All(
async def to_code(config):
hub = await cg.get_variable(config[CONF_DALLAS_ID])
var = cg.new_Pvariable(config[CONF_ID])
if CONF_ADDRESS in config:
address = config[CONF_ADDRESS]
rhs = hub.Pget_sensor_by_address(address, config.get(CONF_RESOLUTION))
cg.add(var.set_address(config[CONF_ADDRESS]))
else:
rhs = hub.Pget_sensor_by_index(config[CONF_INDEX], config.get(CONF_RESOLUTION))
var = cg.Pvariable(config[CONF_ID], rhs)
cg.add(var.set_index(config[CONF_INDEX]))
if CONF_RESOLUTION in config:
cg.add(var.set_resolution(config[CONF_RESOLUTION]))
cg.add(var.set_parent(hub))
cg.add(hub.register_sensor(var))
await sensor.register_sensor(var, config)

View File

@@ -0,0 +1,27 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import uart
from esphome.const import CONF_ID
CODEOWNERS = ["@s1lvi0"]
DEPENDENCIES = ["uart"]
AUTO_LOAD = ["sensor", "text_sensor", "binary_sensor"]
CONF_BMS_DALY_ID = "bms_daly_id"
daly_bms = cg.esphome_ns.namespace("daly_bms")
DalyBmsComponent = daly_bms.class_(
"DalyBmsComponent", cg.PollingComponent, uart.UARTDevice
)
CONFIG_SCHEMA = (
cv.Schema({cv.GenerateID(): cv.declare_id(DalyBmsComponent)})
.extend(uart.UART_DEVICE_SCHEMA)
.extend(cv.polling_component_schema("30s"))
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await uart.register_uart_device(var, config)

View File

@@ -0,0 +1,49 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import binary_sensor
from esphome.const import CONF_ID
from . import DalyBmsComponent, CONF_BMS_DALY_ID
CONF_CHARGING_MOS_ENABLED = "charging_mos_enabled"
CONF_DISCHARGING_MOS_ENABLED = "discharging_mos_enabled"
TYPES = [
CONF_CHARGING_MOS_ENABLED,
CONF_DISCHARGING_MOS_ENABLED,
]
CONFIG_SCHEMA = cv.All(
cv.Schema(
{
cv.GenerateID(CONF_BMS_DALY_ID): cv.use_id(DalyBmsComponent),
cv.Optional(
CONF_CHARGING_MOS_ENABLED
): binary_sensor.BINARY_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(binary_sensor.BinarySensor),
}
),
cv.Optional(
CONF_DISCHARGING_MOS_ENABLED
): binary_sensor.BINARY_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(binary_sensor.BinarySensor),
}
),
}
).extend(cv.COMPONENT_SCHEMA)
)
async def setup_conf(config, key, hub):
if key in config:
conf = config[key]
sens = cg.new_Pvariable(conf[CONF_ID])
await binary_sensor.register_binary_sensor(sens, conf)
cg.add(getattr(hub, f"set_{key}_binary_sensor")(sens))
async def to_code(config):
hub = await cg.get_variable(config[CONF_BMS_DALY_ID])
for key in TYPES:
await setup_conf(config, key, hub)

View File

@@ -0,0 +1,181 @@
#include "daly_bms.h"
#include "esphome/core/log.h"
#include <vector>
namespace esphome {
namespace daly_bms {
static const char *const TAG = "daly_bms";
static const uint8_t DALY_FRAME_SIZE = 13;
static const uint8_t DALY_TEMPERATURE_OFFSET = 40;
static const uint16_t DALY_CURRENT_OFFSET = 30000;
static const uint8_t DALY_REQUEST_BATTERY_LEVEL = 0x90;
static const uint8_t DALY_REQUEST_MIN_MAX_VOLTAGE = 0x91;
static const uint8_t DALY_REQUEST_MIN_MAX_TEMPERATURE = 0x92;
static const uint8_t DALY_REQUEST_MOS = 0x93;
static const uint8_t DALY_REQUEST_STATUS = 0x94;
static const uint8_t DALY_REQUEST_TEMPERATURE = 0x96;
void DalyBmsComponent::setup() {}
void DalyBmsComponent::dump_config() {
ESP_LOGCONFIG(TAG, "Daly BMS:");
this->check_uart_settings(9600);
}
void DalyBmsComponent::update() {
this->request_data_(DALY_REQUEST_BATTERY_LEVEL);
this->request_data_(DALY_REQUEST_MIN_MAX_VOLTAGE);
this->request_data_(DALY_REQUEST_MIN_MAX_TEMPERATURE);
this->request_data_(DALY_REQUEST_MOS);
this->request_data_(DALY_REQUEST_STATUS);
this->request_data_(DALY_REQUEST_TEMPERATURE);
std::vector<uint8_t> get_battery_level_data;
int available_data = this->available();
if (available_data >= DALY_FRAME_SIZE) {
get_battery_level_data.resize(available_data);
this->read_array(get_battery_level_data.data(), available_data);
this->decode_data_(get_battery_level_data);
}
}
float DalyBmsComponent::get_setup_priority() const { return setup_priority::DATA; }
void DalyBmsComponent::request_data_(uint8_t data_id) {
uint8_t request_message[DALY_FRAME_SIZE];
request_message[0] = 0xA5; // Start Flag
request_message[1] = 0x80; // Communication Module Address
request_message[2] = data_id; // Data ID
request_message[3] = 0x08; // Data Length (Fixed)
request_message[4] = 0x00; // Empty Data
request_message[5] = 0x00; // |
request_message[6] = 0x00; // |
request_message[7] = 0x00; // |
request_message[8] = 0x00; // |
request_message[9] = 0x00; // |
request_message[10] = 0x00; // |
request_message[11] = 0x00; // Empty Data
request_message[12] = (uint8_t)(request_message[0] + request_message[1] + request_message[2] +
request_message[3]); // Checksum (Lower byte of the other bytes sum)
this->write_array(request_message, sizeof(request_message));
this->flush();
}
void DalyBmsComponent::decode_data_(std::vector<uint8_t> data) {
auto it = data.begin();
while ((it = std::find(it, data.end(), 0xA5)) != data.end()) {
if (data.end() - it >= DALY_FRAME_SIZE && it[1] == 0x01) {
uint8_t checksum;
int sum = 0;
for (int i = 0; i < 12; i++) {
sum += it[i];
}
checksum = sum;
if (checksum == it[12]) {
switch (it[2]) {
case DALY_REQUEST_BATTERY_LEVEL:
if (this->voltage_sensor_) {
this->voltage_sensor_->publish_state((float) encode_uint16(it[4], it[5]) / 10);
}
if (this->current_sensor_) {
this->current_sensor_->publish_state(((float) (encode_uint16(it[8], it[9]) - DALY_CURRENT_OFFSET) / 10));
}
if (this->battery_level_sensor_) {
this->battery_level_sensor_->publish_state((float) encode_uint16(it[10], it[11]) / 10);
}
break;
case DALY_REQUEST_MIN_MAX_VOLTAGE:
if (this->max_cell_voltage_) {
this->max_cell_voltage_->publish_state((float) encode_uint16(it[4], it[5]) / 1000);
}
if (this->max_cell_voltage_number_) {
this->max_cell_voltage_number_->publish_state(it[6]);
}
if (this->min_cell_voltage_) {
this->min_cell_voltage_->publish_state((float) encode_uint16(it[7], it[8]) / 1000);
}
if (this->min_cell_voltage_number_) {
this->min_cell_voltage_number_->publish_state(it[9]);
}
break;
case DALY_REQUEST_MIN_MAX_TEMPERATURE:
if (this->max_temperature_) {
this->max_temperature_->publish_state(it[4] - DALY_TEMPERATURE_OFFSET);
}
if (this->max_temperature_probe_number_) {
this->max_temperature_probe_number_->publish_state(it[5]);
}
if (this->min_temperature_) {
this->min_temperature_->publish_state(it[6] - DALY_TEMPERATURE_OFFSET);
}
if (this->min_temperature_probe_number_) {
this->min_temperature_probe_number_->publish_state(it[7]);
}
break;
case DALY_REQUEST_MOS:
if (this->status_text_sensor_ != nullptr) {
switch (it[4]) {
case 0:
this->status_text_sensor_->publish_state("Stationary");
break;
case 1:
this->status_text_sensor_->publish_state("Charging");
break;
case 2:
this->status_text_sensor_->publish_state("Discharging");
break;
default:
break;
}
}
if (this->charging_mos_enabled_) {
this->charging_mos_enabled_->publish_state(it[5]);
}
if (this->discharging_mos_enabled_) {
this->discharging_mos_enabled_->publish_state(it[6]);
}
if (this->remaining_capacity_) {
this->remaining_capacity_->publish_state((float) encode_uint32(it[8], it[9], it[10], it[11]) / 1000);
}
break;
case DALY_REQUEST_STATUS:
if (this->cells_number_) {
this->cells_number_->publish_state(it[4]);
}
break;
case DALY_REQUEST_TEMPERATURE:
if (it[4] == 1) {
if (this->temperature_1_sensor_) {
this->temperature_1_sensor_->publish_state(it[5] - DALY_TEMPERATURE_OFFSET);
}
if (this->temperature_2_sensor_) {
this->temperature_2_sensor_->publish_state(it[6] - DALY_TEMPERATURE_OFFSET);
}
}
break;
default:
break;
}
}
std::advance(it, DALY_FRAME_SIZE);
} else {
std::advance(it, 1);
}
}
}
} // namespace daly_bms
} // namespace esphome

View File

@@ -0,0 +1,83 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/text_sensor/text_sensor.h"
#include "esphome/components/binary_sensor/binary_sensor.h"
#include "esphome/components/uart/uart.h"
namespace esphome {
namespace daly_bms {
class DalyBmsComponent : public PollingComponent, public uart::UARTDevice {
public:
DalyBmsComponent() = default;
// SENSORS
void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; }
void set_current_sensor(sensor::Sensor *current_sensor) { current_sensor_ = current_sensor; }
void set_battery_level_sensor(sensor::Sensor *battery_level_sensor) { battery_level_sensor_ = battery_level_sensor; }
void set_max_cell_voltage_sensor(sensor::Sensor *max_cell_voltage) { max_cell_voltage_ = max_cell_voltage; }
void set_max_cell_voltage_number_sensor(sensor::Sensor *max_cell_voltage_number) {
max_cell_voltage_number_ = max_cell_voltage_number;
}
void set_min_cell_voltage_sensor(sensor::Sensor *min_cell_voltage) { min_cell_voltage_ = min_cell_voltage; }
void set_min_cell_voltage_number_sensor(sensor::Sensor *min_cell_voltage_number) {
min_cell_voltage_number_ = min_cell_voltage_number;
}
void set_max_temperature_sensor(sensor::Sensor *max_temperature) { max_temperature_ = max_temperature; }
void set_max_temperature_probe_number_sensor(sensor::Sensor *max_temperature_probe_number) {
max_temperature_probe_number_ = max_temperature_probe_number;
}
void set_min_temperature_sensor(sensor::Sensor *min_temperature) { min_temperature_ = min_temperature; }
void set_min_temperature_probe_number_sensor(sensor::Sensor *min_temperature_probe_number) {
min_temperature_probe_number_ = min_temperature_probe_number;
}
void set_remaining_capacity_sensor(sensor::Sensor *remaining_capacity) { remaining_capacity_ = remaining_capacity; }
void set_cells_number_sensor(sensor::Sensor *cells_number) { cells_number_ = cells_number; }
void set_temperature_1_sensor(sensor::Sensor *temperature_1_sensor) { temperature_1_sensor_ = temperature_1_sensor; }
void set_temperature_2_sensor(sensor::Sensor *temperature_2_sensor) { temperature_2_sensor_ = temperature_2_sensor; }
// TEXT_SENSORS
void set_status_text_sensor(text_sensor::TextSensor *status_text_sensor) { status_text_sensor_ = status_text_sensor; }
// BINARY_SENSORS
void set_charging_mos_enabled_binary_sensor(binary_sensor::BinarySensor *charging_mos_enabled) {
charging_mos_enabled_ = charging_mos_enabled;
}
void set_discharging_mos_enabled_binary_sensor(binary_sensor::BinarySensor *discharging_mos_enabled) {
discharging_mos_enabled_ = discharging_mos_enabled;
}
void setup() override;
void dump_config() override;
void update() override;
float get_setup_priority() const override;
protected:
void request_data_(uint8_t data_id);
void decode_data_(std::vector<uint8_t> data);
sensor::Sensor *voltage_sensor_{nullptr};
sensor::Sensor *current_sensor_{nullptr};
sensor::Sensor *battery_level_sensor_{nullptr};
sensor::Sensor *max_cell_voltage_{nullptr};
sensor::Sensor *max_cell_voltage_number_{nullptr};
sensor::Sensor *min_cell_voltage_{nullptr};
sensor::Sensor *min_cell_voltage_number_{nullptr};
sensor::Sensor *max_temperature_{nullptr};
sensor::Sensor *max_temperature_probe_number_{nullptr};
sensor::Sensor *min_temperature_{nullptr};
sensor::Sensor *min_temperature_probe_number_{nullptr};
sensor::Sensor *remaining_capacity_{nullptr};
sensor::Sensor *cells_number_{nullptr};
sensor::Sensor *temperature_1_sensor_{nullptr};
sensor::Sensor *temperature_2_sensor_{nullptr};
text_sensor::TextSensor *status_text_sensor_{nullptr};
binary_sensor::BinarySensor *charging_mos_enabled_{nullptr};
binary_sensor::BinarySensor *discharging_mos_enabled_{nullptr};
};
} // namespace daly_bms
} // namespace esphome

View File

@@ -0,0 +1,192 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor
from esphome.const import (
CONF_VOLTAGE,
CONF_CURRENT,
CONF_BATTERY_LEVEL,
CONF_MAX_TEMPERATURE,
CONF_MIN_TEMPERATURE,
DEVICE_CLASS_VOLTAGE,
DEVICE_CLASS_CURRENT,
DEVICE_CLASS_BATTERY,
DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_EMPTY,
STATE_CLASS_MEASUREMENT,
STATE_CLASS_NONE,
UNIT_VOLT,
UNIT_AMPERE,
UNIT_PERCENT,
UNIT_CELSIUS,
UNIT_EMPTY,
ICON_FLASH,
ICON_PERCENT,
ICON_COUNTER,
ICON_THERMOMETER,
ICON_GAUGE,
)
from . import DalyBmsComponent, CONF_BMS_DALY_ID
CONF_MAX_CELL_VOLTAGE = "max_cell_voltage"
CONF_MAX_CELL_VOLTAGE_NUMBER = "max_cell_voltage_number"
CONF_MIN_CELL_VOLTAGE = "min_cell_voltage"
CONF_MIN_CELL_VOLTAGE_NUMBER = "min_cell_voltage_number"
CONF_MAX_TEMPERATURE_PROBE_NUMBER = "max_temperature_probe_number"
CONF_MIN_TEMPERATURE_PROBE_NUMBER = "min_temperature_probe_number"
CONF_CELLS_NUMBER = "cells_number"
CONF_REMAINING_CAPACITY = "remaining_capacity"
CONF_TEMPERATURE_1 = "temperature_1"
CONF_TEMPERATURE_2 = "temperature_2"
ICON_CURRENT_DC = "mdi:current-dc"
ICON_BATTERY_OUTLINE = "mdi:battery-outline"
ICON_THERMOMETER_CHEVRON_UP = "mdi:thermometer-chevron-up"
ICON_THERMOMETER_CHEVRON_DOWN = "mdi:thermometer-chevron-down"
ICON_CAR_BATTERY = "mdi:car-battery"
UNIT_AMPERE_HOUR = "Ah"
TYPES = [
CONF_VOLTAGE,
CONF_CURRENT,
CONF_BATTERY_LEVEL,
CONF_MAX_CELL_VOLTAGE,
CONF_MAX_CELL_VOLTAGE_NUMBER,
CONF_MIN_CELL_VOLTAGE,
CONF_MIN_CELL_VOLTAGE_NUMBER,
CONF_MAX_TEMPERATURE,
CONF_MAX_TEMPERATURE_PROBE_NUMBER,
CONF_MIN_TEMPERATURE,
CONF_MIN_TEMPERATURE_PROBE_NUMBER,
CONF_CELLS_NUMBER,
CONF_REMAINING_CAPACITY,
CONF_TEMPERATURE_1,
CONF_TEMPERATURE_2,
]
CONFIG_SCHEMA = cv.All(
cv.Schema(
{
cv.GenerateID(CONF_BMS_DALY_ID): cv.use_id(DalyBmsComponent),
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
UNIT_VOLT,
ICON_FLASH,
1,
DEVICE_CLASS_VOLTAGE,
STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_CURRENT): sensor.sensor_schema(
UNIT_AMPERE,
ICON_CURRENT_DC,
1,
DEVICE_CLASS_CURRENT,
STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(
UNIT_PERCENT,
ICON_PERCENT,
1,
DEVICE_CLASS_BATTERY,
STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_MAX_CELL_VOLTAGE): sensor.sensor_schema(
UNIT_VOLT,
ICON_FLASH,
2,
DEVICE_CLASS_VOLTAGE,
STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_MAX_CELL_VOLTAGE_NUMBER): sensor.sensor_schema(
UNIT_EMPTY,
ICON_COUNTER,
0,
DEVICE_CLASS_EMPTY,
STATE_CLASS_NONE,
),
cv.Optional(CONF_MIN_CELL_VOLTAGE): sensor.sensor_schema(
UNIT_VOLT,
ICON_FLASH,
2,
DEVICE_CLASS_VOLTAGE,
STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_MIN_CELL_VOLTAGE_NUMBER): sensor.sensor_schema(
UNIT_EMPTY,
ICON_COUNTER,
0,
DEVICE_CLASS_EMPTY,
STATE_CLASS_NONE,
),
cv.Optional(CONF_MAX_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS,
ICON_THERMOMETER_CHEVRON_UP,
0,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_MAX_TEMPERATURE_PROBE_NUMBER): sensor.sensor_schema(
UNIT_EMPTY,
ICON_COUNTER,
0,
DEVICE_CLASS_EMPTY,
STATE_CLASS_NONE,
),
cv.Optional(CONF_MIN_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS,
ICON_THERMOMETER_CHEVRON_DOWN,
0,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_MIN_TEMPERATURE_PROBE_NUMBER): sensor.sensor_schema(
UNIT_EMPTY,
ICON_COUNTER,
0,
DEVICE_CLASS_EMPTY,
STATE_CLASS_NONE,
),
cv.Optional(CONF_REMAINING_CAPACITY): sensor.sensor_schema(
UNIT_AMPERE_HOUR,
ICON_GAUGE,
2,
DEVICE_CLASS_VOLTAGE,
STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_CELLS_NUMBER): sensor.sensor_schema(
UNIT_EMPTY,
ICON_COUNTER,
0,
DEVICE_CLASS_EMPTY,
STATE_CLASS_NONE,
),
cv.Optional(CONF_TEMPERATURE_1): sensor.sensor_schema(
UNIT_CELSIUS,
ICON_THERMOMETER,
0,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_TEMPERATURE_2): sensor.sensor_schema(
UNIT_CELSIUS,
ICON_THERMOMETER,
0,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
),
}
).extend(cv.COMPONENT_SCHEMA)
)
async def setup_conf(config, key, hub):
if key in config:
conf = config[key]
sens = await sensor.new_sensor(conf)
cg.add(getattr(hub, f"set_{key}_sensor")(sens))
async def to_code(config):
hub = await cg.get_variable(config[CONF_BMS_DALY_ID])
for key in TYPES:
await setup_conf(config, key, hub)

View File

@@ -0,0 +1,39 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import text_sensor
from esphome.const import CONF_ICON, CONF_ID, CONF_STATUS
from . import DalyBmsComponent, CONF_BMS_DALY_ID
ICON_CAR_BATTERY = "mdi:car-battery"
TYPES = [
CONF_STATUS,
]
CONFIG_SCHEMA = cv.All(
cv.Schema(
{
cv.GenerateID(CONF_BMS_DALY_ID): cv.use_id(DalyBmsComponent),
cv.Optional(CONF_STATUS): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
cv.Optional(CONF_ICON, default=ICON_CAR_BATTERY): cv.icon,
}
),
}
).extend(cv.COMPONENT_SCHEMA)
)
async def setup_conf(config, key, hub):
if key in config:
conf = config[key]
sens = cg.new_Pvariable(conf[CONF_ID])
await text_sensor.register_text_sensor(sens, conf)
cg.add(getattr(hub, f"set_{key}_text_sensor")(sens))
async def to_code(config):
hub = await cg.get_variable(config[CONF_BMS_DALY_ID])
for key in TYPES:
await setup_conf(config, key, hub)

View File

@@ -0,0 +1,59 @@
from pathlib import Path
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components.packages import validate_source_shorthand
from esphome.yaml_util import dump
dashboard_import_ns = cg.esphome_ns.namespace("dashboard_import")
# payload is in `esphomelib` mdns record, which only exists if api
# is enabled
DEPENDENCIES = ["api"]
CODEOWNERS = ["@esphome/core"]
def validate_import_url(value):
value = cv.string_strict(value)
value = cv.Length(max=255)(value)
# ignore result, only check if it's a valid shorthand
validate_source_shorthand(value)
return value
CONF_PACKAGE_IMPORT_URL = "package_import_url"
CONFIG_SCHEMA = cv.Schema(
{
cv.Required(CONF_PACKAGE_IMPORT_URL): validate_import_url,
}
)
WIFI_CONFIG = """
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
"""
async def to_code(config):
cg.add_define("USE_DASHBOARD_IMPORT")
cg.add(dashboard_import_ns.set_package_import_url(config[CONF_PACKAGE_IMPORT_URL]))
def import_config(path: str, name: str, project_name: str, import_url: str) -> None:
p = Path(path)
if p.exists():
raise FileExistsError
config = {
"substitutions": {"name": name},
"packages": {project_name: import_url},
"esphome": {"name_add_mac_suffix": False},
}
p.write_text(
dump(config) + WIFI_CONFIG,
encoding="utf8",
)

View File

@@ -0,0 +1,12 @@
#include "dashboard_import.h"
namespace esphome {
namespace dashboard_import {
static std::string g_package_import_url; // NOLINT
std::string get_package_import_url() { return g_package_import_url; }
void set_package_import_url(std::string url) { g_package_import_url = std::move(url); }
} // namespace dashboard_import
} // namespace esphome

View File

@@ -0,0 +1,12 @@
#pragma once
#include <string>
namespace esphome {
namespace dashboard_import {
std::string get_package_import_url();
void set_package_import_url(std::string url);
} // namespace dashboard_import
} // namespace esphome

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