From 8c165320064547e1403b9c7ccd501f81062b0fda Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 29 May 2023 09:42:13 +1200 Subject: [PATCH 01/34] Bump version to 2023.5.5 --- Doxygen | 2 +- Makefile | 2 +- _static/version | 2 +- conf.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Doxygen b/Doxygen index 333a09524..9c730fc92 100644 --- a/Doxygen +++ b/Doxygen @@ -38,7 +38,7 @@ PROJECT_NAME = "ESPHome" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 2023.5.4 +PROJECT_NUMBER = 2023.5.5 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/Makefile b/Makefile index 1ff89acd4..acd178ba1 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ ESPHOME_PATH = ../esphome -ESPHOME_REF = 2023.5.4 +ESPHOME_REF = 2023.5.5 .PHONY: html html-strict cleanhtml deploy help live-html Makefile netlify netlify-api api netlify-dependencies svg2png copy-svg2png minify diff --git a/_static/version b/_static/version index e109095fb..22dd2445d 100644 --- a/_static/version +++ b/_static/version @@ -1 +1 @@ -2023.5.4 \ No newline at end of file +2023.5.5 \ No newline at end of file diff --git a/conf.py b/conf.py index bed8c8192..0505bcc74 100644 --- a/conf.py +++ b/conf.py @@ -69,7 +69,7 @@ author = "ESPHome" # The short X.Y version. version = "2023.5" # The full version, including alpha/beta/rc tags. -release = "2023.5.4" +release = "2023.5.5" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. From 103f32f5de8afb7197a619bf12c8b4fc4237f03d Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 29 May 2023 09:42:37 +1200 Subject: [PATCH 02/34] Update changelog for 2023.5.5 --- changelog/2023.5.0.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/changelog/2023.5.0.rst b/changelog/2023.5.0.rst index 68f4b9ff4..f2e8583de 100644 --- a/changelog/2023.5.0.rst +++ b/changelog/2023.5.0.rst @@ -80,6 +80,11 @@ Release 2023.5.4 - May 24 - fix modbus sending FP32_R values :esphomepr:`4882` by :ghuser:`ssieb` - Fix esp32_rmt_led_strip color modes :esphomepr:`4886` by :ghuser:`jesserockz` +Release 2023.5.5 - May 29 +------------------------- + +- Fix version printing not breaking yaml parsing :esphomepr:`4904` by :ghuser:`jesserockz` + Full list of changes -------------------- From 0125166c408c2944e4a9a093a370f4919d7105b0 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 29 May 2023 09:43:08 +1200 Subject: [PATCH 03/34] Update supporters for 2023.5.5 --- guides/supporters.rst | 59 ++++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/guides/supporters.rst b/guides/supporters.rst index 5437f485d..dafeee1c5 100644 --- a/guides/supporters.rst +++ b/guides/supporters.rst @@ -64,7 +64,7 @@ Contributors - `Alexandre Danault (@AlexDanault) `__ - `Alex Iribarren (@alexiri) `__ - `Alex Mekkering (@AlexMekkering) `__ -- `Alex Yao (@alexyao2015) `__ +- `Alex (@alexyao2015) `__ - `Alfredo (@alfredopironti) `__ - `Andreas Mandel (@amandel) `__ - `Amish Vishwakarma (@amishv) `__ @@ -80,12 +80,12 @@ Contributors - `Andreas Hergert (@andreashergert1984) `__ - `Andrew J.Swan (@andrewjswan) `__ - `andrewpc (@andrewpc) `__ -- `Andrew Y. (@andrey-yantsen) `__ +- `Andrey Yantsen (@andrey-yantsen) `__ - `Andrzej (@andriej) `__ - `Andreas (@anduchs) `__ - `Andy2No (@Andy2No) `__ - `AndyRPH (@AndyRPH) `__ -- `Angel Nunez Mencias (@angelnu) `__ +- `Vegetto (@angelnu) `__ - `Sergey Anisimov (@anisimovsergey) `__ - `Nikolay Vasilchuk (@Anonym-tsk) `__ - `Anthony Keane (@anthonykeane) `__ @@ -111,12 +111,12 @@ Contributors - `Alexander Turlov (@aturlov) `__ - `aus (@aus) `__ - `AustinMorris (@AustinMorris) `__ -- `Denis Demchenko (@Avirsaam) `__ +- `Avirsaam (@Avirsaam) `__ - `Arsène von Wyss (@avonwyss) `__ - `Andrew Weddle (@aweddle2) `__ - `Achilleas Pipinellis (@axilleas) `__ - `Kamil Trzciński (@ayufan) `__ -- `Azimath (@Azimath) `__ +- `Nicholas Peters (@Azimath) `__ - `Daniel (@azrael783) `__ - `B48D81EFCC (@B48D81EFCC) `__ - `Florian Mösch (@badbadc0ffee) `__ @@ -131,7 +131,7 @@ Contributors - `Bascht74 (@Bascht74) `__ - `Viktr (@BbIKTOP) `__ - `J. Nick Koston (@bdraco) `__ -- `Maxym Ocheretianko (@bearpawmaxim) `__ +- `Maxim Ocheretianko (@bearpawmaxim) `__ - `beaudeanadams (@beaudeanadams) `__ - `Benjamin Freeman (@Beetix) `__ - `beikeland (@beikeland) `__ @@ -151,7 +151,7 @@ Contributors - `Ivan Bessarabov (@bessarabov) `__ - `Brandon (@bgulla) `__ - `Benedikt Hübschen (@bhuebschen) `__ -- `Stef (@Bierchermuesli) `__ +- `Bierchermuesli (@Bierchermuesli) `__ - `Bill Church (@billchurch) `__ - `Brian Kaufman (@bkaufx) `__ - `JDavid (@blackhack) `__ @@ -169,7 +169,7 @@ Contributors - `Mauricio Bonani (@bonanitech) `__ - `Casey Olson (@bookcasey) `__ - `Borja Burgos (@borjaburgos) `__ -- `bouhaa (@BoukeHaarsma23) `__ +- `BoukeHaarsma23 (@BoukeHaarsma23) `__ - `brambo123 (@brambo123) `__ - `Bram Kragten (@bramkragten) `__ - `Brandan Cotton (@Brando47) `__ @@ -288,7 +288,7 @@ Contributors - `Daniel Correa Lobato (@dclobato) `__ - `DeadEnd (@DeadEnded) `__ - `Debashish Sahu (@debsahu) `__ -- `Dek Shanaghy (@declanshanaghy) `__ +- `declanshanaghy (@declanshanaghy) `__ - `Maximilian (@DeerMaximum) `__ - `definitio (@definitio) `__ - `Christiaan Blom (@Deinara) `__ @@ -335,7 +335,7 @@ Contributors - `Tom Soer (@dtx3k) `__ - `dubit0 (@dubit0) `__ - `Mikkel Jeppesen (@Duckle29) `__ -- `Sergey Dudanov (@dudanov) `__ +- `Sergey V. DUDANOV (@dudanov) `__ - `David Girón (@duhow) `__ - `Duncan Findlay (@duncf) `__ - `Jannick (@DutchDeffy) `__ @@ -489,7 +489,7 @@ Contributors - `Héctor Giménez (@hectorgimenez) `__ - `Jimmy Hedman (@HeMan) `__ - `Hemi03 (@Hemi03) `__ -- `Andrei Solodovnikov (@HepoH3) `__ +- `HepoH3 (@HepoH3) `__ - `Hermann Kraus (@herm) `__ - `Herr Frei (@herrfrei) `__ - `highground88 (@highground88) `__ @@ -510,7 +510,7 @@ Contributors - `Adrián Panella (@ianchi) `__ - `Ian Anderson (@ianderso) `__ - `Ian Leeder (@ianleeder) `__ -- `Honza Pobořil (@iBobik) `__ +- `Jan Pobořil (@iBobik) `__ - `igg (@igg) `__ - `Ignacio Hernandez-Ros (@IgnacioHR) `__ - `ikatkov (@ikatkov) `__ @@ -606,13 +606,13 @@ Contributors - `Jonathan Treffler (@JonathanTreffler) `__ - `JonnyaiR (@jonnyair) `__ - `Jonathan V (@jonofmac) `__ -- `Joppy Furr (@JoppyFurr) `__ +- `Joppy (@JoppyFurr) `__ - `Jared Sanson (@jorticus) `__ - `Joshua Spence (@joshuaspence) `__ - `joskfg (@joskfg) `__ - `Joscha Wagner (@jowgn) `__ - `Javier Peletier (@jpeletier) `__ -- `Jos Suanet (@jsuanet) `__ +- `jsuanet (@jsuanet) `__ - `James Szalay (@jtszalay) `__ - `Jules-R (@Jules-R) `__ - `Julie Koubová (@juliekoubova) `__ @@ -621,7 +621,7 @@ Contributors - `Justin Gerhardt (@justin-gerhardt) `__ - `Justyn Shull (@justyns) `__ - `Jasper van der Neut - Stulen (@jvanderneutstulen) `__ -- `João Vitor Miranda Roma (@jvmr1) `__ +- `João Vitor M. Roma (@jvmr1) `__ - `Jack Wozny (@jwozny) `__ - `Jozef Zuzelka (@jzlka) `__ - `Kris (@K-r-i-s-t-i-a-n) `__ @@ -656,7 +656,7 @@ Contributors - `Karl Q. (@kquinsland) `__ - `Kodey Converse (@krconv) `__ - `KristopherMackowiak (@KristopherMackowiak) `__ -- `Stefan Rado (@kroimon) `__ +- `kroimon (@kroimon) `__ - `krunkel (@krunkel) `__ - `kryptonitecb3 (@kryptonitecb3) `__ - `Kendell R (@KTibow) `__ @@ -672,7 +672,7 @@ Contributors - `Lawrie George (@lawriege) `__ - `Laszlo Gazdag (@lazlyhu) `__ - `Ludovic BOUÉ (@lboue) `__ -- `Luca Cavalli (@lcavalli) `__ +- `lcavalli (@lcavalli) `__ - `Craig Fletcher (@leakypixel) `__ - `Dominik Wagenknecht (@LeDominik) `__ - `Benny de Leeuw (@leeuwte) `__ @@ -681,7 +681,7 @@ Contributors - `Leon Loopik (@Lewn) `__ - `Luca Gugelmann (@lgugelmann) `__ - `Lubos Horacek (@lhoracek) `__ -- `LiJu09 (@LiJu09) `__ +- `Juraj Liso (@LiJu09) `__ - `lillborje71 (@lillborje71) `__ - `lingex (@lingex) `__ - `Markus (@Links2004) `__ @@ -704,7 +704,7 @@ Contributors - `Ohad Lutzky (@lutzky) `__ - `Luke Fitzgerald (@lwfitzgerald) `__ - `Alex Peters (@Lx) `__ -- `Marius Dinu (@M95D) `__ +- `M95D (@M95D) `__ - `maaadc (@maaadc) `__ - `Marc-Antoine Courteau (@macourteau) `__ - `Massimiliano Ravelli (@madron) `__ @@ -856,7 +856,7 @@ Contributors - `obrain17 (@obrain17) `__ - `Ockert Marais (@OckertM) `__ - `Dave Walker (@oddsockmachine) `__ -- `Andrii Ganzevych (@odya) `__ +- `Andrey Ganzevich (@odya) `__ - `ogatatsu (@ogatatsu) `__ - `Oğuzhan Başer (@oguzhanbaser) `__ - `OkhammahkO (@OkhammahkO) `__ @@ -864,7 +864,7 @@ Contributors - `Ömer Şiar Baysal (@omersiar) `__ - `optimusprimespace (@optimusprimespace) `__ - `Oscar Bolmsten (@oscar-b) `__ -- `Mauri (@Otamay) `__ +- `Otamay (@Otamay) `__ - `Otto Winter (@OttoWinter) `__ - `Maxime Dufour (@outscale-mdr) `__ - `Ben Owen (@owenb321) `__ @@ -935,7 +935,7 @@ Contributors - `Robert Gabrielson (@rgabrielson11) `__ - `Rafael Goes (@rgriffogoes) `__ - `rheinz (@rheinz) `__ -- `Richard Hopton (@richardhopton) `__ +- `richardhopton (@richardhopton) `__ - `Richard Klingler (@richardklingler) `__ - `Richard Lewis (@richrd) `__ - `Andre Borie (@Rjevski) `__ @@ -964,6 +964,7 @@ Contributors - `rwilson131 (@rwilson131) `__ - `Ryan Lang (@ryan-lang) `__ - `ryanalden (@ryanalden) `__ +- `ryansmigley (@ryansmigley) `__ - `Lukas Bachschwell (@s00500) `__ - `Sabesto (@Sabesto) `__ - `Jan Čermák (@sairon) `__ @@ -1024,7 +1025,7 @@ Contributors - `sticilface (@sticilface) `__ - `Stijn Tintel (@stintel) `__ - `Mathias Stock (@Stock-M) `__ -- `Daniel Jönsson (@Strixx76) `__ +- `Strixx76 (@Strixx76) `__ - `stubs12 (@stubs12) `__ - `Jordan Vohwinkel (@sublime93) `__ - `sud33p (@sud33p) `__ @@ -1068,7 +1069,7 @@ Contributors - `Thomas Langewouters (@thouters) `__ - `Transylvania High Tech (@thtro) `__ - `Thunderbiscuits (@Thunderbiscuits) `__ -- `Tiago Freire (@tiagofreire-pt) `__ +- `tiagofreire-pt (@tiagofreire-pt) `__ - `Tijs-B (@Tijs-B) `__ - `Tim Laurence (@timdaman) `__ - `Aidan Timson (@timmo001) `__ @@ -1077,7 +1078,7 @@ Contributors - `Tinkerfish (@tinkerfish) `__ - `TJ Horner (@tjhorner) `__ - `Christian (@Tntdruid) `__ -- `Philipp Riederer (@toelke) `__ +- `Philipp Tölke (@toelke) `__ - `tomaszduda23 (@tomaszduda23) `__ - `Tom Brien (@TomBrien) `__ - `Tom Hartogs (@TomHartogs) `__ @@ -1086,7 +1087,7 @@ Contributors - `Tom Price (@tomtom5152) `__ - `David Kiliani (@torfbolt) `__ - `tracestep (@tracestep) `__ -- `Felix E (@tribut) `__ +- `Felix Eckhofer (@tribut) `__ - `Trick van Staveren (@trickv) `__ - `TripitakaBC (@TripitakaBC) `__ - `Tobias (@tripplet) `__ @@ -1128,7 +1129,7 @@ Contributors - `Ian Wells (@wellsi) `__ - `wifwucite (@wifwucite) `__ - `wilberforce (@wilberforce) `__ -- `Willem Vooijs (@wildekek) `__ +- `wildekek (@wildekek) `__ - `Wingman3434 (@Wingman3434) `__ - `Emil Hesslow (@WizKid) `__ - `WJCarpenter (@wjcarpenter) `__ @@ -1143,7 +1144,7 @@ Contributors - `Mike Brown (@xenoxaos) `__ - `Xose Pérez (@xoseperez) `__ - `WitchKing (@xvil) `__ -- `Yaroslav Heriatovych (@Yarikx) `__ +- `Yaroslav (@Yarikx) `__ - `Marcin Jaworski (@yawor) `__ - `Yuval Aboulafia (@yuvalabou) `__ - `Björn Stenberg (@zagor) `__ @@ -1158,4 +1159,4 @@ Contributors - `Zack Barett (@zsarnett) `__ - `Christian Zufferey (@zuzu59) `__ -*This page was last updated May 24, 2023.* +*This page was last updated May 29, 2023.* From 805ece2df4925fc989610ef38a59361b427d9577 Mon Sep 17 00:00:00 2001 From: LewisSpring <40576482+LewisSpring@users.noreply.github.com> Date: Mon, 29 May 2023 22:35:45 +0100 Subject: [PATCH 04/34] Update internal_temperature.rst - Clarify 53.3/128 value (#2955) --- components/sensor/internal_temperature.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/sensor/internal_temperature.rst b/components/sensor/internal_temperature.rst index 3949346f8..2aca16fab 100644 --- a/components/sensor/internal_temperature.rst +++ b/components/sensor/internal_temperature.rst @@ -12,7 +12,7 @@ temperature sensor of the ESP32 and RP2040 chip. .. note:: Some ESP32 variants return a large amount of invalid temperature - values. Invalid measurements are ignored by this component. + values, including 53.3°C which equates to a raw value of 128. Invalid measurements are ignored by this component. .. figure:: images/internal_temperature-ui.png :align: center From da0f328272d1c4bfe72cb2094b76255077a91ec5 Mon Sep 17 00:00:00 2001 From: blakadder Date: Tue, 30 May 2023 20:44:22 +0200 Subject: [PATCH 05/34] Update i2s_audio.rst (#2947) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update i2s_audio.rst Superscript title to keep inline with other I2S components * Update components/media_player/i2s_audio.rst --------- Co-authored-by: H. Árkosi Róbert Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- components/media_player/i2s_audio.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/media_player/i2s_audio.rst b/components/media_player/i2s_audio.rst index ceb0e1f2e..c0f4fae58 100644 --- a/components/media_player/i2s_audio.rst +++ b/components/media_player/i2s_audio.rst @@ -1,4 +1,4 @@ -I2S Audio Media Player +I²S Audio Media Player ====================== .. seo:: From d21a0df7272b6f778a2a383813a6eeb8452b734f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=2E=20=C3=81rkosi=20R=C3=B3bert?= Date: Wed, 31 May 2023 14:19:21 +0200 Subject: [PATCH 06/34] Update rtttl.rst (#2959) --- components/rtttl.rst | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/components/rtttl.rst b/components/rtttl.rst index ea66970b4..51f35978f 100644 --- a/components/rtttl.rst +++ b/components/rtttl.rst @@ -55,7 +55,7 @@ Plays an rtttl tone. on_...: then: - - rtttl.play: 'MissionImp:d=16,o=6,b=95:32d,32d#,32d,32d#,32d,32d#,32d,32d#,32d,32d,32d#,32e,32f,32f#,32g,g,8p,g,8p,a#,p,c7,p,g,8p,g,8p,f,p,f#,p,g,8p,g,8p,a#,p,c7,p,g,8p,g,8p,f,p,f#,p,a#,g,2d,32p,a#,g,2c#,32p,a#,g,2c,a#5,8c,2p,32p,a#5,g5,2f#,32p,a#5,g5,2f,32p,a#5,g5,2e,d#,8d' + - rtttl.play: 'siren:d=8,o=5,b=100:d,e,d,e,d,e,d,e' Configuration options: @@ -101,15 +101,17 @@ This Condition returns true while playback is active. Common beeps ------------ -You can do your own beep patterns too! Here are a few I made so you can just use right away or tweak them to your -like: +You can do your own beep patterns too! Here's a short collection so you can just use right away or tweak them to your like: .. code-block:: yaml - two short:d=4,o=5,b=100:16e6,16e6 + two_short:d=4,o=5,b=100:16e6,16e6 long:d=1,o=5,b=100:e6 siren:d=8,o=5,b=100:d,e,d,e,d,e,d,e scale_up:d=32,o=5,b=100:c,c#,d#,e,f#,g#,a#,b + star_wars:d=16,o=5,b=100:4e,4e,4e,8c,p,g,4e,8c,p,g,4e,4p,4b,4b,4b,8c6,p,g,4d#,8c,p,g,4e,8p + mission_imp:d=16,o=6,b=95:32d,32d#,32d,32d#,32d,32d#,32d,32d#,32d,32d,32d#,32e,32f,32f#,32g,g,8p,g,8p,a#,p,c7,p,g,8p,g,8p,f,p,f#,p,g,8p,g,8p,a#,p,c7,p,g,8p,g,8p,f,p,f#,p,a#,g,2d,32p,a#,g,2c#,32p,a#,g,2c,a#5,8c,2p,32p,a#5,g5,2f#,32p,a#5,g5,2f,32p,a#5,g5,2e,d#,8d + mario:d=4,o=5,b=100:16e6,16e6,32p,8e6,16c6,8e6,8g6,8p,8g,8p,8c6,16p,8g,16p,8e,16p,8a,8b,16a#,8a,16g.,16e6,16g6,8a6,16f6,8g6,8e6,16c6,16d6,8b,16p,8c6,16p,8g,16p,8e,16p,8a,8b,16a#,8a,16g.,16e6,16g6,8a6,16f6,8g6,8e6,16c6,16d6,8b,8p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16g#,16a,16c6,16p,16a,16c6,16d6,8p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16c7,16p,16c7,16c7,p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16g#,16a,16c6,16p,16a,16c6,16d6,8p,16d#6,8p,16d6,8p,16c6 Test setup @@ -120,7 +122,7 @@ E.g. for calling ``rtttl.play`` select the service ``esphome.test_esp8266_rtttl_ .. code-block:: yaml - song_str: "mario:d=4,o=5,b=100:16e6,16e6,32p,8e6,16c6,8e6,8g6,8p,8g,8p,8c6,16p,8g,16p,8e,16p,8a,8b,16a#,8a,16g.,16e6,16g6,8a6,16f6,8g6,8e6,16c6,16d6,8b,16p,8c6,16p,8g,16p,8e,16p,8a,8b,16a#,8a,16g.,16e6,16g6,8a6,16f6,8g6,8e6,16c6,16d6,8b,8p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16g#,16a,16c6,16p,16a,16c6,16d6,8p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16c7,16p,16c7,16c7,p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16g#,16a,16c6,16p,16a,16c6,16d6,8p,16d#6,8p,16d6,8p,16c6" + song_str: 'scale_up:d=32,o=5,b=100:c,c#,d#,e,f#,g#,a#,b' Sample code *********** From a0382272be57b3aef0d25979e9aff4ad19629ae4 Mon Sep 17 00:00:00 2001 From: ugoogalizer Date: Wed, 31 May 2023 22:34:30 +1000 Subject: [PATCH 07/34] added note on NFS for container config volume (#2930) Co-authored-by: MC --- guides/getting_started_command_line.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/guides/getting_started_command_line.rst b/guides/getting_started_command_line.rst index a58ed5798..da532d7c7 100644 --- a/guides/getting_started_command_line.rst +++ b/guides/getting_started_command_line.rst @@ -40,6 +40,12 @@ If you want to use `docker-compose` instead, here's a sample file: privileged: true network_mode: host +.. note:: + + If you are using NFS share to back your container's config volume, you may + need to mount the volume with the `nolock` option, otherwise platformio may + freeze on container startup as per `platformIO-core Issue 3089 `__ + The project provides multiple docker tags; please pick the one that suits you better: From c8b352fe482ed8d1b5cafb004b528938b5c3c2a5 Mon Sep 17 00:00:00 2001 From: Aapeli Vuorinen Date: Wed, 31 May 2023 08:34:53 -0400 Subject: [PATCH 08/34] Update sensiron datasheets (#2927) --- components/sensor/scd30.rst | 2 +- components/sensor/sdp3x.rst | 4 ++-- components/sensor/sht4x.rst | 4 ++-- components/sensor/shtcx.rst | 4 ++-- components/sensor/sps30.rst | 2 +- components/sensor/sts3x.rst | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/components/sensor/scd30.rst b/components/sensor/scd30.rst index c33f622ad..4081d8a14 100644 --- a/components/sensor/scd30.rst +++ b/components/sensor/scd30.rst @@ -6,7 +6,7 @@ SCD30 CO₂, Temperature and Relative Humidity Sensor :image: scd30.jpg The ``scd30`` sensor platform allows you to use your Sensirion SCD30 CO₂ -(`datasheet `__) sensors with ESPHome. +(`datasheet `__) sensors with ESPHome. The :ref:`I²C Bus ` is required to be set up in your configuration for this sensor to work. .. figure:: images/scd30.jpg diff --git a/components/sensor/sdp3x.rst b/components/sensor/sdp3x.rst index 4bbfd7159..8944c1b1b 100644 --- a/components/sensor/sdp3x.rst +++ b/components/sensor/sdp3x.rst @@ -7,8 +7,8 @@ SDP3x / SDP800 Series Differential Pressure Sensor :keywords: SDP3x, SDP31, SDP32, SDP800 Series, SDP810, SDP810 The SDP3x Differential Pressure sensor allows you to use your SDP3x -(`datasheet `__, -`sparkfun `__) or SDP800 Series (`datasheet `__) +(`datasheet `__, +`sparkfun `__) or SDP800 Series (`datasheet `__) sensors with ESPHome. .. figure:: images/sdp31.jpg diff --git a/components/sensor/sht4x.rst b/components/sensor/sht4x.rst index c671f8a01..4cb37c1b2 100644 --- a/components/sensor/sht4x.rst +++ b/components/sensor/sht4x.rst @@ -6,7 +6,7 @@ SHT4X Temperature and Humidity Sensor :image: sht4x.jpg The ``sht4x`` sensor platform allows you to use your SHT4X temperature and humidity sensor -(`datasheet `__, `Adafruit`_) with ESPHome. +(`datasheet `__, `Adafruit`_) with ESPHome. The :ref:`I²C Bus ` is required to be set up in your configuration for this sensor to work. @@ -58,7 +58,7 @@ The heater can be enabled by setting ``heater_max_duty`` up to a maximum duty cy of ``5%`` (``0.05``). This runs the heater on a regular interval. While the heater is in operation the sensor disables measurements so no updates will be published. -See the (`datasheet `__) +See the (`datasheet `__) for more information about heater operation. See Also diff --git a/components/sensor/shtcx.rst b/components/sensor/shtcx.rst index e69b2595e..a5fce79eb 100644 --- a/components/sensor/shtcx.rst +++ b/components/sensor/shtcx.rst @@ -6,10 +6,10 @@ SHTCx Temperature+Humidity Sensors :image: shtc3.jpg The ``shtcx`` sensor platform Temperature+Humidity sensor allows you to use your Sensirion SHTC1 -(`datasheet `__, +(`datasheet `__, `Sensirion STHC1 `__) and the newer SHTC3 -(`datasheet `__, +(`datasheet `__, `SparkFun`_ ) sensors with ESPHome. The :ref:`I²C Bus ` is required to be set up in your configuration for this sensor to work. diff --git a/components/sensor/sps30.rst b/components/sensor/sps30.rst index f80081032..ac085be2c 100644 --- a/components/sensor/sps30.rst +++ b/components/sensor/sps30.rst @@ -6,7 +6,7 @@ SPS30 Particulate Matter Sensor :image: sps30.jpg The ``sps30`` sensor platform allows you to use your Sensirion SPS30 -(`datasheet `__) sensors with ESPHome. +(`datasheet `__) sensors with ESPHome. The :ref:`I²C Bus ` is required to be set up in your configuration for this sensor to work. This sensor supports both UART and I²C communication. However, at the moment only I²C communication is implemented. diff --git a/components/sensor/sts3x.rst b/components/sensor/sts3x.rst index 479760059..b3f1b84be 100644 --- a/components/sensor/sts3x.rst +++ b/components/sensor/sts3x.rst @@ -6,7 +6,7 @@ STS3X Temperature Sensor :image: sts3x.jpg The ``sts3x`` sensor platform Temperature sensor allows you to use your Sensirion STS30-DIS, STS31-DIS or STS35-DIS -(`datasheet `__, +(`datasheet `__, `Sensirion STS3x `__) sensors with ESPHome. The :ref:`I²C Bus ` is required to be set up in your configuration for this sensor to work. From f1268d3670f686705067510a6d9f2793f1df8389 Mon Sep 17 00:00:00 2001 From: Tim Laurence Date: Wed, 31 May 2023 08:35:30 -0400 Subject: [PATCH 09/34] Add directions on how to automate firmware updates (#2914) --- cookbook/homeassistant_upgrade.rst | 137 +++++++++++++++++++++++++++++ index.rst | 1 + 2 files changed, 138 insertions(+) create mode 100644 cookbook/homeassistant_upgrade.rst diff --git a/cookbook/homeassistant_upgrade.rst b/cookbook/homeassistant_upgrade.rst new file mode 100644 index 000000000..29e894082 --- /dev/null +++ b/cookbook/homeassistant_upgrade.rst @@ -0,0 +1,137 @@ +Automatic Updates with Home Assistant +===================================== + +.. seo:: + :description: Have Home Assistant keep your ESPHome firmware updated automatically. + :image: ../images/home-assistant.svg + :keywords: update upgrade + +Have Home Assistsant automatically install updates on your ESPHome devices. + +.. figure:: ../images/home-assistant.svg + :align: center + :width: 40% + + +Introduction +------------ + +ESPHome regularly releases new updates but it is tedious to apply them and ensure all of your online devices have the latest version of ESPHome firmware. +Using the "python script" feature of ESPHome with a simple automation you can apply upgrades regularly and get notified when upgrades fail. + +Configuration +------------- + +1. If you don't yet have `Python Scripts `__ enabled in Home Assistant + 1. Add ``python_script:`` on it's own line to your ``configuration.yaml``. + 2. Create a folder in your configuration directory called ``python_scripts`` +2. Create a new file ``python_scripts/update_esphome_device.py`` with the contents below. + + .. code-block:: python + + # This script find an ESPHome device that needs a upgrade and kicks of the + # upgrade. + # It tracks the device it tried to update and next time if confirms + # it is no longer in the list of upgradable devices. If found it creates + # a persistant notifcation and trys the next devices in the list. + # + # This upgrades one device at a time and is designed to do upgrades in the + # background in no particular hurry. I don't mind if it takes a few days for + # my deices to upgrade just so long as I don't have to do anything. + # + # Note: If the last device in the list of devices neeeding upgrades fails the + # script will won't try to update any devices. This script assumes + # someone will follow up on upgrade failures. If you want to restart from the + # front of the list delete the entity idenfied by LAST_UPDATE_ENTITY_REC below + # + + + # To make troubleshooting easier change "logger.info" to "logger.error" to put + # the log lines in the log summary. + LOGGER = logger.info + + # Text helper that tracks the last device we tried to update + LAST_UPDATE_ENTITY_REC="input_text.esphome_last_upgraded_device" + + + last_updated = hass.states.get(LAST_UPDATE_ENTITY_REC).state + # Ensure we have the helper we need to detect if last update failed. + if last_updated is None: + last_updated = "NOT SET" + hass.states.set(LAST_UPDATE_ENTITY_REC, last_updated) + + LOGGER(f"{last_updated=}") + + # Get a list off every ESPHome device that needs a upgrade + all_needing_updates = [] + for state in list(hass.states.all()): + if state.entity_id.startswith("update.") \ + and state.entity_id.endswith("_firmware") \ + and state.state == "on" \ + and state.attributes["title"] == "ESPHome": + + all_needing_updates.append(state.entity_id) + + all_needing_updates.sort() # Don't assume hass.states.all order is dependable + + LOGGER(f"{all_needing_updates=}") + + # If something needs updates + if all_needing_updates: + + # Look for the last updated device and if found to still be in need of a + # update assume it's last updated failed. Alert and move on. + try: + next_idx = all_needing_updates.index(last_updated) + 1 + hass.services.call( + "persistent_notification", + "create", + { + "message": f"Update of {last_updated} may have failed.", + "notification_id": f"update_{last_updated}" + }, + False + ) + except ValueError: + # If we didn't find our last updated device restart to the begining + next_idx = 0 + + # Ensure the failure didn't happen at the end of the list as we have + # nothing else todo + if next_idx < len(all_needing_updates): + entity_to_update = all_needing_updates[next_idx] + hass.states.set(LAST_UPDATE_ENTITY_REC, entity_to_update) + LOGGER(f"Updating {entity_to_update}") + hass.services.call("update", "install", + {"entity_id": entity_to_update}, False) + else: + LOGGER((f"No more devices to try to update, update {last_updated} manually" + " to clear this.")) + else: + LOGGER(f"No ESPHome devices need a update.") + +3. Reload your Home Assistant server +4. In Home Assistant create a auotmation to run the python script. Below is a example suitable for a low performance server. It updates a ESPHome device every 20 minutes between 3-6AM every night. + + .. code-block:: yaml + + alias: "ESPHome: Apply updates" + description: "" + trigger: + - platform: time_pattern + hours: "3" + minutes: /20 + seconds: "23" + - platform: time_pattern + hours: "4" + minutes: /20 + seconds: "23" + - platform: time_pattern + hours: "5" + minutes: /20 + seconds: "23" + condition: [] + action: + - service: python_script.update_esphome_devices + data: {} + mode: single diff --git a/index.rst b/index.rst index 47403365e..ffd0e21b6 100644 --- a/index.rst +++ b/index.rst @@ -838,6 +838,7 @@ Cookbook Sonoff Fishpond Pump, cookbook/sonoff-fishpond-pump, cookbook-sonoff-fishpond-pump.jpg Arduino Port Extender, cookbook/arduino_port_extender, arduino_logo.svg EHMTX a matrix status/text display, cookbook/ehmtx, ehmtx.jpg + Automatic Updates with Home Assistant, cookbook/homeassistant_upgrade, home-assistant.svg Do you have other awesome automations or cool setups? Please feel free to add them to the documentation for others to copy. See :doc:`Contributing `. From f73a7fa829061da89681974dcecdf4cdf82beeee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=2E=20=C3=81rkosi=20R=C3=B3bert?= Date: Wed, 31 May 2023 15:13:48 +0200 Subject: [PATCH 10/34] add "See also" footer and inner reference anchors (#2960) --- web-api/index.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/web-api/index.rst b/web-api/index.rst index 481ab6c1e..addb750e3 100644 --- a/web-api/index.rst +++ b/web-api/index.rst @@ -24,6 +24,8 @@ While it's currently recommended to use ESPHome directly through Home Assistant, to integrate ESPHome with an external or self-built application you can use two available APIs: the real-time event source API and REST API. +.. _api-event-source: + Event Source API ~~~~~~~~~~~~~~~~ @@ -56,6 +58,8 @@ states so that the client can catch up with reality. The payloads of these state events are also the same as the payloads of the REST API GET calls. I would recommend just opening the network debug panel of your web browser to see what's sent. +.. _api-rest: + REST API -------- @@ -297,3 +301,9 @@ method is ``set``. The following parameter can be used: minimum and maximum range of the number otherwise it will be ignored. For example POST ``/number/desired_delay/set?value=24`` will set the number to 24. + +See Also +-------- + +- :doc:`/components/http_request` +- :ghedit:`Edit` From bcc9ed6bc54f8202f3dbd6ea8c2ca39c51d4956c Mon Sep 17 00:00:00 2001 From: yousaf465 <83491212+yousaf465@users.noreply.github.com> Date: Wed, 31 May 2023 18:24:42 +0500 Subject: [PATCH 11/34] Create how to share sensor data via http_request (#2893) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Create how to share sensor data via http_request initial webserver for http_request * Update how to share sensor data via http_request * Rename how to share sensor data via http_request to http_request_sensor.rst * Update http_request_sensor.rst * added client and server images * Update http_request_sensor.rst * Update http_request_sensor.rst * Update http_request_sensor.rst * Update http_request_sensor.rst * Update http_request_sensor.rst * Update http_request_sensor.rst * Update http_request_sensor.rst * Update http_request_sensor.rst * Update http_request_sensor.rst * Update http_request_sensor.rst * Update http_request_sensor.rst * Update http_request_sensor.rst * Update http_request_sensor.rst * Update http_request_sensor.rst * Update http_request_sensor.rst fixed template sensor * Update http_request_sensor.rst fixed template senor indent * Update http_request_sensor.rst * Update http_request_sensor.rst * Update http_request_sensor.rst * Update index.rst added htttp_request to cookbook * Update http_request_sensor.rst updated node ----> nodes * Update index.rst Updated node to nodes in index.rst * Update http_request_sensor.rst fixed headline underline too short * Update http_request_sensor.rst * Update index.rst * Update http_request_sensor.rst * Update http_request_sensor.rst * Update http_request_sensor.rst * Update http_request_sensor.rst --------- Co-authored-by: H. Árkosi Róbert --- cookbook/http_request_sensor.rst | 82 +++++++++++++++++++++++++++++++ cookbook/images/clients.png | Bin 0 -> 37375 bytes cookbook/images/server.png | Bin 0 -> 24422 bytes index.rst | 1 + 4 files changed, 83 insertions(+) create mode 100644 cookbook/http_request_sensor.rst create mode 100644 cookbook/images/clients.png create mode 100644 cookbook/images/server.png diff --git a/cookbook/http_request_sensor.rst b/cookbook/http_request_sensor.rst new file mode 100644 index 000000000..fdcf0fdea --- /dev/null +++ b/cookbook/http_request_sensor.rst @@ -0,0 +1,82 @@ +Share data directly between ESPHome nodes +========================================= +You need two ESPHome nodes. One will be the server, and the others will be the clients (can be multiple). You must set up a webserver on your primary esphome to make available the required sensor data using the :doc:`/components/web_server`. + +Server part +----------- + +.. code-block:: yaml + + # Webserver configration + web_server: + port: 80 + +Client part +----------- + +On the client nodes we need a :doc:`/components/http_request` with an ``http_request_data`` id set, and a :doc:`/components/sensor/template` to make it accessible locally. + +.. code-block:: yaml + + http_request: + useragent: esphome/device + timeout: 10s + id: http_request_data + + sensor: + - platform: template + name: "Name of the template sensor" + id: id_of_the_template_sensor + + +Pulling the data +**************** + +To automate the request for data, we will add an interval component requesting the URL pointing to the sensor id for which the state is needed. See :ref:`api-rest` on how to build up the URL for your sensors. + +.. note:: + + The domain is the type of the component, for example ``sensor`` or ``light``. ``id`` refers to the ID of the component - which is created from the name of the component, stripping out all non-alphanumeric characters, making everything lowercase and replacing all spaces by underscores. To confirm the corrrct ID to use, you can set the log level to VERY_VERBOSE on your server node and look for ``object_id:`` in the logs. + +In the example below we pull the value of a sensor, and after parsing the resulted JSON string we publish it to the template sensor: + +.. code-block:: yaml + + interval: + - interval: 60s + then: + - http_request.get: + url: http://address.of.server.node/sensor/ID.of.the.sensor + on_response: + then: + - lambda: |- + json::parse_json(id(http_request_data).get_string(), [](JsonObject root) { + id(id_of_the_template_sensor).publish_state(root["value"]); + }); + + +Result +------ + +.. figure:: images/server.png + :align: center + :width: 90.0% + +Server side real sensor. + + +.. figure:: images/clients.png + :align: center + :width: 90.0% + +Client side template sensor. + + +See Also +-------- + +- :doc:`/components/web_server` +- :doc:`/components/http_request` +- :ref:`api-rest` +- :doc:`/components/sensor/template` +- :ghedit:`Edit` diff --git a/cookbook/images/clients.png b/cookbook/images/clients.png new file mode 100644 index 0000000000000000000000000000000000000000..5b96deb84204b1f5a3aeafda0cd3bc2148d78d0e GIT binary patch literal 37375 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 YKhe0;>xQG?22d9AboFyt=akR{0CJ`wZ~y=R literal 0 HcmV?d00001 diff --git a/cookbook/images/server.png b/cookbook/images/server.png new file mode 100644 index 0000000000000000000000000000000000000000..93ab1ab0edb79b41f761a1b4a545b272058076de GIT binary patch literal 24422 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ξ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 Y0-yhb&UZl*f8Kq?#L~Fz(vAE72L&k4x&QzG literal 0 HcmV?d00001 diff --git a/index.rst b/index.rst index ffd0e21b6..9dc0dd604 100644 --- a/index.rst +++ b/index.rst @@ -838,6 +838,7 @@ Cookbook Sonoff Fishpond Pump, cookbook/sonoff-fishpond-pump, cookbook-sonoff-fishpond-pump.jpg Arduino Port Extender, cookbook/arduino_port_extender, arduino_logo.svg EHMTX a matrix status/text display, cookbook/ehmtx, ehmtx.jpg + Share data directly between ESPHome nodes, cookbook/http_request_sensor, connection.svg Automatic Updates with Home Assistant, cookbook/homeassistant_upgrade, home-assistant.svg Do you have other awesome automations or cool setups? Please feel free to add them to the From 12f9aefeedfb985d3daa2ca381ab82d2228a7349 Mon Sep 17 00:00:00 2001 From: notsonominal <130870838+notsonominal@users.noreply.github.com> Date: Wed, 31 May 2023 15:27:21 +0200 Subject: [PATCH 12/34] Update captive_portal.rst (#2898) Specify only serial uploads will overwrite the wifi settings. --- components/captive_portal.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/captive_portal.rst b/components/captive_portal.rst index 4b2543347..74305f44c 100644 --- a/components/captive_portal.rst +++ b/components/captive_portal.rst @@ -16,7 +16,7 @@ After 1 minute of unsuccessful WiFi connection attempts, the ESP will start a Wi :width: 70.0% In this web interface, you can manually override the WiFi settings of the device (please note -this will be overwritten by any subsequent upload so make sure to also update your YAML configuration). +this will be overwritten by any subsequent serial upload so make sure to also update your YAML configuration). Additionally, you can upload a new firmware file. From 0b041128a6bfd6fa3ce28e557b3113a3661491d5 Mon Sep 17 00:00:00 2001 From: Guillermo Ruffino Date: Wed, 31 May 2023 10:31:55 -0300 Subject: [PATCH 13/34] Current-gen-schema (#2887) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix enum docs style * schema fixes updates for 2023.2 * support multi key prop * format fixes * Format fixes * set typed * fix code quotes * add platforms --------- Co-authored-by: H. Árkosi Róbert --- components/climate/haier.rst | 8 +- components/climate/index.rst | 4 +- components/climate/pid.rst | 108 +++++++------ components/matrix_keypad.rst | 14 +- components/mdns.rst | 8 +- components/output/index.rst | 2 +- ...sigma_delta.rst => sigma_delta_output.rst} | 0 components/output/slow_pwm.rst | 4 +- components/remote_receiver.rst | 4 +- components/remote_transmitter.rst | 6 +- components/sensor/ads1115.rst | 12 +- components/sensor/ee895.rst | 6 +- components/sensor/index.rst | 2 +- components/sensor/kuntze.rst | 29 ++-- components/sensor/ld2410.rst | 2 +- components/vbus.rst | 8 +- index.rst | 2 +- schema_doc.py | 142 ++++++++++-------- 18 files changed, 185 insertions(+), 176 deletions(-) rename components/output/{sigma_delta.rst => sigma_delta_output.rst} (100%) diff --git a/components/climate/haier.rst b/components/climate/haier.rst index 62be42893..68a2b7685 100644 --- a/components/climate/haier.rst +++ b/components/climate/haier.rst @@ -5,7 +5,7 @@ Haier Climate :description: Instructions for setting up a Haier climate devices. :image: air-conditioner.svg -The `haier` climate platform creates a Haier climate device. +The ``haier`` climate platform creates a Haier climate device. The component can be used as a replacement of a Haier proprietary WiFi modules such as KZW-W001 and KZW-W002. This component requires a :ref:`uart` to be setup. @@ -33,7 +33,7 @@ Configuration variables: - **id** (*Optional*, :ref:`config-id`): Manually specify the ID used for code generation. - **name** (**Required**, string): The name of the climate device. -- **update_interval** (*Optional*, :ref:`config-time`): How often device will be polled for status. Defaults to `5s`. +- **update_interval** (*Optional*, :ref:`config-time`): How often device will be polled for status. Defaults to ``5s``. - **supported_swing_modes** (*Optional*, list): List of supported swing modes. Possible values are: ``VERTICAL``, ``HORIZONTAL``, ``BOTH``. - All other options from :ref:`Climate `. @@ -42,10 +42,10 @@ Hardware setup Most units will have a dedicated USB-A port for Haier WiFi module. The physical USB port is in fact UART and does not "speak" USB protocol. -It uses four USB pins as 5V, GND, RX, TX. +It uses four USB pins as 5V, GND, RX, TX. You can use spare male USB cable to connect esphome device directly to the climate appliance. -Other units will not have USB ports, but will still probably have UART exposed somewhere on the main board. +Other units will not have USB ports, but will still probably have UART exposed somewhere on the main board. .. list-table:: Haier UART pinout :header-rows: 1 diff --git a/components/climate/index.rst b/components/climate/index.rst index ceebe2525..7a49b5e12 100644 --- a/components/climate/index.rst +++ b/components/climate/index.rst @@ -52,8 +52,8 @@ Configuration variables: - **temperature_step** (*Optional*, float): The granularity with which the target temperature can be controlled. Can be a single number, or split as below: - - **target_temperature** (**Required**, float) - - **current_temperature** (**Required**, float) + - **target_temperature** (**Required**, float): The granularity for target temperature + - **current_temperature** (**Required**, float): The granularity for current temperature Advanced options: diff --git a/components/climate/pid.rst b/components/climate/pid.rst index 38f711eec..32a317560 100644 --- a/components/climate/pid.rst +++ b/components/climate/pid.rst @@ -15,12 +15,12 @@ temperature to a user-specified setpoint. .. note:: PID is like cruise control in the cars: it keeps the car's speed constant by continuously - adjusting the fuel quantity, based on load measurements. Eg when the car has to go up on a hill, + adjusting the fuel quantity, based on load measurements. Eg when the car has to go up on a hill, the system notices the load increase thus immediately gives more fuel to the engine; and when it goes down on the other side of the hill, it notices the load decrease thus reduces or cuts off fuel completely so that car speed remains as constant as possible. The calculation takes in consideration - constants like car weight, wind resistance etc. - + constants like car weight, wind resistance etc. + This kind of math can be used for a heating or cooling system too, and an auto-tuning algorithm can help determining such constants, which mainly describe the heat loss of the room or building. Goal is to keep the temperature as constant as possible, and smooth out oscillations otherwise produced by @@ -47,12 +47,12 @@ but there's a nice article explaining the function principle `here ` @@ -72,37 +72,35 @@ Configuration variables: ``ki`` to prevent windup. Defaults to ``-1``. - **max_integral** (*Optional*, float): The minimum value of the integral term multiplied by ``ki`` to prevent windup. Defaults to ``1``. - - **starting_integral_term** (*Optional*, float): Set the initial output, by priming the integral - term. This is useful for when your system is rebooted and you don't want to wait + - **starting_integral_term** (*Optional*, float): Set the initial output, by priming the integral + term. This is useful for when your system is rebooted and you don't want to wait for it to get back equilibrium. - - **output_averaging_samples** (*Optional*, int): average the output over this many samples. PID controllers - can be quite sensitive to small changes on the input sensor. By averaging the last X output samples, - the temperature can be more stable. However, the larger the sampling window, the less responsive the + - **output_averaging_samples** (*Optional*, int): average the output over this many samples. PID controllers + can be quite sensitive to small changes on the input sensor. By averaging the last X output samples, + the temperature can be more stable. However, the larger the sampling window, the less responsive the PID controller. Defaults to ``1`` which is no sampling/averaging. - - **derivative_averaging_samples** (*Optional*, int): average the derivative term over this many samples. Many - controllers don't use the derivative term because it is sensitive to slight changes in the input sensor. - By taking an average of the derivative term it might become more useful for you. Most PID controllers call - this derivative filtering. The derivative term is used to pre-act so don't filter too much. Defaults to ``1`` + - **derivative_averaging_samples** (*Optional*, int): average the derivative term over this many samples. Many + controllers don't use the derivative term because it is sensitive to slight changes in the input sensor. + By taking an average of the derivative term it might become more useful for you. Most PID controllers call + this derivative filtering. The derivative term is used to pre-act so don't filter too much. Defaults to ``1`` which is no sampling/averaging. -- **deadband_parameters** (*Optional*): Enables a deadband to stabilise and minimise changes in the +- **deadband_parameters** (*Optional*): Enables a deadband to stabilise and minimise changes in the output when the temperature is close to the target temperature. See `Deadband Setup`_. - - **threshold_low/threshold_high** (**Required**, float): Specifies a high/low - threshold defining the deadband - around the target temperature. For instance with `default_target_temperature` of ``21°C`` and - thresholds of ``+/-0.5°C``, the deadband will be + - **threshold_high/threshold_low** (**Required**, float): Specifies a high/low + threshold defining the deadband around the target temperature. For instance with + ``default_target_temperature`` of ``21°C`` and thresholds of ``+/-0.5°C``, the deadband will be between ``20.5°C - 21.5°C``. The PID controller will limit output changes within the deadband. + - **kp_multiplier** (*Optional*, float): Set the ``kp`` gain when inside the deadband. Defaults to ``0``. + - **ki_multiplier** (*Optional*, float): Set the ``ki`` gain when inside the deadband. Defaults to ``0``. + - **kd_multiplier** (*Optional*, float): Set the ``kd`` gain when inside the deadband. Recommended this + is set to ``0``. Defaults to ``0``. - - **kp_multiplier** (**Optional**, float): Set the ``kp`` gain when inside the deadband. Defaults to ``0``. - - **ki_multiplier** (**Optional**, float): Set the ``ki`` gain when inside the deadband. Defaults to ``0``. - - **kd_multiplier** (**Optional**, float): Set the ``kd`` gain when inside the deadband. Recommended this - is set to 0. Defaults to ``0``. - - - **deadband_output_averaging_samples** (**Optional**, int): Typically when inside the deadband the PID Controller has - reached a state of equilibrium, so it advantageous to use a higher number of output samples + - **deadband_output_averaging_samples** (*Optional*, int): Typically when inside the deadband the PID Controller has + reached a state of equilibrium, so it advantageous to use a higher number of output samples like 10-30 samples. Defaults to ``1`` which is no sampling/averaging. - All other options from :ref:`Climate `. @@ -116,7 +114,7 @@ To set up a PID climate controller, you need a couple of components: - A :ref:`Sensor ` to read the current temperature (``sensor``). - At least one :ref:`float output ` to drive for heating or cooling (or both). - This could for example be a PWM output via :doc:`/components/output/sigma_delta` or :doc:`/components/output/slow_pwm` that drives a heating unit. + This could for example be a PWM output via :doc:`/components/output/sigma_delta_output` or :doc:`/components/output/slow_pwm` that drives a heating unit. Please note the output *must* be controllable with continuous value (not only ON/OFF, but any state in between for example 50% heating power). @@ -126,22 +124,22 @@ To set up a PID climate controller, you need a couple of components: The sensor should have a short update interval. The PID update frequency is tied to the update interval of the sensor. Set a short ``update_interval`` like ``5s`` on the sensor. - We recommend putting a filter on the sensor (see filters in :doc:`/components/sensor/index`) and + We recommend putting a filter on the sensor (see filters in :doc:`/components/sensor/index`) and using ``output_averaging_samples`` to calm the PID sensor from a noisy input sensor. Deadband Setup -------------- -A deadband is used to prevent the PID controller from further adjusting the power -once the temperature has settled within a range of the target temperature. +A deadband is used to prevent the PID controller from further adjusting the power +once the temperature has settled within a range of the target temperature. -We do this by specifying a high/low threshold of the target temperature. +We do this by specifying a high/low threshold of the target temperature. -To understand the benefit, consider a heating/cooling HVAC which is constantly -oscillating between heating and cooling as the thermostat records very minor -changes from +0.1º to -0.1º. Clearly this is undesirable and will cause wear -and tear as the HVAC oscillates. With a deadband in place the heater won't -activate until the thermostat breaches the low_threshold and the cooler won't activate -until the thermostat breaches the high_threshold. +To understand the benefit, consider a heating/cooling HVAC which is constantly +oscillating between heating and cooling as the thermostat records very minor +changes from +0.1º to -0.1º. Clearly this is undesirable and will cause wear +and tear as the HVAC oscillates. With a deadband in place the heater won't +activate until the thermostat breaches the low_threshold and the cooler won't activate +until the thermostat breaches the high_threshold. The most basic setup specifies the threshold around the target temperature as follows: @@ -153,7 +151,7 @@ The most basic setup specifies the threshold around the target temperature as fo threshold_high: 0.5°C threshold_low: -1.0°C -In this example the deadband is between ``20.0°C - 21.5°C``. The PID controller will limit any output +In this example the deadband is between ``20.0°C - 21.5°C``. The PID controller will limit any output variation inside this deadband. How it limits depends on how you set the `Deadband Multipliers`_. .. figure:: images/deadband1.png @@ -161,19 +159,19 @@ variation inside this deadband. How it limits depends on how you set the `Deadba Deadband Multipliers ******************** -Deadband Multipliers tell the controller how to operate when inside of the deadband. +Deadband Multipliers tell the controller how to operate when inside of the deadband. -Each of the p,i and d terms can be controlled using the kp, ki and kd multipliers. For instance, if the kp_multiplier -is set to 0.05 then the final proportional term will be set to 5% of its normal value within the deadband. +Each of the p,i and d terms can be controlled using the kp, ki and kd multipliers. For instance, if the kp_multiplier +is set to 0.05 then the final proportional term will be set to 5% of its normal value within the deadband. -If all of the multipliers are set to 0, then the controller will not adjust power at all within the +If all of the multipliers are set to 0, then the controller will not adjust power at all within the deadband. This is the default behavior. -Most deadband implementations set kp and ki multipliers to a small gain like ``0.05`` and set -derivative to 0. This means that the PID output will calmly make minor adjustments over a 20x longer -timeframe to stay within the deadband zone. +Most deadband implementations set kp and ki multipliers to a small gain like ``0.05`` and set +derivative to 0. This means that the PID output will calmly make minor adjustments over a 20x longer +timeframe to stay within the deadband zone. -To start with we recommend just setting the ``ki_multiplier`` to ``0.05`` (5%). Then +To start with we recommend just setting the ``ki_multiplier`` to ``0.05`` (5%). Then set ``kp_multiplier`` to ``0.05`` (5%) if the controller is falling out of the deadband too often. .. code-block:: yaml @@ -184,7 +182,7 @@ set ``kp_multiplier`` to ``0.05`` (5%) if the controller is falling out of the d threshold_high: 0.5°C threshold_low: -1.0°C kp_multiplier: 0.0 # proportional gain turned off inside deadband - ki_multiplier: 0.05 # integral accumulates at only 5% of normal ki + ki_multiplier: 0.05 # integral accumulates at only 5% of normal ki kd_multiplier: 0.0 # derviative is turned off inside deadband deadband_output_averaging_samples: 15 # average the output over 15 samples within the deadband @@ -192,8 +190,8 @@ set ``kp_multiplier`` to ``0.05`` (5%) if the controller is falling out of the d Deadband Output Averaging Samples ********************************* -Since we expect the PID Controller to be at equilibrium while inside the deadband, we can -average the output over a longer range of samples, like 15 samples. This helps even further +Since we expect the PID Controller to be at equilibrium while inside the deadband, we can +average the output over a longer range of samples, like 15 samples. This helps even further with temperature and controller stability. .. _pid-autotune: @@ -249,7 +247,7 @@ is automatically calculated. To do this, it needs to observe at least 3 oscillat device can reach. For example if the temperature of a room is to be controlled, the setpoint needs to be above the ambient temperature. If the ambient temperature is 20°C, the setpoint of the climate device should be set to at least ~24°C so that an oscillation can be induced. - + Also take care of external influences, like for example when room temperature is severely affected by outdoor weather like sun, if it starts to warm up the room in parallel with the heating autotune will likely fail or give false results. @@ -273,10 +271,10 @@ is automatically calculated. To do this, it needs to observe at least 3 oscillat .. note:: In the output above, the autotuner is driving the heating output at 100% and trying to reach 24.25 °C. - + This will continue for some time until data for 3 phases (6 crossings of the setpoint; or a bit more, depending on the data quality) have been acquired. - + The autotune algorithm may take a long time to complete, it depends on the time needed to reproduce the heating up and cooling down oscillations the required number of times. @@ -298,7 +296,7 @@ is automatically calculated. To do this, it needs to observe at least 3 oscillat As soon as the the autotune procedure finishes, the climate starts to work with the calculated parameters so that expected operation can be immediately verified. - + If satisfied, copy the values in ``control_parameters`` into your configuration: .. code-block:: yaml @@ -349,7 +347,7 @@ Configuration variables: Defaults to ``-1.0``. The ``positive_output`` and ``negative_output`` parameters can be used to compensate the heating or the -cooling process during the autotune, in the cases when they are not changing the temperature at the +cooling process during the autotune, in the cases when they are not changing the temperature at the same rate, resulting in a not symmetrical oscillation. The autotune result will print a message when it's recommended to repeat the entire procedure with such parameters configured. @@ -435,7 +433,7 @@ See Also - Åström, K. J. and T. Hägglund (1984a), 'Automatic tuning of simple regulators', Proceedings of IFAC 9th World Congress, Budapest, 1867-1872 - :doc:`/components/climate/index` -- :doc:`/components/output/sigma_delta` +- :doc:`/components/output/sigma_delta_output` - :doc:`/components/output/slow_pwm` - `Principles of PID `__ - :apiref:`pid/pid_climate.h` diff --git a/components/matrix_keypad.rst b/components/matrix_keypad.rst index c546e0261..caf143c24 100644 --- a/components/matrix_keypad.rst +++ b/components/matrix_keypad.rst @@ -7,8 +7,8 @@ Matrix keypad :description: Matrix key input panel The ``matrix_keypad`` component allows you to integrate pads which -have the keys connected at the intersection points of the rows and columns -of a matrix. +have the keys connected at the intersection points of the rows and columns +of a matrix. .. figure:: ../images/matrix_keypad.jpg :align: center @@ -44,14 +44,14 @@ Configuration variables: - **columns** (**Required**, list): A list of :ref:`pins ` where the vertical matrix lines are connected, in order from left to right. These pins need to be input capable with pullups enabled. If there is no internal pullup, then an external one is required. -- **keys** (*Optional*, string): The keys present on the matrix, from top left to bottom right, +- **keys** (*Optional*, string): The keys present on the matrix, from top left to bottom right, row by row. Required for ``key_collector`` and ``binary_sensor`` (if using key selection). -- **has_diodes** (*Optional*, boolean): For pads where row pins are outputs, and the keys are +- **has_diodes** (*Optional*, boolean): For pads where row pins are outputs, and the keys are connected with diodes. Defaults to ``false``. -Binary Sensors --------------- +Binary Sensor +------------- Individual keys can be added independently to ESPHome as ``binary_sensor``: @@ -82,7 +82,7 @@ Either the ``row`` and ``col`` parameters, or the ``key`` parameter has to be pr .. note:: - Automatic handling of multiple keys (e.g. PIN code entry) is possible with the + Automatic handling of multiple keys (e.g. PIN code entry) is possible with the the :ref:`Key Collector ` component. See Also diff --git a/components/mdns.rst b/components/mdns.rst index ed388627d..6b14f6dca 100644 --- a/components/mdns.rst +++ b/components/mdns.rst @@ -48,7 +48,7 @@ Configuration variables: - **disabled** (*Optional*, boolean): Set to true to disable mDNS usage. Defaults to false. - **services** (*Optional*, list): List of additional services to expose. - - **service** (*Required*, string): Name of extra service - - **protocol** (*Required*, string): Protocol of service (_udp or _tcp) - - **port** (*Optional*, int): Port number of extra service - - **txt** (*Optional*, mapping): Additional text records to add to service + - **service** (**Required**, string): Name of extra service. + - **protocol** (**Required**, string): Protocol of service (_udp or _tcp). + - **port** (*Optional*, int): Port number of extra service. + - **txt** (*Optional*, mapping): Additional text records to add to service. diff --git a/components/output/index.rst b/components/output/index.rst index 66f689fd6..7c00c46dc 100644 --- a/components/output/index.rst +++ b/components/output/index.rst @@ -98,7 +98,7 @@ This action turns the output with the given ID off when executed. *************************** This action sets the float output to the given level when executed. Note: This only -works with floating point outputs like :doc:`/components/output/esp8266_pwm`, :doc:`/components/output/ledc`, :doc:`/components/output/sigma_delta`, :doc:`/components/output/slow_pwm`. +works with floating point outputs like :doc:`/components/output/esp8266_pwm`, :doc:`/components/output/ledc`, :doc:`/components/output/sigma_delta_output`, :doc:`/components/output/slow_pwm`. .. code-block:: yaml diff --git a/components/output/sigma_delta.rst b/components/output/sigma_delta_output.rst similarity index 100% rename from components/output/sigma_delta.rst rename to components/output/sigma_delta_output.rst diff --git a/components/output/slow_pwm.rst b/components/output/slow_pwm.rst index 9c129f4d9..34d6c8aa7 100644 --- a/components/output/slow_pwm.rst +++ b/components/output/slow_pwm.rst @@ -70,7 +70,7 @@ Example: .. note:: If the duty cycle is not constrained to a maximum value, the - :doc:`/components/output/sigma_delta` component offers faster updates and + :doc:`/components/output/sigma_delta_output` component offers faster updates and greater control over the switching frequency. This is better for loads that need some time to fully change between on and off, like eletric thermal actuator heads or fans. @@ -81,7 +81,7 @@ See Also - :doc:`/components/output/index` - :doc:`/components/output/esp8266_pwm` - :doc:`/components/output/ledc` -- :doc:`/components/output/sigma_delta` +- :doc:`/components/output/sigma_delta_output` - :doc:`/components/light/monochromatic` - :doc:`/components/fan/speed` - :doc:`/components/power_supply` diff --git a/components/remote_receiver.rst b/components/remote_receiver.rst index 9c866f45f..640a3170c 100644 --- a/components/remote_receiver.rst +++ b/components/remote_receiver.rst @@ -204,13 +204,13 @@ Remote code selection (exactly one of these has to be included): - **canalsat**: Trigger on a decoded CanalSat remote code with the given data. - **device** (**Required**, int): The device to trigger on, see dumper output for more info. - - **address** (**Optional**, int): The address (or subdevice) to trigger on, see dumper output for more info. Defaults to ``0`` + - **address** (*Optional*, int): The address (or subdevice) to trigger on, see dumper output for more info. Defaults to ``0`` - **command** (**Required**, int): The command to listen for. - **canalsatld**: Trigger on a decoded CanalSatLD remote code with the given data. - **device** (**Required**, int): The device to trigger on, see dumper output for more info. - - **address** (**Optional**, int): The address (or subdevice) to trigger on, see dumper output for more info. Defaults to ``0`` + - **address** (*Optional*, int): The address (or subdevice) to trigger on, see dumper output for more info. Defaults to ``0`` - **command** (**Required**, int): The command to listen for. - **coolix**: Trigger on a decoded Coolix remote code with the given data. diff --git a/components/remote_transmitter.rst b/components/remote_transmitter.rst index 43c7ff85d..62c36ae95 100644 --- a/components/remote_transmitter.rst +++ b/components/remote_transmitter.rst @@ -122,7 +122,7 @@ This :ref:`action ` sends a CanalSat infrared remote code to a re Configuration variables: - **device** (**Required**, int): The device to send to, see dumper output for more details. -- **address** (**Optional**, int): The address (or subdevice) to send to, see dumper output for more details. Defaults to ``0`` +- **address** (*Optional*, int): The address (or subdevice) to send to, see dumper output for more details. Defaults to ``0`` - **command** (**Required**, int): The command to send. - All other options from :ref:`remote_transmitter-transmit_action`. @@ -149,7 +149,7 @@ This :ref:`action ` sends a CanalSatLD infrared remote code to a Configuration variables: - **device** (**Required**, int): The device to send to, see dumper output for more details. -- **address** (**Optional**, int): The address (or subdevice) to send to, see dumper output for more details. Defaults to ``0`` +- **address** (*Optional*, int): The address (or subdevice) to send to, see dumper output for more details. Defaults to ``0`` - **command** (**Required**, int): The command to send. - All other options from :ref:`remote_transmitter-transmit_action`. @@ -283,7 +283,7 @@ This :ref:`action ` sends a 40-bit Midea code to a remote transmi on_...: - remote_transmitter.transmit_midea: code: [0xA2, 0x08, 0xFF, 0xFF, 0xFF] - + on_...: - remote_transmitter.transmit_midea: code: !lambda |- diff --git a/components/sensor/ads1115.rst b/components/sensor/ads1115.rst index 09ef99c54..cc1640bcc 100644 --- a/components/sensor/ads1115.rst +++ b/components/sensor/ads1115.rst @@ -96,8 +96,8 @@ Configuration variables: - **id** (*Optional*, :ref:`config-id`): Manually specify the ID used for code generation. - **resolution** (*Optional*, string): the resolution of this sensor. Defaults to ``16 bits``. - - **16 bits** - - **12 bits** + - ``16 bits`` + - ``12 bits`` Multiplexer and Gain @@ -105,10 +105,10 @@ Multiplexer and Gain .. note:: - As per (`datasheet `__, `Adafruit`_) Section 7.3 Note 2: + As per (`datasheet `__, `Adafruit`_) Section 7.3 Note 2: "No more than VDD + 0.3V must be applied to the analog inputs of the device." This means if you power the device with 3.3V, take care not to supply the 4 AIN pins with more than 3.6V. - + The ADS1115 has a multiplexer that can be configured to measure voltage between several pin configurations. These are: - ``A0_A1`` (between Pin 0 and Pin 1) @@ -128,11 +128,11 @@ Additionally, the ADS1115 has a Programmable Gain Amplifier (PGA) that can help - ``1.024`` (measures up to 1.024V) - ``0.512`` (measures up to 0.512V) - ``0.256`` (measures up to 0.256V) - + The ADS1115 can be used with defaults settings. When using an ADS1015, the resolution has to be specified and should be defined to ``12_BITS`` (or equivalent notations like ``12 BITS`` or ``12 bits``). - + See Also -------- diff --git a/components/sensor/ee895.rst b/components/sensor/ee895.rst index d56db1579..dfb5f2b7d 100644 --- a/components/sensor/ee895.rst +++ b/components/sensor/ee895.rst @@ -36,19 +36,19 @@ The :ref:`I²C Bus ` is required to be set up in your configuration for thi Configuration variables: ------------------------ -- **temperature** (*Required*): The information for the Temperature sensor. +- **temperature** (**Required**): The information for the Temperature sensor. - **name** (**Required**, string): The name for the temperature sensor. - **id** (*Optional*, :ref:`config-id`): Set the ID of this sensor for use in lambdas. - All other options from :ref:`Sensor `. -- **co2** (*Required*): The information for the CO₂ sensor. +- **co2** (**Required**): The information for the CO₂ sensor. - **name** (**Required**, string): The name for the CO₂eq sensor. - **id** (*Optional*, :ref:`config-id`): Set the ID of this sensor for use in lambdas. - All other options from :ref:`Sensor `. -- **Pressure** (*Required*): The information for the Pressure sensor. +- **pressure** (**Required**): The information for the Pressure sensor. - **name** (**Required**, string): The name for the Pressure sensor. - **id** (*Optional*, :ref:`config-id`): Set the ID of this sensor for use in lambdas. diff --git a/components/sensor/index.rst b/components/sensor/index.rst index 3b15ff77e..3fb874c7e 100644 --- a/components/sensor/index.rst +++ b/components/sensor/index.rst @@ -415,7 +415,7 @@ Configuration variables: ``skip_initial`` **************** -A simple skip filter; `skip_initial: N` skips the first `N` sensor readings and passes on the +A simple skip filter; ``skip_initial: N`` skips the first ``N`` sensor readings and passes on the rest. This can be used when the sensor needs a few readings to 'warm up'. After the initial readings have been skipped, this filter does nothing. diff --git a/components/sensor/kuntze.rst b/components/sensor/kuntze.rst index b4727c693..6bd2372d7 100644 --- a/components/sensor/kuntze.rst +++ b/components/sensor/kuntze.rst @@ -5,7 +5,7 @@ Kuntze pool monitor :description: Instructions for setting up Kuntze pool monitor in ESPHome. :image: kuntze.jpg -The ``kuntze`` component allows you to integrate the Kuntze water measurement +The ``kuntze`` component allows you to integrate the Kuntze water measurement instrument in ESPHome. It uses :ref:`UART ` (ModBUS) for communication. Once configured you can use sensors as described below for your projects. @@ -19,25 +19,25 @@ Once configured you can use sensors as described below for your projects. Overview -------- -Kuntze devices have an RS485 (ModBUS RTU) communication port. Please see the +Kuntze devices have an RS485 (ModBUS RTU) communication port. Please see the Kuntze papers for the pinout of the RS485 connector on your unit. ModBUS line has to be terminated properly (with a ``120Ω`` resistor), and since this is likely your only unit connected to ESPHome, you should activate bus termination in the -Network menu (this component doesn't support multiple Kuntze devices on the same +Network menu (this component doesn't support multiple Kuntze devices on the same bus). ModBUS address should remain at factory default value. -The device communicates at ``19200`` baud ``8E1``. To connect to ESPHome, an RS485 -transceiver is needed. Choose a type which does not need a trigger to send and +The device communicates at ``19200`` baud ``8E1``. To connect to ESPHome, an RS485 +transceiver is needed. Choose a type which does not need a trigger to send and receive data, for example: .. figure:: ../../images/rs485.jpg -The controller connects to the UART of the MCU. For ESP32 GPIO `16` to `TXD` and `17` +The controller connects to the UART of the MCU. For ESP32 GPIO `16` to `TXD` and `17` to RXD are the default ones but any other pins can be used as well. 3.3V to VCC and GND to GND. .. warning:: - If you are using the :ref:`logger` make sure you are not using the same pins for it or otherwise disable the UART + If you are using the :ref:`logger` make sure you are not using the same pins for it or otherwise disable the UART logging with the ``baud_rate: 0`` option. Component @@ -66,14 +66,13 @@ A configured modbus component is optional. It will be automatically created. Configuration variables: -- **ph**: Measured pH value -- **temperature**: Measured temperature value -- **dis1**: Measured DIS 1 value -- **dis2**: Measured DIS 2 value -- **redox**: Measured Redox value -- **ec**: Measured EC value -- **oci**: Measured OCI value - +- **ph** (*Optional*): Measured pH value. +- **temperature** (*Optional*): Measured temperature value. +- **dis1** (*Optional*): Measured DIS 1 value. +- **dis2** (*Optional*): Measured DIS 2 value. +- **redox** (*Optional*): Measured Redox value. +- **ec** (*Optional*): Measured EC value. +- **oci** (*Optional*): Measured OCI value. All sensors are *Optional* and support all other options from :ref:`Sensor `. diff --git a/components/sensor/ld2410.rst b/components/sensor/ld2410.rst index 813ef999b..751388ca2 100644 --- a/components/sensor/ld2410.rst +++ b/components/sensor/ld2410.rst @@ -75,7 +75,7 @@ and binary sensors. Value between ``0.75m`` and ``6m`` inclusive. Defaults to ``4.5m``. - **gX_move_threshold** (*Optional*, int): Threshold for the Xth gate for motion detection (X => 0 to 8). Above this level for the considered gate (distance), movement detection will be triggered. Defaults to ``see table below``. -- **gX _still_threshold** (*Optional*, int): Threshold for the Xth gate for still detection. (X => 0 to 8). +- **gX_still_threshold** (*Optional*, int): Threshold for the Xth gate for still detection. (X => 0 to 8). Above this level for the considered gate (distance), still detection will be triggered. Defaults to ``see table below``. .. list-table:: Default values for gate threshold diff --git a/components/vbus.rst b/components/vbus.rst index 9cb76c84b..72dd10720 100644 --- a/components/vbus.rst +++ b/components/vbus.rst @@ -176,8 +176,8 @@ All binary sensors are *Optional* and support all other options from :ref:`Binar ``Custom`` VBus sensors ----------------------- -Devices on a VBus are identified with a source address. There can be multiple devices on the same bus, -each device type has a different address. +Devices on a VBus are identified with a source address. There can be multiple devices on the same bus, +each device type has a different address. .. code-block:: yaml @@ -203,7 +203,7 @@ Configuration variables: - **sensors** (**Required**): A list of :ref:`Sensor ` definitions that include a ``lambda`` to do the decoding and return a ``float`` value. - **lambda** (**Required**, :ref:`lambda `): Code to parse a value from the incoming data packets and return it. - The data packet is in a `std::vector` called `x`. + The data packet is in a ``std::vector`` called ``x``. ``custom`` VBus binary sensors @@ -218,7 +218,7 @@ Configuration variables: - **binary_sensors** (**Required**): A list of :ref:`Binary Sensor ` definitions that include a ``lambda`` to do the decoding and return a ``bool`` value. - **lambda** (**Required**, :ref:`lambda `): Code to parse a value from the incoming data packets and return it. - The data packet is in a `std::vector` called `x`. + The data packet is in a ``std::vector`` called ``x``. To determine the correct values for the parameters above, visit `packet definitions list `__. In the search field of the **Packets** table, enter the name of your device. diff --git a/index.rst b/index.rst index 9dc0dd604..d18f343e4 100644 --- a/index.rst +++ b/index.rst @@ -499,7 +499,7 @@ Output Components BLE Binary Output, components/output/ble_client, bluetooth.svg Modbus Output, components/output/modbus_controller, modbus.png Custom Output, components/output/custom, language-cpp.svg - Sigma-Delta Output, components/output/sigma_delta, sigma-delta.svg + Sigma-Delta Output, components/output/sigma_delta_output, sigma-delta.svg Template Output, components/output/template, description.svg BP1658CJ, components/output/bp1658cj, bp1658cj.svg BP5758D, components/output/bp5758d, bp5758d.svg diff --git a/schema_doc.py b/schema_doc.py index 9dc5869eb..9607ff415 100644 --- a/schema_doc.py +++ b/schema_doc.py @@ -81,6 +81,8 @@ PLATFORMS_TITLES = { "Stepper": "stepper", "Switch": "switch", "I²C": "i2c", + "Media Player": "media_player", + "Microphone": "microphone", } CUSTOM_DOCS = { @@ -196,6 +198,8 @@ CUSTOM_DOCS = { }, } +REQUIRED_OPTIONAL_TYPE_REGEX = r"(\(((\*\*Required\*\*)|(\*Optional\*))(,\s(.*))*)\):\s" + def get_node_title(node): return list(node.traverse(nodes.title))[0].astext() @@ -250,7 +254,6 @@ class SchemaGeneratorVisitor(nodes.NodeVisitor): ] else: # sub component, e.g. output/esp8266_pwm - # components here might have a core / hub, eg. dallas, ads1115 # and then they can be a binary_sensor, sensor, etc. self.platform = self.path[1] @@ -775,7 +778,6 @@ class SchemaGeneratorVisitor(nodes.NodeVisitor): and (self.props or self.multi_component) and self.bullet_list_level > 1 ): - self.prop_stack.append((self.current_prop, node)) self.accept_props = True return @@ -844,7 +846,7 @@ class SchemaGeneratorVisitor(nodes.NodeVisitor): try: name_type = markdown[: markdown.index(": ") + 2] ntr = re.search( - r"(\(((\*\*Required\*\*)|(\*Optional\*))(,\s(.*))*)\):\s", + REQUIRED_OPTIONAL_TYPE_REGEX, name_type, re.IGNORECASE, ) @@ -875,7 +877,6 @@ class SchemaGeneratorVisitor(nodes.NodeVisitor): return markdown def update_prop(self, node, props): - prop_name = None for s_prop, n in self.prop_stack: @@ -901,10 +902,10 @@ class SchemaGeneratorVisitor(nodes.NodeVisitor): found = True if enum_docs: enum_docs = enum_docs.strip() - if "values_docs" not in inner: - inner["values_docs"] = {name: enum_docs} + if inner["values"][name] is None: + inner["values"][name] = {"docs": enum_docs} else: - inner["values_docs"][name] = enum_docs + inner["values"][name]["docs"] = enum_docs statistics.props_documented += 1 statistics.enums_good += 1 if not found: @@ -941,12 +942,19 @@ class SchemaGeneratorVisitor(nodes.NodeVisitor): return prop_name, False # Example properties formats are: - # **name** (**Required**, string): Long Description... - # **name** (*Optional*, string): Long Description... Defaults to ``value``. - # **name** (*Optional*): Long Description... Defaults to ``value``. + # **prop_name** (**Required**, string): Long Description... + # **prop_name** (*Optional*, string): Long Description... Defaults to ``value``. + # **prop_name** (*Optional*): Long Description... Defaults to ``value``. + # **prop_name** can be a list of names separated by / e.g. **name1/name2** (*Optional*) see climate/pid/ threshold_low/threshold_high + + PROP_NAME_REGEX = r"\*\*(\w*(?:/\w*)*)\*\*" + + FULL_ITEM_PROP_NAME_TYPE_REGEX = ( + r"\* " + PROP_NAME_REGEX + r"\s" + REQUIRED_OPTIONAL_TYPE_REGEX + ) ntr = re.search( - r"\* \*\*(\w*)\*\*\s(\(((\*\*Required\*\*)|(\*Optional\*))(,\s(.*))*)\):\s", + FULL_ITEM_PROP_NAME_TYPE_REGEX, name_type, re.IGNORECASE, ) @@ -956,14 +964,14 @@ class SchemaGeneratorVisitor(nodes.NodeVisitor): param_type = ntr.group(7) else: s2 = re.search( - r"\* \*\*(\w*)\*\*\s(\(((\*\*Required\*\*)|(\*Optional\*))(,\s(.*))*)\):\s", + FULL_ITEM_PROP_NAME_TYPE_REGEX, markdown, re.IGNORECASE, ) if s2: # this is e.g. when a property has a list inside, and the list inside are the options. # just validate **prop_name** - s3 = re.search(r"\* \*\*(\w*)\*\*:\s", name_type) + s3 = re.search(r"\* " + PROP_NAME_REGEX + r"*:\s", name_type) if s3 is not None: prop_name = s3.group(1) else: @@ -977,61 +985,61 @@ class SchemaGeneratorVisitor(nodes.NodeVisitor): ) return prop_name, False - k = str(prop_name) + prop_names = str(prop_name) + for k in prop_names.split("/"): + config_var = props.get(k) - config_var = props.get(k) + if not config_var: + # Create docs for common properties when descriptions are overridden + # in the most specific component. - if not config_var: - # Create docs for common properties when descriptions are overridden - # in the most specific component. - - if k in [ - "id", - "name", - "internal", - # i2c - "address", - "i2c_id", - # polling component - "update_interval", - # uart - "uart_id", - # light - "effects", - "gamma_correct", - "default_transition_length", - "flash_transition_length", - "color_correct", - # display - "lambda", - "pages", - "rotation", - # spi - "spi_id", - "cs_pin", - # output (binary/float output) - "inverted", - "power_supply", - # climate - "receiver_id", - ]: - config_var = props[k] = {} - else: - if self.path[1] == "esphome" and k in [ - # deprecated esphome - "platform", - "board", - "arduino_version", - "esp8266_restore_from_flash", + if k in [ + "id", + "name", + "internal", + # i2c + "address", + "i2c_id", + # polling component + "update_interval", + # uart + "uart_id", + # light + "effects", + "gamma_correct", + "default_transition_length", + "flash_transition_length", + "color_correct", + # display + "lambda", + "pages", + "rotation", + # spi + "spi_id", + "cs_pin", + # output (binary/float output) + "inverted", + "power_supply", + # climate + "receiver_id", ]: - return prop_name, True - return prop_name, False + config_var = props[k] = {} + else: + if self.path[1] == "esphome" and k in [ + # deprecated esphome + "platform", + "board", + "arduino_version", + "esp8266_restore_from_flash", + ]: + return prop_name, True + return prop_name, False - desc = markdown[markdown.index(": ") + 2 :].strip() - if param_type: - desc = "**" + param_type + "**: " + desc + desc = markdown[markdown.index(": ") + 2 :].strip() + if param_type: + desc = "**" + param_type + "**: " + desc - config_var["docs"] = desc + config_var["docs"] = desc statistics.props_documented += 1 @@ -1084,7 +1092,11 @@ class SchemaGeneratorVisitor(nodes.NodeVisitor): def _find_extended(self, component, key): for extended in component.get("extends", []): - schema = self.visitor.get_component_schema(extended).get("schema", {}) + c = self.visitor.get_component_schema(extended) + if c.get("type") == "typed": + p = self.visitor.Props(self.visitor, c) + return p[key] + schema = c.get("schema", {}) for k, cv in schema.get("config_vars", {}).items(): if k == key: return SetObservable( @@ -1124,7 +1136,7 @@ class SchemaGeneratorVisitor(nodes.NodeVisitor): ) def _set_typed(self, inner_key, original_dict, key, value): - if inner_key == self.component.get("typed_key"): + if inner_key == self.component.get("typed_key", "type"): self.component[key] = value else: for tk, tv in self.component["types"].items(): From d2bf9eba8486c5ad94748a825ecac0b2b9107153 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludovic=20BOU=C3=89?= Date: Wed, 31 May 2023 15:33:11 +0200 Subject: [PATCH 14/34] Update binary.rst (#2884) Add binary output component to use for this fan. --- components/fan/binary.rst | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/components/fan/binary.rst b/components/fan/binary.rst index 78e967d09..eefad56e1 100644 --- a/components/fan/binary.rst +++ b/components/fan/binary.rst @@ -14,10 +14,18 @@ The ``binary`` fan platform lets you represent any binary :ref:`output` as a fan .. code-block:: yaml # Example configuration entry + output: + - id: fan_output + platform: gpio + pin: GPIO16 + fan: - platform: binary - output: my_output_1 + output: fan_output name: "Living Room Fan" + + + Configuration variables: ------------------------ From e209f47639e555d93a86c2758edff674c28166f0 Mon Sep 17 00:00:00 2001 From: joaopedrotaveira Date: Wed, 31 May 2023 14:37:13 +0100 Subject: [PATCH 15/34] Update hmc5883l.rst (#2864) Fixed address on example --- components/sensor/hmc5883l.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/sensor/hmc5883l.rst b/components/sensor/hmc5883l.rst index c2551acb0..1430b54f9 100644 --- a/components/sensor/hmc5883l.rst +++ b/components/sensor/hmc5883l.rst @@ -26,7 +26,7 @@ for this sensor to work. # Example configuration entry sensor: - platform: hmc5883l - address: 0x68 + address: 0x1E field_strength_x: name: "HMC5883L Field Strength X" field_strength_y: From 396737431803634d751daa364996830f3ca75a41 Mon Sep 17 00:00:00 2001 From: smoke007 Date: Wed, 31 May 2023 09:38:01 -0400 Subject: [PATCH 16/34] Update mics_4514.rst (#2862) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MICS-4514 being sold on AliExpress has the same model number, but is a different board that doesn't use I²C Bus. Adding a note about this as a warning for anyone shopping for this sensor. --- components/sensor/mics_4514.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/sensor/mics_4514.rst b/components/sensor/mics_4514.rst index 731e89b1c..1fb366c3a 100644 --- a/components/sensor/mics_4514.rst +++ b/components/sensor/mics_4514.rst @@ -6,7 +6,7 @@ MiCS 4514 Gas Sensor :image: mics_4514.jpg :keywords: MiCS, 4514, MICS-4514 -This component exposes the different gas concentration sensors from the `MiCS-4514 `__. +This component exposes the different gas concentration sensors from the `MiCS-4514 `__. This is a differnet sensor than the MICS-4514 being sold on AliExpress. .. note:: From b3fb5b3b842bb2a7f1eabaf0a67ee4f299fd428a Mon Sep 17 00:00:00 2001 From: Staffan Larsen Date: Wed, 31 May 2023 15:39:31 +0200 Subject: [PATCH 17/34] Add "dimensions" and remove "cs_pin" (#2848) * Add "dimensions" and remove "cs_pin" Added "dimensions" since it was not documented. Removed "cs_pin" since ili9xxx does not use that config. * Remove cs_pin from examples --- components/display/ili9xxx.rst | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/components/display/ili9xxx.rst b/components/display/ili9xxx.rst index 030e287cd..2fce0d340 100644 --- a/components/display/ili9xxx.rst +++ b/components/display/ili9xxx.rst @@ -33,7 +33,6 @@ beyond the typical SPI connections, it is better suited for use with the ESP32. display: - platform: ili9xxx model: ili9341 - cs_pin: 14 dc_pin: 27 reset_pin: 33 lambda: |- @@ -48,7 +47,6 @@ Configuration variables: - ``M5STACK``, ``TFT 2.4``, ``TFT 2.4R`` - ``ILI9341``, ``ILI9342``, ``ILI9481``, ``ILI9486``, ``ILI9488``, ``ST7796`` -- **cs_pin** (*Optional*, :ref:`Pin Schema `): The CS pin. - **dc_pin** (**Required**, :ref:`Pin Schema `): The DC pin. - **reset_pin** (*Optional*, :ref:`Pin Schema `): The RESET pin. - **rotation** (*Optional*): Set the rotation of the display. Everything drawn in the ``lambda:`` will be rotated @@ -66,6 +64,7 @@ Configuration variables: - ``GRAYSCALE`` - ``IMAGE_ADAPTIVE`` - **color_palette_images** (*Optional*): A list of image files that will be used to generate the color pallet for the display. This should only be used in conjunction with ``-color_palette: IMAGE_ADAPTIVE`` above. The images will be analysed at compile time and a custom color pallet will be created based on the most commonly occuring colors. A typical setting would be a sample image that represented the fully populated display. This can significantly improve the quality of displayed images. Note that these images are not stored on the ESP device, just the 256byte color pallet created from them. +- **dimensions** (*Optional*): Dimensions of the screen with WIDTHxHEIGHT. Usually not needed since ``model:`` has good defaults. Configuration examples ********************** @@ -140,7 +139,6 @@ To configure an image adaptive color pallet to show greater than 8 bit color dep display: - platform: ili9xxx model: ili9341 - cs_pin: 5 dc_pin: 4 reset_pin: 22 rotation: 90 From 89451cbe464629cfb42bcc68189c7a2e67b99dba Mon Sep 17 00:00:00 2001 From: muddyfeet Date: Wed, 31 May 2023 23:40:41 +1000 Subject: [PATCH 18/34] Update custom.rst to include changes made in CoverTraits (#2944) Update documentation to add changes made in https://github.com/esphome/esphome/pull/3897 traits.set_supports_stop(true) required now in the custom cover for stop button to be exposed. --- components/cover/custom.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/components/cover/custom.rst b/components/cover/custom.rst index 305e1a5f9..dcd4a1643 100644 --- a/components/cover/custom.rst +++ b/components/cover/custom.rst @@ -30,6 +30,7 @@ two methods: traits.set_is_assumed_state(false); traits.set_supports_position(true); traits.set_supports_tilt(false); + traits.set_supports_stop(true); return traits; } void control(const CoverCall &call) override { From 5b16e023b550441e1e8878d3d976c375497737d0 Mon Sep 17 00:00:00 2001 From: MicroPhonon Date: Wed, 31 May 2023 06:41:39 -0700 Subject: [PATCH 19/34] Expand custom I2C documentation (#2912) * Update i2c.rst * Update i2c.rst * Update i2c.rst * Update i2c.rst * Update i2c.rst * Update i2c.rst * Update i2c.rst * Update i2c.rst * Update i2c.rst * Update i2c.rst * Update i2c.rst * Update i2c.rst * Update i2c.rst * Update i2c.rst --- custom/i2c.rst | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/custom/i2c.rst b/custom/i2c.rst index ce1c30c71..78b68fa2d 100644 --- a/custom/i2c.rst +++ b/custom/i2c.rst @@ -29,6 +29,64 @@ them publish values. } }; + +I²C Write +--------- +It may be useful to write to a register via I²C using a numerical input. For example, the following yaml code snippet captures a user-supplied numerical input in the range 1--255 from the dashboard: + +.. code-block:: yaml + + number: + - platform: template + name: "Input 1" + optimistic: true + min_value: 1 + max_value: 255 + initial_value: 20 + step: 1 + mode: box + id: input_1 + icon: "mdi:counter" + +We want to write this number to a ``REGISTER_ADDRESS`` on the slave device via I²C. The Arduino-based looping code shown above is modified following the guidance in :doc:`Custom Sensor Component `. + +.. code-block:: cpp + + #include "esphome.h" + + const uint16_t I2C_ADDRESS = 0x21; + const uint16_t REGISTER_ADDRESS = 0x78; + const uint16_t POLLING_PERIOD = 15000; //milliseconds + char temp = 20; //Initial value of the register + + class MyCustomComponent : public PollingComponent { + public: + MyCustomComponent() : PollingComponent(POLLING_PERIOD) {} + float get_setup_priority() const override { return esphome::setup_priority::BUS; } //Access I2C bus + + void setup() override { + //Add code here as needed + Wire.begin(); + } + + void update() override { + char register_value = id(input_1).state; //Read the number set on the dashboard + //Did the user change the input? + if(register_value != temp){ + Wire.beginTransmission(I2C_ADDRESS); + Wire.write(REGISTER_ADDRESS); + Wire.write(register_value); + Wire.endTransmission(); + temp = register_value; //Swap in the new value + } + } + }; + +The ``Component`` class has been replaced with ``PollingComponent`` and the free-running ``loop()`` is changed to the ``update()`` method with period set by ``POLLING_PERIOD``. The numerical value from the dashboard is accessed with its ``id`` tag and its state is set to the byte variable that we call ``register_value``. To prevent an I²C write on every iteration, the contents of the register are stored in ``temp`` and checked for a change. Configuring the hardware with ``get_setup_priority()`` is explained in :doc:`Step 1 `. + + + + See Also -------- From 59f791e6c9b5e71205abbcc9387fe42dcea82ea8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=2E=20=C3=81rkosi=20R=C3=B3bert?= Date: Thu, 1 Jun 2023 08:49:16 +0200 Subject: [PATCH 20/34] Update http_request_sensor.rst (#2961) fix typos and add a bit of explanation --- cookbook/http_request_sensor.rst | 52 +++++++++++++++++--------------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/cookbook/http_request_sensor.rst b/cookbook/http_request_sensor.rst index fdcf0fdea..0afaffc5d 100644 --- a/cookbook/http_request_sensor.rst +++ b/cookbook/http_request_sensor.rst @@ -1,58 +1,61 @@ Share data directly between ESPHome nodes ========================================= -You need two ESPHome nodes. One will be the server, and the others will be the clients (can be multiple). You must set up a webserver on your primary esphome to make available the required sensor data using the :doc:`/components/web_server`. + +In certain special cases you might want to avoid placing any middleware like an MQTT or a home automation server just to transfer small bits of data from one node to another. Direct data polling is possibvle using HTTP, but beware that the involved components are resource hungry and may be less stable on long term than the usual methods. The webserver embedded in the node is not designed to constantly serve a large amount of requests. + +The node holding the data we need to retrieve will be the server, and the others polling for it will be the clients (can be multiple). Server part ----------- +You must set up a webserver on your primary node to make available the required sensor data using the :doc:`/components/web_server`. + .. code-block:: yaml - # Webserver configration web_server: port: 80 Client part ----------- -On the client nodes we need a :doc:`/components/http_request` with an ``http_request_data`` id set, and a :doc:`/components/sensor/template` to make it accessible locally. +On the client nodes we need a :doc:`/components/http_request` with an ``id`` set, and a :doc:`/components/sensor/template` to make it accessible locally. .. code-block:: yaml http_request: useragent: esphome/device - timeout: 10s - id: http_request_data + id: http_request_id sensor: - platform: template - name: "Name of the template sensor" - id: id_of_the_template_sensor + name: "Template sensor on client" + id: template_sensor_id Pulling the data **************** -To automate the request for data, we will add an interval component requesting the URL pointing to the sensor id for which the state is needed. See :ref:`api-rest` on how to build up the URL for your sensors. +To automate the request for data, we use an :ref:`interval` component requesting the URL pointing to the sensor id for which the state is needed. See :ref:`api-rest` on how to build up the URL for your sensors. .. note:: - The domain is the type of the component, for example ``sensor`` or ``light``. ``id`` refers to the ID of the component - which is created from the name of the component, stripping out all non-alphanumeric characters, making everything lowercase and replacing all spaces by underscores. To confirm the corrrct ID to use, you can set the log level to VERY_VERBOSE on your server node and look for ``object_id:`` in the logs. + The domain is the type of the component, for example ``sensor`` or ``light``. ``id`` refers to the internal ID of the component - which is created from the name of the component, stripping out all non-alphanumeric characters, making everything lowercase and replacing all spaces by underscores. To confirm the corrrct ID to use, you can set the log level to VERY_VERBOSE on your server node and look for ``object_id:`` in the logs. In the example below we pull the value of a sensor, and after parsing the resulted JSON string we publish it to the template sensor: .. code-block:: yaml interval: - - interval: 60s - then: - - http_request.get: - url: http://address.of.server.node/sensor/ID.of.the.sensor - on_response: - then: - - lambda: |- - json::parse_json(id(http_request_data).get_string(), [](JsonObject root) { - id(id_of_the_template_sensor).publish_state(root["value"]); - }); + - interval: 60s + then: + - http_request.get: + url: http://address.of.server.node/sensor/ID.of.the.sensor + on_response: + then: + - lambda: |- + json::parse_json(id(http_request_id).get_string(), [](JsonObject root) { + id(template_sensor_id).publish_state(root["value"]); + }); Result @@ -60,16 +63,16 @@ Result .. figure:: images/server.png :align: center - :width: 90.0% + :width: 95.0% -Server side real sensor. + Server side real sensor. .. figure:: images/clients.png :align: center - :width: 90.0% + :width: 95.0% -Client side template sensor. + Client side template sensor. See Also @@ -77,6 +80,7 @@ See Also - :doc:`/components/web_server` - :doc:`/components/http_request` -- :ref:`api-rest` - :doc:`/components/sensor/template` +- :ref:`interval` +- :ref:`api-rest` - :ghedit:`Edit` From 3e5fb954986fb87be81a17f54e5d40530e574c62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=2E=20=C3=81rkosi=20R=C3=B3bert?= Date: Thu, 1 Jun 2023 11:14:16 +0200 Subject: [PATCH 21/34] Update http_request_sensor.rst (#2962) Fix some typos --- cookbook/http_request_sensor.rst | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/cookbook/http_request_sensor.rst b/cookbook/http_request_sensor.rst index 0afaffc5d..954ab8362 100644 --- a/cookbook/http_request_sensor.rst +++ b/cookbook/http_request_sensor.rst @@ -1,14 +1,14 @@ Share data directly between ESPHome nodes ========================================= -In certain special cases you might want to avoid placing any middleware like an MQTT or a home automation server just to transfer small bits of data from one node to another. Direct data polling is possibvle using HTTP, but beware that the involved components are resource hungry and may be less stable on long term than the usual methods. The webserver embedded in the node is not designed to constantly serve a large amount of requests. +In certain special cases it might be desired to avoid placing any middleware like an MQTT or a home automation server just to transfer small bits of data from one node to another. Direct data polling is possibvle using HTTP, but beware that the involved components are resource hungry and may be less stable on long term. The webserver embedded in the node is not designed to constantly serve a large amount of requests. -The node holding the data we need to retrieve will be the server, and the others polling for it will be the clients (can be multiple). +The primary node holding the data we need to retrieve from will be the server, and the others polling for it will be the clients (can be multiple). Server part ----------- -You must set up a webserver on your primary node to make available the required sensor data using the :doc:`/components/web_server`. +Setting up a webserver using the :doc:`/components/web_server` on the primary node will make available the required sensor data through a :ref:`api-rest` interface. .. code-block:: yaml @@ -18,7 +18,7 @@ You must set up a webserver on your primary node to make available the required Client part ----------- -On the client nodes we need a :doc:`/components/http_request` with an ``id`` set, and a :doc:`/components/sensor/template` to make it accessible locally. +On the client nodes we need an :doc:`/components/http_request` with an ``id`` set, and a :doc:`/components/sensor/template` to make it accessible locally. .. code-block:: yaml @@ -35,13 +35,9 @@ On the client nodes we need a :doc:`/components/http_request` with an ``id`` set Pulling the data **************** -To automate the request for data, we use an :ref:`interval` component requesting the URL pointing to the sensor id for which the state is needed. See :ref:`api-rest` on how to build up the URL for your sensors. +To automate the request for data, we use an :ref:`interval` requesting the URL pointing to the sensor id for which the state is needed. See :ref:`api-rest` on how to build up the URL for your sensors. -.. note:: - - The domain is the type of the component, for example ``sensor`` or ``light``. ``id`` refers to the internal ID of the component - which is created from the name of the component, stripping out all non-alphanumeric characters, making everything lowercase and replacing all spaces by underscores. To confirm the corrrct ID to use, you can set the log level to VERY_VERBOSE on your server node and look for ``object_id:`` in the logs. - -In the example below we pull the value of a sensor, and after parsing the resulted JSON string we publish it to the template sensor: +In the example below we request the value of a sensor from the server node, and after parsing the resulted JSON string we publish it to the local template sensor: .. code-block:: yaml @@ -65,14 +61,14 @@ Result :align: center :width: 95.0% - Server side real sensor. + Server side real sensor .. figure:: images/clients.png :align: center :width: 95.0% - Client side template sensor. + Client side template sensor See Also From f245b7a1c629b1ffe7b8b3672cda8add4cbd5d23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=2E=20=C3=81rkosi=20R=C3=B3bert?= Date: Thu, 1 Jun 2023 11:20:57 +0200 Subject: [PATCH 22/34] Add more links to See Also (#2963) --- web-api/index.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/web-api/index.rst b/web-api/index.rst index addb750e3..3abe30003 100644 --- a/web-api/index.rst +++ b/web-api/index.rst @@ -305,5 +305,7 @@ For example POST ``/number/desired_delay/set?value=24`` will set the number to 2 See Also -------- +- :doc:`/components/web_server` +- :doc:`/components/prometheus` - :doc:`/components/http_request` - :ghedit:`Edit` From 13c55ea24bd5b8bc6e0a7c92271af95d07d0f701 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=2E=20=C3=81rkosi=20R=C3=B3bert?= Date: Thu, 1 Jun 2023 11:24:00 +0200 Subject: [PATCH 23/34] Add more links to See Also, fix REST API hook (#2964) --- components/web_server.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/web_server.rst b/components/web_server.rst index 4e1e29efe..aa954ce18 100644 --- a/components/web_server.rst +++ b/components/web_server.rst @@ -7,7 +7,7 @@ Web Server Component :keywords: web server, http, REST API The ``web_server`` component creates a simple web server on the node that can be accessed -through any browser and a simple `REST API`_. Please note that enabling this component +through any browser and a simple :ref:`api-rest`. Please note that enabling this component will take up *a lot* of memory and can lead to problems, especially on the ESP8266. To navigate to the web server in your browser, either use the IP address of the node or @@ -17,8 +17,6 @@ To conserve flash size, the CSS and JS files used on the root page to show a sim interface are hosted by esphome.io. If you want to use your own service, use the ``css_url`` and ``js_url`` options in your configuration. -.. _REST API: /web-api/index.html - .. figure:: /components/images/web_server.png Example web server frontend (Version 1) @@ -137,6 +135,8 @@ Copy https://oi.esphome.io/v2/www.js to a V2 folder in your yaml folder. See Also -------- +- :ref:`api-event-source` +- :ref:`api-rest` - :apiref:`web_server/web_server.h` - :doc:`prometheus` - :ghedit:`Edit` From ca829aeca31441f42001d7d8350be9eecd799bdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=2E=20=C3=81rkosi=20R=C3=B3bert?= Date: Thu, 1 Jun 2023 12:02:37 +0200 Subject: [PATCH 24/34] Add more links to See Also (#2965) Add more links to See Also --- components/prometheus.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/components/prometheus.rst b/components/prometheus.rst index 151a8c6cb..5df1dfa00 100644 --- a/components/prometheus.rst +++ b/components/prometheus.rst @@ -71,5 +71,9 @@ Set the ``id`` and ``name`` label values of the Prometheus metric for the sensor See Also -------- +- :doc:`/components/web_server` +- :ref:`api-rest` +- :doc:`/components/http_request` - :apiref:`prometheus/prometheus_handler.h` +- `Prometheus `__ - :ghedit:`Edit` From 62aa463b991d907c6f79566061d5bee339fe8dc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=2E=20=C3=81rkosi=20R=C3=B3bert?= Date: Thu, 1 Jun 2023 13:14:44 +0200 Subject: [PATCH 25/34] Correct web_server auth type (#2966) * Correct web_server auth type It's not Basic, but Digest! * Fix typos --- components/web_server.rst | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/components/web_server.rst b/components/web_server.rst index aa954ce18..63e95fae8 100644 --- a/components/web_server.rst +++ b/components/web_server.rst @@ -8,7 +8,7 @@ Web Server Component The ``web_server`` component creates a simple web server on the node that can be accessed through any browser and a simple :ref:`api-rest`. Please note that enabling this component -will take up *a lot* of memory and can lead to problems, especially on the ESP8266. +will take up *a lot* of memory and may decrease stability, especially on ESP8266. To navigate to the web server in your browser, either use the IP address of the node or use ``.local/`` (note the trailing forward slash) via mDNS. @@ -48,7 +48,7 @@ Configuration variables: - **js_include** (*Optional*, local file): Path to local file to be included in web server index page. Contents of this file will be served as ``/0.js`` and used as JS script by internal webserver. Useful when building device without internet access, where you want to use built-in AP and webserver. -- **auth** (*Optional*): Enables basic authentication with username and password. +- **auth** (*Optional*): Enables *Digest* authentication with username and password. - **username** (**Required**, string): The username to use for authentication. - **password** (**Required**, string): The password to check for authentication. @@ -62,7 +62,7 @@ Configuration variables: .. note:: - Example web_server configuration using HTTP authentication: + Example ``web_server`` configuration using HTTP authentication: .. code-block:: yaml @@ -70,10 +70,10 @@ Configuration variables: web_server: port: 80 auth: - username: admin + username: !secret web_server_username password: !secret web_server_password - Example web_server configuration using version 1 (previous behaviour): + Example ``web_server`` configuration using version 1: .. code-block:: yaml @@ -82,7 +82,7 @@ Configuration variables: port: 80 version: 1 - Example web_server configuration using version 2 - no internet/intranet required: + Example ``web_server`` configuration using version 2 - no internet/intranet required on the client: .. code-block:: yaml @@ -91,21 +91,20 @@ Configuration variables: local: true - All of the assets are inlined, compressed and served from flash -Here be Dragons -=============== +Advanced usage +============== The following assume copies of the files with local paths - which are config dependant. -Example web_server version 1 configuration with CSS and JS included from esphome-docs. +Example ``web_server`` version 1 configuration with CSS and JS included from esphome-docs. CSS and JS URL's are set to empty value, so no internet access is needed for this device to show it's web interface. Force to turn off OTA function because the missing authentication. .. code-block:: yaml - # Example configuration entry V1 + # Example configuration entry v1 web_server: port: 80 version: 1 @@ -115,14 +114,14 @@ Force to turn off OTA function because the missing authentication. js_include: "../../../esphome-docs/_static/webserver-v1.min.js" js_url: "" -Example web_server version 2 configuration with JS included from a local file. +Example ``web_server`` version 2 configuration with JS included from a local file. CSS and JS URL's are set to empty value, so no internet access is needed for this device to show it's web interface. V2 embeds the css within the js file so is not required, however you could include your own CSS. .. code-block:: yaml - # Example configuration entry V2 + # Example configuration entry v2 web_server: js_include: "./v2/www.js" js_url: "" From 5f834afd0db26968f9c6689cb2d7a3895ed5486d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=2E=20=C3=81rkosi=20R=C3=B3bert?= Date: Thu, 1 Jun 2023 14:06:25 +0200 Subject: [PATCH 26/34] Update http_request_sensor.rst (#2967) * Update http_request_sensor.rst Add security notes * Update http_request_sensor.rst * Update http_request_sensor.rst --- cookbook/http_request_sensor.rst | 43 ++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/cookbook/http_request_sensor.rst b/cookbook/http_request_sensor.rst index 954ab8362..d5363fc9a 100644 --- a/cookbook/http_request_sensor.rst +++ b/cookbook/http_request_sensor.rst @@ -14,7 +14,7 @@ Setting up a webserver using the :doc:`/components/web_server` on the primary n web_server: port: 80 - + Client part ----------- @@ -45,7 +45,7 @@ In the example below we request the value of a sensor from the server node, and - interval: 60s then: - http_request.get: - url: http://address.of.server.node/sensor/ID.of.the.sensor + url: http://ip or nodename.local/sensor/ID_of_the_sensor on_response: then: - lambda: |- @@ -71,6 +71,45 @@ Result Client side template sensor +Increasing security +------------------- + +For security reasons, it's always recommended to protect the web interface of the nodes with authentication, even if you're using them on your local network. + +Server part +*********** + +Add authentication to the ``web_server`` component on the primary node: + +.. code-block:: yaml + + web_server: + port: 80 + auth: + username: !secret admin + password: !secret web_server_password + +Client part +*********** + +Add an ``Authorization`` header to your ``http_request.get`` action. The simplest way to determine a working authorization header is to visit the password-protected REST URL of the primary node using a browser while watching the network traffic in the browser's developer tools. If you look at the headers of the request sent by the browser, you'll find the ``Authorization`` header it sends to the node, and you can copy it for your own use: + +.. code-block:: yaml + + interval: + - interval: 60s + then: + - http_request.get: + url: http://ip or nodename.local/sensor/ID_of_the_sensor + headers: + Authorization: 'Digest username="admin", realm="asyncesp", nonce="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", uri="/sensor/ID_of_the_sensor", response="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", opaque="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", qop=auth, nc=xxxxxxxx, cnonce="xxxxxxxxxxxxxxxx"' + on_response: + then: + - lambda: |- + json::parse_json(id(http_request_id).get_string(), [](JsonObject root) { + id(template_sensor_id).publish_state(root["value"]); + }); + See Also -------- From 96a306087b76829dbc6b370e75f7aa66088898d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=2E=20=C3=81rkosi=20R=C3=B3bert?= Date: Thu, 1 Jun 2023 15:36:51 +0200 Subject: [PATCH 27/34] Update http_request_sensor.rst (#2968) --- cookbook/http_request_sensor.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/http_request_sensor.rst b/cookbook/http_request_sensor.rst index d5363fc9a..86cedeb01 100644 --- a/cookbook/http_request_sensor.rst +++ b/cookbook/http_request_sensor.rst @@ -92,7 +92,7 @@ Add authentication to the ``web_server`` component on the primary node: Client part *********** -Add an ``Authorization`` header to your ``http_request.get`` action. The simplest way to determine a working authorization header is to visit the password-protected REST URL of the primary node using a browser while watching the network traffic in the browser's developer tools. If you look at the headers of the request sent by the browser, you'll find the ``Authorization`` header it sends to the node, and you can copy it for your own use: +Add an ``Authorization`` header to your ``http_request.get`` action. The simplest way to determine a working authorization header is to visit the password-protected REST URL of the primary node using a browser while watching the network traffic in the browser's developer tools. If you look at the headers of the request sent by the browser, you'll find the ``Authorization`` header it sends to the node, and you can copy it for your own replay: .. code-block:: yaml From 67513701e8c82500af0f978886c7c98d0661a907 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=2E=20=C3=81rkosi=20R=C3=B3bert?= Date: Thu, 1 Jun 2023 16:03:01 +0200 Subject: [PATCH 28/34] Brush up page looks for Web Server Component (#2969) * Brush up page looks for Web Server Component * Update web_server.rst * Update web_server.rst * Update web_server.rst --- components/web_server.rst | 79 ++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 39 deletions(-) diff --git a/components/web_server.rst b/components/web_server.rst index 63e95fae8..50fdb638e 100644 --- a/components/web_server.rst +++ b/components/web_server.rst @@ -10,22 +10,23 @@ The ``web_server`` component creates a simple web server on the node that can be through any browser and a simple :ref:`api-rest`. Please note that enabling this component will take up *a lot* of memory and may decrease stability, especially on ESP8266. +.. figure:: /components/images/web_server.png + :align: center + :width: 86.0% + + Web server version 1 + + +.. figure:: /components/images/web_server-v2.png + :align: center + :width: 86.0% + + Web server version 2 + + To navigate to the web server in your browser, either use the IP address of the node or use ``.local/`` (note the trailing forward slash) via mDNS. -To conserve flash size, the CSS and JS files used on the root page to show a simple user -interface are hosted by esphome.io. If you want to use your own service, use the -``css_url`` and ``js_url`` options in your configuration. - -.. figure:: /components/images/web_server.png - - Example web server frontend (Version 1) - -Version 2: ----------- -.. figure:: /components/images/web_server-v2.png - - Web Components (Version 2) .. code-block:: yaml @@ -48,7 +49,7 @@ Configuration variables: - **js_include** (*Optional*, local file): Path to local file to be included in web server index page. Contents of this file will be served as ``/0.js`` and used as JS script by internal webserver. Useful when building device without internet access, where you want to use built-in AP and webserver. -- **auth** (*Optional*): Enables *Digest* authentication with username and password. +- **auth** (*Optional*): Enables a simple *Digest* authentication with username and password. - **username** (**Required**, string): The username to use for authentication. - **password** (**Required**, string): The password to check for authentication. @@ -60,41 +61,44 @@ Configuration variables: - **local** (*Optional*, boolean): Include supporting javascript locally allowing it to work without internet access. Defaults to ``false``. - **version** (*Optional*, string): ``1`` or ``2``. Version 1 displays as a table. Version 2 uses web components and has more functionality. Defaults to ``2``. -.. note:: +To conserve flash size, the CSS and JS files used on the root page to show a simple user +interface are hosted by esphome.io. If you want to use your own service, use the +``css_url`` and ``js_url`` options in your configuration. - Example ``web_server`` configuration using HTTP authentication: +Example configurations: +----------------------- - .. code-block:: yaml +Enabling HTTP authentication: - # Example configuration entry - web_server: - port: 80 - auth: - username: !secret web_server_username - password: !secret web_server_password +.. code-block:: yaml - Example ``web_server`` configuration using version 1: + # Example configuration entry + web_server: + port: 80 + auth: + username: !secret web_server_username + password: !secret web_server_password - .. code-block:: yaml +Use version 1 user interface: - # Example configuration entry - web_server: - port: 80 - version: 1 +.. code-block:: yaml - Example ``web_server`` configuration using version 2 - no internet/intranet required on the client: + # Example configuration entry + web_server: + port: 80 + version: 1 - .. code-block:: yaml +No internet/intranet required on the clients (all assets are inlined, compressed and served from flash): - # Example configuration entry - web_server: - local: true +.. code-block:: yaml + # Example configuration entry + web_server: + local: true - All of the assets are inlined, compressed and served from flash Advanced usage -============== +-------------- The following assume copies of the files with local paths - which are config dependant. @@ -104,7 +108,6 @@ Force to turn off OTA function because the missing authentication. .. code-block:: yaml - # Example configuration entry v1 web_server: port: 80 version: 1 @@ -115,7 +118,6 @@ Force to turn off OTA function because the missing authentication. js_url: "" Example ``web_server`` version 2 configuration with JS included from a local file. - CSS and JS URL's are set to empty value, so no internet access is needed for this device to show it's web interface. V2 embeds the css within the js file so is not required, however you could include your own CSS. @@ -127,7 +129,6 @@ V2 embeds the css within the js file so is not required, however you could inclu js_url: "" version: 2 - Copy https://oi.esphome.io/v2/www.js to a V2 folder in your yaml folder. From e02a41469c4671e0c1784d22a85d24d56cd5a483 Mon Sep 17 00:00:00 2001 From: c0ffeeca7 <38767475+c0ffeeca7@users.noreply.github.com> Date: Fri, 2 Jun 2023 17:59:10 +0200 Subject: [PATCH 29/34] Update links to voice control (#2971) --- changelog/2023.4.0.rst | 2 +- components/voice_assistant.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/changelog/2023.4.0.rst b/changelog/2023.4.0.rst index 96d6554d0..375fd8540 100644 --- a/changelog/2023.4.0.rst +++ b/changelog/2023.4.0.rst @@ -22,7 +22,7 @@ Voice Assistant This year is the Year of the Voice for Home Assistant, and ESPHome is charging ahead with this in mind. We've added a new :doc:`/components/voice_assistant` component that allows you to use ESPHome devices as an input -for `assist `__ in Home Assistant **2023.5 or later**. +for `assist `__ in Home Assistant **2023.5 or later**. With this also comes preliminary :doc:`microphone ` support, which has been built in a way that multiple components, like ``voice_assistant`` can request start / stop of the microphone and get the data. We diff --git a/components/voice_assistant.rst b/components/voice_assistant.rst index 66be3d29e..4a8fa6af8 100644 --- a/components/voice_assistant.rst +++ b/components/voice_assistant.rst @@ -5,7 +5,7 @@ Voice Assistant :description: Instructions for setting up a Voice Assistant in ESPHome. :image: voice-assistant.svg -ESPHome devices with a microphone are able to stream the audio to Home Assistant and be processed there by `assist `__. +ESPHome devices with a microphone are able to stream the audio to Home Assistant and be processed there by `assist `__. .. note:: From 1240f2c9047ed75c41def6ddc9b8ae5c46e9f028 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=2E=20=C3=81rkosi=20R=C3=B3bert?= Date: Fri, 2 Jun 2023 21:45:40 +0200 Subject: [PATCH 30/34] Update index.rst (#2972) Remove python cookbook from index --- index.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/index.rst b/index.rst index d18f343e4..69f523f5f 100644 --- a/index.rst +++ b/index.rst @@ -839,7 +839,6 @@ Cookbook Arduino Port Extender, cookbook/arduino_port_extender, arduino_logo.svg EHMTX a matrix status/text display, cookbook/ehmtx, ehmtx.jpg Share data directly between ESPHome nodes, cookbook/http_request_sensor, connection.svg - Automatic Updates with Home Assistant, cookbook/homeassistant_upgrade, home-assistant.svg Do you have other awesome automations or cool setups? Please feel free to add them to the documentation for others to copy. See :doc:`Contributing `. From 6f42aab00e210206c06af065039a1ab3247700a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=2E=20=C3=81rkosi=20R=C3=B3bert?= Date: Fri, 2 Jun 2023 21:46:07 +0200 Subject: [PATCH 31/34] Delete homeassistant_upgrade.rst (#2973) As per admin request https://discord.com/channels/429907082951524364/930955987584684102/1114109005749571584 --- cookbook/homeassistant_upgrade.rst | 137 ----------------------------- 1 file changed, 137 deletions(-) delete mode 100644 cookbook/homeassistant_upgrade.rst diff --git a/cookbook/homeassistant_upgrade.rst b/cookbook/homeassistant_upgrade.rst deleted file mode 100644 index 29e894082..000000000 --- a/cookbook/homeassistant_upgrade.rst +++ /dev/null @@ -1,137 +0,0 @@ -Automatic Updates with Home Assistant -===================================== - -.. seo:: - :description: Have Home Assistant keep your ESPHome firmware updated automatically. - :image: ../images/home-assistant.svg - :keywords: update upgrade - -Have Home Assistsant automatically install updates on your ESPHome devices. - -.. figure:: ../images/home-assistant.svg - :align: center - :width: 40% - - -Introduction ------------- - -ESPHome regularly releases new updates but it is tedious to apply them and ensure all of your online devices have the latest version of ESPHome firmware. -Using the "python script" feature of ESPHome with a simple automation you can apply upgrades regularly and get notified when upgrades fail. - -Configuration -------------- - -1. If you don't yet have `Python Scripts `__ enabled in Home Assistant - 1. Add ``python_script:`` on it's own line to your ``configuration.yaml``. - 2. Create a folder in your configuration directory called ``python_scripts`` -2. Create a new file ``python_scripts/update_esphome_device.py`` with the contents below. - - .. code-block:: python - - # This script find an ESPHome device that needs a upgrade and kicks of the - # upgrade. - # It tracks the device it tried to update and next time if confirms - # it is no longer in the list of upgradable devices. If found it creates - # a persistant notifcation and trys the next devices in the list. - # - # This upgrades one device at a time and is designed to do upgrades in the - # background in no particular hurry. I don't mind if it takes a few days for - # my deices to upgrade just so long as I don't have to do anything. - # - # Note: If the last device in the list of devices neeeding upgrades fails the - # script will won't try to update any devices. This script assumes - # someone will follow up on upgrade failures. If you want to restart from the - # front of the list delete the entity idenfied by LAST_UPDATE_ENTITY_REC below - # - - - # To make troubleshooting easier change "logger.info" to "logger.error" to put - # the log lines in the log summary. - LOGGER = logger.info - - # Text helper that tracks the last device we tried to update - LAST_UPDATE_ENTITY_REC="input_text.esphome_last_upgraded_device" - - - last_updated = hass.states.get(LAST_UPDATE_ENTITY_REC).state - # Ensure we have the helper we need to detect if last update failed. - if last_updated is None: - last_updated = "NOT SET" - hass.states.set(LAST_UPDATE_ENTITY_REC, last_updated) - - LOGGER(f"{last_updated=}") - - # Get a list off every ESPHome device that needs a upgrade - all_needing_updates = [] - for state in list(hass.states.all()): - if state.entity_id.startswith("update.") \ - and state.entity_id.endswith("_firmware") \ - and state.state == "on" \ - and state.attributes["title"] == "ESPHome": - - all_needing_updates.append(state.entity_id) - - all_needing_updates.sort() # Don't assume hass.states.all order is dependable - - LOGGER(f"{all_needing_updates=}") - - # If something needs updates - if all_needing_updates: - - # Look for the last updated device and if found to still be in need of a - # update assume it's last updated failed. Alert and move on. - try: - next_idx = all_needing_updates.index(last_updated) + 1 - hass.services.call( - "persistent_notification", - "create", - { - "message": f"Update of {last_updated} may have failed.", - "notification_id": f"update_{last_updated}" - }, - False - ) - except ValueError: - # If we didn't find our last updated device restart to the begining - next_idx = 0 - - # Ensure the failure didn't happen at the end of the list as we have - # nothing else todo - if next_idx < len(all_needing_updates): - entity_to_update = all_needing_updates[next_idx] - hass.states.set(LAST_UPDATE_ENTITY_REC, entity_to_update) - LOGGER(f"Updating {entity_to_update}") - hass.services.call("update", "install", - {"entity_id": entity_to_update}, False) - else: - LOGGER((f"No more devices to try to update, update {last_updated} manually" - " to clear this.")) - else: - LOGGER(f"No ESPHome devices need a update.") - -3. Reload your Home Assistant server -4. In Home Assistant create a auotmation to run the python script. Below is a example suitable for a low performance server. It updates a ESPHome device every 20 minutes between 3-6AM every night. - - .. code-block:: yaml - - alias: "ESPHome: Apply updates" - description: "" - trigger: - - platform: time_pattern - hours: "3" - minutes: /20 - seconds: "23" - - platform: time_pattern - hours: "4" - minutes: /20 - seconds: "23" - - platform: time_pattern - hours: "5" - minutes: /20 - seconds: "23" - condition: [] - action: - - service: python_script.update_esphome_devices - data: {} - mode: single From ba24edff4bf0a0a7009ae8f20d8c064d1695152c Mon Sep 17 00:00:00 2001 From: "Kevin P. Fleming" Date: Sun, 4 Jun 2023 16:35:17 -0400 Subject: [PATCH 32/34] configuration-types: Demonstrate usage of packages as templates (#2980) (#2980) --- guides/configuration-types.rst | 70 ++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/guides/configuration-types.rst b/guides/configuration-types.rst index a8a0dcc53..cf9661eb7 100644 --- a/guides/configuration-types.rst +++ b/guides/configuration-types.rst @@ -451,6 +451,76 @@ them locally with their own subsitution value. # shorthand form github://username/repository/[folder/]file-path.yml[@branch-or-tag] remote_package_three: github://esphome/non-existant-repo/file1.yml@main +Packages as Templates +********************* + +Since packages are incorporated using the ``!include`` system, +variables can be provided to them. This means that packages can be +used as `templates`, allowing complex or repetitive configurations to +be stored in a package file and then incorporated into the +configuration more than once. + +As an example, if the configuration needed to support three garage +doors using the ``gpio`` switch platform and the ``time_based`` cover +platform, it could be constructed like this: + +.. code-block:: yaml + + # In config.yaml + packages: + left_garage_door: !include + file: garage-door.yaml + vars: + door_name: Left + door_location: left + open_switch_gpio: 25 + close_switch_gpio: 26 + middle_garage_door: !include + file: garage-door.yaml + vars: + door_name: Middle + door_location: middle + open_switch_gpio: 27 + close_switch_gpio: 29 + right_garage_door: !include + file: garage-door.yaml + vars: + door_name: Right + door_location: right + open_switch_gpio: 15 + close_switch_gpio: 18 + + +.. code-block:: yaml + + # In garage-door.yaml + switch: + - id: open_${door_location}_door_switch + name: ${door_name} Garage Door Open Switch + platform: gpio + pin: ${open_switch_gpio} + + - id: close_${door_location}_door_switch + name: ${door_name} Garage Door Close Switch + platform: gpio + pin: ${close_switch_gpio} + + cover: + - platform: time_based + name: ${door_name} Garage Door + + open_action: + - switch.turn_on: open_${door_location}_door_switch + open_duration: 2.1min + + close_action: + - switch.turn_on: close_${door_location}_door_switch + close_duration: 2min + + stop_action: + - switch.turn_off: open_${door_location}_door_switch + - switch.turn_off: close_${door_location}_door_switch + See Also -------- From 8398c79c10a08a90cee4935ef4e20a9e20f3161f Mon Sep 17 00:00:00 2001 From: Ariff Saad <80538339+arffsaad@users.noreply.github.com> Date: Wed, 14 Jun 2023 01:37:30 +0800 Subject: [PATCH 33/34] adding clarity (#2985) --- components/deep_sleep.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/deep_sleep.rst b/components/deep_sleep.rst index 3eb6b2d61..c9914a96a 100644 --- a/components/deep_sleep.rst +++ b/components/deep_sleep.rst @@ -32,7 +32,7 @@ even Over The Air updates. .. note:: - ESP8266 that have an onboard USB chip (e.g. D1 mini) one the chips' control lines is connected to the RST pin. This enables the flasher can reboot the ESP when required. This may interfere with deep sleep on some devices and prevent the ESP from waking when it's powered through its USB connector. Powering the ESP from a separate 3.3V source connected to the 3.3V pin and GND will solve this issue. In these cases using a USB to TTL adapter will allow you to log ESP activity. + Some ESP8266s have an onboard USB chip (e.g. D1 mini) on the chips' control line that is connected to the RST pin. This enables the flasher to reboot the ESP when required. This may interfere with deep sleep on some devices and prevent the ESP from waking when it's powered through its USB connector. Powering the ESP from a separate 3.3V source connected to the 3.3V pin and GND will solve this issue. In these cases, using a USB to TTL adapter will allow you to log ESP activity. Configuration variables: ------------------------ From 323874b76a9c5060f4ea4ef9ce52b337ad0334ca Mon Sep 17 00:00:00 2001 From: Alexander Dimitrov Date: Wed, 14 Jun 2023 09:42:23 +0300 Subject: [PATCH 34/34] Add warning addressable lights on esp-idf (#2989) --- components/light/fastled.rst | 6 ++++++ components/light/neopixelbus.rst | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/components/light/fastled.rst b/components/light/fastled.rst index 9cf7cb482..5cceb0992 100644 --- a/components/light/fastled.rst +++ b/components/light/fastled.rst @@ -20,6 +20,12 @@ FastLED Light - https://github.com/FastLED/FastLED/issues/1322 - https://github.com/FastLED/FastLED/issues/1264 +.. warning:: + + FastLED does **not** work with ESP-IDF. + + For addressable lights, you can use :doc:`esp32_rmt_led_strip`. + .. _fastled-clockless: Clockless diff --git a/components/light/neopixelbus.rst b/components/light/neopixelbus.rst index cc5808a11..3eed9eff0 100644 --- a/components/light/neopixelbus.rst +++ b/components/light/neopixelbus.rst @@ -5,6 +5,12 @@ NeoPixelBus Light :description: Instructions for setting up Neopixel addressable lights. :image: color_lens.svg +.. warning:: + + NeoPixelBus does **not** work with ESP-IDF. + + For addressable lights, you can use :doc:`esp32_rmt_led_strip`. + The ``neopixelbus`` light platform allows you to create RGB lights in ESPHome for an individually addressable lights like NeoPixel or WS2812.