mirror of
				https://github.com/esphome/esphome.git
				synced 2025-11-03 16:41:50 +00:00 
			
		
		
		
	Compare commits
	
		
			396 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					546bfe6db5 | ||
| 
						 | 
					0af10c58f5 | ||
| 
						 | 
					5ac9d301ea | ||
| 
						 | 
					a70f926971 | ||
| 
						 | 
					dfacf1bbfe | ||
| 
						 | 
					038f24fcea | ||
| 
						 | 
					ad0118dd4a | ||
| 
						 | 
					7c24f1ba6d | ||
| 
						 | 
					6e863305aa | ||
| 
						 | 
					0ac549d208 | ||
| 
						 | 
					41813b0a1f | ||
| 
						 | 
					4690e227b8 | ||
| 
						 | 
					5bec0a6534 | ||
| 
						 | 
					626ed815fb | ||
| 
						 | 
					74aee1d453 | ||
| 
						 | 
					d187340fc4 | ||
| 
						 | 
					de0e549187 | ||
| 
						 | 
					e15d0ee150 | ||
| 
						 | 
					93e0c71c2f | ||
| 
						 | 
					c512d5ebb6 | ||
| 
						 | 
					f153a7b0fd | ||
| 
						 | 
					de43c4e6ab | ||
| 
						 | 
					4af8230b4f | ||
| 
						 | 
					0bbefb5b2a | ||
| 
						 | 
					41baf70660 | ||
| 
						 | 
					eaf2bb70d9 | ||
| 
						 | 
					71236b170d | ||
| 
						 | 
					bb92ab01d7 | ||
| 
						 | 
					316a0e1c96 | ||
| 
						 | 
					0c2f9b9dbb | ||
| 
						 | 
					c6c1d3a3ad | ||
| 
						 | 
					fbab0aceb0 | ||
| 
						 | 
					54b77a1174 | ||
| 
						 | 
					a34cec217e | ||
| 
						 | 
					91bb38553d | ||
| 
						 | 
					531f33a158 | ||
| 
						 | 
					2d826768b0 | ||
| 
						 | 
					d7f6d4436e | ||
| 
						 | 
					bdd0a36aa3 | ||
| 
						 | 
					8a89dac5d5 | ||
| 
						 | 
					8d28c53fd3 | ||
| 
						 | 
					114476d8b1 | ||
| 
						 | 
					d1bfad9890 | ||
| 
						 | 
					04b268e319 | ||
| 
						 | 
					6417f1f907 | ||
| 
						 | 
					2f669c99f8 | ||
| 
						 | 
					aa8c963c50 | ||
| 
						 | 
					2873c6bbaf | ||
| 
						 | 
					2da939c81c | ||
| 
						 | 
					ee398441b6 | ||
| 
						 | 
					894d81c577 | ||
| 
						 | 
					4c6a17e304 | ||
| 
						 | 
					ddaa84683b | ||
| 
						 | 
					dd1e480142 | ||
| 
						 | 
					6ca7b30f75 | ||
| 
						 | 
					b0a3b5e080 | ||
| 
						 | 
					5fa54b0885 | ||
| 
						 | 
					803f3f2e13 | ||
| 
						 | 
					de19588d10 | ||
| 
						 | 
					bc2bc13eb1 | ||
| 
						 | 
					6c96281a1d | ||
| 
						 | 
					3727342bce | ||
| 
						 | 
					fc3f806555 | ||
| 
						 | 
					c013c3bf61 | ||
| 
						 | 
					849a98d5b4 | ||
| 
						 | 
					dd1a72e4d9 | ||
| 
						 | 
					e4e404d54f | ||
| 
						 | 
					ee6f2bfecb | ||
| 
						 | 
					995db1d0e1 | ||
| 
						 | 
					5cb80619dd | ||
| 
						 | 
					0914dc7198 | ||
| 
						 | 
					12f00a9d3d | ||
| 
						 | 
					3fb9c93a24 | ||
| 
						 | 
					d8f0dce08f | ||
| 
						 | 
					5e6c69b930 | ||
| 
						 | 
					83f9664efb | ||
| 
						 | 
					582386d3a2 | ||
| 
						 | 
					7aaa5ce9c8 | ||
| 
						 | 
					5278ae4b5e | ||
| 
						 | 
					b89dea97d9 | ||
| 
						 | 
					715184070d | ||
| 
						 | 
					6294c3b913 | ||
| 
						 | 
					e9cf3623d1 | ||
| 
						 | 
					d0ab2a16a6 | ||
| 
						 | 
					1f5442f1ba | ||
| 
						 | 
					7b3d6747d5 | ||
| 
						 | 
					7904d3b157 | ||
| 
						 | 
					3a48b10757 | ||
| 
						 | 
					0e50cac399 | ||
| 
						 | 
					dc4a93f5d0 | ||
| 
						 | 
					e23153d090 | ||
| 
						 | 
					9a26cdb336 | ||
| 
						 | 
					decf50ed49 | ||
| 
						 | 
					bfdf63055f | ||
| 
						 | 
					cd7894ae8f | ||
| 
						 | 
					10504c4d68 | ||
| 
						 | 
					192718fee6 | ||
| 
						 | 
					855d154439 | ||
| 
						 | 
					300d48a55e | ||
| 
						 | 
					7174cf35dd | ||
| 
						 | 
					0b3145a6df | ||
| 
						 | 
					04225d5717 | ||
| 
						 | 
					86791422f0 | ||
| 
						 | 
					9c2af6318c | ||
| 
						 | 
					c747d7d45d | ||
| 
						 | 
					bbd7c9cf86 | ||
| 
						 | 
					169fb79c97 | ||
| 
						 | 
					1579dfeb80 | ||
| 
						 | 
					d8a6d8594a | ||
| 
						 | 
					7be071a0e9 | ||
| 
						 | 
					01bcf5fb97 | ||
| 
						 | 
					91766afb64 | ||
| 
						 | 
					cc4f1c667e | ||
| 
						 | 
					bc26de2d68 | ||
| 
						 | 
					0179358f9c | ||
| 
						 | 
					d8a5c1ea0c | ||
| 
						 | 
					fb9844463b | ||
| 
						 | 
					481cf7384a | ||
| 
						 | 
					c9a0daf4b6 | ||
| 
						 | 
					8a25bedaf9 | ||
| 
						 | 
					11b8e2e1af | ||
| 
						 | 
					53cfa8d3a1 | ||
| 
						 | 
					0262a99274 | ||
| 
						 | 
					09a947beaa | ||
| 
						 | 
					a6e1ef2dd1 | ||
| 
						 | 
					c5aae8ee25 | ||
| 
						 | 
					5bd5b777a6 | ||
| 
						 | 
					e39961f7f1 | ||
| 
						 | 
					0d3cf5cb78 | ||
| 
						 | 
					96d63de292 | ||
| 
						 | 
					ae2962259e | ||
| 
						 | 
					7dbc20b776 | ||
| 
						 | 
					a21dab334c | ||
| 
						 | 
					78450da6f3 | ||
| 
						 | 
					b1868123db | ||
| 
						 | 
					f7af51b92c | ||
| 
						 | 
					7ee1406f64 | ||
| 
						 | 
					0f49b58e0a | ||
| 
						 | 
					17204baac0 | ||
| 
						 | 
					1e05bcaa61 | ||
| 
						 | 
					18690d51f5 | ||
| 
						 | 
					2aacf14e96 | ||
| 
						 | 
					9c5507ab46 | ||
| 
						 | 
					0a9703bff9 | ||
| 
						 | 
					67bd5db6d6 | ||
| 
						 | 
					6c11f0bd51 | ||
| 
						 | 
					e7556271e7 | ||
| 
						 | 
					8045b889d3 | ||
| 
						 | 
					6f074d3692 | ||
| 
						 | 
					b09781afa5 | ||
| 
						 | 
					1863523cfd | ||
| 
						 | 
					a7a9eb6f71 | ||
| 
						 | 
					c868dae44a | ||
| 
						 | 
					ad8cf69897 | ||
| 
						 | 
					96f1a146a6 | ||
| 
						 | 
					775e03cfd9 | ||
| 
						 | 
					80e5e19956 | ||
| 
						 | 
					8f16268572 | ||
| 
						 | 
					0fe18a6144 | ||
| 
						 | 
					a6d1aa91de | ||
| 
						 | 
					ba11f2ab0c | ||
| 
						 | 
					9747811b82 | ||
| 
						 | 
					ff803aa108 | ||
| 
						 | 
					8bac82f804 | ||
| 
						 | 
					6682451ee0 | ||
| 
						 | 
					c17090c1e5 | ||
| 
						 | 
					acf69bb56f | ||
| 
						 | 
					fd7a212562 | ||
| 
						 | 
					8567877f07 | ||
| 
						 | 
					310f850ee4 | ||
| 
						 | 
					896cdab22d | ||
| 
						 | 
					ed6462fa00 | ||
| 
						 | 
					65b05af014 | ||
| 
						 | 
					c18056bdda | ||
| 
						 | 
					65a79acfb9 | ||
| 
						 | 
					18d331d284 | ||
| 
						 | 
					c053a33fe8 | ||
| 
						 | 
					ff07637dfd | ||
| 
						 | 
					43b5c2deb7 | ||
| 
						 | 
					d27e7b3b70 | ||
| 
						 | 
					5dec62bf1e | ||
| 
						 | 
					7d642147c1 | ||
| 
						 | 
					4c313bc198 | ||
| 
						 | 
					a78b2d0128 | ||
| 
						 | 
					f6848fe24d | ||
| 
						 | 
					a59c9b4f77 | ||
| 
						 | 
					c30913ccde | ||
| 
						 | 
					41f810f828 | ||
| 
						 | 
					d604c8ae64 | ||
| 
						 | 
					67d8c7c691 | ||
| 
						 | 
					015cd42a2e | ||
| 
						 | 
					51c5d1714c | ||
| 
						 | 
					1ff302b341 | ||
| 
						 | 
					cfe28ce7a3 | ||
| 
						 | 
					25a3db1637 | ||
| 
						 | 
					65638bf614 | ||
| 
						 | 
					1e66241b26 | ||
| 
						 | 
					eb50f0eafd | ||
| 
						 | 
					6b89763ad6 | ||
| 
						 | 
					253303f3a9 | ||
| 
						 | 
					d49f2cbec8 | ||
| 
						 | 
					290816be11 | ||
| 
						 | 
					2fc43fa9c7 | ||
| 
						 | 
					5adadeaa07 | ||
| 
						 | 
					761aae6f89 | ||
| 
						 | 
					b29e1acab8 | ||
| 
						 | 
					49d4260cfe | ||
| 
						 | 
					4e8a7986cd | ||
| 
						 | 
					3db71b98ae | ||
| 
						 | 
					73cb3ec852 | ||
| 
						 | 
					91e72fe121 | ||
| 
						 | 
					be486e0ca6 | ||
| 
						 | 
					fdefc825bb | ||
| 
						 | 
					c4c46c206f | ||
| 
						 | 
					8453d9a70d | ||
| 
						 | 
					68dbf35b09 | ||
| 
						 | 
					1a242f94db | ||
| 
						 | 
					df52bc3493 | ||
| 
						 | 
					2044c7e4d4 | ||
| 
						 | 
					b401b5eca8 | ||
| 
						 | 
					67f41a0c72 | ||
| 
						 | 
					8a83670f54 | ||
| 
						 | 
					bd7e8fbf86 | ||
| 
						 | 
					f9f98fa6c6 | ||
| 
						 | 
					f25c296303 | ||
| 
						 | 
					bc408ad08c | ||
| 
						 | 
					e2c1af199c | ||
| 
						 | 
					7c843437a7 | ||
| 
						 | 
					4bf7c97088 | ||
| 
						 | 
					7b9fb57bb2 | ||
| 
						 | 
					699d00e218 | ||
| 
						 | 
					e2784d077d | ||
| 
						 | 
					13fabf1cd8 | ||
| 
						 | 
					7b60543afd | ||
| 
						 | 
					562700bd2c | ||
| 
						 | 
					a64106e48c | ||
| 
						 | 
					c723fd1f80 | ||
| 
						 | 
					3a97244b83 | ||
| 
						 | 
					1f8449ec0e | ||
| 
						 | 
					3cd2fb0843 | ||
| 
						 | 
					7dc07c5632 | ||
| 
						 | 
					95e45dc12c | ||
| 
						 | 
					51a8a7e875 | ||
| 
						 | 
					dceab6ce29 | ||
| 
						 | 
					6de79d6cfb | ||
| 
						 | 
					7b45498de6 | ||
| 
						 | 
					618102fe8c | ||
| 
						 | 
					38b7bed2fa | ||
| 
						 | 
					d77ea46157 | ||
| 
						 | 
					8718e15a6a | ||
| 
						 | 
					861a23d039 | ||
| 
						 | 
					276eea2b69 | ||
| 
						 | 
					ccab57fc58 | ||
| 
						 | 
					8ef4aaa70e | ||
| 
						 | 
					7143e9cd9e | ||
| 
						 | 
					cc217d8a83 | ||
| 
						 | 
					c52d5c0279 | ||
| 
						 | 
					f36a96c8e2 | ||
| 
						 | 
					594856899a | ||
| 
						 | 
					f7742cdf19 | ||
| 
						 | 
					5b062a222c | ||
| 
						 | 
					664ee56dc5 | ||
| 
						 | 
					388b2c2de0 | ||
| 
						 | 
					ce4a3d9950 | ||
| 
						 | 
					ac9f57600d | ||
| 
						 | 
					69d38f6137 | ||
| 
						 | 
					eb75778f84 | ||
| 
						 | 
					2d56d8d84f | ||
| 
						 | 
					cdf83c5d8c | ||
| 
						 | 
					78b48209aa | ||
| 
						 | 
					05491e756b | ||
| 
						 | 
					b8d2a6f574 | ||
| 
						 | 
					2353b2b5e1 | ||
| 
						 | 
					2beb1f0336 | ||
| 
						 | 
					41e13fa6f4 | ||
| 
						 | 
					1f301df51d | ||
| 
						 | 
					2894a138e7 | ||
| 
						 | 
					8dfe1d5220 | ||
| 
						 | 
					dd27881336 | ||
| 
						 | 
					8aba890e69 | ||
| 
						 | 
					63fc8ab10a | ||
| 
						 | 
					9de8eaff24 | ||
| 
						 | 
					c130ddbe9c | ||
| 
						 | 
					a7fc1a6298 | ||
| 
						 | 
					854d3f2e4a | ||
| 
						 | 
					5ae32e81c3 | ||
| 
						 | 
					439fd94718 | ||
| 
						 | 
					6d5d382f3d | ||
| 
						 | 
					60433c5e64 | ||
| 
						 | 
					bff24e2977 | ||
| 
						 | 
					ec3164f800 | ||
| 
						 | 
					2b691ad5ad | ||
| 
						 | 
					06996def72 | ||
| 
						 | 
					db6f6f0cb7 | ||
| 
						 | 
					497cf8742f | ||
| 
						 | 
					d2b35adcc8 | ||
| 
						 | 
					3fe2fc9b56 | ||
| 
						 | 
					4cd4b168b4 | ||
| 
						 | 
					f07479419c | ||
| 
						 | 
					54b51269ab | ||
| 
						 | 
					6e4fd428e7 | ||
| 
						 | 
					e285196709 | ||
| 
						 | 
					17c6bf57cd | ||
| 
						 | 
					4125b48b86 | ||
| 
						 | 
					6d341ce4e7 | ||
| 
						 | 
					964410bd64 | ||
| 
						 | 
					863bee28d9 | ||
| 
						 | 
					9d03f47233 | ||
| 
						 | 
					c2d67659f3 | ||
| 
						 | 
					aed0593793 | ||
| 
						 | 
					4ab7a5d964 | ||
| 
						 | 
					7f9383c83b | ||
| 
						 | 
					9a6fde21ee | ||
| 
						 | 
					1ca7c2d7dd | ||
| 
						 | 
					76abf2200c | ||
| 
						 | 
					83d3584173 | ||
| 
						 | 
					525c4891d5 | ||
| 
						 | 
					ce6dc040da | ||
| 
						 | 
					9de67feccd | ||
| 
						 | 
					bad400e1cd | ||
| 
						 | 
					59b1e9c1b0 | ||
| 
						 | 
					25ee24299a | ||
| 
						 | 
					81ef67cfbb | ||
| 
						 | 
					b9bb3cd4be | ||
| 
						 | 
					91e7a44c31 | ||
| 
						 | 
					080f8bc86e | ||
| 
						 | 
					a85d37a1cf | ||
| 
						 | 
					ba73187c1b | ||
| 
						 | 
					4469ba4024 | ||
| 
						 | 
					70e0925f9a | ||
| 
						 | 
					1164cb8610 | ||
| 
						 | 
					94b63d7bc2 | ||
| 
						 | 
					df838b5788 | ||
| 
						 | 
					d410cc4f7b | ||
| 
						 | 
					b06e0746f5 | ||
| 
						 | 
					034c196ad8 | ||
| 
						 | 
					996f71c03c | ||
| 
						 | 
					98cb6555df | ||
| 
						 | 
					0bb2773c64 | ||
| 
						 | 
					7c243dafb3 | ||
| 
						 | 
					247b2eee30 | ||
| 
						 | 
					f46c499c4e | ||
| 
						 | 
					f91c31f093 | ||
| 
						 | 
					a27c05483c | ||
| 
						 | 
					bf48ccaf22 | ||
| 
						 | 
					f2ef06d8b5 | ||
| 
						 | 
					f0ec900e48 | ||
| 
						 | 
					7d804bf90f | ||
| 
						 | 
					2921831b55 | ||
| 
						 | 
					08509f7755 | ||
| 
						 | 
					ebfccc64c7 | ||
| 
						 | 
					8952719045 | ||
| 
						 | 
					073fb4c124 | ||
| 
						 | 
					46eee4a4f0 | ||
| 
						 | 
					773951d85e | ||
| 
						 | 
					1f29023c92 | ||
| 
						 | 
					9f1ba00b7c | ||
| 
						 | 
					891f56b421 | ||
| 
						 | 
					d7cb953994 | ||
| 
						 | 
					9a6e90af54 | ||
| 
						 | 
					55e4532a88 | ||
| 
						 | 
					6f53607e5a | ||
| 
						 | 
					d5eeab81d6 | ||
| 
						 | 
					636037cec1 | ||
| 
						 | 
					7d791cbdfb | ||
| 
						 | 
					036a666e36 | ||
| 
						 | 
					921e56f2c6 | ||
| 
						 | 
					c94f638c0b | ||
| 
						 | 
					2ac0821cab | ||
| 
						 | 
					47b40505c2 | ||
| 
						 | 
					eae97dbaa0 | ||
| 
						 | 
					91007952e2 | ||
| 
						 | 
					5ee4bf3802 | ||
| 
						 | 
					a23d1631e1 | ||
| 
						 | 
					dd81c83686 | ||
| 
						 | 
					13e3920c13 | ||
| 
						 | 
					67ca60e2af | ||
| 
						 | 
					61b65e2726 | ||
| 
						 | 
					1a45858904 | ||
| 
						 | 
					47a1710b1e | ||
| 
						 | 
					ca5050d4a5 | ||
| 
						 | 
					8280772b91 | ||
| 
						 | 
					026c3a69b8 | ||
| 
						 | 
					78d1a50853 | ||
| 
						 | 
					ca031287a1 | ||
| 
						 | 
					5956bebcb7 | ||
| 
						 | 
					afe81184a8 | ||
| 
						 | 
					d0120cefd2 | ||
| 
						 | 
					3ec4a66c9e | ||
| 
						 | 
					819be76013 | ||
| 
						 | 
					72481006e4 | ||
| 
						 | 
					487e171443 | ||
| 
						 | 
					e48d02495b | ||
| 
						 | 
					7764ab2411 | ||
| 
						 | 
					225beb305d | ||
| 
						 | 
					b7c6125a0b | 
@@ -1,7 +1,9 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "ESPHome Dev",
 | 
			
		||||
  "image": "ghcr.io/esphome/esphome-lint:dev",
 | 
			
		||||
  "postCreateCommand": ["script/devcontainer-post-create"],
 | 
			
		||||
  "postCreateCommand": [
 | 
			
		||||
    "script/devcontainer-post-create"
 | 
			
		||||
  ],
 | 
			
		||||
  "containerEnv": {
 | 
			
		||||
    "DEVCONTAINER": "1",
 | 
			
		||||
    "PIP_BREAK_SYSTEM_PACKAGES": "1",
 | 
			
		||||
@@ -27,6 +29,9 @@
 | 
			
		||||
      "extensions": [
 | 
			
		||||
        // python
 | 
			
		||||
        "ms-python.python",
 | 
			
		||||
        "ms-python.pylint",
 | 
			
		||||
        "ms-python.flake8",
 | 
			
		||||
        "ms-python.black-formatter",
 | 
			
		||||
        "visualstudioexptteam.vscodeintellicode",
 | 
			
		||||
        // yaml
 | 
			
		||||
        "redhat.vscode-yaml",
 | 
			
		||||
@@ -38,9 +43,21 @@
 | 
			
		||||
      "settings": {
 | 
			
		||||
        "python.languageServer": "Pylance",
 | 
			
		||||
        "python.pythonPath": "/usr/bin/python3",
 | 
			
		||||
        "python.linting.pylintEnabled": true,
 | 
			
		||||
        "python.linting.enabled": true,
 | 
			
		||||
        "python.formatting.provider": "black",
 | 
			
		||||
        "pylint.args": [
 | 
			
		||||
          "--rcfile=${workspaceFolder}/pyproject.toml"
 | 
			
		||||
        ],
 | 
			
		||||
        "flake8.args": [
 | 
			
		||||
          "--config=${workspaceFolder}/.flake8"
 | 
			
		||||
        ],
 | 
			
		||||
        "black-formatter.args": [
 | 
			
		||||
          "--config",
 | 
			
		||||
          "${workspaceFolder}/pyproject.toml"
 | 
			
		||||
        ],
 | 
			
		||||
        "[python]": {
 | 
			
		||||
          // VS will say "Value is not accepted" before building the devcontainer, but the warning
 | 
			
		||||
          // should go away after build is completed.
 | 
			
		||||
          "editor.defaultFormatter": "ms-python.black-formatter"
 | 
			
		||||
        },
 | 
			
		||||
        "editor.formatOnPaste": false,
 | 
			
		||||
        "editor.formatOnSave": true,
 | 
			
		||||
        "editor.formatOnType": true,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,19 +1,3 @@
 | 
			
		||||
[metadata]
 | 
			
		||||
license      = MIT
 | 
			
		||||
license_file = LICENSE
 | 
			
		||||
platforms = any
 | 
			
		||||
description  = Make creating custom firmwares for ESP32/ESP8266 super easy.
 | 
			
		||||
long_description = file: README.md
 | 
			
		||||
keywords     = home, automation
 | 
			
		||||
classifier =
 | 
			
		||||
    Environment :: Console
 | 
			
		||||
    Intended Audience :: Developers
 | 
			
		||||
    Intended Audience :: End Users/Desktop
 | 
			
		||||
    License :: OSI Approved :: MIT License
 | 
			
		||||
    Programming Language :: C++
 | 
			
		||||
    Programming Language :: Python :: 3
 | 
			
		||||
    Topic :: Home Automation
 | 
			
		||||
 | 
			
		||||
[flake8]
 | 
			
		||||
max-line-length = 120
 | 
			
		||||
# Following 4 for black compatibility
 | 
			
		||||
@@ -37,25 +21,22 @@ max-line-length = 120
 | 
			
		||||
# D401 First line should be in imperative mood
 | 
			
		||||
 | 
			
		||||
ignore =
 | 
			
		||||
    E501,
 | 
			
		||||
    W503,
 | 
			
		||||
    E203,
 | 
			
		||||
    D202,
 | 
			
		||||
  E501,
 | 
			
		||||
  W503,
 | 
			
		||||
  E203,
 | 
			
		||||
  D202,
 | 
			
		||||
 | 
			
		||||
    D100,
 | 
			
		||||
    D101,
 | 
			
		||||
    D102,
 | 
			
		||||
    D103,
 | 
			
		||||
    D104,
 | 
			
		||||
    D105,
 | 
			
		||||
    D107,
 | 
			
		||||
    D200,
 | 
			
		||||
    D205,
 | 
			
		||||
    D209,
 | 
			
		||||
    D400,
 | 
			
		||||
    D401,
 | 
			
		||||
  D100,
 | 
			
		||||
  D101,
 | 
			
		||||
  D102,
 | 
			
		||||
  D103,
 | 
			
		||||
  D104,
 | 
			
		||||
  D105,
 | 
			
		||||
  D107,
 | 
			
		||||
  D200,
 | 
			
		||||
  D205,
 | 
			
		||||
  D209,
 | 
			
		||||
  D400,
 | 
			
		||||
  D401,
 | 
			
		||||
 | 
			
		||||
exclude = api_pb2.py
 | 
			
		||||
 | 
			
		||||
[bdist_wheel]
 | 
			
		||||
universal = 1
 | 
			
		||||
							
								
								
									
										18
									
								
								.github/actions/build-image/action.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										18
									
								
								.github/actions/build-image/action.yaml
									
									
									
									
										vendored
									
									
								
							@@ -34,16 +34,26 @@ runs:
 | 
			
		||||
          echo $l >> $GITHUB_OUTPUT
 | 
			
		||||
        done
 | 
			
		||||
 | 
			
		||||
    # set cache-to only if dev branch
 | 
			
		||||
    - id: cache-to
 | 
			
		||||
      shell: bash
 | 
			
		||||
      run: |-
 | 
			
		||||
        if [[ "${{ github.ref }}" == "refs/heads/dev" ]]; then
 | 
			
		||||
          echo "value=type=gha,mode=max" >> $GITHUB_OUTPUT
 | 
			
		||||
        else
 | 
			
		||||
          echo "value=" >> $GITHUB_OUTPUT
 | 
			
		||||
        fi
 | 
			
		||||
 | 
			
		||||
    - name: Build and push to ghcr by digest
 | 
			
		||||
      id: build-ghcr
 | 
			
		||||
      uses: docker/build-push-action@v5.3.0
 | 
			
		||||
      uses: docker/build-push-action@v6.3.0
 | 
			
		||||
      with:
 | 
			
		||||
        context: .
 | 
			
		||||
        file: ./docker/Dockerfile
 | 
			
		||||
        platforms: ${{ inputs.platform }}
 | 
			
		||||
        target: ${{ inputs.target }}
 | 
			
		||||
        cache-from: type=gha
 | 
			
		||||
        cache-to: type=gha,mode=max
 | 
			
		||||
        cache-to: ${{ steps.cache-to.outputs.value }}
 | 
			
		||||
        build-args: |
 | 
			
		||||
          BASEIMGTYPE=${{ inputs.baseimg }}
 | 
			
		||||
          BUILD_VERSION=${{ inputs.version }}
 | 
			
		||||
@@ -59,14 +69,14 @@ runs:
 | 
			
		||||
 | 
			
		||||
    - name: Build and push to dockerhub by digest
 | 
			
		||||
      id: build-dockerhub
 | 
			
		||||
      uses: docker/build-push-action@v5.3.0
 | 
			
		||||
      uses: docker/build-push-action@v6.3.0
 | 
			
		||||
      with:
 | 
			
		||||
        context: .
 | 
			
		||||
        file: ./docker/Dockerfile
 | 
			
		||||
        platforms: ${{ inputs.platform }}
 | 
			
		||||
        target: ${{ inputs.target }}
 | 
			
		||||
        cache-from: type=gha
 | 
			
		||||
        cache-to: type=gha,mode=max
 | 
			
		||||
        cache-to: ${{ steps.cache-to.outputs.value }}
 | 
			
		||||
        build-args: |
 | 
			
		||||
          BASEIMGTYPE=${{ inputs.baseimg }}
 | 
			
		||||
          BUILD_VERSION=${{ inputs.version }}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/actions/restore-python/action.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/actions/restore-python/action.yml
									
									
									
									
										vendored
									
									
								
							@@ -17,7 +17,7 @@ runs:
 | 
			
		||||
  steps:
 | 
			
		||||
    - name: Set up Python ${{ inputs.python-version }}
 | 
			
		||||
      id: python
 | 
			
		||||
      uses: actions/setup-python@v5.1.0
 | 
			
		||||
      uses: actions/setup-python@v5.1.1
 | 
			
		||||
      with:
 | 
			
		||||
        python-version: ${{ inputs.python-version }}
 | 
			
		||||
    - name: Restore Python virtual environment
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/workflows/ci-api-proto.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/ci-api-proto.yml
									
									
									
									
										vendored
									
									
								
							@@ -21,7 +21,7 @@ jobs:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout
 | 
			
		||||
        uses: actions/checkout@v4.1.5
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
      - name: Set up Python
 | 
			
		||||
        uses: actions/setup-python@v5.1.0
 | 
			
		||||
        with:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								.github/workflows/ci-docker.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.github/workflows/ci-docker.yml
									
									
									
									
										vendored
									
									
								
							@@ -40,15 +40,15 @@ jobs:
 | 
			
		||||
        arch: [amd64, armv7, aarch64]
 | 
			
		||||
        build_type: ["ha-addon", "docker", "lint"]
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v4.1.5
 | 
			
		||||
      - uses: actions/checkout@v4.1.7
 | 
			
		||||
      - name: Set up Python
 | 
			
		||||
        uses: actions/setup-python@v5.1.0
 | 
			
		||||
        with:
 | 
			
		||||
          python-version: "3.9"
 | 
			
		||||
      - name: Set up Docker Buildx
 | 
			
		||||
        uses: docker/setup-buildx-action@v3.3.0
 | 
			
		||||
        uses: docker/setup-buildx-action@v3.4.0
 | 
			
		||||
      - name: Set up QEMU
 | 
			
		||||
        uses: docker/setup-qemu-action@v3.0.0
 | 
			
		||||
        uses: docker/setup-qemu-action@v3.1.0
 | 
			
		||||
 | 
			
		||||
      - name: Set TAG
 | 
			
		||||
        run: |
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										120
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										120
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							@@ -34,7 +34,7 @@ jobs:
 | 
			
		||||
      cache-key: ${{ steps.cache-key.outputs.key }}
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.1.5
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
      - name: Generate cache-key
 | 
			
		||||
        id: cache-key
 | 
			
		||||
        run: echo key="${{ hashFiles('requirements.txt', 'requirements_optional.txt', 'requirements_test.txt') }}" >> $GITHUB_OUTPUT
 | 
			
		||||
@@ -66,7 +66,7 @@ jobs:
 | 
			
		||||
      - common
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.1.5
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
      - name: Restore Python
 | 
			
		||||
        uses: ./.github/actions/restore-python
 | 
			
		||||
        with:
 | 
			
		||||
@@ -87,7 +87,7 @@ jobs:
 | 
			
		||||
      - common
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.1.5
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
      - name: Restore Python
 | 
			
		||||
        uses: ./.github/actions/restore-python
 | 
			
		||||
        with:
 | 
			
		||||
@@ -108,7 +108,7 @@ jobs:
 | 
			
		||||
      - common
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.1.5
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
      - name: Restore Python
 | 
			
		||||
        uses: ./.github/actions/restore-python
 | 
			
		||||
        with:
 | 
			
		||||
@@ -129,7 +129,7 @@ jobs:
 | 
			
		||||
      - common
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.1.5
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
      - name: Restore Python
 | 
			
		||||
        uses: ./.github/actions/restore-python
 | 
			
		||||
        with:
 | 
			
		||||
@@ -150,7 +150,7 @@ jobs:
 | 
			
		||||
      - common
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.1.5
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
      - name: Restore Python
 | 
			
		||||
        uses: ./.github/actions/restore-python
 | 
			
		||||
        with:
 | 
			
		||||
@@ -199,7 +199,7 @@ jobs:
 | 
			
		||||
      - common
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.1.5
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
      - name: Restore Python
 | 
			
		||||
        uses: ./.github/actions/restore-python
 | 
			
		||||
        with:
 | 
			
		||||
@@ -229,7 +229,7 @@ jobs:
 | 
			
		||||
      - common
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.1.5
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
      - name: Restore Python
 | 
			
		||||
        uses: ./.github/actions/restore-python
 | 
			
		||||
        with:
 | 
			
		||||
@@ -248,72 +248,6 @@ jobs:
 | 
			
		||||
        run: script/ci-suggest-changes
 | 
			
		||||
        if: always()
 | 
			
		||||
 | 
			
		||||
  compile-tests-list:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    outputs:
 | 
			
		||||
      matrix: ${{ steps.set-matrix.outputs.matrix }}
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.1.5
 | 
			
		||||
      - name: Find all YAML test files
 | 
			
		||||
        id: set-matrix
 | 
			
		||||
        run: echo "matrix=$(ls tests/test*.yaml | jq -R -s -c 'split("\n")[:-1]')" >> $GITHUB_OUTPUT
 | 
			
		||||
 | 
			
		||||
  validate-tests:
 | 
			
		||||
    name: Validate YAML test ${{ matrix.file }}
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    needs:
 | 
			
		||||
      - common
 | 
			
		||||
      - compile-tests-list
 | 
			
		||||
    strategy:
 | 
			
		||||
      fail-fast: false
 | 
			
		||||
      matrix:
 | 
			
		||||
        file: ${{ fromJson(needs.compile-tests-list.outputs.matrix) }}
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.1.5
 | 
			
		||||
      - name: Restore Python
 | 
			
		||||
        uses: ./.github/actions/restore-python
 | 
			
		||||
        with:
 | 
			
		||||
          python-version: ${{ env.DEFAULT_PYTHON }}
 | 
			
		||||
          cache-key: ${{ needs.common.outputs.cache-key }}
 | 
			
		||||
      - name: Run esphome config ${{ matrix.file }}
 | 
			
		||||
        run: |
 | 
			
		||||
          . venv/bin/activate
 | 
			
		||||
          esphome config ${{ matrix.file }}
 | 
			
		||||
 | 
			
		||||
  compile-tests:
 | 
			
		||||
    name: Run YAML test ${{ matrix.file }}
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    needs:
 | 
			
		||||
      - common
 | 
			
		||||
      - black
 | 
			
		||||
      - ci-custom
 | 
			
		||||
      - clang-format
 | 
			
		||||
      - flake8
 | 
			
		||||
      - pylint
 | 
			
		||||
      - pytest
 | 
			
		||||
      - pyupgrade
 | 
			
		||||
      - compile-tests-list
 | 
			
		||||
      - validate-tests
 | 
			
		||||
    strategy:
 | 
			
		||||
      fail-fast: false
 | 
			
		||||
      max-parallel: 2
 | 
			
		||||
      matrix:
 | 
			
		||||
        file: ${{ fromJson(needs.compile-tests-list.outputs.matrix) }}
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.1.5
 | 
			
		||||
      - name: Restore Python
 | 
			
		||||
        uses: ./.github/actions/restore-python
 | 
			
		||||
        with:
 | 
			
		||||
          python-version: ${{ env.DEFAULT_PYTHON }}
 | 
			
		||||
          cache-key: ${{ needs.common.outputs.cache-key }}
 | 
			
		||||
      - name: Run esphome compile ${{ matrix.file }}
 | 
			
		||||
        run: |
 | 
			
		||||
          . venv/bin/activate
 | 
			
		||||
          esphome compile ${{ matrix.file }}
 | 
			
		||||
 | 
			
		||||
  clang-tidy:
 | 
			
		||||
    name: ${{ matrix.name }}
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
@@ -358,18 +292,26 @@ jobs:
 | 
			
		||||
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.1.5
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
      - name: Restore Python
 | 
			
		||||
        uses: ./.github/actions/restore-python
 | 
			
		||||
        with:
 | 
			
		||||
          python-version: ${{ env.DEFAULT_PYTHON }}
 | 
			
		||||
          cache-key: ${{ needs.common.outputs.cache-key }}
 | 
			
		||||
 | 
			
		||||
      - name: Cache platformio
 | 
			
		||||
        if: github.ref == 'refs/heads/dev'
 | 
			
		||||
        uses: actions/cache@v4.0.2
 | 
			
		||||
        with:
 | 
			
		||||
          path: ~/.platformio
 | 
			
		||||
          # yamllint disable-line rule:line-length
 | 
			
		||||
          key: platformio-${{ matrix.pio_cache_key }}-${{ hashFiles('platformio.ini') }}
 | 
			
		||||
          key: platformio-${{ matrix.pio_cache_key }}
 | 
			
		||||
 | 
			
		||||
      - name: Cache platformio
 | 
			
		||||
        if: github.ref != 'refs/heads/dev'
 | 
			
		||||
        uses: actions/cache/restore@v4.0.2
 | 
			
		||||
        with:
 | 
			
		||||
          path: ~/.platformio
 | 
			
		||||
          key: platformio-${{ matrix.pio_cache_key }}
 | 
			
		||||
 | 
			
		||||
      - name: Install clang-tidy
 | 
			
		||||
        run: sudo apt-get install clang-tidy-14
 | 
			
		||||
@@ -379,6 +321,13 @@ jobs:
 | 
			
		||||
          echo "::add-matcher::.github/workflows/matchers/gcc.json"
 | 
			
		||||
          echo "::add-matcher::.github/workflows/matchers/clang-tidy.json"
 | 
			
		||||
 | 
			
		||||
      - name: Run 'pio run --list-targets -e esp32-idf-tidy'
 | 
			
		||||
        if: matrix.name == 'Run script/clang-tidy for ESP32 IDF'
 | 
			
		||||
        run: |
 | 
			
		||||
          . venv/bin/activate
 | 
			
		||||
          mkdir -p .temp
 | 
			
		||||
          pio run --list-targets -e esp32-idf-tidy
 | 
			
		||||
 | 
			
		||||
      - name: Run clang-tidy
 | 
			
		||||
        run: |
 | 
			
		||||
          . venv/bin/activate
 | 
			
		||||
@@ -402,7 +351,7 @@ jobs:
 | 
			
		||||
      count: ${{ steps.list-components.outputs.count }}
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.1.5
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
        with:
 | 
			
		||||
          # Fetch enough history so `git merge-base refs/remotes/origin/dev HEAD` works.
 | 
			
		||||
          fetch-depth: 500
 | 
			
		||||
@@ -446,11 +395,11 @@ jobs:
 | 
			
		||||
      matrix:
 | 
			
		||||
        file: ${{ fromJson(needs.list-components.outputs.components) }}
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Install libsodium
 | 
			
		||||
        run: sudo apt-get install libsodium-dev
 | 
			
		||||
      - name: Install dependencies
 | 
			
		||||
        run: sudo apt-get install libsodium-dev libsdl2-dev
 | 
			
		||||
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.1.5
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
      - name: Restore Python
 | 
			
		||||
        uses: ./.github/actions/restore-python
 | 
			
		||||
        with:
 | 
			
		||||
@@ -476,7 +425,7 @@ jobs:
 | 
			
		||||
      matrix: ${{ steps.split.outputs.components }}
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.1.5
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
      - name: Split components into 20 groups
 | 
			
		||||
        id: split
 | 
			
		||||
        run: |
 | 
			
		||||
@@ -500,11 +449,11 @@ jobs:
 | 
			
		||||
      - name: List components
 | 
			
		||||
        run: echo ${{ matrix.components }}
 | 
			
		||||
 | 
			
		||||
      - name: Install libsodium
 | 
			
		||||
        run: sudo apt-get install libsodium-dev
 | 
			
		||||
      - name: Install dependencies
 | 
			
		||||
        run: sudo apt-get install libsodium-dev libsdl2-dev
 | 
			
		||||
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.1.5
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
      - name: Restore Python
 | 
			
		||||
        uses: ./.github/actions/restore-python
 | 
			
		||||
        with:
 | 
			
		||||
@@ -535,7 +484,6 @@ jobs:
 | 
			
		||||
      - pylint
 | 
			
		||||
      - pytest
 | 
			
		||||
      - pyupgrade
 | 
			
		||||
      - compile-tests
 | 
			
		||||
      - clang-tidy
 | 
			
		||||
      - list-components
 | 
			
		||||
      - test-build-components
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										32
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										32
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							@@ -19,7 +19,7 @@ jobs:
 | 
			
		||||
      tag: ${{ steps.tag.outputs.tag }}
 | 
			
		||||
      branch_build: ${{ steps.tag.outputs.branch_build }}
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v4.1.5
 | 
			
		||||
      - uses: actions/checkout@v4.1.7
 | 
			
		||||
      - name: Get tag
 | 
			
		||||
        id: tag
 | 
			
		||||
        # yamllint disable rule:line-length
 | 
			
		||||
@@ -51,7 +51,7 @@ jobs:
 | 
			
		||||
      contents: read
 | 
			
		||||
      id-token: write
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v4.1.5
 | 
			
		||||
      - uses: actions/checkout@v4.1.7
 | 
			
		||||
      - name: Set up Python
 | 
			
		||||
        uses: actions/setup-python@v5.1.0
 | 
			
		||||
        with:
 | 
			
		||||
@@ -61,9 +61,11 @@ jobs:
 | 
			
		||||
          ESPHOME_NO_VENV: 1
 | 
			
		||||
        run: script/setup
 | 
			
		||||
      - name: Build
 | 
			
		||||
        run: python setup.py sdist bdist_wheel
 | 
			
		||||
        run: |-
 | 
			
		||||
          pip3 install build
 | 
			
		||||
          python3 -m build
 | 
			
		||||
      - name: Publish
 | 
			
		||||
        uses: pypa/gh-action-pypi-publish@v1.8.14
 | 
			
		||||
        uses: pypa/gh-action-pypi-publish@v1.9.0
 | 
			
		||||
 | 
			
		||||
  deploy-docker:
 | 
			
		||||
    name: Build ESPHome ${{ matrix.platform }}
 | 
			
		||||
@@ -81,25 +83,25 @@ jobs:
 | 
			
		||||
          - linux/arm/v7
 | 
			
		||||
          - linux/arm64
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v4.1.5
 | 
			
		||||
      - uses: actions/checkout@v4.1.7
 | 
			
		||||
      - name: Set up Python
 | 
			
		||||
        uses: actions/setup-python@v5.1.0
 | 
			
		||||
        with:
 | 
			
		||||
          python-version: "3.9"
 | 
			
		||||
 | 
			
		||||
      - name: Set up Docker Buildx
 | 
			
		||||
        uses: docker/setup-buildx-action@v3.3.0
 | 
			
		||||
        uses: docker/setup-buildx-action@v3.4.0
 | 
			
		||||
      - name: Set up QEMU
 | 
			
		||||
        if: matrix.platform != 'linux/amd64'
 | 
			
		||||
        uses: docker/setup-qemu-action@v3.0.0
 | 
			
		||||
        uses: docker/setup-qemu-action@v3.1.0
 | 
			
		||||
 | 
			
		||||
      - name: Log in to docker hub
 | 
			
		||||
        uses: docker/login-action@v3.1.0
 | 
			
		||||
        uses: docker/login-action@v3.2.0
 | 
			
		||||
        with:
 | 
			
		||||
          username: ${{ secrets.DOCKER_USER }}
 | 
			
		||||
          password: ${{ secrets.DOCKER_PASSWORD }}
 | 
			
		||||
      - name: Log in to the GitHub container registry
 | 
			
		||||
        uses: docker/login-action@v3.1.0
 | 
			
		||||
        uses: docker/login-action@v3.2.0
 | 
			
		||||
        with:
 | 
			
		||||
          registry: ghcr.io
 | 
			
		||||
          username: ${{ github.actor }}
 | 
			
		||||
@@ -139,7 +141,7 @@ jobs:
 | 
			
		||||
          echo name=$(cat /tmp/platform) >> $GITHUB_OUTPUT
 | 
			
		||||
 | 
			
		||||
      - name: Upload digests
 | 
			
		||||
        uses: actions/upload-artifact@v4.3.3
 | 
			
		||||
        uses: actions/upload-artifact@v4.3.4
 | 
			
		||||
        with:
 | 
			
		||||
          name: digests-${{ steps.sanitize.outputs.name }}
 | 
			
		||||
          path: /tmp/digests
 | 
			
		||||
@@ -172,27 +174,27 @@ jobs:
 | 
			
		||||
          - ghcr
 | 
			
		||||
          - dockerhub
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v4.1.5
 | 
			
		||||
      - uses: actions/checkout@v4.1.7
 | 
			
		||||
 | 
			
		||||
      - name: Download digests
 | 
			
		||||
        uses: actions/download-artifact@v4.1.7
 | 
			
		||||
        uses: actions/download-artifact@v4.1.8
 | 
			
		||||
        with:
 | 
			
		||||
          pattern: digests-*
 | 
			
		||||
          path: /tmp/digests
 | 
			
		||||
          merge-multiple: true
 | 
			
		||||
 | 
			
		||||
      - name: Set up Docker Buildx
 | 
			
		||||
        uses: docker/setup-buildx-action@v3.3.0
 | 
			
		||||
        uses: docker/setup-buildx-action@v3.4.0
 | 
			
		||||
 | 
			
		||||
      - name: Log in to docker hub
 | 
			
		||||
        if: matrix.registry == 'dockerhub'
 | 
			
		||||
        uses: docker/login-action@v3.1.0
 | 
			
		||||
        uses: docker/login-action@v3.2.0
 | 
			
		||||
        with:
 | 
			
		||||
          username: ${{ secrets.DOCKER_USER }}
 | 
			
		||||
          password: ${{ secrets.DOCKER_PASSWORD }}
 | 
			
		||||
      - name: Log in to the GitHub container registry
 | 
			
		||||
        if: matrix.registry == 'ghcr'
 | 
			
		||||
        uses: docker/login-action@v3.1.0
 | 
			
		||||
        uses: docker/login-action@v3.2.0
 | 
			
		||||
        with:
 | 
			
		||||
          registry: ghcr.io
 | 
			
		||||
          username: ${{ github.actor }}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								.github/workflows/sync-device-classes.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.github/workflows/sync-device-classes.yml
									
									
									
									
										vendored
									
									
								
							@@ -13,10 +13,10 @@ jobs:
 | 
			
		||||
    if: github.repository == 'esphome/esphome'
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout
 | 
			
		||||
        uses: actions/checkout@v4.1.5
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
 | 
			
		||||
      - name: Checkout Home Assistant
 | 
			
		||||
        uses: actions/checkout@v4.1.5
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
        with:
 | 
			
		||||
          repository: home-assistant/core
 | 
			
		||||
          path: lib/home-assistant
 | 
			
		||||
@@ -36,7 +36,7 @@ jobs:
 | 
			
		||||
          python ./script/sync-device_class.py
 | 
			
		||||
 | 
			
		||||
      - name: Commit changes
 | 
			
		||||
        uses: peter-evans/create-pull-request@v6.0.4
 | 
			
		||||
        uses: peter-evans/create-pull-request@v6.1.0
 | 
			
		||||
        with:
 | 
			
		||||
          commit-message: "Synchronise Device Classes from Home Assistant"
 | 
			
		||||
          committer: esphomebot <esphome@nabucasa.com>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/workflows/yaml-lint.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/yaml-lint.yml
									
									
									
									
										vendored
									
									
								
							@@ -18,7 +18,7 @@ jobs:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Check out code from GitHub
 | 
			
		||||
        uses: actions/checkout@v4.1.5
 | 
			
		||||
        uses: actions/checkout@v4.1.7
 | 
			
		||||
      - name: Run yamllint
 | 
			
		||||
        uses: frenck/action-yamllint@v1.5.0
 | 
			
		||||
        with:
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
# See https://pre-commit.com/hooks.html for more hooks
 | 
			
		||||
repos:
 | 
			
		||||
  - repo: https://github.com/psf/black-pre-commit-mirror
 | 
			
		||||
    rev: 24.2.0
 | 
			
		||||
    rev: 24.4.2
 | 
			
		||||
    hooks:
 | 
			
		||||
      - id: black
 | 
			
		||||
        args:
 | 
			
		||||
@@ -40,3 +40,10 @@ repos:
 | 
			
		||||
    hooks:
 | 
			
		||||
      - id: clang-format
 | 
			
		||||
        types_or: [c, c++]
 | 
			
		||||
  - repo: local
 | 
			
		||||
    hooks:
 | 
			
		||||
      - id: pylint
 | 
			
		||||
        name: pylint
 | 
			
		||||
        entry: script/run-in-env.sh pylint
 | 
			
		||||
        language: script
 | 
			
		||||
        types: [python]
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										34
									
								
								CODEOWNERS
									
									
									
									
									
								
							
							
						
						
									
										34
									
								
								CODEOWNERS
									
									
									
									
									
								
							@@ -6,7 +6,7 @@
 | 
			
		||||
# the integration's code owner is automatically notified.
 | 
			
		||||
 | 
			
		||||
# Core Code
 | 
			
		||||
setup.py @esphome/core
 | 
			
		||||
pyproject.toml @esphome/core
 | 
			
		||||
esphome/*.py @esphome/core
 | 
			
		||||
esphome/core/* @esphome/core
 | 
			
		||||
 | 
			
		||||
@@ -51,6 +51,8 @@ esphome/components/bang_bang/* @OttoWinter
 | 
			
		||||
esphome/components/bedjet/* @jhansche
 | 
			
		||||
esphome/components/bedjet/climate/* @jhansche
 | 
			
		||||
esphome/components/bedjet/fan/* @jhansche
 | 
			
		||||
esphome/components/bedjet/sensor/* @javawizard @jhansche
 | 
			
		||||
esphome/components/beken_spi_led_strip/* @Mat931
 | 
			
		||||
esphome/components/bh1750/* @OttoWinter
 | 
			
		||||
esphome/components/binary_sensor/* @esphome/core
 | 
			
		||||
esphome/components/bk72xx/* @kuba2k2
 | 
			
		||||
@@ -92,6 +94,7 @@ esphome/components/current_based/* @djwmarcx
 | 
			
		||||
esphome/components/dac7678/* @NickB1
 | 
			
		||||
esphome/components/daikin_arc/* @MagicBear
 | 
			
		||||
esphome/components/daikin_brc/* @hagak
 | 
			
		||||
esphome/components/dallas_temp/* @ssieb
 | 
			
		||||
esphome/components/daly_bms/* @s1lvi0
 | 
			
		||||
esphome/components/dashboard_import/* @esphome/core
 | 
			
		||||
esphome/components/datetime/* @jesserockz @rfdarter
 | 
			
		||||
@@ -109,7 +112,10 @@ esphome/components/ee895/* @Stock-M
 | 
			
		||||
esphome/components/ektf2232/touchscreen/* @jesserockz
 | 
			
		||||
esphome/components/emc2101/* @ellull
 | 
			
		||||
esphome/components/emmeti/* @E440QF
 | 
			
		||||
esphome/components/ens160/* @vincentscode
 | 
			
		||||
esphome/components/ens160/* @latonita
 | 
			
		||||
esphome/components/ens160_base/* @latonita @vincentscode
 | 
			
		||||
esphome/components/ens160_i2c/* @latonita
 | 
			
		||||
esphome/components/ens160_spi/* @latonita
 | 
			
		||||
esphome/components/ens210/* @itn3rd77
 | 
			
		||||
esphome/components/esp32/* @esphome/core
 | 
			
		||||
esphome/components/esp32_ble/* @Rapsssito @jesserockz
 | 
			
		||||
@@ -135,9 +141,11 @@ esphome/components/fs3000/* @kahrendt
 | 
			
		||||
esphome/components/ft5x06/* @clydebarrow
 | 
			
		||||
esphome/components/ft63x6/* @gpambrozio
 | 
			
		||||
esphome/components/gcja5/* @gcormier
 | 
			
		||||
esphome/components/gdk101/* @Szewcson
 | 
			
		||||
esphome/components/globals/* @esphome/core
 | 
			
		||||
esphome/components/gp8403/* @jesserockz
 | 
			
		||||
esphome/components/gpio/* @esphome/core
 | 
			
		||||
esphome/components/gpio/one_wire/* @ssieb
 | 
			
		||||
esphome/components/gps/* @coogle
 | 
			
		||||
esphome/components/graph/* @synco
 | 
			
		||||
esphome/components/graphical_display_menu/* @MrMDavidson
 | 
			
		||||
@@ -146,6 +154,10 @@ esphome/components/grove_tb6612fng/* @max246
 | 
			
		||||
esphome/components/growatt_solar/* @leeuwte
 | 
			
		||||
esphome/components/gt911/* @clydebarrow @jesserockz
 | 
			
		||||
esphome/components/haier/* @paveldn
 | 
			
		||||
esphome/components/haier/binary_sensor/* @paveldn
 | 
			
		||||
esphome/components/haier/button/* @paveldn
 | 
			
		||||
esphome/components/haier/sensor/* @paveldn
 | 
			
		||||
esphome/components/haier/text_sensor/* @paveldn
 | 
			
		||||
esphome/components/havells_solar/* @sourabhjaiswal
 | 
			
		||||
esphome/components/hbridge/fan/* @WeekendWarrior
 | 
			
		||||
esphome/components/hbridge/light/* @DotNetDann
 | 
			
		||||
@@ -157,9 +169,12 @@ esphome/components/homeassistant/* @OttoWinter
 | 
			
		||||
esphome/components/honeywell_hih_i2c/* @Benichou34
 | 
			
		||||
esphome/components/honeywellabp/* @RubyBailey
 | 
			
		||||
esphome/components/honeywellabp2_i2c/* @jpfaff
 | 
			
		||||
esphome/components/host/* @esphome/core
 | 
			
		||||
esphome/components/host/* @clydebarrow @esphome/core
 | 
			
		||||
esphome/components/host/time/* @clydebarrow
 | 
			
		||||
esphome/components/hrxl_maxsonar_wr/* @netmikey
 | 
			
		||||
esphome/components/hte501/* @Stock-M
 | 
			
		||||
esphome/components/http_request/ota/* @oarcher
 | 
			
		||||
esphome/components/http_request/update/* @jesserockz
 | 
			
		||||
esphome/components/htu31d/* @betterengineering
 | 
			
		||||
esphome/components/hydreon_rgxx/* @functionpointer
 | 
			
		||||
esphome/components/hyt271/* @Philippe12
 | 
			
		||||
@@ -174,6 +189,9 @@ esphome/components/improv_base/* @esphome/core
 | 
			
		||||
esphome/components/improv_serial/* @esphome/core
 | 
			
		||||
esphome/components/ina226/* @Sergio303 @latonita
 | 
			
		||||
esphome/components/ina260/* @mreditor97
 | 
			
		||||
esphome/components/ina2xx_base/* @latonita
 | 
			
		||||
esphome/components/ina2xx_i2c/* @latonita
 | 
			
		||||
esphome/components/ina2xx_spi/* @latonita
 | 
			
		||||
esphome/components/inkbird_ibsth1_mini/* @fkirill
 | 
			
		||||
esphome/components/inkplate6/* @jesserockz
 | 
			
		||||
esphome/components/integration/* @OttoWinter
 | 
			
		||||
@@ -196,7 +214,8 @@ esphome/components/lightwaverf/* @max246
 | 
			
		||||
esphome/components/lilygo_t5_47/touchscreen/* @jesserockz
 | 
			
		||||
esphome/components/lock/* @esphome/core
 | 
			
		||||
esphome/components/logger/* @esphome/core
 | 
			
		||||
esphome/components/ltr390/* @sjtrny
 | 
			
		||||
esphome/components/ltr390/* @latonita @sjtrny
 | 
			
		||||
esphome/components/ltr_als_ps/* @latonita
 | 
			
		||||
esphome/components/matrix_keypad/* @ssieb
 | 
			
		||||
esphome/components/max31865/* @DAVe3283
 | 
			
		||||
esphome/components/max44009/* @berfenger
 | 
			
		||||
@@ -253,6 +272,7 @@ esphome/components/nextion/text_sensor/* @senexcrenshaw
 | 
			
		||||
esphome/components/nfc/* @jesserockz @kbx81
 | 
			
		||||
esphome/components/noblex/* @AGalfra
 | 
			
		||||
esphome/components/number/* @esphome/core
 | 
			
		||||
esphome/components/one_wire/* @ssieb
 | 
			
		||||
esphome/components/ota/* @esphome/core
 | 
			
		||||
esphome/components/output/* @esphome/core
 | 
			
		||||
esphome/components/pca6416a/* @Mat931
 | 
			
		||||
@@ -297,9 +317,10 @@ esphome/components/rp2040_pwm/* @jesserockz
 | 
			
		||||
esphome/components/rpi_dpi_rgb/* @clydebarrow
 | 
			
		||||
esphome/components/rtl87xx/* @kuba2k2
 | 
			
		||||
esphome/components/rtttl/* @glmnet
 | 
			
		||||
esphome/components/safe_mode/* @jsuanet @paulmonigatti
 | 
			
		||||
esphome/components/safe_mode/* @jsuanet @kbx81 @paulmonigatti
 | 
			
		||||
esphome/components/scd4x/* @martgras @sjtrny
 | 
			
		||||
esphome/components/script/* @esphome/core
 | 
			
		||||
esphome/components/sdl/* @clydebarrow
 | 
			
		||||
esphome/components/sdm_meter/* @jesserockz @polyfaces
 | 
			
		||||
esphome/components/sdp3x/* @Azimath
 | 
			
		||||
esphome/components/seeed_mr24hpc1/* @limengdu
 | 
			
		||||
@@ -394,6 +415,7 @@ esphome/components/uart/button/* @ssieb
 | 
			
		||||
esphome/components/ufire_ec/* @pvizeli
 | 
			
		||||
esphome/components/ufire_ise/* @pvizeli
 | 
			
		||||
esphome/components/ultrasonic/* @OttoWinter
 | 
			
		||||
esphome/components/update/* @jesserockz
 | 
			
		||||
esphome/components/uponor_smatrix/* @kroimon
 | 
			
		||||
esphome/components/valve/* @esphome/core
 | 
			
		||||
esphome/components/vbus/* @ssieb
 | 
			
		||||
@@ -401,7 +423,7 @@ esphome/components/veml3235/* @kbx81
 | 
			
		||||
esphome/components/veml7700/* @latonita
 | 
			
		||||
esphome/components/version/* @esphome/core
 | 
			
		||||
esphome/components/voice_assistant/* @jesserockz
 | 
			
		||||
esphome/components/wake_on_lan/* @willwill2will54
 | 
			
		||||
esphome/components/wake_on_lan/* @clydebarrow @willwill2will54
 | 
			
		||||
esphome/components/waveshare_epaper/* @clydebarrow
 | 
			
		||||
esphome/components/web_server_base/* @OttoWinter
 | 
			
		||||
esphome/components/web_server_idf/* @dentra
 | 
			
		||||
 
 | 
			
		||||
@@ -34,28 +34,32 @@ RUN \
 | 
			
		||||
        python3-wheel=0.38.4-2 \
 | 
			
		||||
        iputils-ping=3:20221126-1 \
 | 
			
		||||
        git=1:2.39.2-1.1 \
 | 
			
		||||
        curl=7.88.1-10+deb12u5 \
 | 
			
		||||
        curl=7.88.1-10+deb12u6 \
 | 
			
		||||
        openssh-client=1:9.2p1-2+deb12u2 \
 | 
			
		||||
        python3-cffi=1.15.1-5 \
 | 
			
		||||
        libcairo2=1.16.0-7 \
 | 
			
		||||
        libmagic1=1:5.44-3 \
 | 
			
		||||
        patch=2.7.6-7; \
 | 
			
		||||
    if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \
 | 
			
		||||
        apt-get install -y --no-install-recommends \
 | 
			
		||||
          build-essential=12.9 \
 | 
			
		||||
          python3-dev=3.11.2-1+b1 \
 | 
			
		||||
          zlib1g-dev=1:1.2.13.dfsg-1 \
 | 
			
		||||
          libjpeg-dev=1:2.1.5-2 \
 | 
			
		||||
          libfreetype-dev=2.12.1+dfsg-5 \
 | 
			
		||||
          libssl-dev=3.0.11-1~deb12u2 \
 | 
			
		||||
          libffi-dev=3.4.4-1 \
 | 
			
		||||
          libopenjp2-7=2.5.0-2 \
 | 
			
		||||
          libtiff6=4.5.0-6+deb12u1 \
 | 
			
		||||
          cargo=0.66.0+ds1-1 \
 | 
			
		||||
          pkg-config=1.8.1-1 \
 | 
			
		||||
          gcc-arm-linux-gnueabihf=4:12.2.0-3; \
 | 
			
		||||
    fi; \
 | 
			
		||||
    rm -rf \
 | 
			
		||||
        patch=2.7.6-7 \
 | 
			
		||||
    && ( \
 | 
			
		||||
        ( \
 | 
			
		||||
            [ "$TARGETARCH$TARGETVARIANT" = "armv7" ] && \
 | 
			
		||||
                apt-get install -y --no-install-recommends \
 | 
			
		||||
                build-essential=12.9 \
 | 
			
		||||
                python3-dev=3.11.2-1+b1 \
 | 
			
		||||
                zlib1g-dev=1:1.2.13.dfsg-1 \
 | 
			
		||||
                libjpeg-dev=1:2.1.5-2 \
 | 
			
		||||
                libfreetype-dev=2.12.1+dfsg-5+deb12u3 \
 | 
			
		||||
                libssl-dev=3.0.13-1~deb12u1 \
 | 
			
		||||
                libffi-dev=3.4.4-1 \
 | 
			
		||||
                libopenjp2-7=2.5.0-2 \
 | 
			
		||||
                libtiff6=4.5.0-6+deb12u1 \
 | 
			
		||||
                cargo=0.66.0+ds1-1 \
 | 
			
		||||
                pkg-config=1.8.1-1 \
 | 
			
		||||
                gcc-arm-linux-gnueabihf=4:12.2.0-3 \
 | 
			
		||||
        ) \
 | 
			
		||||
        || [ "$TARGETARCH$TARGETVARIANT" != "armv7" ] \
 | 
			
		||||
    ) \
 | 
			
		||||
    && rm -rf \
 | 
			
		||||
        /tmp/* \
 | 
			
		||||
        /var/{cache,log}/* \
 | 
			
		||||
        /var/lib/apt/lists/*
 | 
			
		||||
@@ -81,7 +85,8 @@ RUN \
 | 
			
		||||
    fi; \
 | 
			
		||||
    pip3 install \
 | 
			
		||||
    --break-system-packages --no-cache-dir \
 | 
			
		||||
    platformio==6.1.13 \
 | 
			
		||||
    # Keep platformio version in sync with requirements.txt
 | 
			
		||||
    platformio==6.1.15 \
 | 
			
		||||
    # Change some platformio settings
 | 
			
		||||
    && platformio settings set enable_telemetry No \
 | 
			
		||||
    && platformio settings set check_platformio_interval 1000000 \
 | 
			
		||||
@@ -100,6 +105,9 @@ RUN --mount=type=tmpfs,target=/root/.cargo if [ "$TARGETARCH$TARGETVARIANT" = "a
 | 
			
		||||
    --break-system-packages --no-cache-dir -r /requirements.txt -r /requirements_optional.txt \
 | 
			
		||||
    && /platformio_install_deps.py /platformio.ini --libraries
 | 
			
		||||
 | 
			
		||||
# Avoid unsafe git error when container user and file config volume permissions don't match
 | 
			
		||||
RUN git config --system --add safe.directory '*'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# ======================= docker-type image =======================
 | 
			
		||||
FROM base AS docker
 | 
			
		||||
@@ -110,7 +118,7 @@ RUN if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \
 | 
			
		||||
        export PIP_EXTRA_INDEX_URL="https://www.piwheels.org/simple"; \
 | 
			
		||||
  fi; \
 | 
			
		||||
  pip3 install \
 | 
			
		||||
  --break-system-packages --no-cache-dir --no-use-pep517 -e /esphome
 | 
			
		||||
  --break-system-packages --no-cache-dir -e /esphome
 | 
			
		||||
 | 
			
		||||
# Settings for dashboard
 | 
			
		||||
ENV USERNAME="" PASSWORD=""
 | 
			
		||||
@@ -160,7 +168,7 @@ RUN if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \
 | 
			
		||||
        export PIP_EXTRA_INDEX_URL="https://www.piwheels.org/simple"; \
 | 
			
		||||
  fi; \
 | 
			
		||||
  pip3 install \
 | 
			
		||||
  --break-system-packages --no-cache-dir --no-use-pep517 -e /esphome
 | 
			
		||||
  --break-system-packages --no-cache-dir -e /esphome
 | 
			
		||||
 | 
			
		||||
# Labels
 | 
			
		||||
LABEL \
 | 
			
		||||
@@ -186,8 +194,8 @@ RUN \
 | 
			
		||||
        clang-format-13=1:13.0.1-11+b2 \
 | 
			
		||||
        clang-tidy-14=1:14.0.6-12 \
 | 
			
		||||
        patch=2.7.6-7 \
 | 
			
		||||
        software-properties-common=0.99.30-4 \
 | 
			
		||||
        nano=7.2-1 \
 | 
			
		||||
        software-properties-common=0.99.30-4.1~deb12u1 \
 | 
			
		||||
        nano=7.2-1+deb12u1 \
 | 
			
		||||
        build-essential=12.9 \
 | 
			
		||||
        python3-dev=3.11.2-1+b1 \
 | 
			
		||||
    && rm -rf \
 | 
			
		||||
 
 | 
			
		||||
@@ -18,22 +18,23 @@ from esphome.const import (
 | 
			
		||||
    CONF_BAUD_RATE,
 | 
			
		||||
    CONF_BROKER,
 | 
			
		||||
    CONF_DEASSERT_RTS_DTR,
 | 
			
		||||
    CONF_DISABLED,
 | 
			
		||||
    CONF_ESPHOME,
 | 
			
		||||
    CONF_LOGGER,
 | 
			
		||||
    CONF_MDNS,
 | 
			
		||||
    CONF_MQTT,
 | 
			
		||||
    CONF_NAME,
 | 
			
		||||
    CONF_OTA,
 | 
			
		||||
    CONF_MQTT,
 | 
			
		||||
    CONF_MDNS,
 | 
			
		||||
    CONF_DISABLED,
 | 
			
		||||
    CONF_PASSWORD,
 | 
			
		||||
    CONF_PORT,
 | 
			
		||||
    CONF_ESPHOME,
 | 
			
		||||
    CONF_PLATFORM,
 | 
			
		||||
    CONF_PLATFORMIO_OPTIONS,
 | 
			
		||||
    CONF_PORT,
 | 
			
		||||
    CONF_SUBSTITUTIONS,
 | 
			
		||||
    PLATFORM_BK72XX,
 | 
			
		||||
    PLATFORM_RTL87XX,
 | 
			
		||||
    PLATFORM_ESP32,
 | 
			
		||||
    PLATFORM_ESP8266,
 | 
			
		||||
    PLATFORM_RP2040,
 | 
			
		||||
    PLATFORM_RTL87XX,
 | 
			
		||||
    SECRETS_FILES,
 | 
			
		||||
)
 | 
			
		||||
from esphome.core import CORE, EsphomeError, coroutine
 | 
			
		||||
@@ -65,7 +66,7 @@ def choose_prompt(options, purpose: str = None):
 | 
			
		||||
        f'Found multiple options{f" for {purpose}" if purpose else ""}, please choose one:'
 | 
			
		||||
    )
 | 
			
		||||
    for i, (desc, _) in enumerate(options):
 | 
			
		||||
        safe_print(f"  [{i+1}] {desc}")
 | 
			
		||||
        safe_print(f"  [{i + 1}] {desc}")
 | 
			
		||||
 | 
			
		||||
    while True:
 | 
			
		||||
        opt = input("(number): ")
 | 
			
		||||
@@ -330,15 +331,19 @@ def upload_program(config, args, host):
 | 
			
		||||
 | 
			
		||||
        return 1  # Unknown target platform
 | 
			
		||||
 | 
			
		||||
    if CONF_OTA not in config:
 | 
			
		||||
    ota_conf = {}
 | 
			
		||||
    for ota_item in config.get(CONF_OTA, []):
 | 
			
		||||
        if ota_item[CONF_PLATFORM] == CONF_ESPHOME:
 | 
			
		||||
            ota_conf = ota_item
 | 
			
		||||
            break
 | 
			
		||||
 | 
			
		||||
    if not ota_conf:
 | 
			
		||||
        raise EsphomeError(
 | 
			
		||||
            "Cannot upload Over the Air as the config does not include the ota: "
 | 
			
		||||
            "component"
 | 
			
		||||
            f"Cannot upload Over the Air as the {CONF_OTA} configuration is not present or does not include {CONF_PLATFORM}: {CONF_ESPHOME}"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    from esphome import espota2
 | 
			
		||||
 | 
			
		||||
    ota_conf = config[CONF_OTA]
 | 
			
		||||
    remote_port = ota_conf[CONF_PORT]
 | 
			
		||||
    password = ota_conf.get(CONF_PASSWORD, "")
 | 
			
		||||
 | 
			
		||||
@@ -483,6 +488,15 @@ def command_run(args, config):
 | 
			
		||||
    if exit_code != 0:
 | 
			
		||||
        return exit_code
 | 
			
		||||
    _LOGGER.info("Successfully compiled program.")
 | 
			
		||||
    if CORE.is_host:
 | 
			
		||||
        from esphome.platformio_api import get_idedata
 | 
			
		||||
 | 
			
		||||
        idedata = get_idedata(config)
 | 
			
		||||
        if idedata is None:
 | 
			
		||||
            return 1
 | 
			
		||||
        program_path = idedata.raw["prog_path"]
 | 
			
		||||
        return run_external_process(program_path)
 | 
			
		||||
 | 
			
		||||
    port = choose_upload_log_host(
 | 
			
		||||
        default=args.device,
 | 
			
		||||
        check_default=None,
 | 
			
		||||
@@ -681,7 +695,8 @@ def command_rename(args, config):
 | 
			
		||||
        os.remove(new_path)
 | 
			
		||||
        return 1
 | 
			
		||||
 | 
			
		||||
    os.remove(CORE.config_path)
 | 
			
		||||
    if CORE.config_path != new_path:
 | 
			
		||||
        os.remove(CORE.config_path)
 | 
			
		||||
 | 
			
		||||
    print(color(Fore.BOLD_GREEN, "SUCCESS"))
 | 
			
		||||
    print()
 | 
			
		||||
 
 | 
			
		||||
@@ -58,7 +58,9 @@ from esphome.cpp_types import (  # noqa
 | 
			
		||||
    bool_,
 | 
			
		||||
    int_,
 | 
			
		||||
    std_ns,
 | 
			
		||||
    std_shared_ptr,
 | 
			
		||||
    std_string,
 | 
			
		||||
    std_string_ref,
 | 
			
		||||
    std_vector,
 | 
			
		||||
    uint8,
 | 
			
		||||
    uint16,
 | 
			
		||||
 
 | 
			
		||||
@@ -4,11 +4,11 @@ from esphome.const import (
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    ICON_ARROW_EXPAND_VERTICAL,
 | 
			
		||||
    DEVICE_CLASS_DISTANCE,
 | 
			
		||||
    UNIT_MILLIMETER,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ["@TH-Braemer"]
 | 
			
		||||
DEPENDENCIES = ["uart"]
 | 
			
		||||
UNIT_MILLIMETERS = "mm"
 | 
			
		||||
 | 
			
		||||
a02yyuw_ns = cg.esphome_ns.namespace("a02yyuw")
 | 
			
		||||
A02yyuwComponent = a02yyuw_ns.class_(
 | 
			
		||||
@@ -17,7 +17,7 @@ A02yyuwComponent = a02yyuw_ns.class_(
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = sensor.sensor_schema(
 | 
			
		||||
    A02yyuwComponent,
 | 
			
		||||
    unit_of_measurement=UNIT_MILLIMETERS,
 | 
			
		||||
    unit_of_measurement=UNIT_MILLIMETER,
 | 
			
		||||
    icon=ICON_ARROW_EXPAND_VERTICAL,
 | 
			
		||||
    accuracy_decimals=0,
 | 
			
		||||
    state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
 
 | 
			
		||||
@@ -46,27 +46,27 @@ extern "C"
 | 
			
		||||
    ADCSensor::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
 | 
			
		||||
#if !defined(USE_ADC_SENSOR_VCC) && !defined(USE_RP2040)
 | 
			
		||||
  pin_->setup();
 | 
			
		||||
  this->pin_->setup();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
  if (channel1_ != ADC1_CHANNEL_MAX) {
 | 
			
		||||
  if (this->channel1_ != ADC1_CHANNEL_MAX) {
 | 
			
		||||
    adc1_config_width(ADC_WIDTH_MAX_SOC_BITS);
 | 
			
		||||
    if (!autorange_) {
 | 
			
		||||
      adc1_config_channel_atten(channel1_, attenuation_);
 | 
			
		||||
    if (!this->autorange_) {
 | 
			
		||||
      adc1_config_channel_atten(this->channel1_, this->attenuation_);
 | 
			
		||||
    }
 | 
			
		||||
  } else if (channel2_ != ADC2_CHANNEL_MAX) {
 | 
			
		||||
    if (!autorange_) {
 | 
			
		||||
      adc2_config_channel_atten(channel2_, attenuation_);
 | 
			
		||||
  } else if (this->channel2_ != ADC2_CHANNEL_MAX) {
 | 
			
		||||
    if (!this->autorange_) {
 | 
			
		||||
      adc2_config_channel_atten(this->channel2_, this->attenuation_);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // load characteristics for each attenuation
 | 
			
		||||
  for (int32_t i = 0; i <= ADC_ATTEN_DB_12_COMPAT; i++) {
 | 
			
		||||
    auto adc_unit = channel1_ != ADC1_CHANNEL_MAX ? ADC_UNIT_1 : ADC_UNIT_2;
 | 
			
		||||
    auto adc_unit = this->channel1_ != ADC1_CHANNEL_MAX ? ADC_UNIT_1 : ADC_UNIT_2;
 | 
			
		||||
    auto cal_value = esp_adc_cal_characterize(adc_unit, (adc_atten_t) i, ADC_WIDTH_MAX_SOC_BITS,
 | 
			
		||||
                                              1100,  // default vref
 | 
			
		||||
                                              &cal_characteristics_[i]);
 | 
			
		||||
                                              &this->cal_characteristics_[i]);
 | 
			
		||||
    switch (cal_value) {
 | 
			
		||||
      case ESP_ADC_CAL_VAL_EFUSE_VREF:
 | 
			
		||||
        ESP_LOGV(TAG, "Using eFuse Vref for calibration");
 | 
			
		||||
@@ -99,27 +99,27 @@ void ADCSensor::dump_config() {
 | 
			
		||||
#ifdef USE_ADC_SENSOR_VCC
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Pin: VCC");
 | 
			
		||||
#else
 | 
			
		||||
  LOG_PIN("  Pin: ", pin_);
 | 
			
		||||
  LOG_PIN("  Pin: ", this->pin_);
 | 
			
		||||
#endif
 | 
			
		||||
#endif  // USE_ESP8266 || USE_LIBRETINY
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
  LOG_PIN("  Pin: ", pin_);
 | 
			
		||||
  if (autorange_) {
 | 
			
		||||
    ESP_LOGCONFIG(TAG, " Attenuation: auto");
 | 
			
		||||
  LOG_PIN("  Pin: ", this->pin_);
 | 
			
		||||
  if (this->autorange_) {
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  Attenuation: auto");
 | 
			
		||||
  } else {
 | 
			
		||||
    switch (this->attenuation_) {
 | 
			
		||||
      case ADC_ATTEN_DB_0:
 | 
			
		||||
        ESP_LOGCONFIG(TAG, " Attenuation: 0db");
 | 
			
		||||
        ESP_LOGCONFIG(TAG, "  Attenuation: 0db");
 | 
			
		||||
        break;
 | 
			
		||||
      case ADC_ATTEN_DB_2_5:
 | 
			
		||||
        ESP_LOGCONFIG(TAG, " Attenuation: 2.5db");
 | 
			
		||||
        ESP_LOGCONFIG(TAG, "  Attenuation: 2.5db");
 | 
			
		||||
        break;
 | 
			
		||||
      case ADC_ATTEN_DB_6:
 | 
			
		||||
        ESP_LOGCONFIG(TAG, " Attenuation: 6db");
 | 
			
		||||
        ESP_LOGCONFIG(TAG, "  Attenuation: 6db");
 | 
			
		||||
        break;
 | 
			
		||||
      case ADC_ATTEN_DB_12_COMPAT:
 | 
			
		||||
        ESP_LOGCONFIG(TAG, " Attenuation: 12db");
 | 
			
		||||
        ESP_LOGCONFIG(TAG, "  Attenuation: 12db");
 | 
			
		||||
        break;
 | 
			
		||||
      default:  // This is to satisfy the unused ADC_ATTEN_MAX
 | 
			
		||||
        break;
 | 
			
		||||
@@ -134,11 +134,11 @@ void ADCSensor::dump_config() {
 | 
			
		||||
#ifdef USE_ADC_SENSOR_VCC
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  Pin: VCC");
 | 
			
		||||
#else
 | 
			
		||||
    LOG_PIN("  Pin: ", pin_);
 | 
			
		||||
    LOG_PIN("  Pin: ", this->pin_);
 | 
			
		||||
#endif  // USE_ADC_SENSOR_VCC
 | 
			
		||||
  }
 | 
			
		||||
#endif  // USE_RP2040
 | 
			
		||||
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Samples: %i", this->sample_count_);
 | 
			
		||||
  LOG_UPDATE_INTERVAL(this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -149,14 +149,24 @@ void ADCSensor::update() {
 | 
			
		||||
  this->publish_state(value_v);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ADCSensor::set_sample_count(uint8_t sample_count) {
 | 
			
		||||
  if (sample_count != 0) {
 | 
			
		||||
    this->sample_count_ = sample_count;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP8266
 | 
			
		||||
float ADCSensor::sample() {
 | 
			
		||||
  uint32_t raw = 0;
 | 
			
		||||
  for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
 | 
			
		||||
#ifdef USE_ADC_SENSOR_VCC
 | 
			
		||||
  int32_t raw = ESP.getVcc();  // NOLINT(readability-static-accessed-through-instance)
 | 
			
		||||
    raw += ESP.getVcc();  // NOLINT(readability-static-accessed-through-instance)
 | 
			
		||||
#else
 | 
			
		||||
  int32_t raw = analogRead(this->pin_->get_pin());  // NOLINT
 | 
			
		||||
    raw += analogRead(this->pin_->get_pin());  // NOLINT
 | 
			
		||||
#endif
 | 
			
		||||
  if (output_raw_) {
 | 
			
		||||
  }
 | 
			
		||||
  raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_;  // NOLINT(clang-analyzer-core.DivideZero)
 | 
			
		||||
  if (this->output_raw_) {
 | 
			
		||||
    return raw;
 | 
			
		||||
  }
 | 
			
		||||
  return raw / 1024.0f;
 | 
			
		||||
@@ -165,53 +175,57 @@ float ADCSensor::sample() {
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
float ADCSensor::sample() {
 | 
			
		||||
  if (!autorange_) {
 | 
			
		||||
    int raw = -1;
 | 
			
		||||
    if (channel1_ != ADC1_CHANNEL_MAX) {
 | 
			
		||||
      raw = adc1_get_raw(channel1_);
 | 
			
		||||
    } else if (channel2_ != ADC2_CHANNEL_MAX) {
 | 
			
		||||
      adc2_get_raw(channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw);
 | 
			
		||||
  if (!this->autorange_) {
 | 
			
		||||
    uint32_t sum = 0;
 | 
			
		||||
    for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
 | 
			
		||||
      int raw = -1;
 | 
			
		||||
      if (this->channel1_ != ADC1_CHANNEL_MAX) {
 | 
			
		||||
        raw = adc1_get_raw(this->channel1_);
 | 
			
		||||
      } else if (this->channel2_ != ADC2_CHANNEL_MAX) {
 | 
			
		||||
        adc2_get_raw(this->channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw);
 | 
			
		||||
      }
 | 
			
		||||
      if (raw == -1) {
 | 
			
		||||
        return NAN;
 | 
			
		||||
      }
 | 
			
		||||
      sum += raw;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (raw == -1) {
 | 
			
		||||
      return NAN;
 | 
			
		||||
    sum = (sum + (this->sample_count_ >> 1)) / this->sample_count_;  // NOLINT(clang-analyzer-core.DivideZero)
 | 
			
		||||
    if (this->output_raw_) {
 | 
			
		||||
      return sum;
 | 
			
		||||
    }
 | 
			
		||||
    if (output_raw_) {
 | 
			
		||||
      return raw;
 | 
			
		||||
    }
 | 
			
		||||
    uint32_t mv = esp_adc_cal_raw_to_voltage(raw, &cal_characteristics_[(int32_t) attenuation_]);
 | 
			
		||||
    uint32_t mv = esp_adc_cal_raw_to_voltage(sum, &this->cal_characteristics_[(int32_t) this->attenuation_]);
 | 
			
		||||
    return mv / 1000.0f;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  int raw12 = ADC_MAX, raw6 = ADC_MAX, raw2 = ADC_MAX, raw0 = ADC_MAX;
 | 
			
		||||
 | 
			
		||||
  if (channel1_ != ADC1_CHANNEL_MAX) {
 | 
			
		||||
    adc1_config_channel_atten(channel1_, ADC_ATTEN_DB_12_COMPAT);
 | 
			
		||||
    raw12 = adc1_get_raw(channel1_);
 | 
			
		||||
  if (this->channel1_ != ADC1_CHANNEL_MAX) {
 | 
			
		||||
    adc1_config_channel_atten(this->channel1_, ADC_ATTEN_DB_12_COMPAT);
 | 
			
		||||
    raw12 = adc1_get_raw(this->channel1_);
 | 
			
		||||
    if (raw12 < ADC_MAX) {
 | 
			
		||||
      adc1_config_channel_atten(channel1_, ADC_ATTEN_DB_6);
 | 
			
		||||
      raw6 = adc1_get_raw(channel1_);
 | 
			
		||||
      adc1_config_channel_atten(this->channel1_, ADC_ATTEN_DB_6);
 | 
			
		||||
      raw6 = adc1_get_raw(this->channel1_);
 | 
			
		||||
      if (raw6 < ADC_MAX) {
 | 
			
		||||
        adc1_config_channel_atten(channel1_, ADC_ATTEN_DB_2_5);
 | 
			
		||||
        raw2 = adc1_get_raw(channel1_);
 | 
			
		||||
        adc1_config_channel_atten(this->channel1_, ADC_ATTEN_DB_2_5);
 | 
			
		||||
        raw2 = adc1_get_raw(this->channel1_);
 | 
			
		||||
        if (raw2 < ADC_MAX) {
 | 
			
		||||
          adc1_config_channel_atten(channel1_, ADC_ATTEN_DB_0);
 | 
			
		||||
          raw0 = adc1_get_raw(channel1_);
 | 
			
		||||
          adc1_config_channel_atten(this->channel1_, ADC_ATTEN_DB_0);
 | 
			
		||||
          raw0 = adc1_get_raw(this->channel1_);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  } else if (channel2_ != ADC2_CHANNEL_MAX) {
 | 
			
		||||
    adc2_config_channel_atten(channel2_, ADC_ATTEN_DB_12_COMPAT);
 | 
			
		||||
    adc2_get_raw(channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw12);
 | 
			
		||||
  } else if (this->channel2_ != ADC2_CHANNEL_MAX) {
 | 
			
		||||
    adc2_config_channel_atten(this->channel2_, ADC_ATTEN_DB_12_COMPAT);
 | 
			
		||||
    adc2_get_raw(this->channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw12);
 | 
			
		||||
    if (raw12 < ADC_MAX) {
 | 
			
		||||
      adc2_config_channel_atten(channel2_, ADC_ATTEN_DB_6);
 | 
			
		||||
      adc2_get_raw(channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw6);
 | 
			
		||||
      adc2_config_channel_atten(this->channel2_, ADC_ATTEN_DB_6);
 | 
			
		||||
      adc2_get_raw(this->channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw6);
 | 
			
		||||
      if (raw6 < ADC_MAX) {
 | 
			
		||||
        adc2_config_channel_atten(channel2_, ADC_ATTEN_DB_2_5);
 | 
			
		||||
        adc2_get_raw(channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw2);
 | 
			
		||||
        adc2_config_channel_atten(this->channel2_, ADC_ATTEN_DB_2_5);
 | 
			
		||||
        adc2_get_raw(this->channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw2);
 | 
			
		||||
        if (raw2 < ADC_MAX) {
 | 
			
		||||
          adc2_config_channel_atten(channel2_, ADC_ATTEN_DB_0);
 | 
			
		||||
          adc2_get_raw(channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw0);
 | 
			
		||||
          adc2_config_channel_atten(this->channel2_, ADC_ATTEN_DB_0);
 | 
			
		||||
          adc2_get_raw(this->channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw0);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
@@ -221,10 +235,10 @@ float ADCSensor::sample() {
 | 
			
		||||
    return NAN;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint32_t mv12 = esp_adc_cal_raw_to_voltage(raw12, &cal_characteristics_[(int32_t) ADC_ATTEN_DB_12_COMPAT]);
 | 
			
		||||
  uint32_t mv6 = esp_adc_cal_raw_to_voltage(raw6, &cal_characteristics_[(int32_t) ADC_ATTEN_DB_6]);
 | 
			
		||||
  uint32_t mv2 = esp_adc_cal_raw_to_voltage(raw2, &cal_characteristics_[(int32_t) ADC_ATTEN_DB_2_5]);
 | 
			
		||||
  uint32_t mv0 = esp_adc_cal_raw_to_voltage(raw0, &cal_characteristics_[(int32_t) ADC_ATTEN_DB_0]);
 | 
			
		||||
  uint32_t mv12 = esp_adc_cal_raw_to_voltage(raw12, &this->cal_characteristics_[(int32_t) ADC_ATTEN_DB_12_COMPAT]);
 | 
			
		||||
  uint32_t mv6 = esp_adc_cal_raw_to_voltage(raw6, &this->cal_characteristics_[(int32_t) ADC_ATTEN_DB_6]);
 | 
			
		||||
  uint32_t mv2 = esp_adc_cal_raw_to_voltage(raw2, &this->cal_characteristics_[(int32_t) ADC_ATTEN_DB_2_5]);
 | 
			
		||||
  uint32_t mv0 = esp_adc_cal_raw_to_voltage(raw0, &this->cal_characteristics_[(int32_t) ADC_ATTEN_DB_0]);
 | 
			
		||||
 | 
			
		||||
  // Contribution of each value, in range 0-2048 (12 bit ADC) or 0-4096 (13 bit ADC)
 | 
			
		||||
  uint32_t c12 = std::min(raw12, ADC_HALF);
 | 
			
		||||
@@ -246,8 +260,11 @@ float ADCSensor::sample() {
 | 
			
		||||
    adc_set_temp_sensor_enabled(true);
 | 
			
		||||
    delay(1);
 | 
			
		||||
    adc_select_input(4);
 | 
			
		||||
 | 
			
		||||
    int32_t raw = adc_read();
 | 
			
		||||
    uint32_t raw = 0;
 | 
			
		||||
    for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
 | 
			
		||||
      raw += adc_read();
 | 
			
		||||
    }
 | 
			
		||||
    raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_;  // NOLINT(clang-analyzer-core.DivideZero)
 | 
			
		||||
    adc_set_temp_sensor_enabled(false);
 | 
			
		||||
    if (this->output_raw_) {
 | 
			
		||||
      return raw;
 | 
			
		||||
@@ -268,7 +285,11 @@ float ADCSensor::sample() {
 | 
			
		||||
    adc_gpio_init(pin);
 | 
			
		||||
    adc_select_input(pin - 26);
 | 
			
		||||
 | 
			
		||||
    int32_t raw = adc_read();
 | 
			
		||||
    uint32_t raw = 0;
 | 
			
		||||
    for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
 | 
			
		||||
      raw += adc_read();
 | 
			
		||||
    }
 | 
			
		||||
    raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_;  // NOLINT(clang-analyzer-core.DivideZero)
 | 
			
		||||
 | 
			
		||||
#ifdef CYW43_USES_VSYS_PIN
 | 
			
		||||
    if (pin == PICO_VSYS_PIN) {
 | 
			
		||||
@@ -276,7 +297,7 @@ float ADCSensor::sample() {
 | 
			
		||||
    }
 | 
			
		||||
#endif  // CYW43_USES_VSYS_PIN
 | 
			
		||||
 | 
			
		||||
    if (output_raw_) {
 | 
			
		||||
    if (this->output_raw_) {
 | 
			
		||||
      return raw;
 | 
			
		||||
    }
 | 
			
		||||
    float coeff = pin == PICO_VSYS_PIN ? 3.0 : 1.0;
 | 
			
		||||
@@ -287,10 +308,19 @@ float ADCSensor::sample() {
 | 
			
		||||
 | 
			
		||||
#ifdef USE_LIBRETINY
 | 
			
		||||
float ADCSensor::sample() {
 | 
			
		||||
  if (output_raw_) {
 | 
			
		||||
    return analogRead(this->pin_->get_pin());  // NOLINT
 | 
			
		||||
  uint32_t raw = 0;
 | 
			
		||||
  if (this->output_raw_) {
 | 
			
		||||
    for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
 | 
			
		||||
      raw += analogRead(this->pin_->get_pin());  // NOLINT
 | 
			
		||||
    }
 | 
			
		||||
    raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_;  // NOLINT(clang-analyzer-core.DivideZero)
 | 
			
		||||
    return raw;
 | 
			
		||||
  }
 | 
			
		||||
  return analogReadVoltage(this->pin_->get_pin()) / 1000.0f;  // NOLINT
 | 
			
		||||
  for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
 | 
			
		||||
    raw += analogReadVoltage(this->pin_->get_pin());  // NOLINT
 | 
			
		||||
  }
 | 
			
		||||
  raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_;  // NOLINT(clang-analyzer-core.DivideZero)
 | 
			
		||||
  return raw / 1000.0f;
 | 
			
		||||
}
 | 
			
		||||
#endif  // USE_LIBRETINY
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -33,16 +33,16 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
 | 
			
		||||
 public:
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
  /// Set the attenuation for this pin. Only available on the ESP32.
 | 
			
		||||
  void set_attenuation(adc_atten_t attenuation) { attenuation_ = attenuation; }
 | 
			
		||||
  void set_attenuation(adc_atten_t attenuation) { this->attenuation_ = attenuation; }
 | 
			
		||||
  void set_channel1(adc1_channel_t channel) {
 | 
			
		||||
    channel1_ = channel;
 | 
			
		||||
    channel2_ = ADC2_CHANNEL_MAX;
 | 
			
		||||
    this->channel1_ = channel;
 | 
			
		||||
    this->channel2_ = ADC2_CHANNEL_MAX;
 | 
			
		||||
  }
 | 
			
		||||
  void set_channel2(adc2_channel_t channel) {
 | 
			
		||||
    channel2_ = channel;
 | 
			
		||||
    channel1_ = ADC1_CHANNEL_MAX;
 | 
			
		||||
    this->channel2_ = channel;
 | 
			
		||||
    this->channel1_ = ADC1_CHANNEL_MAX;
 | 
			
		||||
  }
 | 
			
		||||
  void set_autorange(bool autorange) { autorange_ = autorange; }
 | 
			
		||||
  void set_autorange(bool autorange) { this->autorange_ = autorange; }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  /// Update ADC values
 | 
			
		||||
@@ -53,7 +53,8 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
 | 
			
		||||
  /// `HARDWARE_LATE` setup priority
 | 
			
		||||
  float get_setup_priority() const override;
 | 
			
		||||
  void set_pin(InternalGPIOPin *pin) { this->pin_ = pin; }
 | 
			
		||||
  void set_output_raw(bool output_raw) { output_raw_ = output_raw; }
 | 
			
		||||
  void set_output_raw(bool output_raw) { this->output_raw_ = output_raw; }
 | 
			
		||||
  void set_sample_count(uint8_t sample_count);
 | 
			
		||||
  float sample() override;
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP8266
 | 
			
		||||
@@ -61,12 +62,13 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_RP2040
 | 
			
		||||
  void set_is_temperature() { is_temperature_ = true; }
 | 
			
		||||
  void set_is_temperature() { this->is_temperature_ = true; }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  InternalGPIOPin *pin_;
 | 
			
		||||
  bool output_raw_{false};
 | 
			
		||||
  uint8_t sample_count_{1};
 | 
			
		||||
 | 
			
		||||
#ifdef USE_RP2040
 | 
			
		||||
  bool is_temperature_{false};
 | 
			
		||||
 
 | 
			
		||||
@@ -29,6 +29,8 @@ _LOGGER = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
AUTO_LOAD = ["voltage_sampler"]
 | 
			
		||||
 | 
			
		||||
CONF_SAMPLES = "samples"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
_attenuation = cv.enum(ATTENUATION_MODES, lower=True)
 | 
			
		||||
 | 
			
		||||
@@ -37,6 +39,10 @@ def validate_config(config):
 | 
			
		||||
    if config[CONF_RAW] and config.get(CONF_ATTENUATION, None) == "auto":
 | 
			
		||||
        raise cv.Invalid("Automatic attenuation cannot be used when raw output is set")
 | 
			
		||||
 | 
			
		||||
    if config.get(CONF_ATTENUATION, None) == "auto" and config.get(CONF_SAMPLES, 1) > 1:
 | 
			
		||||
        raise cv.Invalid(
 | 
			
		||||
            "Automatic attenuation cannot be used when multisampling is set"
 | 
			
		||||
        )
 | 
			
		||||
    if config.get(CONF_ATTENUATION) == "11db":
 | 
			
		||||
        _LOGGER.warning(
 | 
			
		||||
            "`attenuation: 11db` is deprecated, use `attenuation: 12db` instead"
 | 
			
		||||
@@ -81,6 +87,7 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
            cv.SplitDefault(CONF_ATTENUATION, esp32="0db"): cv.All(
 | 
			
		||||
                cv.only_on_esp32, _attenuation
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_SAMPLES, default=1): cv.int_range(min=1, max=255),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
    .extend(cv.polling_component_schema("60s")),
 | 
			
		||||
@@ -104,6 +111,7 @@ async def to_code(config):
 | 
			
		||||
        cg.add(var.set_pin(pin))
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_output_raw(config[CONF_RAW]))
 | 
			
		||||
    cg.add(var.set_sample_count(config[CONF_SAMPLES]))
 | 
			
		||||
 | 
			
		||||
    if attenuation := config.get(CONF_ATTENUATION):
 | 
			
		||||
        if attenuation == "auto":
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,8 @@
 | 
			
		||||
#include "ade7880_registers.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
#include <cinttypes>
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace ade7880 {
 | 
			
		||||
 | 
			
		||||
@@ -156,7 +158,7 @@ void ADE7880::update() {
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ESP_LOGD(TAG, "update took %u ms", millis() - start);
 | 
			
		||||
  ESP_LOGD(TAG, "update took %" PRIu32 " ms", millis() - start);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ADE7880::dump_config() {
 | 
			
		||||
@@ -176,9 +178,9 @@ void ADE7880::dump_config() {
 | 
			
		||||
    LOG_SENSOR("    ", "Forward Active Energy", this->channel_a_->forward_active_energy);
 | 
			
		||||
    LOG_SENSOR("    ", "Reverse Active Energy", this->channel_a_->reverse_active_energy);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "    Calibration:");
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "     Current: %u", this->channel_a_->current_gain_calibration);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "     Voltage: %d", this->channel_a_->voltage_gain_calibration);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "     Power: %d", this->channel_a_->power_gain_calibration);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "     Current: %" PRId32, this->channel_a_->current_gain_calibration);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "     Voltage: %" PRId32, this->channel_a_->voltage_gain_calibration);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "     Power: %" PRId32, this->channel_a_->power_gain_calibration);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "     Phase Angle: %u", this->channel_a_->phase_angle_calibration);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -192,9 +194,9 @@ void ADE7880::dump_config() {
 | 
			
		||||
    LOG_SENSOR("    ", "Forward Active Energy", this->channel_b_->forward_active_energy);
 | 
			
		||||
    LOG_SENSOR("    ", "Reverse Active Energy", this->channel_b_->reverse_active_energy);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "    Calibration:");
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "     Current: %u", this->channel_b_->current_gain_calibration);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "     Voltage: %d", this->channel_b_->voltage_gain_calibration);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "     Power: %d", this->channel_b_->power_gain_calibration);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "     Current: %" PRId32, this->channel_b_->current_gain_calibration);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "     Voltage: %" PRId32, this->channel_b_->voltage_gain_calibration);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "     Power: %" PRId32, this->channel_b_->power_gain_calibration);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "     Phase Angle: %u", this->channel_b_->phase_angle_calibration);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -208,9 +210,9 @@ void ADE7880::dump_config() {
 | 
			
		||||
    LOG_SENSOR("    ", "Forward Active Energy", this->channel_c_->forward_active_energy);
 | 
			
		||||
    LOG_SENSOR("    ", "Reverse Active Energy", this->channel_c_->reverse_active_energy);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "    Calibration:");
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "     Current: %u", this->channel_c_->current_gain_calibration);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "     Voltage: %d", this->channel_c_->voltage_gain_calibration);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "     Power: %d", this->channel_c_->power_gain_calibration);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "     Current: %" PRId32, this->channel_c_->current_gain_calibration);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "     Voltage: %" PRId32, this->channel_c_->voltage_gain_calibration);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "     Power: %" PRId32, this->channel_c_->power_gain_calibration);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "     Phase Angle: %u", this->channel_c_->phase_angle_calibration);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -218,7 +220,7 @@ void ADE7880::dump_config() {
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  Neutral:");
 | 
			
		||||
    LOG_SENSOR("    ", "Current", this->channel_n_->current);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "    Calibration:");
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "     Current: %u", this->channel_n_->current_gain_calibration);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "     Current: %" PRId32, this->channel_n_->current_gain_calibration);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  LOG_I2C_DEVICE(this);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,8 @@
 | 
			
		||||
#include "ade7953_base.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
#include <cinttypes>
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace ade7953_base {
 | 
			
		||||
 | 
			
		||||
@@ -105,7 +107,7 @@ void ADE7953::update() {
 | 
			
		||||
    this->last_update_ = now;
 | 
			
		||||
    // prevent DIV/0
 | 
			
		||||
    pf = ADE_WATTSEC_POWER_FACTOR * (diff < 10 ? 10 : diff) / 1000;
 | 
			
		||||
    ESP_LOGVV(TAG, "ADE7953::update() diff=%d pf=%f", diff, pf);
 | 
			
		||||
    ESP_LOGVV(TAG, "ADE7953::update() diff=%" PRIu32 " pf=%f", diff, pf);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Apparent power
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,7 @@
 | 
			
		||||
#include "ags10.h"
 | 
			
		||||
 | 
			
		||||
#include <cinttypes>
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace ags10 {
 | 
			
		||||
static const char *const TAG = "ags10";
 | 
			
		||||
@@ -35,7 +37,7 @@ void AGS10Component::setup() {
 | 
			
		||||
 | 
			
		||||
  auto resistance = this->read_resistance_();
 | 
			
		||||
  if (resistance) {
 | 
			
		||||
    ESP_LOGD(TAG, "AGS10 Sensor Resistance: 0x%08X", *resistance);
 | 
			
		||||
    ESP_LOGD(TAG, "AGS10 Sensor Resistance: 0x%08" PRIX32, *resistance);
 | 
			
		||||
    if (this->resistance_ != nullptr) {
 | 
			
		||||
      this->resistance_->publish_state(*resistance);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -93,8 +93,9 @@ void AHT10Component::restart_read_() {
 | 
			
		||||
 | 
			
		||||
void AHT10Component::read_data_() {
 | 
			
		||||
  uint8_t data[6];
 | 
			
		||||
  if (this->read_count_ > 1)
 | 
			
		||||
  if (this->read_count_ > 1) {
 | 
			
		||||
    ESP_LOGD(TAG, "Read attempt %d at %ums", this->read_count_, (unsigned) (millis() - this->start_time_));
 | 
			
		||||
  }
 | 
			
		||||
  if (this->read(data, 6) != i2c::ERROR_OK) {
 | 
			
		||||
    this->status_set_warning("AHT10 read failed, retrying soon");
 | 
			
		||||
    this->restart_read_();
 | 
			
		||||
@@ -119,8 +120,9 @@ void AHT10Component::read_data_() {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if (this->read_count_ > 1)
 | 
			
		||||
  if (this->read_count_ > 1) {
 | 
			
		||||
    ESP_LOGD(TAG, "Success at %ums", (unsigned) (millis() - this->start_time_));
 | 
			
		||||
  }
 | 
			
		||||
  uint32_t raw_temperature = ((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5];
 | 
			
		||||
  uint32_t raw_humidity = ((data[1] << 16) | (data[2] << 8) | data[3]) >> 4;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import web_server
 | 
			
		||||
from esphome import automation
 | 
			
		||||
from esphome.automation import maybe_simple_id
 | 
			
		||||
from esphome.core import CORE, coroutine_with_priority
 | 
			
		||||
@@ -8,6 +9,7 @@ from esphome.const import (
 | 
			
		||||
    CONF_ON_STATE,
 | 
			
		||||
    CONF_TRIGGER_ID,
 | 
			
		||||
    CONF_CODE,
 | 
			
		||||
    CONF_WEB_SERVER_ID,
 | 
			
		||||
)
 | 
			
		||||
from esphome.cpp_helpers import setup_entity
 | 
			
		||||
 | 
			
		||||
@@ -76,6 +78,8 @@ AlarmControlPanelCondition = alarm_control_panel_ns.class_(
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
ALARM_CONTROL_PANEL_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(
 | 
			
		||||
    web_server.WEBSERVER_SORTING_SCHEMA
 | 
			
		||||
).extend(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(): cv.declare_id(AlarmControlPanel),
 | 
			
		||||
        cv.Optional(CONF_ON_STATE): automation.validate_automation(
 | 
			
		||||
@@ -185,6 +189,9 @@ async def setup_alarm_control_panel_core_(var, config):
 | 
			
		||||
    for conf in config.get(CONF_ON_READY, []):
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
			
		||||
        await automation.build_automation(trigger, [], conf)
 | 
			
		||||
    if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None:
 | 
			
		||||
        web_server_ = await cg.get_variable(webserver_id)
 | 
			
		||||
        web_server.add_entity_to_sorting_list(web_server_, var, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def register_alarm_control_panel(var, config):
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,13 @@ import logging
 | 
			
		||||
from esphome import automation, core
 | 
			
		||||
from esphome.components import font
 | 
			
		||||
import esphome.components.image as espImage
 | 
			
		||||
from esphome.components.image import CONF_USE_TRANSPARENCY
 | 
			
		||||
from esphome.components.image import (
 | 
			
		||||
    CONF_USE_TRANSPARENCY,
 | 
			
		||||
    LOCAL_SCHEMA,
 | 
			
		||||
    WEB_SCHEMA,
 | 
			
		||||
    SOURCE_WEB,
 | 
			
		||||
    SOURCE_LOCAL,
 | 
			
		||||
)
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
@@ -13,6 +19,9 @@ from esphome.const import (
 | 
			
		||||
    CONF_REPEAT,
 | 
			
		||||
    CONF_RESIZE,
 | 
			
		||||
    CONF_TYPE,
 | 
			
		||||
    CONF_SOURCE,
 | 
			
		||||
    CONF_PATH,
 | 
			
		||||
    CONF_URL,
 | 
			
		||||
)
 | 
			
		||||
from esphome.core import CORE, HexInt
 | 
			
		||||
 | 
			
		||||
@@ -43,6 +52,40 @@ SetFrameAction = animation_ns.class_(
 | 
			
		||||
    "AnimationSetFrameAction", automation.Action, cg.Parented.template(Animation_)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
TYPED_FILE_SCHEMA = cv.typed_schema(
 | 
			
		||||
    {
 | 
			
		||||
        SOURCE_LOCAL: LOCAL_SCHEMA,
 | 
			
		||||
        SOURCE_WEB: WEB_SCHEMA,
 | 
			
		||||
    },
 | 
			
		||||
    key=CONF_SOURCE,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _file_schema(value):
 | 
			
		||||
    if isinstance(value, str):
 | 
			
		||||
        return validate_file_shorthand(value)
 | 
			
		||||
    return TYPED_FILE_SCHEMA(value)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
FILE_SCHEMA = cv.Schema(_file_schema)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_file_shorthand(value):
 | 
			
		||||
    value = cv.string_strict(value)
 | 
			
		||||
    if value.startswith("http://") or value.startswith("https://"):
 | 
			
		||||
        return FILE_SCHEMA(
 | 
			
		||||
            {
 | 
			
		||||
                CONF_SOURCE: SOURCE_WEB,
 | 
			
		||||
                CONF_URL: value,
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
    return FILE_SCHEMA(
 | 
			
		||||
        {
 | 
			
		||||
            CONF_SOURCE: SOURCE_LOCAL,
 | 
			
		||||
            CONF_PATH: value,
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_cross_dependencies(config):
 | 
			
		||||
    """
 | 
			
		||||
@@ -67,7 +110,7 @@ ANIMATION_SCHEMA = cv.Schema(
 | 
			
		||||
    cv.All(
 | 
			
		||||
        {
 | 
			
		||||
            cv.Required(CONF_ID): cv.declare_id(Animation_),
 | 
			
		||||
            cv.Required(CONF_FILE): cv.file_,
 | 
			
		||||
            cv.Required(CONF_FILE): FILE_SCHEMA,
 | 
			
		||||
            cv.Optional(CONF_RESIZE): cv.dimensions,
 | 
			
		||||
            cv.Optional(CONF_TYPE, default="BINARY"): cv.enum(
 | 
			
		||||
                espImage.IMAGE_TYPE, upper=True
 | 
			
		||||
@@ -124,7 +167,11 @@ async def animation_action_to_code(config, action_id, template_arg, args):
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    from PIL import Image
 | 
			
		||||
 | 
			
		||||
    path = CORE.relative_config_path(config[CONF_FILE])
 | 
			
		||||
    conf_file = config[CONF_FILE]
 | 
			
		||||
    if conf_file[CONF_SOURCE] == SOURCE_LOCAL:
 | 
			
		||||
        path = CORE.relative_config_path(conf_file[CONF_PATH])
 | 
			
		||||
    elif conf_file[CONF_SOURCE] == SOURCE_WEB:
 | 
			
		||||
        path = espImage.compute_local_image_path(conf_file).as_posix()
 | 
			
		||||
    try:
 | 
			
		||||
        image = Image.open(path)
 | 
			
		||||
    except Exception as e:
 | 
			
		||||
@@ -157,7 +204,7 @@ async def to_code(config):
 | 
			
		||||
            pixels = list(frame.getdata())
 | 
			
		||||
            if len(pixels) != height * width:
 | 
			
		||||
                raise core.EsphomeError(
 | 
			
		||||
                    f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height*width})"
 | 
			
		||||
                    f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height * width})"
 | 
			
		||||
                )
 | 
			
		||||
            for pix, a in pixels:
 | 
			
		||||
                if transparent:
 | 
			
		||||
@@ -180,7 +227,7 @@ async def to_code(config):
 | 
			
		||||
            pixels = list(frame.getdata())
 | 
			
		||||
            if len(pixels) != height * width:
 | 
			
		||||
                raise core.EsphomeError(
 | 
			
		||||
                    f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height*width})"
 | 
			
		||||
                    f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height * width})"
 | 
			
		||||
                )
 | 
			
		||||
            for pix in pixels:
 | 
			
		||||
                data[pos] = pix[0]
 | 
			
		||||
@@ -203,7 +250,7 @@ async def to_code(config):
 | 
			
		||||
            pixels = list(frame.getdata())
 | 
			
		||||
            if len(pixels) != height * width:
 | 
			
		||||
                raise core.EsphomeError(
 | 
			
		||||
                    f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height*width})"
 | 
			
		||||
                    f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height * width})"
 | 
			
		||||
                )
 | 
			
		||||
            for r, g, b, a in pixels:
 | 
			
		||||
                if transparent:
 | 
			
		||||
@@ -232,7 +279,7 @@ async def to_code(config):
 | 
			
		||||
            pixels = list(frame.getdata())
 | 
			
		||||
            if len(pixels) != height * width:
 | 
			
		||||
                raise core.EsphomeError(
 | 
			
		||||
                    f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height*width})"
 | 
			
		||||
                    f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height * width})"
 | 
			
		||||
                )
 | 
			
		||||
            for r, g, b, a in pixels:
 | 
			
		||||
                R = r >> 3
 | 
			
		||||
 
 | 
			
		||||
@@ -48,6 +48,7 @@ service APIConnection {
 | 
			
		||||
  rpc date_command (DateCommandRequest) returns (void) {}
 | 
			
		||||
  rpc time_command (TimeCommandRequest) returns (void) {}
 | 
			
		||||
  rpc datetime_command (DateTimeCommandRequest) returns (void) {}
 | 
			
		||||
  rpc update_command (UpdateCommandRequest) returns (void) {}
 | 
			
		||||
 | 
			
		||||
  rpc subscribe_bluetooth_le_advertisements(SubscribeBluetoothLEAdvertisementsRequest) returns (void) {}
 | 
			
		||||
  rpc bluetooth_device_request(BluetoothDeviceRequest) returns (void) {}
 | 
			
		||||
@@ -1517,6 +1518,25 @@ message VoiceAssistantAudio {
 | 
			
		||||
  bool end = 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum VoiceAssistantTimerEvent {
 | 
			
		||||
  VOICE_ASSISTANT_TIMER_STARTED = 0;
 | 
			
		||||
  VOICE_ASSISTANT_TIMER_UPDATED = 1;
 | 
			
		||||
  VOICE_ASSISTANT_TIMER_CANCELLED = 2;
 | 
			
		||||
  VOICE_ASSISTANT_TIMER_FINISHED = 3;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message VoiceAssistantTimerEventResponse {
 | 
			
		||||
  option (id) = 115;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
  option (ifdef) = "USE_VOICE_ASSISTANT";
 | 
			
		||||
 | 
			
		||||
  VoiceAssistantTimerEvent event_type = 1;
 | 
			
		||||
  string timer_id = 2;
 | 
			
		||||
  string name = 3;
 | 
			
		||||
  uint32 total_seconds = 4;
 | 
			
		||||
  uint32 seconds_left = 5;
 | 
			
		||||
  bool is_active = 6;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ==================== ALARM CONTROL PANEL ====================
 | 
			
		||||
enum AlarmControlPanelState {
 | 
			
		||||
@@ -1818,3 +1838,46 @@ message DateTimeCommandRequest {
 | 
			
		||||
  fixed32 key = 1;
 | 
			
		||||
  fixed32 epoch_seconds = 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ==================== UPDATE ====================
 | 
			
		||||
message ListEntitiesUpdateResponse {
 | 
			
		||||
  option (id) = 116;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_UPDATE";
 | 
			
		||||
 | 
			
		||||
  string object_id = 1;
 | 
			
		||||
  fixed32 key = 2;
 | 
			
		||||
  string name = 3;
 | 
			
		||||
  string unique_id = 4;
 | 
			
		||||
 | 
			
		||||
  string icon = 5;
 | 
			
		||||
  bool disabled_by_default = 6;
 | 
			
		||||
  EntityCategory entity_category = 7;
 | 
			
		||||
  string device_class = 8;
 | 
			
		||||
}
 | 
			
		||||
message UpdateStateResponse {
 | 
			
		||||
  option (id) = 117;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_UPDATE";
 | 
			
		||||
  option (no_delay) = true;
 | 
			
		||||
 | 
			
		||||
  fixed32 key = 1;
 | 
			
		||||
  bool missing_state = 2;
 | 
			
		||||
  bool in_progress = 3;
 | 
			
		||||
  bool has_progress = 4;
 | 
			
		||||
  float progress = 5;
 | 
			
		||||
  string current_version = 6;
 | 
			
		||||
  string latest_version = 7;
 | 
			
		||||
  string title = 8;
 | 
			
		||||
  string release_summary = 9;
 | 
			
		||||
  string release_url = 10;
 | 
			
		||||
}
 | 
			
		||||
message UpdateCommandRequest {
 | 
			
		||||
  option (id) = 118;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
  option (ifdef) = "USE_UPDATE";
 | 
			
		||||
  option (no_delay) = true;
 | 
			
		||||
 | 
			
		||||
  fixed32 key = 1;
 | 
			
		||||
  bool install = 2;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1193,6 +1193,15 @@ void APIConnection::on_voice_assistant_audio(const VoiceAssistantAudio &msg) {
 | 
			
		||||
    voice_assistant::global_voice_assistant->on_audio(msg);
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
void APIConnection::on_voice_assistant_timer_event_response(const VoiceAssistantTimerEventResponse &msg) {
 | 
			
		||||
  if (voice_assistant::global_voice_assistant != nullptr) {
 | 
			
		||||
    if (voice_assistant::global_voice_assistant->get_api_connection() != this) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    voice_assistant::global_voice_assistant->on_timer_event(msg);
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@@ -1278,6 +1287,51 @@ bool APIConnection::send_event_info(event::Event *event) {
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_UPDATE
 | 
			
		||||
bool APIConnection::send_update_state(update::UpdateEntity *update) {
 | 
			
		||||
  if (!this->state_subscription_)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  UpdateStateResponse resp{};
 | 
			
		||||
  resp.key = update->get_object_id_hash();
 | 
			
		||||
  resp.missing_state = !update->has_state();
 | 
			
		||||
  if (update->has_state()) {
 | 
			
		||||
    resp.in_progress = update->state == update::UpdateState::UPDATE_STATE_INSTALLING;
 | 
			
		||||
    if (update->update_info.has_progress) {
 | 
			
		||||
      resp.has_progress = true;
 | 
			
		||||
      resp.progress = update->update_info.progress;
 | 
			
		||||
    }
 | 
			
		||||
    resp.current_version = update->update_info.current_version;
 | 
			
		||||
    resp.latest_version = update->update_info.latest_version;
 | 
			
		||||
    resp.title = update->update_info.title;
 | 
			
		||||
    resp.release_summary = update->update_info.summary;
 | 
			
		||||
    resp.release_url = update->update_info.release_url;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return this->send_update_state_response(resp);
 | 
			
		||||
}
 | 
			
		||||
bool APIConnection::send_update_info(update::UpdateEntity *update) {
 | 
			
		||||
  ListEntitiesUpdateResponse msg;
 | 
			
		||||
  msg.key = update->get_object_id_hash();
 | 
			
		||||
  msg.object_id = update->get_object_id();
 | 
			
		||||
  if (update->has_own_name())
 | 
			
		||||
    msg.name = update->get_name();
 | 
			
		||||
  msg.unique_id = get_default_unique_id("update", update);
 | 
			
		||||
  msg.icon = update->get_icon();
 | 
			
		||||
  msg.disabled_by_default = update->is_disabled_by_default();
 | 
			
		||||
  msg.entity_category = static_cast<enums::EntityCategory>(update->get_entity_category());
 | 
			
		||||
  msg.device_class = update->get_device_class();
 | 
			
		||||
  return this->send_list_entities_update_response(msg);
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::update_command(const UpdateCommandRequest &msg) {
 | 
			
		||||
  update::UpdateEntity *update = App.get_update_by_key(msg.key);
 | 
			
		||||
  if (update == nullptr)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  update->perform();
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
bool APIConnection::send_log_message(int level, const char *tag, const char *line) {
 | 
			
		||||
  if (this->log_subscription_ < level)
 | 
			
		||||
    return false;
 | 
			
		||||
 
 | 
			
		||||
@@ -150,6 +150,7 @@ class APIConnection : public APIServerConnection {
 | 
			
		||||
  void on_voice_assistant_response(const VoiceAssistantResponse &msg) override;
 | 
			
		||||
  void on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) override;
 | 
			
		||||
  void on_voice_assistant_audio(const VoiceAssistantAudio &msg) override;
 | 
			
		||||
  void on_voice_assistant_timer_event_response(const VoiceAssistantTimerEventResponse &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ALARM_CONTROL_PANEL
 | 
			
		||||
@@ -163,6 +164,12 @@ class APIConnection : public APIServerConnection {
 | 
			
		||||
  bool send_event_info(event::Event *event);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_UPDATE
 | 
			
		||||
  bool send_update_state(update::UpdateEntity *update);
 | 
			
		||||
  bool send_update_info(update::UpdateEntity *update);
 | 
			
		||||
  void update_command(const UpdateCommandRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  void on_disconnect_response(const DisconnectResponse &value) override;
 | 
			
		||||
  void on_ping_response(const PingResponse &value) override {
 | 
			
		||||
    // we initiated ping
 | 
			
		||||
 
 | 
			
		||||
@@ -475,6 +475,22 @@ template<> const char *proto_enum_to_string<enums::VoiceAssistantEvent>(enums::V
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
template<> const char *proto_enum_to_string<enums::VoiceAssistantTimerEvent>(enums::VoiceAssistantTimerEvent value) {
 | 
			
		||||
  switch (value) {
 | 
			
		||||
    case enums::VOICE_ASSISTANT_TIMER_STARTED:
 | 
			
		||||
      return "VOICE_ASSISTANT_TIMER_STARTED";
 | 
			
		||||
    case enums::VOICE_ASSISTANT_TIMER_UPDATED:
 | 
			
		||||
      return "VOICE_ASSISTANT_TIMER_UPDATED";
 | 
			
		||||
    case enums::VOICE_ASSISTANT_TIMER_CANCELLED:
 | 
			
		||||
      return "VOICE_ASSISTANT_TIMER_CANCELLED";
 | 
			
		||||
    case enums::VOICE_ASSISTANT_TIMER_FINISHED:
 | 
			
		||||
      return "VOICE_ASSISTANT_TIMER_FINISHED";
 | 
			
		||||
    default:
 | 
			
		||||
      return "UNKNOWN";
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
template<> const char *proto_enum_to_string<enums::AlarmControlPanelState>(enums::AlarmControlPanelState value) {
 | 
			
		||||
  switch (value) {
 | 
			
		||||
    case enums::ALARM_STATE_DISARMED:
 | 
			
		||||
@@ -6857,6 +6873,82 @@ void VoiceAssistantAudio::dump_to(std::string &out) const {
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
bool VoiceAssistantTimerEventResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 1: {
 | 
			
		||||
      this->event_type = value.as_enum<enums::VoiceAssistantTimerEvent>();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 4: {
 | 
			
		||||
      this->total_seconds = value.as_uint32();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 5: {
 | 
			
		||||
      this->seconds_left = value.as_uint32();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 6: {
 | 
			
		||||
      this->is_active = value.as_bool();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
bool VoiceAssistantTimerEventResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 2: {
 | 
			
		||||
      this->timer_id = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 3: {
 | 
			
		||||
      this->name = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void VoiceAssistantTimerEventResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
  buffer.encode_enum<enums::VoiceAssistantTimerEvent>(1, this->event_type);
 | 
			
		||||
  buffer.encode_string(2, this->timer_id);
 | 
			
		||||
  buffer.encode_string(3, this->name);
 | 
			
		||||
  buffer.encode_uint32(4, this->total_seconds);
 | 
			
		||||
  buffer.encode_uint32(5, this->seconds_left);
 | 
			
		||||
  buffer.encode_bool(6, this->is_active);
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void VoiceAssistantTimerEventResponse::dump_to(std::string &out) const {
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("VoiceAssistantTimerEventResponse {\n");
 | 
			
		||||
  out.append("  event_type: ");
 | 
			
		||||
  out.append(proto_enum_to_string<enums::VoiceAssistantTimerEvent>(this->event_type));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  timer_id: ");
 | 
			
		||||
  out.append("'").append(this->timer_id).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  name: ");
 | 
			
		||||
  out.append("'").append(this->name).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  total_seconds: ");
 | 
			
		||||
  sprintf(buffer, "%" PRIu32, this->total_seconds);
 | 
			
		||||
  out.append(buffer);
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  seconds_left: ");
 | 
			
		||||
  sprintf(buffer, "%" PRIu32, this->seconds_left);
 | 
			
		||||
  out.append(buffer);
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  is_active: ");
 | 
			
		||||
  out.append(YESNO(this->is_active));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
bool ListEntitiesAlarmControlPanelResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 6: {
 | 
			
		||||
@@ -8284,6 +8376,262 @@ void DateTimeCommandRequest::dump_to(std::string &out) const {
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
bool ListEntitiesUpdateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 6: {
 | 
			
		||||
      this->disabled_by_default = value.as_bool();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 7: {
 | 
			
		||||
      this->entity_category = value.as_enum<enums::EntityCategory>();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
bool ListEntitiesUpdateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 1: {
 | 
			
		||||
      this->object_id = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 3: {
 | 
			
		||||
      this->name = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 4: {
 | 
			
		||||
      this->unique_id = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 5: {
 | 
			
		||||
      this->icon = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 8: {
 | 
			
		||||
      this->device_class = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
bool ListEntitiesUpdateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 2: {
 | 
			
		||||
      this->key = value.as_fixed32();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void ListEntitiesUpdateResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
  buffer.encode_string(1, this->object_id);
 | 
			
		||||
  buffer.encode_fixed32(2, this->key);
 | 
			
		||||
  buffer.encode_string(3, this->name);
 | 
			
		||||
  buffer.encode_string(4, this->unique_id);
 | 
			
		||||
  buffer.encode_string(5, this->icon);
 | 
			
		||||
  buffer.encode_bool(6, this->disabled_by_default);
 | 
			
		||||
  buffer.encode_enum<enums::EntityCategory>(7, this->entity_category);
 | 
			
		||||
  buffer.encode_string(8, this->device_class);
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void ListEntitiesUpdateResponse::dump_to(std::string &out) const {
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("ListEntitiesUpdateResponse {\n");
 | 
			
		||||
  out.append("  object_id: ");
 | 
			
		||||
  out.append("'").append(this->object_id).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  key: ");
 | 
			
		||||
  sprintf(buffer, "%" PRIu32, this->key);
 | 
			
		||||
  out.append(buffer);
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  name: ");
 | 
			
		||||
  out.append("'").append(this->name).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  unique_id: ");
 | 
			
		||||
  out.append("'").append(this->unique_id).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  icon: ");
 | 
			
		||||
  out.append("'").append(this->icon).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  disabled_by_default: ");
 | 
			
		||||
  out.append(YESNO(this->disabled_by_default));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  entity_category: ");
 | 
			
		||||
  out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  device_class: ");
 | 
			
		||||
  out.append("'").append(this->device_class).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
bool UpdateStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 2: {
 | 
			
		||||
      this->missing_state = value.as_bool();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 3: {
 | 
			
		||||
      this->in_progress = value.as_bool();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 4: {
 | 
			
		||||
      this->has_progress = value.as_bool();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
bool UpdateStateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 6: {
 | 
			
		||||
      this->current_version = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 7: {
 | 
			
		||||
      this->latest_version = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 8: {
 | 
			
		||||
      this->title = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 9: {
 | 
			
		||||
      this->release_summary = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 10: {
 | 
			
		||||
      this->release_url = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
bool UpdateStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 1: {
 | 
			
		||||
      this->key = value.as_fixed32();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 5: {
 | 
			
		||||
      this->progress = value.as_float();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void UpdateStateResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
  buffer.encode_fixed32(1, this->key);
 | 
			
		||||
  buffer.encode_bool(2, this->missing_state);
 | 
			
		||||
  buffer.encode_bool(3, this->in_progress);
 | 
			
		||||
  buffer.encode_bool(4, this->has_progress);
 | 
			
		||||
  buffer.encode_float(5, this->progress);
 | 
			
		||||
  buffer.encode_string(6, this->current_version);
 | 
			
		||||
  buffer.encode_string(7, this->latest_version);
 | 
			
		||||
  buffer.encode_string(8, this->title);
 | 
			
		||||
  buffer.encode_string(9, this->release_summary);
 | 
			
		||||
  buffer.encode_string(10, this->release_url);
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void UpdateStateResponse::dump_to(std::string &out) const {
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("UpdateStateResponse {\n");
 | 
			
		||||
  out.append("  key: ");
 | 
			
		||||
  sprintf(buffer, "%" PRIu32, this->key);
 | 
			
		||||
  out.append(buffer);
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  missing_state: ");
 | 
			
		||||
  out.append(YESNO(this->missing_state));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  in_progress: ");
 | 
			
		||||
  out.append(YESNO(this->in_progress));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  has_progress: ");
 | 
			
		||||
  out.append(YESNO(this->has_progress));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  progress: ");
 | 
			
		||||
  sprintf(buffer, "%g", this->progress);
 | 
			
		||||
  out.append(buffer);
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  current_version: ");
 | 
			
		||||
  out.append("'").append(this->current_version).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  latest_version: ");
 | 
			
		||||
  out.append("'").append(this->latest_version).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  title: ");
 | 
			
		||||
  out.append("'").append(this->title).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  release_summary: ");
 | 
			
		||||
  out.append("'").append(this->release_summary).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  release_url: ");
 | 
			
		||||
  out.append("'").append(this->release_url).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
bool UpdateCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 2: {
 | 
			
		||||
      this->install = value.as_bool();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
bool UpdateCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 1: {
 | 
			
		||||
      this->key = value.as_fixed32();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void UpdateCommandRequest::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
  buffer.encode_fixed32(1, this->key);
 | 
			
		||||
  buffer.encode_bool(2, this->install);
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void UpdateCommandRequest::dump_to(std::string &out) const {
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("UpdateCommandRequest {\n");
 | 
			
		||||
  out.append("  key: ");
 | 
			
		||||
  sprintf(buffer, "%" PRIu32, this->key);
 | 
			
		||||
  out.append(buffer);
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  install: ");
 | 
			
		||||
  out.append(YESNO(this->install));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -191,6 +191,12 @@ enum VoiceAssistantEvent : uint32_t {
 | 
			
		||||
  VOICE_ASSISTANT_TTS_STREAM_START = 98,
 | 
			
		||||
  VOICE_ASSISTANT_TTS_STREAM_END = 99,
 | 
			
		||||
};
 | 
			
		||||
enum VoiceAssistantTimerEvent : uint32_t {
 | 
			
		||||
  VOICE_ASSISTANT_TIMER_STARTED = 0,
 | 
			
		||||
  VOICE_ASSISTANT_TIMER_UPDATED = 1,
 | 
			
		||||
  VOICE_ASSISTANT_TIMER_CANCELLED = 2,
 | 
			
		||||
  VOICE_ASSISTANT_TIMER_FINISHED = 3,
 | 
			
		||||
};
 | 
			
		||||
enum AlarmControlPanelState : uint32_t {
 | 
			
		||||
  ALARM_STATE_DISARMED = 0,
 | 
			
		||||
  ALARM_STATE_ARMED_HOME = 1,
 | 
			
		||||
@@ -1775,6 +1781,23 @@ class VoiceAssistantAudio : public ProtoMessage {
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class VoiceAssistantTimerEventResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  enums::VoiceAssistantTimerEvent event_type{};
 | 
			
		||||
  std::string timer_id{};
 | 
			
		||||
  std::string name{};
 | 
			
		||||
  uint32_t total_seconds{0};
 | 
			
		||||
  uint32_t seconds_left{0};
 | 
			
		||||
  bool is_active{false};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class ListEntitiesAlarmControlPanelResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string object_id{};
 | 
			
		||||
@@ -2107,6 +2130,61 @@ class DateTimeCommandRequest : public ProtoMessage {
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
 | 
			
		||||
};
 | 
			
		||||
class ListEntitiesUpdateResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string object_id{};
 | 
			
		||||
  uint32_t key{0};
 | 
			
		||||
  std::string name{};
 | 
			
		||||
  std::string unique_id{};
 | 
			
		||||
  std::string icon{};
 | 
			
		||||
  bool disabled_by_default{false};
 | 
			
		||||
  enums::EntityCategory entity_category{};
 | 
			
		||||
  std::string device_class{};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class UpdateStateResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint32_t key{0};
 | 
			
		||||
  bool missing_state{false};
 | 
			
		||||
  bool in_progress{false};
 | 
			
		||||
  bool has_progress{false};
 | 
			
		||||
  float progress{0.0f};
 | 
			
		||||
  std::string current_version{};
 | 
			
		||||
  std::string latest_version{};
 | 
			
		||||
  std::string title{};
 | 
			
		||||
  std::string release_summary{};
 | 
			
		||||
  std::string release_url{};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class UpdateCommandRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint32_t key{0};
 | 
			
		||||
  bool install{false};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -484,6 +484,8 @@ bool APIServerConnectionBase::send_voice_assistant_audio(const VoiceAssistantAud
 | 
			
		||||
  return this->send_message_<VoiceAssistantAudio>(msg, 106);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_ALARM_CONTROL_PANEL
 | 
			
		||||
bool APIServerConnectionBase::send_list_entities_alarm_control_panel_response(
 | 
			
		||||
    const ListEntitiesAlarmControlPanelResponse &msg) {
 | 
			
		||||
@@ -609,6 +611,24 @@ bool APIServerConnectionBase::send_date_time_state_response(const DateTimeStateR
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_DATETIME_DATETIME
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_UPDATE
 | 
			
		||||
bool APIServerConnectionBase::send_list_entities_update_response(const ListEntitiesUpdateResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_list_entities_update_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<ListEntitiesUpdateResponse>(msg, 116);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_UPDATE
 | 
			
		||||
bool APIServerConnectionBase::send_update_state_response(const UpdateStateResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_update_state_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<UpdateStateResponse>(msg, 117);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_UPDATE
 | 
			
		||||
#endif
 | 
			
		||||
bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {
 | 
			
		||||
  switch (msg_type) {
 | 
			
		||||
    case 1: {
 | 
			
		||||
@@ -1093,6 +1113,28 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
 | 
			
		||||
      ESP_LOGVV(TAG, "on_date_time_command_request: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
      this->on_date_time_command_request(msg);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 115: {
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
      VoiceAssistantTimerEventResponse msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
      ESP_LOGVV(TAG, "on_voice_assistant_timer_event_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
      this->on_voice_assistant_timer_event_response(msg);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 118: {
 | 
			
		||||
#ifdef USE_UPDATE
 | 
			
		||||
      UpdateCommandRequest msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
      ESP_LOGVV(TAG, "on_update_command_request: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
      this->on_update_command_request(msg);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
@@ -1421,6 +1463,19 @@ void APIServerConnection::on_date_time_command_request(const DateTimeCommandRequ
 | 
			
		||||
  this->datetime_command(msg);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_UPDATE
 | 
			
		||||
void APIServerConnection::on_update_command_request(const UpdateCommandRequest &msg) {
 | 
			
		||||
  if (!this->is_connection_setup()) {
 | 
			
		||||
    this->on_no_setup_connection();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (!this->is_authenticated()) {
 | 
			
		||||
    this->on_unauthenticated_access();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->update_command(msg);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
void APIServerConnection::on_subscribe_bluetooth_le_advertisements_request(
 | 
			
		||||
    const SubscribeBluetoothLEAdvertisementsRequest &msg) {
 | 
			
		||||
 
 | 
			
		||||
@@ -244,6 +244,9 @@ class APIServerConnectionBase : public ProtoService {
 | 
			
		||||
  bool send_voice_assistant_audio(const VoiceAssistantAudio &msg);
 | 
			
		||||
  virtual void on_voice_assistant_audio(const VoiceAssistantAudio &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_VOICE_ASSISTANT
 | 
			
		||||
  virtual void on_voice_assistant_timer_event_response(const VoiceAssistantTimerEventResponse &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_ALARM_CONTROL_PANEL
 | 
			
		||||
  bool send_list_entities_alarm_control_panel_response(const ListEntitiesAlarmControlPanelResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
@@ -303,6 +306,15 @@ class APIServerConnectionBase : public ProtoService {
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_DATETIME_DATETIME
 | 
			
		||||
  virtual void on_date_time_command_request(const DateTimeCommandRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_UPDATE
 | 
			
		||||
  bool send_list_entities_update_response(const ListEntitiesUpdateResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_UPDATE
 | 
			
		||||
  bool send_update_state_response(const UpdateStateResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_UPDATE
 | 
			
		||||
  virtual void on_update_command_request(const UpdateCommandRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
 protected:
 | 
			
		||||
  bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;
 | 
			
		||||
@@ -370,6 +382,9 @@ class APIServerConnection : public APIServerConnectionBase {
 | 
			
		||||
#ifdef USE_DATETIME_DATETIME
 | 
			
		||||
  virtual void datetime_command(const DateTimeCommandRequest &msg) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_UPDATE
 | 
			
		||||
  virtual void update_command(const UpdateCommandRequest &msg) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  virtual void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
@@ -468,6 +483,9 @@ class APIServerConnection : public APIServerConnectionBase {
 | 
			
		||||
#ifdef USE_DATETIME_DATETIME
 | 
			
		||||
  void on_date_time_command_request(const DateTimeCommandRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_UPDATE
 | 
			
		||||
  void on_update_command_request(const UpdateCommandRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BLUETOOTH_PROXY
 | 
			
		||||
  void on_subscribe_bluetooth_le_advertisements_request(const SubscribeBluetoothLEAdvertisementsRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -334,6 +334,13 @@ void APIServer::on_event(event::Event *obj, const std::string &event_type) {
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_UPDATE
 | 
			
		||||
void APIServer::on_update(update::UpdateEntity *obj) {
 | 
			
		||||
  for (auto &c : this->clients_)
 | 
			
		||||
    c->send_update_state(obj);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
float APIServer::get_setup_priority() const { return setup_priority::AFTER_WIFI; }
 | 
			
		||||
void APIServer::set_port(uint16_t port) { this->port_ = port; }
 | 
			
		||||
APIServer *global_api_server = nullptr;  // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
 | 
			
		||||
 
 | 
			
		||||
@@ -102,6 +102,9 @@ class APIServer : public Component, public Controller {
 | 
			
		||||
#ifdef USE_EVENT
 | 
			
		||||
  void on_event(event::Event *obj, const std::string &event_type) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_UPDATE
 | 
			
		||||
  void on_update(update::UpdateEntity *obj) override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  bool is_connected() const;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -105,7 +105,7 @@ class CustomAPIDevice {
 | 
			
		||||
  /** Subscribe to the state (or attribute state) of an entity from Home Assistant.
 | 
			
		||||
   *
 | 
			
		||||
   * Usage:
 | 
			
		||||
   *å
 | 
			
		||||
   *
 | 
			
		||||
   * ```cpp
 | 
			
		||||
   * void setup() override {
 | 
			
		||||
   *   subscribe_homeassistant_state(&CustomNativeAPI::on_state_changed, "sensor.weather_forecast");
 | 
			
		||||
 
 | 
			
		||||
@@ -98,6 +98,9 @@ bool ListEntitiesIterator::on_alarm_control_panel(alarm_control_panel::AlarmCont
 | 
			
		||||
#ifdef USE_EVENT
 | 
			
		||||
bool ListEntitiesIterator::on_event(event::Event *event) { return this->client_->send_event_info(event); }
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_UPDATE
 | 
			
		||||
bool ListEntitiesIterator::on_update(update::UpdateEntity *update) { return this->client_->send_update_info(update); }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -75,6 +75,9 @@ class ListEntitiesIterator : public ComponentIterator {
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_EVENT
 | 
			
		||||
  bool on_event(event::Event *event) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_UPDATE
 | 
			
		||||
  bool on_update(update::UpdateEntity *update) override;
 | 
			
		||||
#endif
 | 
			
		||||
  bool on_end() override;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -77,6 +77,9 @@ bool InitialStateIterator::on_alarm_control_panel(alarm_control_panel::AlarmCont
 | 
			
		||||
  return this->client_->send_alarm_control_panel_state(a_alarm_control_panel);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_UPDATE
 | 
			
		||||
bool InitialStateIterator::on_update(update::UpdateEntity *update) { return this->client_->send_update_state(update); }
 | 
			
		||||
#endif
 | 
			
		||||
InitialStateIterator::InitialStateIterator(APIConnection *client) : client_(client) {}
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
 
 | 
			
		||||
@@ -72,6 +72,9 @@ class InitialStateIterator : public ComponentIterator {
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_EVENT
 | 
			
		||||
  bool on_event(event::Event *event) override { return true; };
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_UPDATE
 | 
			
		||||
  bool on_update(update::UpdateEntity *update) override;
 | 
			
		||||
#endif
 | 
			
		||||
 protected:
 | 
			
		||||
  APIConnection *client_;
 | 
			
		||||
 
 | 
			
		||||
@@ -31,7 +31,7 @@ CONFIG_SCHEMA = (
 | 
			
		||||
 | 
			
		||||
BEDJET_CLIENT_SCHEMA = cv.Schema(
 | 
			
		||||
    {
 | 
			
		||||
        cv.Required(CONF_BEDJET_ID): cv.use_id(BedJetHub),
 | 
			
		||||
        cv.GenerateID(CONF_BEDJET_ID): cv.use_id(BedJetHub),
 | 
			
		||||
    }
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -157,5 +157,11 @@ bool BedjetCodec::compare(const uint8_t *data, uint16_t length) {
 | 
			
		||||
  return explicit_fields_changed;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Converts a BedJet temp step into degrees Celsius.
 | 
			
		||||
float bedjet_temp_to_c(uint8_t temp) {
 | 
			
		||||
  // BedJet temp is "C*2"; to get C, divide by 2.
 | 
			
		||||
  return temp / 2.0f;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace bedjet
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -187,5 +187,8 @@ class BedjetCodec {
 | 
			
		||||
  BedjetStatusPacket buf_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Converts a BedJet temp step into degrees Celsius.
 | 
			
		||||
float bedjet_temp_to_c(uint8_t temp);
 | 
			
		||||
 | 
			
		||||
}  // namespace bedjet
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -40,6 +40,14 @@ enum BedjetHeatMode {
 | 
			
		||||
  HEAT_MODE_EXTENDED,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Which temperature to use as the climate entity's current temperature reading
 | 
			
		||||
enum BedjetTemperatureSource {
 | 
			
		||||
  // Use the temperature of the air the BedJet is putting out
 | 
			
		||||
  TEMPERATURE_SOURCE_OUTLET,
 | 
			
		||||
  // Use the ambient temperature of the room the BedJet is in
 | 
			
		||||
  TEMPERATURE_SOURCE_AMBIENT
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum BedjetButton : uint8_t {
 | 
			
		||||
  /// Turn BedJet off
 | 
			
		||||
  BTN_OFF = 0x1,
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@ from esphome.const import (
 | 
			
		||||
    CONF_HEAT_MODE,
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_RECEIVE_TIMEOUT,
 | 
			
		||||
    CONF_TEMPERATURE_SOURCE,
 | 
			
		||||
    CONF_TIME_ID,
 | 
			
		||||
)
 | 
			
		||||
from .. import (
 | 
			
		||||
@@ -21,10 +22,15 @@ DEPENDENCIES = ["bedjet"]
 | 
			
		||||
 | 
			
		||||
BedJetClimate = bedjet_ns.class_("BedJetClimate", climate.Climate, cg.PollingComponent)
 | 
			
		||||
BedjetHeatMode = bedjet_ns.enum("BedjetHeatMode")
 | 
			
		||||
BedjetTemperatureSource = bedjet_ns.enum("BedjetTemperatureSource")
 | 
			
		||||
BEDJET_HEAT_MODES = {
 | 
			
		||||
    "heat": BedjetHeatMode.HEAT_MODE_HEAT,
 | 
			
		||||
    "extended": BedjetHeatMode.HEAT_MODE_EXTENDED,
 | 
			
		||||
}
 | 
			
		||||
BEDJET_TEMPERATURE_SOURCES = {
 | 
			
		||||
    "outlet": BedjetTemperatureSource.TEMPERATURE_SOURCE_OUTLET,
 | 
			
		||||
    "ambient": BedjetTemperatureSource.TEMPERATURE_SOURCE_AMBIENT,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = (
 | 
			
		||||
    climate.CLIMATE_SCHEMA.extend(
 | 
			
		||||
@@ -33,6 +39,9 @@ CONFIG_SCHEMA = (
 | 
			
		||||
            cv.Optional(CONF_HEAT_MODE, default="heat"): cv.enum(
 | 
			
		||||
                BEDJET_HEAT_MODES, lower=True
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_TEMPERATURE_SOURCE, default="ambient"): cv.enum(
 | 
			
		||||
                BEDJET_TEMPERATURE_SOURCES, lower=True
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
    .extend(cv.polling_component_schema("60s"))
 | 
			
		||||
@@ -63,3 +72,4 @@ async def to_code(config):
 | 
			
		||||
    await register_bedjet_child(var, config)
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_heating_mode(config[CONF_HEAT_MODE]))
 | 
			
		||||
    cg.add(var.set_temperature_source(config[CONF_TEMPERATURE_SOURCE]))
 | 
			
		||||
 
 | 
			
		||||
@@ -8,12 +8,6 @@ namespace bedjet {
 | 
			
		||||
 | 
			
		||||
using namespace esphome::climate;
 | 
			
		||||
 | 
			
		||||
/// Converts a BedJet temp step into degrees Celsius.
 | 
			
		||||
float bedjet_temp_to_c(const uint8_t temp) {
 | 
			
		||||
  // BedJet temp is "C*2"; to get C, divide by 2.
 | 
			
		||||
  return temp / 2.0f;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const std::string *bedjet_fan_step_to_fan_mode(const uint8_t fan_step) {
 | 
			
		||||
  if (fan_step < BEDJET_FAN_SPEED_COUNT)
 | 
			
		||||
    return &BEDJET_FAN_STEP_NAME_STRINGS[fan_step];
 | 
			
		||||
@@ -236,9 +230,14 @@ void BedJetClimate::on_status(const BedjetStatusPacket *data) {
 | 
			
		||||
  if (converted_temp > 0)
 | 
			
		||||
    this->target_temperature = converted_temp;
 | 
			
		||||
 | 
			
		||||
  converted_temp = bedjet_temp_to_c(data->ambient_temp_step);
 | 
			
		||||
  if (converted_temp > 0)
 | 
			
		||||
  if (this->temperature_source_ == TEMPERATURE_SOURCE_OUTLET) {
 | 
			
		||||
    converted_temp = bedjet_temp_to_c(data->actual_temp_step);
 | 
			
		||||
  } else {
 | 
			
		||||
    converted_temp = bedjet_temp_to_c(data->ambient_temp_step);
 | 
			
		||||
  }
 | 
			
		||||
  if (converted_temp > 0) {
 | 
			
		||||
    this->current_temperature = converted_temp;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const auto *fan_mode_name = bedjet_fan_step_to_fan_mode(data->fan_step);
 | 
			
		||||
  if (fan_mode_name != nullptr) {
 | 
			
		||||
 
 | 
			
		||||
@@ -28,6 +28,8 @@ class BedJetClimate : public climate::Climate, public BedJetClient, public Polli
 | 
			
		||||
 | 
			
		||||
  /** Sets the default strategy to use for climate::CLIMATE_MODE_HEAT. */
 | 
			
		||||
  void set_heating_mode(BedjetHeatMode mode) { this->heating_mode_ = mode; }
 | 
			
		||||
  /** Sets the temperature source to use for the climate entity's current temperature */
 | 
			
		||||
  void set_temperature_source(BedjetTemperatureSource source) { this->temperature_source_ = source; }
 | 
			
		||||
 | 
			
		||||
  climate::ClimateTraits traits() override {
 | 
			
		||||
    auto traits = climate::ClimateTraits();
 | 
			
		||||
@@ -74,6 +76,7 @@ class BedJetClimate : public climate::Climate, public BedJetClient, public Polli
 | 
			
		||||
  void control(const climate::ClimateCall &call) override;
 | 
			
		||||
 | 
			
		||||
  BedjetHeatMode heating_mode_ = HEAT_MODE_HEAT;
 | 
			
		||||
  BedjetTemperatureSource temperature_source_ = TEMPERATURE_SOURCE_AMBIENT;
 | 
			
		||||
 | 
			
		||||
  void reset_state_();
 | 
			
		||||
  bool update_status_();
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										55
									
								
								esphome/components/bedjet/sensor/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								esphome/components/bedjet/sensor/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import sensor
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    DEVICE_CLASS_TEMPERATURE,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    UNIT_CELSIUS,
 | 
			
		||||
)
 | 
			
		||||
from .. import (
 | 
			
		||||
    BEDJET_CLIENT_SCHEMA,
 | 
			
		||||
    bedjet_ns,
 | 
			
		||||
    register_bedjet_child,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
_LOGGER = logging.getLogger(__name__)
 | 
			
		||||
CODEOWNERS = ["@jhansche", "@javawizard"]
 | 
			
		||||
DEPENDENCIES = ["bedjet"]
 | 
			
		||||
 | 
			
		||||
CONF_OUTLET_TEMPERATURE = "outlet_temperature"
 | 
			
		||||
CONF_AMBIENT_TEMPERATURE = "ambient_temperature"
 | 
			
		||||
 | 
			
		||||
BedjetSensor = bedjet_ns.class_("BedjetSensor", cg.Component)
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(): cv.declare_id(BedjetSensor),
 | 
			
		||||
        cv.Optional(CONF_OUTLET_TEMPERATURE): sensor.sensor_schema(
 | 
			
		||||
            unit_of_measurement=UNIT_CELSIUS,
 | 
			
		||||
            device_class=DEVICE_CLASS_TEMPERATURE,
 | 
			
		||||
            state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_AMBIENT_TEMPERATURE): sensor.sensor_schema(
 | 
			
		||||
            unit_of_measurement=UNIT_CELSIUS,
 | 
			
		||||
            device_class=DEVICE_CLASS_TEMPERATURE,
 | 
			
		||||
            state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
        ),
 | 
			
		||||
    }
 | 
			
		||||
).extend(BEDJET_CLIENT_SCHEMA)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await register_bedjet_child(var, config)
 | 
			
		||||
 | 
			
		||||
    if outlet_temperature_sensor := config.get(CONF_OUTLET_TEMPERATURE):
 | 
			
		||||
        sensor_var = await sensor.new_sensor(outlet_temperature_sensor)
 | 
			
		||||
        cg.add(var.set_outlet_temperature_sensor(sensor_var))
 | 
			
		||||
 | 
			
		||||
    if ambient_temperature_sensor := config.get(CONF_AMBIENT_TEMPERATURE):
 | 
			
		||||
        sensor_var = await sensor.new_sensor(ambient_temperature_sensor)
 | 
			
		||||
        cg.add(var.set_ambient_temperature_sensor(sensor_var))
 | 
			
		||||
							
								
								
									
										34
									
								
								esphome/components/bedjet/sensor/bedjet_sensor.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								esphome/components/bedjet/sensor/bedjet_sensor.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
			
		||||
#include "bedjet_sensor.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace bedjet {
 | 
			
		||||
 | 
			
		||||
std::string BedjetSensor::describe() { return "BedJet Sensor"; }
 | 
			
		||||
 | 
			
		||||
void BedjetSensor::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "BedJet Sensor:");
 | 
			
		||||
  LOG_SENSOR("  ", "Outlet Temperature", this->outlet_temperature_sensor_);
 | 
			
		||||
  LOG_SENSOR("  ", "Ambient Temperature", this->ambient_temperature_sensor_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BedjetSensor::on_bedjet_state(bool is_ready) {}
 | 
			
		||||
 | 
			
		||||
void BedjetSensor::on_status(const BedjetStatusPacket *data) {
 | 
			
		||||
  if (this->outlet_temperature_sensor_ != nullptr) {
 | 
			
		||||
    float converted_temp = bedjet_temp_to_c(data->actual_temp_step);
 | 
			
		||||
    if (converted_temp > 0) {
 | 
			
		||||
      this->outlet_temperature_sensor_->publish_state(converted_temp);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (this->ambient_temperature_sensor_ != nullptr) {
 | 
			
		||||
    float converted_temp = bedjet_temp_to_c(data->ambient_temp_step);
 | 
			
		||||
    if (converted_temp > 0) {
 | 
			
		||||
      this->ambient_temperature_sensor_->publish_state(converted_temp);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace bedjet
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										32
									
								
								esphome/components/bedjet/sensor/bedjet_sensor.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								esphome/components/bedjet/sensor/bedjet_sensor.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/components/sensor/sensor.h"
 | 
			
		||||
#include "esphome/components/bedjet/bedjet_child.h"
 | 
			
		||||
#include "esphome/components/bedjet/bedjet_codec.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace bedjet {
 | 
			
		||||
 | 
			
		||||
class BedjetSensor : public BedJetClient, public Component {
 | 
			
		||||
 public:
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
 | 
			
		||||
  void on_status(const BedjetStatusPacket *data) override;
 | 
			
		||||
  void on_bedjet_state(bool is_ready) override;
 | 
			
		||||
  std::string describe() override;
 | 
			
		||||
 | 
			
		||||
  void set_outlet_temperature_sensor(sensor::Sensor *outlet_temperature_sensor) {
 | 
			
		||||
    this->outlet_temperature_sensor_ = outlet_temperature_sensor;
 | 
			
		||||
  }
 | 
			
		||||
  void set_ambient_temperature_sensor(sensor::Sensor *ambient_temperature_sensor) {
 | 
			
		||||
    this->ambient_temperature_sensor_ = ambient_temperature_sensor;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  sensor::Sensor *outlet_temperature_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *ambient_temperature_sensor_{nullptr};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace bedjet
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										0
									
								
								esphome/components/beken_spi_led_strip/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								esphome/components/beken_spi_led_strip/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										384
									
								
								esphome/components/beken_spi_led_strip/led_strip.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										384
									
								
								esphome/components/beken_spi_led_strip/led_strip.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,384 @@
 | 
			
		||||
#include "led_strip.h"
 | 
			
		||||
 | 
			
		||||
#ifdef USE_BK72XX
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include "rtos_pub.h"
 | 
			
		||||
#include "spi.h"
 | 
			
		||||
#include "arm_arch.h"
 | 
			
		||||
#include "general_dma_pub.h"
 | 
			
		||||
#include "gpio_pub.h"
 | 
			
		||||
#include "icu_pub.h"
 | 
			
		||||
#undef SPI_DAT
 | 
			
		||||
#undef SPI_BASE
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const uint32_t SPI_TX_DMA_CHANNEL = GDMA_CHANNEL_3;
 | 
			
		||||
 | 
			
		||||
// TODO: Check if SPI_PERI_CLK_DCO depends on the chip variant
 | 
			
		||||
static const uint32_t SPI_PERI_CLK_26M = 26000000;
 | 
			
		||||
static const uint32_t SPI_PERI_CLK_DCO = 120000000;
 | 
			
		||||
 | 
			
		||||
static const uint32_t SPI_BASE = 0x00802700;
 | 
			
		||||
static const uint32_t SPI_DAT = SPI_BASE + 3 * 4;
 | 
			
		||||
static const uint32_t SPI_CONFIG = SPI_BASE + 1 * 4;
 | 
			
		||||
 | 
			
		||||
static const uint32_t SPI_TX_EN = 1 << 0;
 | 
			
		||||
static const uint32_t CTRL_NSSMD_3 = 1 << 17;
 | 
			
		||||
static const uint32_t SPI_TX_FINISH_EN = 1 << 2;
 | 
			
		||||
static const uint32_t SPI_RX_FINISH_EN = 1 << 3;
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace beken_spi_led_strip {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "beken_spi_led_strip";
 | 
			
		||||
 | 
			
		||||
struct spi_data_t {
 | 
			
		||||
  SemaphoreHandle_t dma_tx_semaphore;
 | 
			
		||||
  volatile bool tx_in_progress;
 | 
			
		||||
  bool first_run;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static spi_data_t *spi_data = nullptr;
 | 
			
		||||
 | 
			
		||||
static void set_spi_ctrl_register(unsigned long bit, bool val) {
 | 
			
		||||
  uint32_t value = REG_READ(SPI_CTRL);
 | 
			
		||||
  if (val == 0) {
 | 
			
		||||
    value &= ~bit;
 | 
			
		||||
  } else if (val == 1) {
 | 
			
		||||
    value |= bit;
 | 
			
		||||
  }
 | 
			
		||||
  REG_WRITE(SPI_CTRL, value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void set_spi_config_register(unsigned long bit, bool val) {
 | 
			
		||||
  uint32_t value = REG_READ(SPI_CONFIG);
 | 
			
		||||
  if (val == 0) {
 | 
			
		||||
    value &= ~bit;
 | 
			
		||||
  } else if (val == 1) {
 | 
			
		||||
    value |= bit;
 | 
			
		||||
  }
 | 
			
		||||
  REG_WRITE(SPI_CONFIG, value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void spi_dma_tx_enable(bool enable) {
 | 
			
		||||
  GDMA_CFG_ST en_cfg;
 | 
			
		||||
  set_spi_config_register(SPI_TX_EN, enable ? 1 : 0);
 | 
			
		||||
  en_cfg.channel = SPI_TX_DMA_CHANNEL;
 | 
			
		||||
  en_cfg.param = enable ? 1 : 0;
 | 
			
		||||
  sddev_control(GDMA_DEV_NAME, CMD_GDMA_SET_DMA_ENABLE, &en_cfg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void spi_set_clock(uint32_t max_hz) {
 | 
			
		||||
  int source_clk = 0;
 | 
			
		||||
  int spi_clk = 0;
 | 
			
		||||
  int div = 0;
 | 
			
		||||
  uint32_t param;
 | 
			
		||||
  if (max_hz > 4333000) {
 | 
			
		||||
    if (max_hz > 30000000) {
 | 
			
		||||
      spi_clk = 30000000;
 | 
			
		||||
    } else {
 | 
			
		||||
      spi_clk = max_hz;
 | 
			
		||||
    }
 | 
			
		||||
    sddev_control(ICU_DEV_NAME, CMD_CLK_PWR_DOWN, ¶m);
 | 
			
		||||
    source_clk = SPI_PERI_CLK_DCO;
 | 
			
		||||
    param = PCLK_POSI_SPI;
 | 
			
		||||
    sddev_control(ICU_DEV_NAME, CMD_CONF_PCLK_DCO, ¶m);
 | 
			
		||||
    param = PWD_SPI_CLK_BIT;
 | 
			
		||||
    sddev_control(ICU_DEV_NAME, CMD_CLK_PWR_UP, ¶m);
 | 
			
		||||
  } else {
 | 
			
		||||
    spi_clk = max_hz;
 | 
			
		||||
#if CFG_XTAL_FREQUENCE
 | 
			
		||||
    source_clk = CFG_XTAL_FREQUENCE;
 | 
			
		||||
#else
 | 
			
		||||
    source_clk = SPI_PERI_CLK_26M;
 | 
			
		||||
#endif
 | 
			
		||||
    param = PCLK_POSI_SPI;
 | 
			
		||||
    sddev_control(ICU_DEV_NAME, CMD_CONF_PCLK_26M, ¶m);
 | 
			
		||||
  }
 | 
			
		||||
  div = ((source_clk >> 1) / spi_clk);
 | 
			
		||||
  if (div < 2) {
 | 
			
		||||
    div = 2;
 | 
			
		||||
  } else if (div >= 255) {
 | 
			
		||||
    div = 255;
 | 
			
		||||
  }
 | 
			
		||||
  param = REG_READ(SPI_CTRL);
 | 
			
		||||
  param &= ~(SPI_CKR_MASK << SPI_CKR_POSI);
 | 
			
		||||
  param |= (div << SPI_CKR_POSI);
 | 
			
		||||
  REG_WRITE(SPI_CTRL, param);
 | 
			
		||||
  ESP_LOGD(TAG, "target frequency: %d, actual frequency: %d", max_hz, source_clk / 2 / div);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void spi_dma_tx_finish_callback(unsigned int param) {
 | 
			
		||||
  spi_data->tx_in_progress = false;
 | 
			
		||||
  xSemaphoreGive(spi_data->dma_tx_semaphore);
 | 
			
		||||
  spi_dma_tx_enable(0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BekenSPILEDStripLightOutput::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up Beken SPI LED Strip...");
 | 
			
		||||
 | 
			
		||||
  size_t buffer_size = this->get_buffer_size_();
 | 
			
		||||
  size_t dma_buffer_size = (buffer_size * 8) + (2 * 64);
 | 
			
		||||
 | 
			
		||||
  ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
 | 
			
		||||
  this->buf_ = allocator.allocate(buffer_size);
 | 
			
		||||
  if (this->buf_ == nullptr) {
 | 
			
		||||
    ESP_LOGE(TAG, "Cannot allocate LED buffer!");
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this->effect_data_ = allocator.allocate(this->num_leds_);
 | 
			
		||||
  if (this->effect_data_ == nullptr) {
 | 
			
		||||
    ESP_LOGE(TAG, "Cannot allocate effect data!");
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this->dma_buf_ = allocator.allocate(dma_buffer_size);
 | 
			
		||||
  if (this->dma_buf_ == nullptr) {
 | 
			
		||||
    ESP_LOGE(TAG, "Cannot allocate DMA buffer!");
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  memset(this->buf_, 0, buffer_size);
 | 
			
		||||
  memset(this->effect_data_, 0, this->num_leds_);
 | 
			
		||||
  memset(this->dma_buf_, 0, dma_buffer_size);
 | 
			
		||||
 | 
			
		||||
  uint32_t value = PCLK_POSI_SPI;
 | 
			
		||||
  sddev_control(ICU_DEV_NAME, CMD_CONF_PCLK_26M, &value);
 | 
			
		||||
 | 
			
		||||
  value = PWD_SPI_CLK_BIT;
 | 
			
		||||
  sddev_control(ICU_DEV_NAME, CMD_CLK_PWR_UP, &value);
 | 
			
		||||
 | 
			
		||||
  if (spi_data != nullptr) {
 | 
			
		||||
    ESP_LOGE(TAG, "SPI device already initialized!");
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  spi_data = (spi_data_t *) calloc(1, sizeof(spi_data_t));
 | 
			
		||||
  if (spi_data == nullptr) {
 | 
			
		||||
    ESP_LOGE(TAG, "Cannot allocate spi_data!");
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  spi_data->dma_tx_semaphore = xSemaphoreCreateBinary();
 | 
			
		||||
  if (spi_data->dma_tx_semaphore == nullptr) {
 | 
			
		||||
    ESP_LOGE(TAG, "TX Semaphore init faild!");
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  spi_data->first_run = true;
 | 
			
		||||
 | 
			
		||||
  set_spi_ctrl_register(MSTEN, 0);
 | 
			
		||||
  set_spi_ctrl_register(BIT_WDTH, 0);
 | 
			
		||||
  spi_set_clock(this->spi_frequency_);
 | 
			
		||||
  set_spi_ctrl_register(CKPOL, 0);
 | 
			
		||||
  set_spi_ctrl_register(CKPHA, 0);
 | 
			
		||||
  set_spi_ctrl_register(MSTEN, 1);
 | 
			
		||||
  set_spi_ctrl_register(SPIEN, 1);
 | 
			
		||||
 | 
			
		||||
  set_spi_ctrl_register(TXINT_EN, 0);
 | 
			
		||||
  set_spi_ctrl_register(RXINT_EN, 0);
 | 
			
		||||
  set_spi_config_register(SPI_TX_FINISH_EN, 1);
 | 
			
		||||
  set_spi_config_register(SPI_RX_FINISH_EN, 1);
 | 
			
		||||
  set_spi_ctrl_register(RXOVR_EN, 0);
 | 
			
		||||
  set_spi_ctrl_register(TXOVR_EN, 0);
 | 
			
		||||
 | 
			
		||||
  value = REG_READ(SPI_CTRL);
 | 
			
		||||
  value &= ~CTRL_NSSMD_3;
 | 
			
		||||
  value |= (1 << 17);
 | 
			
		||||
  REG_WRITE(SPI_CTRL, value);
 | 
			
		||||
 | 
			
		||||
  value = GFUNC_MODE_SPI_DMA;
 | 
			
		||||
  sddev_control(GPIO_DEV_NAME, CMD_GPIO_ENABLE_SECOND, &value);
 | 
			
		||||
  set_spi_ctrl_register(SPI_S_CS_UP_INT_EN, 0);
 | 
			
		||||
 | 
			
		||||
  GDMA_CFG_ST en_cfg;
 | 
			
		||||
  GDMACFG_TPYES_ST init_cfg;
 | 
			
		||||
  memset(&init_cfg, 0, sizeof(GDMACFG_TPYES_ST));
 | 
			
		||||
 | 
			
		||||
  init_cfg.dstdat_width = 8;
 | 
			
		||||
  init_cfg.srcdat_width = 32;
 | 
			
		||||
  init_cfg.dstptr_incr = 0;
 | 
			
		||||
  init_cfg.srcptr_incr = 1;
 | 
			
		||||
  init_cfg.src_start_addr = this->dma_buf_;
 | 
			
		||||
  init_cfg.dst_start_addr = (void *) SPI_DAT;  // SPI_DMA_REG4_TXFIFO
 | 
			
		||||
  init_cfg.channel = SPI_TX_DMA_CHANNEL;
 | 
			
		||||
  init_cfg.prio = 0;  // 10
 | 
			
		||||
  init_cfg.u.type4.src_loop_start_addr = this->dma_buf_;
 | 
			
		||||
  init_cfg.u.type4.src_loop_end_addr = this->dma_buf_ + dma_buffer_size;
 | 
			
		||||
  init_cfg.half_fin_handler = nullptr;
 | 
			
		||||
  init_cfg.fin_handler = spi_dma_tx_finish_callback;
 | 
			
		||||
  init_cfg.src_module = GDMA_X_SRC_DTCM_RD_REQ;
 | 
			
		||||
  init_cfg.dst_module = GDMA_X_DST_GSPI_TX_REQ;  // GDMA_X_DST_HSSPI_TX_REQ
 | 
			
		||||
  sddev_control(GDMA_DEV_NAME, CMD_GDMA_CFG_TYPE4, (void *) &init_cfg);
 | 
			
		||||
  en_cfg.channel = SPI_TX_DMA_CHANNEL;
 | 
			
		||||
  en_cfg.param = dma_buffer_size;
 | 
			
		||||
  sddev_control(GDMA_DEV_NAME, CMD_GDMA_SET_TRANS_LENGTH, (void *) &en_cfg);
 | 
			
		||||
  en_cfg.channel = SPI_TX_DMA_CHANNEL;
 | 
			
		||||
  en_cfg.param = 0;
 | 
			
		||||
  sddev_control(GDMA_DEV_NAME, CMD_GDMA_CFG_WORK_MODE, (void *) &en_cfg);
 | 
			
		||||
  en_cfg.channel = SPI_TX_DMA_CHANNEL;
 | 
			
		||||
  en_cfg.param = 0;
 | 
			
		||||
  sddev_control(GDMA_DEV_NAME, CMD_GDMA_CFG_SRCADDR_LOOP, &en_cfg);
 | 
			
		||||
 | 
			
		||||
  spi_dma_tx_enable(0);
 | 
			
		||||
 | 
			
		||||
  value = REG_READ(SPI_CONFIG);
 | 
			
		||||
  value &= ~(0xFFF << 8);
 | 
			
		||||
  value |= ((dma_buffer_size & 0xFFF) << 8);
 | 
			
		||||
  REG_WRITE(SPI_CONFIG, value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BekenSPILEDStripLightOutput::set_led_params(uint8_t bit0, uint8_t bit1, uint32_t spi_frequency) {
 | 
			
		||||
  this->bit0_ = bit0;
 | 
			
		||||
  this->bit1_ = bit1;
 | 
			
		||||
  this->spi_frequency_ = spi_frequency;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BekenSPILEDStripLightOutput::write_state(light::LightState *state) {
 | 
			
		||||
  // protect from refreshing too often
 | 
			
		||||
  uint32_t now = micros();
 | 
			
		||||
  if (*this->max_refresh_rate_ != 0 && (now - this->last_refresh_) < *this->max_refresh_rate_) {
 | 
			
		||||
    // try again next loop iteration, so that this change won't get lost
 | 
			
		||||
    this->schedule_show();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->last_refresh_ = now;
 | 
			
		||||
  this->mark_shown_();
 | 
			
		||||
 | 
			
		||||
  ESP_LOGVV(TAG, "Writing RGB values to bus...");
 | 
			
		||||
 | 
			
		||||
  if (spi_data == nullptr) {
 | 
			
		||||
    ESP_LOGE(TAG, "SPI not initialized");
 | 
			
		||||
    this->status_set_warning();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!spi_data->first_run && !xSemaphoreTake(spi_data->dma_tx_semaphore, 10 / portTICK_PERIOD_MS)) {
 | 
			
		||||
    ESP_LOGE(TAG, "Timed out waiting for semaphore");
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (spi_data->tx_in_progress) {
 | 
			
		||||
    ESP_LOGE(TAG, "tx_in_progress is set");
 | 
			
		||||
    this->status_set_warning();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  spi_data->tx_in_progress = true;
 | 
			
		||||
 | 
			
		||||
  size_t buffer_size = this->get_buffer_size_();
 | 
			
		||||
  size_t size = 0;
 | 
			
		||||
  uint8_t *psrc = this->buf_;
 | 
			
		||||
  uint8_t *pdest = this->dma_buf_ + 64;
 | 
			
		||||
  // The 64 byte padding is a workaround for a SPI DMA bug where the
 | 
			
		||||
  // output doesn't exactly start at the beginning of dma_buf_
 | 
			
		||||
 | 
			
		||||
  while (size < buffer_size) {
 | 
			
		||||
    uint8_t b = *psrc;
 | 
			
		||||
    for (int i = 0; i < 8; i++) {
 | 
			
		||||
      *pdest++ = b & (1 << (7 - i)) ? this->bit1_ : this->bit0_;
 | 
			
		||||
    }
 | 
			
		||||
    size++;
 | 
			
		||||
    psrc++;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  spi_data->first_run = false;
 | 
			
		||||
  spi_dma_tx_enable(1);
 | 
			
		||||
 | 
			
		||||
  this->status_clear_warning();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
light::ESPColorView BekenSPILEDStripLightOutput::get_view_internal(int32_t index) const {
 | 
			
		||||
  int32_t r = 0, g = 0, b = 0;
 | 
			
		||||
  switch (this->rgb_order_) {
 | 
			
		||||
    case ORDER_RGB:
 | 
			
		||||
      r = 0;
 | 
			
		||||
      g = 1;
 | 
			
		||||
      b = 2;
 | 
			
		||||
      break;
 | 
			
		||||
    case ORDER_RBG:
 | 
			
		||||
      r = 0;
 | 
			
		||||
      g = 2;
 | 
			
		||||
      b = 1;
 | 
			
		||||
      break;
 | 
			
		||||
    case ORDER_GRB:
 | 
			
		||||
      r = 1;
 | 
			
		||||
      g = 0;
 | 
			
		||||
      b = 2;
 | 
			
		||||
      break;
 | 
			
		||||
    case ORDER_GBR:
 | 
			
		||||
      r = 2;
 | 
			
		||||
      g = 0;
 | 
			
		||||
      b = 1;
 | 
			
		||||
      break;
 | 
			
		||||
    case ORDER_BGR:
 | 
			
		||||
      r = 2;
 | 
			
		||||
      g = 1;
 | 
			
		||||
      b = 0;
 | 
			
		||||
      break;
 | 
			
		||||
    case ORDER_BRG:
 | 
			
		||||
      r = 1;
 | 
			
		||||
      g = 2;
 | 
			
		||||
      b = 0;
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
  uint8_t multiplier = this->is_rgbw_ || this->is_wrgb_ ? 4 : 3;
 | 
			
		||||
  uint8_t white = this->is_wrgb_ ? 0 : 3;
 | 
			
		||||
 | 
			
		||||
  return {this->buf_ + (index * multiplier) + r + this->is_wrgb_,
 | 
			
		||||
          this->buf_ + (index * multiplier) + g + this->is_wrgb_,
 | 
			
		||||
          this->buf_ + (index * multiplier) + b + this->is_wrgb_,
 | 
			
		||||
          this->is_rgbw_ || this->is_wrgb_ ? this->buf_ + (index * multiplier) + white : nullptr,
 | 
			
		||||
          &this->effect_data_[index],
 | 
			
		||||
          &this->correction_};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BekenSPILEDStripLightOutput::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Beken SPI LED Strip:");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Pin: %u", this->pin_);
 | 
			
		||||
  const char *rgb_order;
 | 
			
		||||
  switch (this->rgb_order_) {
 | 
			
		||||
    case ORDER_RGB:
 | 
			
		||||
      rgb_order = "RGB";
 | 
			
		||||
      break;
 | 
			
		||||
    case ORDER_RBG:
 | 
			
		||||
      rgb_order = "RBG";
 | 
			
		||||
      break;
 | 
			
		||||
    case ORDER_GRB:
 | 
			
		||||
      rgb_order = "GRB";
 | 
			
		||||
      break;
 | 
			
		||||
    case ORDER_GBR:
 | 
			
		||||
      rgb_order = "GBR";
 | 
			
		||||
      break;
 | 
			
		||||
    case ORDER_BGR:
 | 
			
		||||
      rgb_order = "BGR";
 | 
			
		||||
      break;
 | 
			
		||||
    case ORDER_BRG:
 | 
			
		||||
      rgb_order = "BRG";
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      rgb_order = "UNKNOWN";
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  RGB Order: %s", rgb_order);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Max refresh rate: %" PRIu32, *this->max_refresh_rate_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Number of LEDs: %u", this->num_leds_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float BekenSPILEDStripLightOutput::get_setup_priority() const { return setup_priority::HARDWARE; }
 | 
			
		||||
 | 
			
		||||
}  // namespace beken_spi_led_strip
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 | 
			
		||||
#endif  // USE_BK72XX
 | 
			
		||||
							
								
								
									
										85
									
								
								esphome/components/beken_spi_led_strip/led_strip.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								esphome/components/beken_spi_led_strip/led_strip.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,85 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#ifdef USE_BK72XX
 | 
			
		||||
 | 
			
		||||
#include "esphome/components/light/addressable_light.h"
 | 
			
		||||
#include "esphome/components/light/light_output.h"
 | 
			
		||||
#include "esphome/core/color.h"
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace beken_spi_led_strip {
 | 
			
		||||
 | 
			
		||||
enum RGBOrder : uint8_t {
 | 
			
		||||
  ORDER_RGB,
 | 
			
		||||
  ORDER_RBG,
 | 
			
		||||
  ORDER_GRB,
 | 
			
		||||
  ORDER_GBR,
 | 
			
		||||
  ORDER_BGR,
 | 
			
		||||
  ORDER_BRG,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class BekenSPILEDStripLightOutput : public light::AddressableLight {
 | 
			
		||||
 public:
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void write_state(light::LightState *state) override;
 | 
			
		||||
  float get_setup_priority() const override;
 | 
			
		||||
 | 
			
		||||
  int32_t size() const override { return this->num_leds_; }
 | 
			
		||||
  light::LightTraits get_traits() override {
 | 
			
		||||
    auto traits = light::LightTraits();
 | 
			
		||||
    if (this->is_rgbw_ || this->is_wrgb_) {
 | 
			
		||||
      traits.set_supported_color_modes({light::ColorMode::RGB_WHITE, light::ColorMode::WHITE});
 | 
			
		||||
    } else {
 | 
			
		||||
      traits.set_supported_color_modes({light::ColorMode::RGB});
 | 
			
		||||
    }
 | 
			
		||||
    return traits;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void set_pin(uint8_t pin) { this->pin_ = pin; }
 | 
			
		||||
  void set_num_leds(uint16_t num_leds) { this->num_leds_ = num_leds; }
 | 
			
		||||
  void set_is_rgbw(bool is_rgbw) { this->is_rgbw_ = is_rgbw; }
 | 
			
		||||
  void set_is_wrgb(bool is_wrgb) { this->is_wrgb_ = is_wrgb; }
 | 
			
		||||
 | 
			
		||||
  /// Set a maximum refresh rate in µs as some lights do not like being updated too often.
 | 
			
		||||
  void set_max_refresh_rate(uint32_t interval_us) { this->max_refresh_rate_ = interval_us; }
 | 
			
		||||
 | 
			
		||||
  void set_led_params(uint8_t bit0, uint8_t bit1, uint32_t spi_frequency);
 | 
			
		||||
 | 
			
		||||
  void set_rgb_order(RGBOrder rgb_order) { this->rgb_order_ = rgb_order; }
 | 
			
		||||
 | 
			
		||||
  void clear_effect_data() override {
 | 
			
		||||
    for (int i = 0; i < this->size(); i++)
 | 
			
		||||
      this->effect_data_[i] = 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  light::ESPColorView get_view_internal(int32_t index) const override;
 | 
			
		||||
 | 
			
		||||
  size_t get_buffer_size_() const { return this->num_leds_ * (this->is_rgbw_ || this->is_wrgb_ ? 4 : 3); }
 | 
			
		||||
 | 
			
		||||
  uint8_t *buf_{nullptr};
 | 
			
		||||
  uint8_t *effect_data_{nullptr};
 | 
			
		||||
  uint8_t *dma_buf_{nullptr};
 | 
			
		||||
 | 
			
		||||
  uint8_t pin_;
 | 
			
		||||
  uint16_t num_leds_;
 | 
			
		||||
  bool is_rgbw_;
 | 
			
		||||
  bool is_wrgb_;
 | 
			
		||||
 | 
			
		||||
  uint32_t spi_frequency_{6666666};
 | 
			
		||||
  uint8_t bit0_{0xE0};
 | 
			
		||||
  uint8_t bit1_{0xFC};
 | 
			
		||||
  RGBOrder rgb_order_;
 | 
			
		||||
 | 
			
		||||
  uint32_t last_refresh_{0};
 | 
			
		||||
  optional<uint32_t> max_refresh_rate_{};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace beken_spi_led_strip
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 | 
			
		||||
#endif  // USE_BK72XX
 | 
			
		||||
							
								
								
									
										134
									
								
								esphome/components/beken_spi_led_strip/light.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								esphome/components/beken_spi_led_strip/light.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,134 @@
 | 
			
		||||
from dataclasses import dataclass
 | 
			
		||||
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome import pins
 | 
			
		||||
from esphome.components import libretiny, light
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_CHIPSET,
 | 
			
		||||
    CONF_IS_RGBW,
 | 
			
		||||
    CONF_MAX_REFRESH_RATE,
 | 
			
		||||
    CONF_NUM_LEDS,
 | 
			
		||||
    CONF_OUTPUT_ID,
 | 
			
		||||
    CONF_PIN,
 | 
			
		||||
    CONF_RGB_ORDER,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ["@Mat931"]
 | 
			
		||||
DEPENDENCIES = ["libretiny"]
 | 
			
		||||
 | 
			
		||||
beken_spi_led_strip_ns = cg.esphome_ns.namespace("beken_spi_led_strip")
 | 
			
		||||
BekenSPILEDStripLightOutput = beken_spi_led_strip_ns.class_(
 | 
			
		||||
    "BekenSPILEDStripLightOutput", light.AddressableLight
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
RGBOrder = beken_spi_led_strip_ns.enum("RGBOrder")
 | 
			
		||||
 | 
			
		||||
RGB_ORDERS = {
 | 
			
		||||
    "RGB": RGBOrder.ORDER_RGB,
 | 
			
		||||
    "RBG": RGBOrder.ORDER_RBG,
 | 
			
		||||
    "GRB": RGBOrder.ORDER_GRB,
 | 
			
		||||
    "GBR": RGBOrder.ORDER_GBR,
 | 
			
		||||
    "BGR": RGBOrder.ORDER_BGR,
 | 
			
		||||
    "BRG": RGBOrder.ORDER_BRG,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@dataclass
 | 
			
		||||
class LEDStripTimings:
 | 
			
		||||
    bit0: int
 | 
			
		||||
    bit1: int
 | 
			
		||||
    spi_frequency: int
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CHIPSETS = {
 | 
			
		||||
    "WS2812": LEDStripTimings(
 | 
			
		||||
        0b11100000, 0b11111100, 6666666
 | 
			
		||||
    ),  # Clock divider: 9, Bit time: 1350ns
 | 
			
		||||
    "SK6812": LEDStripTimings(
 | 
			
		||||
        0b11000000, 0b11111000, 7500000
 | 
			
		||||
    ),  # Clock divider: 8, Bit time: 1200ns
 | 
			
		||||
    "APA106": LEDStripTimings(
 | 
			
		||||
        0b11000000, 0b11111110, 5454545
 | 
			
		||||
    ),  # Clock divider: 11, Bit time: 1650ns
 | 
			
		||||
    "SM16703": LEDStripTimings(
 | 
			
		||||
        0b11000000, 0b11111110, 7500000
 | 
			
		||||
    ),  # Clock divider: 8, Bit time: 1200ns
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CONF_IS_WRGB = "is_wrgb"
 | 
			
		||||
 | 
			
		||||
SUPPORTED_PINS = {
 | 
			
		||||
    libretiny.const.FAMILY_BK7231N: [16],
 | 
			
		||||
    libretiny.const.FAMILY_BK7231T: [16],
 | 
			
		||||
    libretiny.const.FAMILY_BK7251: [16],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _validate_pin(value):
 | 
			
		||||
    family = libretiny.get_libretiny_family()
 | 
			
		||||
    if family not in SUPPORTED_PINS:
 | 
			
		||||
        raise cv.Invalid(f"Chip family {family} is not supported.")
 | 
			
		||||
    if value not in SUPPORTED_PINS[family]:
 | 
			
		||||
        supported_pin_info = ", ".join(f"{x}" for x in SUPPORTED_PINS[family])
 | 
			
		||||
        raise cv.Invalid(
 | 
			
		||||
            f"Pin {value} is not supported on the {family}. Supported pins: {supported_pin_info}"
 | 
			
		||||
        )
 | 
			
		||||
    return value
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _validate_num_leds(value):
 | 
			
		||||
    max_num_leds = 165  # 170
 | 
			
		||||
    if value[CONF_IS_RGBW] or value[CONF_IS_WRGB]:
 | 
			
		||||
        max_num_leds = 123  # 127
 | 
			
		||||
    if value[CONF_NUM_LEDS] > max_num_leds:
 | 
			
		||||
        raise cv.Invalid(
 | 
			
		||||
            f"The maximum number of LEDs for this configuration is {max_num_leds}.",
 | 
			
		||||
            path=CONF_NUM_LEDS,
 | 
			
		||||
        )
 | 
			
		||||
    return value
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.All(
 | 
			
		||||
    light.ADDRESSABLE_LIGHT_SCHEMA.extend(
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(BekenSPILEDStripLightOutput),
 | 
			
		||||
            cv.Required(CONF_PIN): cv.All(
 | 
			
		||||
                pins.internal_gpio_output_pin_number, _validate_pin
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Required(CONF_NUM_LEDS): cv.positive_not_null_int,
 | 
			
		||||
            cv.Required(CONF_RGB_ORDER): cv.enum(RGB_ORDERS, upper=True),
 | 
			
		||||
            cv.Optional(CONF_MAX_REFRESH_RATE): cv.positive_time_period_microseconds,
 | 
			
		||||
            cv.Required(CONF_CHIPSET): cv.one_of(*CHIPSETS, upper=True),
 | 
			
		||||
            cv.Optional(CONF_IS_RGBW, default=False): cv.boolean,
 | 
			
		||||
            cv.Optional(CONF_IS_WRGB, default=False): cv.boolean,
 | 
			
		||||
        }
 | 
			
		||||
    ),
 | 
			
		||||
    _validate_num_leds,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_OUTPUT_ID])
 | 
			
		||||
    await light.register_light(var, config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_num_leds(config[CONF_NUM_LEDS]))
 | 
			
		||||
    cg.add(var.set_pin(config[CONF_PIN]))
 | 
			
		||||
 | 
			
		||||
    if CONF_MAX_REFRESH_RATE in config:
 | 
			
		||||
        cg.add(var.set_max_refresh_rate(config[CONF_MAX_REFRESH_RATE]))
 | 
			
		||||
 | 
			
		||||
    chipset = CHIPSETS[config[CONF_CHIPSET]]
 | 
			
		||||
    cg.add(
 | 
			
		||||
        var.set_led_params(
 | 
			
		||||
            chipset.bit0,
 | 
			
		||||
            chipset.bit1,
 | 
			
		||||
            chipset.spi_frequency,
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_rgb_order(config[CONF_RGB_ORDER]))
 | 
			
		||||
    cg.add(var.set_is_rgbw(config[CONF_IS_RGBW]))
 | 
			
		||||
    cg.add(var.set_is_wrgb(config[CONF_IS_WRGB]))
 | 
			
		||||
@@ -4,7 +4,7 @@ from esphome.cpp_generator import MockObjClass
 | 
			
		||||
from esphome.cpp_helpers import setup_entity
 | 
			
		||||
from esphome import automation, core
 | 
			
		||||
from esphome.automation import Condition, maybe_simple_id
 | 
			
		||||
from esphome.components import mqtt
 | 
			
		||||
from esphome.components import mqtt, web_server
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_DELAY,
 | 
			
		||||
    CONF_DEVICE_CLASS,
 | 
			
		||||
@@ -27,6 +27,7 @@ from esphome.const import (
 | 
			
		||||
    CONF_TIMING,
 | 
			
		||||
    CONF_TRIGGER_ID,
 | 
			
		||||
    CONF_MQTT_ID,
 | 
			
		||||
    CONF_WEB_SERVER_ID,
 | 
			
		||||
    DEVICE_CLASS_BATTERY,
 | 
			
		||||
    DEVICE_CLASS_BATTERY_CHARGING,
 | 
			
		||||
    DEVICE_CLASS_CARBON_MONOXIDE,
 | 
			
		||||
@@ -385,70 +386,76 @@ def validate_click_timing(value):
 | 
			
		||||
    return value
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BINARY_SENSOR_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(): cv.declare_id(BinarySensor),
 | 
			
		||||
        cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(
 | 
			
		||||
            mqtt.MQTTBinarySensorComponent
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_PUBLISH_INITIAL_STATE): cv.boolean,
 | 
			
		||||
        cv.Optional(CONF_DEVICE_CLASS): validate_device_class,
 | 
			
		||||
        cv.Optional(CONF_FILTERS): validate_filters,
 | 
			
		||||
        cv.Optional(CONF_ON_PRESS): automation.validate_automation(
 | 
			
		||||
            {
 | 
			
		||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PressTrigger),
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_ON_RELEASE): automation.validate_automation(
 | 
			
		||||
            {
 | 
			
		||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ReleaseTrigger),
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_ON_CLICK): cv.All(
 | 
			
		||||
            automation.validate_automation(
 | 
			
		||||
BINARY_SENSOR_SCHEMA = (
 | 
			
		||||
    cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
 | 
			
		||||
    .extend(cv.MQTT_COMPONENT_SCHEMA)
 | 
			
		||||
    .extend(
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(BinarySensor),
 | 
			
		||||
            cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(
 | 
			
		||||
                mqtt.MQTTBinarySensorComponent
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_PUBLISH_INITIAL_STATE): cv.boolean,
 | 
			
		||||
            cv.Optional(CONF_DEVICE_CLASS): validate_device_class,
 | 
			
		||||
            cv.Optional(CONF_FILTERS): validate_filters,
 | 
			
		||||
            cv.Optional(CONF_ON_PRESS): automation.validate_automation(
 | 
			
		||||
                {
 | 
			
		||||
                    cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ClickTrigger),
 | 
			
		||||
                    cv.Optional(
 | 
			
		||||
                        CONF_MIN_LENGTH, default="50ms"
 | 
			
		||||
                    ): cv.positive_time_period_milliseconds,
 | 
			
		||||
                    cv.Optional(
 | 
			
		||||
                        CONF_MAX_LENGTH, default="350ms"
 | 
			
		||||
                    ): cv.positive_time_period_milliseconds,
 | 
			
		||||
                    cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PressTrigger),
 | 
			
		||||
                }
 | 
			
		||||
            ),
 | 
			
		||||
            validate_click_timing,
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_ON_DOUBLE_CLICK): cv.All(
 | 
			
		||||
            automation.validate_automation(
 | 
			
		||||
            cv.Optional(CONF_ON_RELEASE): automation.validate_automation(
 | 
			
		||||
                {
 | 
			
		||||
                    cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DoubleClickTrigger),
 | 
			
		||||
                    cv.Optional(
 | 
			
		||||
                        CONF_MIN_LENGTH, default="50ms"
 | 
			
		||||
                    ): cv.positive_time_period_milliseconds,
 | 
			
		||||
                    cv.Optional(
 | 
			
		||||
                        CONF_MAX_LENGTH, default="350ms"
 | 
			
		||||
                    ): cv.positive_time_period_milliseconds,
 | 
			
		||||
                    cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ReleaseTrigger),
 | 
			
		||||
                }
 | 
			
		||||
            ),
 | 
			
		||||
            validate_click_timing,
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_ON_MULTI_CLICK): automation.validate_automation(
 | 
			
		||||
            {
 | 
			
		||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(MultiClickTrigger),
 | 
			
		||||
                cv.Required(CONF_TIMING): cv.All(
 | 
			
		||||
                    [parse_multi_click_timing_str], validate_multi_click_timing
 | 
			
		||||
            cv.Optional(CONF_ON_CLICK): cv.All(
 | 
			
		||||
                automation.validate_automation(
 | 
			
		||||
                    {
 | 
			
		||||
                        cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ClickTrigger),
 | 
			
		||||
                        cv.Optional(
 | 
			
		||||
                            CONF_MIN_LENGTH, default="50ms"
 | 
			
		||||
                        ): cv.positive_time_period_milliseconds,
 | 
			
		||||
                        cv.Optional(
 | 
			
		||||
                            CONF_MAX_LENGTH, default="350ms"
 | 
			
		||||
                        ): cv.positive_time_period_milliseconds,
 | 
			
		||||
                    }
 | 
			
		||||
                ),
 | 
			
		||||
                cv.Optional(
 | 
			
		||||
                    CONF_INVALID_COOLDOWN, default="1s"
 | 
			
		||||
                ): cv.positive_time_period_milliseconds,
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_ON_STATE): automation.validate_automation(
 | 
			
		||||
            {
 | 
			
		||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateTrigger),
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
    }
 | 
			
		||||
                validate_click_timing,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_ON_DOUBLE_CLICK): cv.All(
 | 
			
		||||
                automation.validate_automation(
 | 
			
		||||
                    {
 | 
			
		||||
                        cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
 | 
			
		||||
                            DoubleClickTrigger
 | 
			
		||||
                        ),
 | 
			
		||||
                        cv.Optional(
 | 
			
		||||
                            CONF_MIN_LENGTH, default="50ms"
 | 
			
		||||
                        ): cv.positive_time_period_milliseconds,
 | 
			
		||||
                        cv.Optional(
 | 
			
		||||
                            CONF_MAX_LENGTH, default="350ms"
 | 
			
		||||
                        ): cv.positive_time_period_milliseconds,
 | 
			
		||||
                    }
 | 
			
		||||
                ),
 | 
			
		||||
                validate_click_timing,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_ON_MULTI_CLICK): automation.validate_automation(
 | 
			
		||||
                {
 | 
			
		||||
                    cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(MultiClickTrigger),
 | 
			
		||||
                    cv.Required(CONF_TIMING): cv.All(
 | 
			
		||||
                        [parse_multi_click_timing_str], validate_multi_click_timing
 | 
			
		||||
                    ),
 | 
			
		||||
                    cv.Optional(
 | 
			
		||||
                        CONF_INVALID_COOLDOWN, default="1s"
 | 
			
		||||
                    ): cv.positive_time_period_milliseconds,
 | 
			
		||||
                }
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_ON_STATE): automation.validate_automation(
 | 
			
		||||
                {
 | 
			
		||||
                    cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateTrigger),
 | 
			
		||||
                }
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
_UNDEF = object()
 | 
			
		||||
@@ -536,6 +543,10 @@ async def setup_binary_sensor_core_(var, config):
 | 
			
		||||
        mqtt_ = cg.new_Pvariable(mqtt_id, var)
 | 
			
		||||
        await mqtt.register_mqtt_component(mqtt_, config)
 | 
			
		||||
 | 
			
		||||
    if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None:
 | 
			
		||||
        web_server_ = await cg.get_variable(webserver_id)
 | 
			
		||||
        web_server.add_entity_to_sorting_list(web_server_, var, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def register_binary_sensor(var, config):
 | 
			
		||||
    if not CORE.has_id(config[CONF_ID]):
 | 
			
		||||
 
 | 
			
		||||
@@ -98,6 +98,11 @@ void binary_sensor::MultiClickTrigger::schedule_is_not_valid_(uint32_t max_lengt
 | 
			
		||||
    this->schedule_cooldown_();
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
void binary_sensor::MultiClickTrigger::cancel() {
 | 
			
		||||
  ESP_LOGV(TAG, "Multi Click: Sequence explicitly cancelled.");
 | 
			
		||||
  this->is_valid_ = false;
 | 
			
		||||
  this->schedule_cooldown_();
 | 
			
		||||
}
 | 
			
		||||
void binary_sensor::MultiClickTrigger::trigger_() {
 | 
			
		||||
  ESP_LOGV(TAG, "Multi Click: Hooray, multi click is valid. Triggering!");
 | 
			
		||||
  this->at_index_.reset();
 | 
			
		||||
 
 | 
			
		||||
@@ -105,6 +105,8 @@ class MultiClickTrigger : public Trigger<>, public Component {
 | 
			
		||||
 | 
			
		||||
  void set_invalid_cooldown(uint32_t invalid_cooldown) { this->invalid_cooldown_ = invalid_cooldown; }
 | 
			
		||||
 | 
			
		||||
  void cancel();
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  void on_state_(bool state);
 | 
			
		||||
  void schedule_cooldown_();
 | 
			
		||||
 
 | 
			
		||||
@@ -6,16 +6,6 @@
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ARDUINO
 | 
			
		||||
#include "mbedtls/aes.h"
 | 
			
		||||
#include "mbedtls/base64.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP_IDF
 | 
			
		||||
#define MBEDTLS_AES_ALT
 | 
			
		||||
#include <aes_alt.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace ble_presence {
 | 
			
		||||
 | 
			
		||||
@@ -72,7 +62,7 @@ class BLEPresenceDevice : public binary_sensor::BinarySensorInitiallyOff,
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      case MATCH_BY_IRK:
 | 
			
		||||
        if (resolve_irk_(device.address_uint64(), this->irk_)) {
 | 
			
		||||
        if (device.resolve_irk(this->irk_)) {
 | 
			
		||||
          this->set_found_(true);
 | 
			
		||||
          return true;
 | 
			
		||||
        }
 | 
			
		||||
@@ -142,43 +132,6 @@ class BLEPresenceDevice : public binary_sensor::BinarySensorInitiallyOff,
 | 
			
		||||
  bool check_ibeacon_minor_{false};
 | 
			
		||||
  bool check_minimum_rssi_{false};
 | 
			
		||||
 | 
			
		||||
  bool resolve_irk_(uint64_t addr64, const uint8_t *irk) {
 | 
			
		||||
    uint8_t ecb_key[16];
 | 
			
		||||
    uint8_t ecb_plaintext[16];
 | 
			
		||||
    uint8_t ecb_ciphertext[16];
 | 
			
		||||
 | 
			
		||||
    memcpy(&ecb_key, irk, 16);
 | 
			
		||||
    memset(&ecb_plaintext, 0, 16);
 | 
			
		||||
 | 
			
		||||
    ecb_plaintext[13] = (addr64 >> 40) & 0xff;
 | 
			
		||||
    ecb_plaintext[14] = (addr64 >> 32) & 0xff;
 | 
			
		||||
    ecb_plaintext[15] = (addr64 >> 24) & 0xff;
 | 
			
		||||
 | 
			
		||||
    mbedtls_aes_context ctx = {0, 0, {0}};
 | 
			
		||||
    mbedtls_aes_init(&ctx);
 | 
			
		||||
 | 
			
		||||
    if (mbedtls_aes_setkey_enc(&ctx, ecb_key, 128) != 0) {
 | 
			
		||||
      mbedtls_aes_free(&ctx);
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (mbedtls_aes_crypt_ecb(&ctx,
 | 
			
		||||
#ifdef USE_ARDUINO
 | 
			
		||||
                              MBEDTLS_AES_ENCRYPT,
 | 
			
		||||
#elif defined(USE_ESP_IDF)
 | 
			
		||||
                              ESP_AES_ENCRYPT,
 | 
			
		||||
#endif
 | 
			
		||||
                              ecb_plaintext, ecb_ciphertext) != 0) {
 | 
			
		||||
      mbedtls_aes_free(&ctx);
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    mbedtls_aes_free(&ctx);
 | 
			
		||||
 | 
			
		||||
    return ecb_ciphertext[15] == (addr64 & 0xff) && ecb_ciphertext[14] == ((addr64 >> 8) & 0xff) &&
 | 
			
		||||
           ecb_ciphertext[13] == ((addr64 >> 16) & 0xff);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  bool found_{false};
 | 
			
		||||
  uint32_t last_seen_{};
 | 
			
		||||
  uint32_t timeout_{};
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,10 @@ class BLERSSISensor : public sensor::Sensor, public esp32_ble_tracker::ESPBTDevi
 | 
			
		||||
    this->match_by_ = MATCH_BY_MAC_ADDRESS;
 | 
			
		||||
    this->address_ = address;
 | 
			
		||||
  }
 | 
			
		||||
  void set_irk(uint8_t *irk) {
 | 
			
		||||
    this->match_by_ = MATCH_BY_IRK;
 | 
			
		||||
    this->irk_ = irk;
 | 
			
		||||
  }
 | 
			
		||||
  void set_service_uuid16(uint16_t uuid) {
 | 
			
		||||
    this->match_by_ = MATCH_BY_SERVICE_UUID;
 | 
			
		||||
    this->uuid_ = esp32_ble_tracker::ESPBTUUID::from_uint16(uuid);
 | 
			
		||||
@@ -53,6 +57,13 @@ class BLERSSISensor : public sensor::Sensor, public esp32_ble_tracker::ESPBTDevi
 | 
			
		||||
          return true;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      case MATCH_BY_IRK:
 | 
			
		||||
        if (device.resolve_irk(this->irk_)) {
 | 
			
		||||
          this->publish_state(device.get_rssi());
 | 
			
		||||
          this->found_ = true;
 | 
			
		||||
          return true;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      case MATCH_BY_SERVICE_UUID:
 | 
			
		||||
        for (auto uuid : device.get_service_uuids()) {
 | 
			
		||||
          if (this->uuid_ == uuid) {
 | 
			
		||||
@@ -91,12 +102,13 @@ class BLERSSISensor : public sensor::Sensor, public esp32_ble_tracker::ESPBTDevi
 | 
			
		||||
  float get_setup_priority() const override { return setup_priority::DATA; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  enum MatchType { MATCH_BY_MAC_ADDRESS, MATCH_BY_SERVICE_UUID, MATCH_BY_IBEACON_UUID };
 | 
			
		||||
  enum MatchType { MATCH_BY_MAC_ADDRESS, MATCH_BY_IRK, MATCH_BY_SERVICE_UUID, MATCH_BY_IBEACON_UUID };
 | 
			
		||||
  MatchType match_by_;
 | 
			
		||||
 | 
			
		||||
  bool found_{false};
 | 
			
		||||
 | 
			
		||||
  uint64_t address_;
 | 
			
		||||
  uint8_t *irk_;
 | 
			
		||||
 | 
			
		||||
  esp32_ble_tracker::ESPBTUUID uuid_;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,8 @@ from esphome.const import (
 | 
			
		||||
    UNIT_DECIBEL_MILLIWATT,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CONF_IRK = "irk"
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ["esp32_ble_tracker"]
 | 
			
		||||
 | 
			
		||||
ble_rssi_ns = cg.esphome_ns.namespace("ble_rssi")
 | 
			
		||||
@@ -39,6 +41,7 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
    .extend(
 | 
			
		||||
        {
 | 
			
		||||
            cv.Optional(CONF_MAC_ADDRESS): cv.mac_address,
 | 
			
		||||
            cv.Optional(CONF_IRK): cv.uuid,
 | 
			
		||||
            cv.Optional(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid,
 | 
			
		||||
            cv.Optional(CONF_IBEACON_MAJOR): cv.uint16_t,
 | 
			
		||||
            cv.Optional(CONF_IBEACON_MINOR): cv.uint16_t,
 | 
			
		||||
@@ -47,7 +50,9 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
    )
 | 
			
		||||
    .extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
 | 
			
		||||
    .extend(cv.COMPONENT_SCHEMA),
 | 
			
		||||
    cv.has_exactly_one_key(CONF_MAC_ADDRESS, CONF_SERVICE_UUID, CONF_IBEACON_UUID),
 | 
			
		||||
    cv.has_exactly_one_key(
 | 
			
		||||
        CONF_MAC_ADDRESS, CONF_IRK, CONF_SERVICE_UUID, CONF_IBEACON_UUID
 | 
			
		||||
    ),
 | 
			
		||||
    _validate,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -60,6 +65,10 @@ async def to_code(config):
 | 
			
		||||
    if mac_address := config.get(CONF_MAC_ADDRESS):
 | 
			
		||||
        cg.add(var.set_address(mac_address.as_hex))
 | 
			
		||||
 | 
			
		||||
    if irk := config.get(CONF_IRK):
 | 
			
		||||
        irk = esp32_ble_tracker.as_hex_array(str(irk))
 | 
			
		||||
        cg.add(var.set_irk(irk))
 | 
			
		||||
 | 
			
		||||
    if service_uuid := config.get(CONF_SERVICE_UUID):
 | 
			
		||||
        if len(service_uuid) == len(esp32_ble_tracker.bt_uuid16_format):
 | 
			
		||||
            cg.add(var.set_service_uuid16(esp32_ble_tracker.as_hex(service_uuid)))
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import i2c, esp32
 | 
			
		||||
from esphome.const import CONF_ID, CONF_TEMPERATURE_OFFSET
 | 
			
		||||
from esphome.const import CONF_ID, CONF_SAMPLE_RATE, CONF_TEMPERATURE_OFFSET
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ["@trvrnrth"]
 | 
			
		||||
DEPENDENCIES = ["i2c"]
 | 
			
		||||
@@ -11,7 +11,6 @@ MULTI_CONF = True
 | 
			
		||||
CONF_BME680_BSEC_ID = "bme680_bsec_id"
 | 
			
		||||
CONF_IAQ_MODE = "iaq_mode"
 | 
			
		||||
CONF_SUPPLY_VOLTAGE = "supply_voltage"
 | 
			
		||||
CONF_SAMPLE_RATE = "sample_rate"
 | 
			
		||||
CONF_STATE_SAVE_INTERVAL = "state_save_interval"
 | 
			
		||||
 | 
			
		||||
bme680_bsec_ns = cg.esphome_ns.namespace("bme680_bsec")
 | 
			
		||||
 
 | 
			
		||||
@@ -4,33 +4,33 @@ from esphome.components import sensor
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_GAS_RESISTANCE,
 | 
			
		||||
    CONF_HUMIDITY,
 | 
			
		||||
    CONF_IAQ_ACCURACY,
 | 
			
		||||
    CONF_PRESSURE,
 | 
			
		||||
    CONF_SAMPLE_RATE,
 | 
			
		||||
    CONF_TEMPERATURE,
 | 
			
		||||
    DEVICE_CLASS_ATMOSPHERIC_PRESSURE,
 | 
			
		||||
    DEVICE_CLASS_CARBON_DIOXIDE,
 | 
			
		||||
    DEVICE_CLASS_HUMIDITY,
 | 
			
		||||
    DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS,
 | 
			
		||||
    DEVICE_CLASS_ATMOSPHERIC_PRESSURE,
 | 
			
		||||
    DEVICE_CLASS_TEMPERATURE,
 | 
			
		||||
    DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS,
 | 
			
		||||
    ICON_GAS_CYLINDER,
 | 
			
		||||
    ICON_GAUGE,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    UNIT_CELSIUS,
 | 
			
		||||
    UNIT_HECTOPASCAL,
 | 
			
		||||
    UNIT_OHM,
 | 
			
		||||
    UNIT_PARTS_PER_MILLION,
 | 
			
		||||
    UNIT_PERCENT,
 | 
			
		||||
    ICON_GAS_CYLINDER,
 | 
			
		||||
    ICON_GAUGE,
 | 
			
		||||
)
 | 
			
		||||
from . import (
 | 
			
		||||
    BME680BSECComponent,
 | 
			
		||||
    CONF_BME680_BSEC_ID,
 | 
			
		||||
    CONF_SAMPLE_RATE,
 | 
			
		||||
    SAMPLE_RATE_OPTIONS,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ["bme680_bsec"]
 | 
			
		||||
 | 
			
		||||
CONF_IAQ = "iaq"
 | 
			
		||||
CONF_IAQ_ACCURACY = "iaq_accuracy"
 | 
			
		||||
CONF_CO2_EQUIVALENT = "co2_equivalent"
 | 
			
		||||
CONF_BREATH_VOC_EQUIVALENT = "breath_voc_equivalent"
 | 
			
		||||
UNIT_IAQ = "IAQ"
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,11 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import text_sensor
 | 
			
		||||
from esphome.const import CONF_IAQ_ACCURACY
 | 
			
		||||
from . import BME680BSECComponent, CONF_BME680_BSEC_ID
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ["bme680_bsec"]
 | 
			
		||||
 | 
			
		||||
CONF_IAQ_ACCURACY = "iaq_accuracy"
 | 
			
		||||
ICON_ACCURACY = "mdi:checkbox-marked-circle-outline"
 | 
			
		||||
 | 
			
		||||
TYPES = [CONF_IAQ_ACCURACY]
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@ import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome import automation
 | 
			
		||||
from esphome.automation import maybe_simple_id
 | 
			
		||||
from esphome.components import mqtt
 | 
			
		||||
from esphome.components import mqtt, web_server
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_DEVICE_CLASS,
 | 
			
		||||
    CONF_ENTITY_CATEGORY,
 | 
			
		||||
@@ -11,6 +11,7 @@ from esphome.const import (
 | 
			
		||||
    CONF_ON_PRESS,
 | 
			
		||||
    CONF_TRIGGER_ID,
 | 
			
		||||
    CONF_MQTT_ID,
 | 
			
		||||
    CONF_WEB_SERVER_ID,
 | 
			
		||||
    DEVICE_CLASS_EMPTY,
 | 
			
		||||
    DEVICE_CLASS_IDENTIFY,
 | 
			
		||||
    DEVICE_CLASS_RESTART,
 | 
			
		||||
@@ -43,16 +44,20 @@ ButtonPressTrigger = button_ns.class_(
 | 
			
		||||
validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BUTTON_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
 | 
			
		||||
    {
 | 
			
		||||
        cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTButtonComponent),
 | 
			
		||||
        cv.Optional(CONF_DEVICE_CLASS): validate_device_class,
 | 
			
		||||
        cv.Optional(CONF_ON_PRESS): automation.validate_automation(
 | 
			
		||||
            {
 | 
			
		||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ButtonPressTrigger),
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
    }
 | 
			
		||||
BUTTON_SCHEMA = (
 | 
			
		||||
    cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
 | 
			
		||||
    .extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA)
 | 
			
		||||
    .extend(
 | 
			
		||||
        {
 | 
			
		||||
            cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTButtonComponent),
 | 
			
		||||
            cv.Optional(CONF_DEVICE_CLASS): validate_device_class,
 | 
			
		||||
            cv.Optional(CONF_ON_PRESS): automation.validate_automation(
 | 
			
		||||
                {
 | 
			
		||||
                    cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ButtonPressTrigger),
 | 
			
		||||
                }
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
_UNDEF = object()
 | 
			
		||||
@@ -92,6 +97,10 @@ async def setup_button_core_(var, config):
 | 
			
		||||
        mqtt_ = cg.new_Pvariable(mqtt_id, var)
 | 
			
		||||
        await mqtt.register_mqtt_component(mqtt_, config)
 | 
			
		||||
 | 
			
		||||
    if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None:
 | 
			
		||||
        web_server_ = await cg.get_variable(webserver_id)
 | 
			
		||||
        web_server.add_entity_to_sorting_list(web_server_, var, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def register_button(var, config):
 | 
			
		||||
    if not CORE.has_id(config[CONF_ID]):
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@ import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.cpp_helpers import setup_entity
 | 
			
		||||
from esphome import automation
 | 
			
		||||
from esphome.components import mqtt
 | 
			
		||||
from esphome.components import mqtt, web_server
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_ACTION_STATE_TOPIC,
 | 
			
		||||
    CONF_AWAY,
 | 
			
		||||
@@ -44,6 +44,7 @@ from esphome.const import (
 | 
			
		||||
    CONF_TRIGGER_ID,
 | 
			
		||||
    CONF_VISUAL,
 | 
			
		||||
    CONF_MQTT_ID,
 | 
			
		||||
    CONF_WEB_SERVER_ID,
 | 
			
		||||
)
 | 
			
		||||
from esphome.core import CORE, coroutine_with_priority
 | 
			
		||||
 | 
			
		||||
@@ -150,93 +151,97 @@ VISUAL_TEMPERATURE_STEP_SCHEMA = cv.Any(
 | 
			
		||||
    ),
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CLIMATE_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(): cv.declare_id(Climate),
 | 
			
		||||
        cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTClimateComponent),
 | 
			
		||||
        cv.Optional(CONF_VISUAL, default={}): cv.Schema(
 | 
			
		||||
            {
 | 
			
		||||
                cv.Optional(CONF_MIN_TEMPERATURE): cv.temperature,
 | 
			
		||||
                cv.Optional(CONF_MAX_TEMPERATURE): cv.temperature,
 | 
			
		||||
                cv.Optional(CONF_TEMPERATURE_STEP): VISUAL_TEMPERATURE_STEP_SCHEMA,
 | 
			
		||||
                cv.Optional(CONF_MIN_HUMIDITY): cv.percentage_int,
 | 
			
		||||
                cv.Optional(CONF_MAX_HUMIDITY): cv.percentage_int,
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_ACTION_STATE_TOPIC): cv.All(
 | 
			
		||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_AWAY_COMMAND_TOPIC): cv.All(
 | 
			
		||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_AWAY_STATE_TOPIC): cv.All(
 | 
			
		||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_CURRENT_TEMPERATURE_STATE_TOPIC): cv.All(
 | 
			
		||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_CURRENT_HUMIDITY_STATE_TOPIC): cv.All(
 | 
			
		||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_FAN_MODE_COMMAND_TOPIC): cv.All(
 | 
			
		||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_FAN_MODE_STATE_TOPIC): cv.All(
 | 
			
		||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_MODE_COMMAND_TOPIC): cv.All(
 | 
			
		||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_MODE_STATE_TOPIC): cv.All(
 | 
			
		||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_PRESET_COMMAND_TOPIC): cv.All(
 | 
			
		||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_PRESET_STATE_TOPIC): cv.All(
 | 
			
		||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_SWING_MODE_COMMAND_TOPIC): cv.All(
 | 
			
		||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_SWING_MODE_STATE_TOPIC): cv.All(
 | 
			
		||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_TARGET_TEMPERATURE_COMMAND_TOPIC): cv.All(
 | 
			
		||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_TARGET_TEMPERATURE_STATE_TOPIC): cv.All(
 | 
			
		||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_TARGET_TEMPERATURE_HIGH_COMMAND_TOPIC): cv.All(
 | 
			
		||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_TARGET_TEMPERATURE_HIGH_STATE_TOPIC): cv.All(
 | 
			
		||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_TARGET_TEMPERATURE_LOW_COMMAND_TOPIC): cv.All(
 | 
			
		||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_TARGET_TEMPERATURE_LOW_STATE_TOPIC): cv.All(
 | 
			
		||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_TARGET_HUMIDITY_COMMAND_TOPIC): cv.All(
 | 
			
		||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_TARGET_HUMIDITY_STATE_TOPIC): cv.All(
 | 
			
		||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_ON_CONTROL): automation.validate_automation(
 | 
			
		||||
            {
 | 
			
		||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ControlTrigger),
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_ON_STATE): automation.validate_automation(
 | 
			
		||||
            {
 | 
			
		||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateTrigger),
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
    }
 | 
			
		||||
CLIMATE_SCHEMA = (
 | 
			
		||||
    cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
 | 
			
		||||
    .extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA)
 | 
			
		||||
    .extend(
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(Climate),
 | 
			
		||||
            cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTClimateComponent),
 | 
			
		||||
            cv.Optional(CONF_VISUAL, default={}): cv.Schema(
 | 
			
		||||
                {
 | 
			
		||||
                    cv.Optional(CONF_MIN_TEMPERATURE): cv.temperature,
 | 
			
		||||
                    cv.Optional(CONF_MAX_TEMPERATURE): cv.temperature,
 | 
			
		||||
                    cv.Optional(CONF_TEMPERATURE_STEP): VISUAL_TEMPERATURE_STEP_SCHEMA,
 | 
			
		||||
                    cv.Optional(CONF_MIN_HUMIDITY): cv.percentage_int,
 | 
			
		||||
                    cv.Optional(CONF_MAX_HUMIDITY): cv.percentage_int,
 | 
			
		||||
                }
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_ACTION_STATE_TOPIC): cv.All(
 | 
			
		||||
                cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_AWAY_COMMAND_TOPIC): cv.All(
 | 
			
		||||
                cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_AWAY_STATE_TOPIC): cv.All(
 | 
			
		||||
                cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_CURRENT_TEMPERATURE_STATE_TOPIC): cv.All(
 | 
			
		||||
                cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_CURRENT_HUMIDITY_STATE_TOPIC): cv.All(
 | 
			
		||||
                cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_FAN_MODE_COMMAND_TOPIC): cv.All(
 | 
			
		||||
                cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_FAN_MODE_STATE_TOPIC): cv.All(
 | 
			
		||||
                cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_MODE_COMMAND_TOPIC): cv.All(
 | 
			
		||||
                cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_MODE_STATE_TOPIC): cv.All(
 | 
			
		||||
                cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_PRESET_COMMAND_TOPIC): cv.All(
 | 
			
		||||
                cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_PRESET_STATE_TOPIC): cv.All(
 | 
			
		||||
                cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_SWING_MODE_COMMAND_TOPIC): cv.All(
 | 
			
		||||
                cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_SWING_MODE_STATE_TOPIC): cv.All(
 | 
			
		||||
                cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_TARGET_TEMPERATURE_COMMAND_TOPIC): cv.All(
 | 
			
		||||
                cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_TARGET_TEMPERATURE_STATE_TOPIC): cv.All(
 | 
			
		||||
                cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_TARGET_TEMPERATURE_HIGH_COMMAND_TOPIC): cv.All(
 | 
			
		||||
                cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_TARGET_TEMPERATURE_HIGH_STATE_TOPIC): cv.All(
 | 
			
		||||
                cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_TARGET_TEMPERATURE_LOW_COMMAND_TOPIC): cv.All(
 | 
			
		||||
                cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_TARGET_TEMPERATURE_LOW_STATE_TOPIC): cv.All(
 | 
			
		||||
                cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_TARGET_HUMIDITY_COMMAND_TOPIC): cv.All(
 | 
			
		||||
                cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_TARGET_HUMIDITY_STATE_TOPIC): cv.All(
 | 
			
		||||
                cv.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_ON_CONTROL): automation.validate_automation(
 | 
			
		||||
                {
 | 
			
		||||
                    cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ControlTrigger),
 | 
			
		||||
                }
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_ON_STATE): automation.validate_automation(
 | 
			
		||||
                {
 | 
			
		||||
                    cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateTrigger),
 | 
			
		||||
                }
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -403,6 +408,10 @@ async def setup_climate_core_(var, config):
 | 
			
		||||
            trigger, [(ClimateCall.operator("ref"), "x")], conf
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None:
 | 
			
		||||
        web_server_ = await cg.get_variable(webserver_id)
 | 
			
		||||
        web_server.add_entity_to_sorting_list(web_server_, var, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def register_climate(var, config):
 | 
			
		||||
    if not CORE.has_id(config[CONF_ID]):
 | 
			
		||||
 
 | 
			
		||||
@@ -574,21 +574,25 @@ void Climate::dump_traits_(const char *tag) {
 | 
			
		||||
  ESP_LOGCONFIG(tag, "      - Max temperature: %.1f", traits.get_visual_max_temperature());
 | 
			
		||||
  ESP_LOGCONFIG(tag, "      - Temperature step:");
 | 
			
		||||
  ESP_LOGCONFIG(tag, "          Target: %.1f", traits.get_visual_target_temperature_step());
 | 
			
		||||
  ESP_LOGCONFIG(tag, "          Current: %.1f", traits.get_visual_current_temperature_step());
 | 
			
		||||
  ESP_LOGCONFIG(tag, "      - Min humidity: %.0f", traits.get_visual_min_humidity());
 | 
			
		||||
  ESP_LOGCONFIG(tag, "      - Max humidity: %.0f", traits.get_visual_max_humidity());
 | 
			
		||||
  if (traits.get_supports_current_temperature()) {
 | 
			
		||||
    ESP_LOGCONFIG(tag, "  [x] Supports current temperature");
 | 
			
		||||
    ESP_LOGCONFIG(tag, "          Current: %.1f", traits.get_visual_current_temperature_step());
 | 
			
		||||
  }
 | 
			
		||||
  if (traits.get_supports_current_humidity()) {
 | 
			
		||||
    ESP_LOGCONFIG(tag, "  [x] Supports current humidity");
 | 
			
		||||
  if (traits.get_supports_target_humidity() || traits.get_supports_current_humidity()) {
 | 
			
		||||
    ESP_LOGCONFIG(tag, "      - Min humidity: %.0f", traits.get_visual_min_humidity());
 | 
			
		||||
    ESP_LOGCONFIG(tag, "      - Max humidity: %.0f", traits.get_visual_max_humidity());
 | 
			
		||||
  }
 | 
			
		||||
  if (traits.get_supports_two_point_target_temperature()) {
 | 
			
		||||
    ESP_LOGCONFIG(tag, "  [x] Supports two-point target temperature");
 | 
			
		||||
  }
 | 
			
		||||
  if (traits.get_supports_current_temperature()) {
 | 
			
		||||
    ESP_LOGCONFIG(tag, "  [x] Supports current temperature");
 | 
			
		||||
  }
 | 
			
		||||
  if (traits.get_supports_target_humidity()) {
 | 
			
		||||
    ESP_LOGCONFIG(tag, "  [x] Supports target humidity");
 | 
			
		||||
  }
 | 
			
		||||
  if (traits.get_supports_current_humidity()) {
 | 
			
		||||
    ESP_LOGCONFIG(tag, "  [x] Supports current humidity");
 | 
			
		||||
  }
 | 
			
		||||
  if (traits.get_supports_action()) {
 | 
			
		||||
    ESP_LOGCONFIG(tag, "  [x] Supports action");
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -73,7 +73,7 @@ class ClimateTraits {
 | 
			
		||||
  ESPDEPRECATED("This method is deprecated, use set_supported_modes() instead", "v1.20")
 | 
			
		||||
  void set_supports_dry_mode(bool supports_dry_mode) { set_mode_support_(CLIMATE_MODE_DRY, supports_dry_mode); }
 | 
			
		||||
  bool supports_mode(ClimateMode mode) const { return supported_modes_.count(mode); }
 | 
			
		||||
  std::set<ClimateMode> get_supported_modes() const { return supported_modes_; }
 | 
			
		||||
  const std::set<ClimateMode> &get_supported_modes() const { return supported_modes_; }
 | 
			
		||||
 | 
			
		||||
  void set_supports_action(bool supports_action) { supports_action_ = supports_action; }
 | 
			
		||||
  bool get_supports_action() const { return supports_action_; }
 | 
			
		||||
@@ -101,7 +101,7 @@ class ClimateTraits {
 | 
			
		||||
  void set_supports_fan_mode_diffuse(bool supported) { set_fan_mode_support_(CLIMATE_FAN_DIFFUSE, supported); }
 | 
			
		||||
  bool supports_fan_mode(ClimateFanMode fan_mode) const { return supported_fan_modes_.count(fan_mode); }
 | 
			
		||||
  bool get_supports_fan_modes() const { return !supported_fan_modes_.empty() || !supported_custom_fan_modes_.empty(); }
 | 
			
		||||
  std::set<ClimateFanMode> get_supported_fan_modes() const { return supported_fan_modes_; }
 | 
			
		||||
  const std::set<ClimateFanMode> &get_supported_fan_modes() const { return supported_fan_modes_; }
 | 
			
		||||
 | 
			
		||||
  void set_supported_custom_fan_modes(std::set<std::string> supported_custom_fan_modes) {
 | 
			
		||||
    supported_custom_fan_modes_ = std::move(supported_custom_fan_modes);
 | 
			
		||||
@@ -140,7 +140,7 @@ class ClimateTraits {
 | 
			
		||||
  }
 | 
			
		||||
  bool supports_swing_mode(ClimateSwingMode swing_mode) const { return supported_swing_modes_.count(swing_mode); }
 | 
			
		||||
  bool get_supports_swing_modes() const { return !supported_swing_modes_.empty(); }
 | 
			
		||||
  std::set<ClimateSwingMode> get_supported_swing_modes() const { return supported_swing_modes_; }
 | 
			
		||||
  const std::set<ClimateSwingMode> &get_supported_swing_modes() const { return supported_swing_modes_; }
 | 
			
		||||
 | 
			
		||||
  float get_visual_min_temperature() const { return visual_min_temperature_; }
 | 
			
		||||
  void set_visual_min_temperature(float visual_min_temperature) { visual_min_temperature_ = visual_min_temperature; }
 | 
			
		||||
 
 | 
			
		||||
@@ -6,18 +6,24 @@ namespace climate_ir_lg {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "climate.climate_ir_lg";
 | 
			
		||||
 | 
			
		||||
const uint32_t COMMAND_ON = 0x00000;
 | 
			
		||||
const uint32_t COMMAND_ON_AI = 0x03000;
 | 
			
		||||
const uint32_t COMMAND_COOL = 0x08000;
 | 
			
		||||
const uint32_t COMMAND_HEAT = 0x0C000;
 | 
			
		||||
// Commands
 | 
			
		||||
const uint32_t COMMAND_MASK = 0xFF000;
 | 
			
		||||
const uint32_t COMMAND_OFF = 0xC0000;
 | 
			
		||||
const uint32_t COMMAND_SWING = 0x10000;
 | 
			
		||||
// On, 25C, Mode: Auto, Fan: Auto, Zone Follow: Off, Sensor Temp: Ignore.
 | 
			
		||||
const uint32_t COMMAND_AUTO = 0x0B000;
 | 
			
		||||
const uint32_t COMMAND_DRY_FAN = 0x09000;
 | 
			
		||||
 | 
			
		||||
const uint32_t COMMAND_MASK = 0xFF000;
 | 
			
		||||
const uint32_t COMMAND_ON_COOL = 0x00000;
 | 
			
		||||
const uint32_t COMMAND_ON_DRY = 0x01000;
 | 
			
		||||
const uint32_t COMMAND_ON_FAN_ONLY = 0x02000;
 | 
			
		||||
const uint32_t COMMAND_ON_AI = 0x03000;
 | 
			
		||||
const uint32_t COMMAND_ON_HEAT = 0x04000;
 | 
			
		||||
 | 
			
		||||
const uint32_t COMMAND_COOL = 0x08000;
 | 
			
		||||
const uint32_t COMMAND_DRY = 0x09000;
 | 
			
		||||
const uint32_t COMMAND_FAN_ONLY = 0x0A000;
 | 
			
		||||
const uint32_t COMMAND_AI = 0x0B000;
 | 
			
		||||
const uint32_t COMMAND_HEAT = 0x0C000;
 | 
			
		||||
 | 
			
		||||
// Fan speed
 | 
			
		||||
const uint32_t FAN_MASK = 0xF0;
 | 
			
		||||
const uint32_t FAN_AUTO = 0x50;
 | 
			
		||||
const uint32_t FAN_MIN = 0x00;
 | 
			
		||||
@@ -35,69 +41,67 @@ void LgIrClimate::transmit_state() {
 | 
			
		||||
  uint32_t remote_state = 0x8800000;
 | 
			
		||||
 | 
			
		||||
  // ESP_LOGD(TAG, "climate_lg_ir mode_before_ code: 0x%02X", modeBefore_);
 | 
			
		||||
 | 
			
		||||
  // Set command
 | 
			
		||||
  if (send_swing_cmd_) {
 | 
			
		||||
    send_swing_cmd_ = false;
 | 
			
		||||
    remote_state |= COMMAND_SWING;
 | 
			
		||||
  } else {
 | 
			
		||||
    if (mode_before_ == climate::CLIMATE_MODE_OFF && this->mode == climate::CLIMATE_MODE_HEAT_COOL) {
 | 
			
		||||
      remote_state |= COMMAND_ON_AI;
 | 
			
		||||
    } else if (mode_before_ == climate::CLIMATE_MODE_OFF && this->mode != climate::CLIMATE_MODE_OFF) {
 | 
			
		||||
      remote_state |= COMMAND_ON;
 | 
			
		||||
      this->mode = climate::CLIMATE_MODE_COOL;
 | 
			
		||||
    } else {
 | 
			
		||||
      switch (this->mode) {
 | 
			
		||||
        case climate::CLIMATE_MODE_COOL:
 | 
			
		||||
          remote_state |= COMMAND_COOL;
 | 
			
		||||
          break;
 | 
			
		||||
        case climate::CLIMATE_MODE_HEAT:
 | 
			
		||||
          remote_state |= COMMAND_HEAT;
 | 
			
		||||
          break;
 | 
			
		||||
        case climate::CLIMATE_MODE_HEAT_COOL:
 | 
			
		||||
          remote_state |= COMMAND_AUTO;
 | 
			
		||||
          break;
 | 
			
		||||
        case climate::CLIMATE_MODE_DRY:
 | 
			
		||||
          remote_state |= COMMAND_DRY_FAN;
 | 
			
		||||
          break;
 | 
			
		||||
        case climate::CLIMATE_MODE_OFF:
 | 
			
		||||
        default:
 | 
			
		||||
          remote_state |= COMMAND_OFF;
 | 
			
		||||
          break;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    mode_before_ = this->mode;
 | 
			
		||||
 | 
			
		||||
    ESP_LOGD(TAG, "climate_lg_ir mode code: 0x%02X", this->mode);
 | 
			
		||||
 | 
			
		||||
    if (this->mode == climate::CLIMATE_MODE_OFF) {
 | 
			
		||||
      remote_state |= FAN_AUTO;
 | 
			
		||||
    } else if (this->mode == climate::CLIMATE_MODE_COOL || this->mode == climate::CLIMATE_MODE_DRY ||
 | 
			
		||||
               this->mode == climate::CLIMATE_MODE_HEAT) {
 | 
			
		||||
      switch (this->fan_mode.value()) {
 | 
			
		||||
        case climate::CLIMATE_FAN_HIGH:
 | 
			
		||||
          remote_state |= FAN_MAX;
 | 
			
		||||
          break;
 | 
			
		||||
        case climate::CLIMATE_FAN_MEDIUM:
 | 
			
		||||
          remote_state |= FAN_MED;
 | 
			
		||||
          break;
 | 
			
		||||
        case climate::CLIMATE_FAN_LOW:
 | 
			
		||||
          remote_state |= FAN_MIN;
 | 
			
		||||
          break;
 | 
			
		||||
        case climate::CLIMATE_FAN_AUTO:
 | 
			
		||||
        default:
 | 
			
		||||
          remote_state |= FAN_AUTO;
 | 
			
		||||
          break;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (this->mode == climate::CLIMATE_MODE_HEAT_COOL) {
 | 
			
		||||
      this->fan_mode = climate::CLIMATE_FAN_AUTO;
 | 
			
		||||
      // remote_state |= FAN_MODE_AUTO_DRY;
 | 
			
		||||
    }
 | 
			
		||||
    if (this->mode == climate::CLIMATE_MODE_COOL || this->mode == climate::CLIMATE_MODE_HEAT) {
 | 
			
		||||
      auto temp = (uint8_t) roundf(clamp<float>(this->target_temperature, TEMP_MIN, TEMP_MAX));
 | 
			
		||||
      remote_state |= ((temp - 15) << TEMP_SHIFT);
 | 
			
		||||
    bool climate_is_off = (mode_before_ == climate::CLIMATE_MODE_OFF);
 | 
			
		||||
    switch (this->mode) {
 | 
			
		||||
      case climate::CLIMATE_MODE_COOL:
 | 
			
		||||
        remote_state |= climate_is_off ? COMMAND_ON_COOL : COMMAND_COOL;
 | 
			
		||||
        break;
 | 
			
		||||
      case climate::CLIMATE_MODE_DRY:
 | 
			
		||||
        remote_state |= climate_is_off ? COMMAND_ON_DRY : COMMAND_DRY;
 | 
			
		||||
        break;
 | 
			
		||||
      case climate::CLIMATE_MODE_FAN_ONLY:
 | 
			
		||||
        remote_state |= climate_is_off ? COMMAND_ON_FAN_ONLY : COMMAND_FAN_ONLY;
 | 
			
		||||
        break;
 | 
			
		||||
      case climate::CLIMATE_MODE_HEAT_COOL:
 | 
			
		||||
        remote_state |= climate_is_off ? COMMAND_ON_AI : COMMAND_AI;
 | 
			
		||||
        break;
 | 
			
		||||
      case climate::CLIMATE_MODE_HEAT:
 | 
			
		||||
        remote_state |= climate_is_off ? COMMAND_ON_HEAT : COMMAND_HEAT;
 | 
			
		||||
        break;
 | 
			
		||||
      case climate::CLIMATE_MODE_OFF:
 | 
			
		||||
      default:
 | 
			
		||||
        remote_state |= COMMAND_OFF;
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  mode_before_ = this->mode;
 | 
			
		||||
 | 
			
		||||
  ESP_LOGD(TAG, "climate_lg_ir mode code: 0x%02X", this->mode);
 | 
			
		||||
 | 
			
		||||
  // Set fan speed
 | 
			
		||||
  if (this->mode == climate::CLIMATE_MODE_OFF) {
 | 
			
		||||
    remote_state |= FAN_AUTO;
 | 
			
		||||
  } else {
 | 
			
		||||
    switch (this->fan_mode.value()) {
 | 
			
		||||
      case climate::CLIMATE_FAN_HIGH:
 | 
			
		||||
        remote_state |= FAN_MAX;
 | 
			
		||||
        break;
 | 
			
		||||
      case climate::CLIMATE_FAN_MEDIUM:
 | 
			
		||||
        remote_state |= FAN_MED;
 | 
			
		||||
        break;
 | 
			
		||||
      case climate::CLIMATE_FAN_LOW:
 | 
			
		||||
        remote_state |= FAN_MIN;
 | 
			
		||||
        break;
 | 
			
		||||
      case climate::CLIMATE_FAN_AUTO:
 | 
			
		||||
      default:
 | 
			
		||||
        remote_state |= FAN_AUTO;
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Set temperature
 | 
			
		||||
  if (this->mode == climate::CLIMATE_MODE_COOL || this->mode == climate::CLIMATE_MODE_HEAT) {
 | 
			
		||||
    auto temp = (uint8_t) roundf(clamp<float>(this->target_temperature, TEMP_MIN, TEMP_MAX));
 | 
			
		||||
    remote_state |= ((temp - 15) << TEMP_SHIFT);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  transmit_(remote_state);
 | 
			
		||||
  this->publish_state();
 | 
			
		||||
}
 | 
			
		||||
@@ -125,37 +129,42 @@ bool LgIrClimate::on_receive(remote_base::RemoteReceiveData data) {
 | 
			
		||||
  if ((remote_state & 0xFF00000) != 0x8800000)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  if ((remote_state & COMMAND_MASK) == COMMAND_ON) {
 | 
			
		||||
    this->mode = climate::CLIMATE_MODE_COOL;
 | 
			
		||||
  } else if ((remote_state & COMMAND_MASK) == COMMAND_ON_AI) {
 | 
			
		||||
    this->mode = climate::CLIMATE_MODE_HEAT_COOL;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Get command
 | 
			
		||||
  if ((remote_state & COMMAND_MASK) == COMMAND_OFF) {
 | 
			
		||||
    this->mode = climate::CLIMATE_MODE_OFF;
 | 
			
		||||
  } else if ((remote_state & COMMAND_MASK) == COMMAND_SWING) {
 | 
			
		||||
    this->swing_mode =
 | 
			
		||||
        this->swing_mode == climate::CLIMATE_SWING_OFF ? climate::CLIMATE_SWING_VERTICAL : climate::CLIMATE_SWING_OFF;
 | 
			
		||||
  } else {
 | 
			
		||||
    if ((remote_state & COMMAND_MASK) == COMMAND_AUTO) {
 | 
			
		||||
      this->mode = climate::CLIMATE_MODE_HEAT_COOL;
 | 
			
		||||
    } else if ((remote_state & COMMAND_MASK) == COMMAND_DRY_FAN) {
 | 
			
		||||
      this->mode = climate::CLIMATE_MODE_DRY;
 | 
			
		||||
    } else if ((remote_state & COMMAND_MASK) == COMMAND_HEAT) {
 | 
			
		||||
      this->mode = climate::CLIMATE_MODE_HEAT;
 | 
			
		||||
    } else {
 | 
			
		||||
      this->mode = climate::CLIMATE_MODE_COOL;
 | 
			
		||||
    switch (remote_state & COMMAND_MASK) {
 | 
			
		||||
      case COMMAND_DRY:
 | 
			
		||||
      case COMMAND_ON_DRY:
 | 
			
		||||
        this->mode = climate::CLIMATE_MODE_DRY;
 | 
			
		||||
        break;
 | 
			
		||||
      case COMMAND_FAN_ONLY:
 | 
			
		||||
      case COMMAND_ON_FAN_ONLY:
 | 
			
		||||
        this->mode = climate::CLIMATE_MODE_FAN_ONLY;
 | 
			
		||||
        break;
 | 
			
		||||
      case COMMAND_AI:
 | 
			
		||||
      case COMMAND_ON_AI:
 | 
			
		||||
        this->mode = climate::CLIMATE_MODE_HEAT_COOL;
 | 
			
		||||
        break;
 | 
			
		||||
      case COMMAND_HEAT:
 | 
			
		||||
      case COMMAND_ON_HEAT:
 | 
			
		||||
        this->mode = climate::CLIMATE_MODE_HEAT;
 | 
			
		||||
        break;
 | 
			
		||||
      case COMMAND_COOL:
 | 
			
		||||
      case COMMAND_ON_COOL:
 | 
			
		||||
      default:
 | 
			
		||||
        this->mode = climate::CLIMATE_MODE_COOL;
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Temperature
 | 
			
		||||
    if (this->mode == climate::CLIMATE_MODE_COOL || this->mode == climate::CLIMATE_MODE_HEAT)
 | 
			
		||||
      this->target_temperature = ((remote_state & TEMP_MASK) >> TEMP_SHIFT) + 15;
 | 
			
		||||
 | 
			
		||||
    // Fan Speed
 | 
			
		||||
    // Get fan speed
 | 
			
		||||
    if (this->mode == climate::CLIMATE_MODE_HEAT_COOL) {
 | 
			
		||||
      this->fan_mode = climate::CLIMATE_FAN_AUTO;
 | 
			
		||||
    } else if (this->mode == climate::CLIMATE_MODE_COOL || this->mode == climate::CLIMATE_MODE_HEAT ||
 | 
			
		||||
               this->mode == climate::CLIMATE_MODE_DRY) {
 | 
			
		||||
    } else if (this->mode == climate::CLIMATE_MODE_COOL || this->mode == climate::CLIMATE_MODE_DRY ||
 | 
			
		||||
               this->mode == climate::CLIMATE_MODE_FAN_ONLY || this->mode == climate::CLIMATE_MODE_HEAT) {
 | 
			
		||||
      if ((remote_state & FAN_MASK) == FAN_AUTO) {
 | 
			
		||||
        this->fan_mode = climate::CLIMATE_FAN_AUTO;
 | 
			
		||||
      } else if ((remote_state & FAN_MASK) == FAN_MIN) {
 | 
			
		||||
@@ -166,11 +175,17 @@ bool LgIrClimate::on_receive(remote_base::RemoteReceiveData data) {
 | 
			
		||||
        this->fan_mode = climate::CLIMATE_FAN_HIGH;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Get temperature
 | 
			
		||||
    if (this->mode == climate::CLIMATE_MODE_COOL || this->mode == climate::CLIMATE_MODE_HEAT) {
 | 
			
		||||
      this->target_temperature = ((remote_state & TEMP_MASK) >> TEMP_SHIFT) + 15;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  this->publish_state();
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void LgIrClimate::transmit_(uint32_t value) {
 | 
			
		||||
  calc_checksum_(value);
 | 
			
		||||
  ESP_LOGD(TAG, "Sending climate_lg_ir code: 0x%02" PRIX32, value);
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@ const uint8_t TEMP_MAX = 30;  // Celsius
 | 
			
		||||
class LgIrClimate : public climate_ir::ClimateIR {
 | 
			
		||||
 public:
 | 
			
		||||
  LgIrClimate()
 | 
			
		||||
      : climate_ir::ClimateIR(TEMP_MIN, TEMP_MAX, 1.0f, true, false,
 | 
			
		||||
      : climate_ir::ClimateIR(TEMP_MIN, TEMP_MAX, 1.0f, true, true,
 | 
			
		||||
                              {climate::CLIMATE_FAN_AUTO, climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM,
 | 
			
		||||
                               climate::CLIMATE_FAN_HIGH},
 | 
			
		||||
                              {climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_VERTICAL}) {}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@ import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome import automation
 | 
			
		||||
from esphome.automation import maybe_simple_id, Condition
 | 
			
		||||
from esphome.components import mqtt
 | 
			
		||||
from esphome.components import mqtt, web_server
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_DEVICE_CLASS,
 | 
			
		||||
@@ -16,6 +16,7 @@ from esphome.const import (
 | 
			
		||||
    CONF_TILT_STATE_TOPIC,
 | 
			
		||||
    CONF_STOP,
 | 
			
		||||
    CONF_MQTT_ID,
 | 
			
		||||
    CONF_WEB_SERVER_ID,
 | 
			
		||||
    CONF_TRIGGER_ID,
 | 
			
		||||
    DEVICE_CLASS_AWNING,
 | 
			
		||||
    DEVICE_CLASS_BLIND,
 | 
			
		||||
@@ -88,34 +89,38 @@ CoverClosedTrigger = cover_ns.class_(
 | 
			
		||||
 | 
			
		||||
CONF_ON_CLOSED = "on_closed"
 | 
			
		||||
 | 
			
		||||
COVER_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(): cv.declare_id(Cover),
 | 
			
		||||
        cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTCoverComponent),
 | 
			
		||||
        cv.Optional(CONF_DEVICE_CLASS): cv.one_of(*DEVICE_CLASSES, lower=True),
 | 
			
		||||
        cv.Optional(CONF_POSITION_COMMAND_TOPIC): cv.All(
 | 
			
		||||
            cv.requires_component("mqtt"), cv.subscribe_topic
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_POSITION_STATE_TOPIC): cv.All(
 | 
			
		||||
            cv.requires_component("mqtt"), cv.subscribe_topic
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_TILT_COMMAND_TOPIC): cv.All(
 | 
			
		||||
            cv.requires_component("mqtt"), cv.subscribe_topic
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_TILT_STATE_TOPIC): cv.All(
 | 
			
		||||
            cv.requires_component("mqtt"), cv.subscribe_topic
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_ON_OPEN): automation.validate_automation(
 | 
			
		||||
            {
 | 
			
		||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CoverOpenTrigger),
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_ON_CLOSED): automation.validate_automation(
 | 
			
		||||
            {
 | 
			
		||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CoverClosedTrigger),
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
    }
 | 
			
		||||
COVER_SCHEMA = (
 | 
			
		||||
    cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
 | 
			
		||||
    .extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA)
 | 
			
		||||
    .extend(
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(Cover),
 | 
			
		||||
            cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTCoverComponent),
 | 
			
		||||
            cv.Optional(CONF_DEVICE_CLASS): cv.one_of(*DEVICE_CLASSES, lower=True),
 | 
			
		||||
            cv.Optional(CONF_POSITION_COMMAND_TOPIC): cv.All(
 | 
			
		||||
                cv.requires_component("mqtt"), cv.subscribe_topic
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_POSITION_STATE_TOPIC): cv.All(
 | 
			
		||||
                cv.requires_component("mqtt"), cv.subscribe_topic
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_TILT_COMMAND_TOPIC): cv.All(
 | 
			
		||||
                cv.requires_component("mqtt"), cv.subscribe_topic
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_TILT_STATE_TOPIC): cv.All(
 | 
			
		||||
                cv.requires_component("mqtt"), cv.subscribe_topic
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_ON_OPEN): automation.validate_automation(
 | 
			
		||||
                {
 | 
			
		||||
                    cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CoverOpenTrigger),
 | 
			
		||||
                }
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_ON_CLOSED): automation.validate_automation(
 | 
			
		||||
                {
 | 
			
		||||
                    cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CoverClosedTrigger),
 | 
			
		||||
                }
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -132,6 +137,10 @@ async def setup_cover_core_(var, config):
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
			
		||||
        await automation.build_automation(trigger, [], conf)
 | 
			
		||||
 | 
			
		||||
    if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None:
 | 
			
		||||
        web_server_ = await cg.get_variable(webserver_id)
 | 
			
		||||
        web_server.add_entity_to_sorting_list(web_server_, var, config)
 | 
			
		||||
 | 
			
		||||
    if (mqtt_id := config.get(CONF_MQTT_ID)) is not None:
 | 
			
		||||
        mqtt_ = cg.new_Pvariable(mqtt_id, var)
 | 
			
		||||
        await mqtt.register_mqtt_component(mqtt_, config)
 | 
			
		||||
 
 | 
			
		||||
@@ -129,13 +129,13 @@ class Cover : public EntityBase, public EntityBase_DeviceClass {
 | 
			
		||||
   *
 | 
			
		||||
   * This is a legacy method and may be removed later, please use `.make_call()` instead.
 | 
			
		||||
   */
 | 
			
		||||
  ESPDEPRECATED("open() is deprecated, use make_call().set_command_open() instead.", "2021.9")
 | 
			
		||||
  ESPDEPRECATED("open() is deprecated, use make_call().set_command_open().perform() instead.", "2021.9")
 | 
			
		||||
  void open();
 | 
			
		||||
  /** Close the cover.
 | 
			
		||||
   *
 | 
			
		||||
   * This is a legacy method and may be removed later, please use `.make_call()` instead.
 | 
			
		||||
   */
 | 
			
		||||
  ESPDEPRECATED("close() is deprecated, use make_call().set_command_close() instead.", "2021.9")
 | 
			
		||||
  ESPDEPRECATED("close() is deprecated, use make_call().set_command_close().perform() instead.", "2021.9")
 | 
			
		||||
  void close();
 | 
			
		||||
  /** Stop the cover.
 | 
			
		||||
   *
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,7 @@ void CST816Touchscreen::continue_setup_() {
 | 
			
		||||
  }
 | 
			
		||||
  switch (this->chip_id_) {
 | 
			
		||||
    case CST820_CHIP_ID:
 | 
			
		||||
    case CST826_CHIP_ID:
 | 
			
		||||
    case CST716_CHIP_ID:
 | 
			
		||||
    case CST816S_CHIP_ID:
 | 
			
		||||
    case CST816D_CHIP_ID:
 | 
			
		||||
@@ -90,6 +91,9 @@ void CST816Touchscreen::dump_config() {
 | 
			
		||||
    case CST820_CHIP_ID:
 | 
			
		||||
      name = "CST820";
 | 
			
		||||
      break;
 | 
			
		||||
    case CST826_CHIP_ID:
 | 
			
		||||
      name = "CST826";
 | 
			
		||||
      break;
 | 
			
		||||
    case CST816S_CHIP_ID:
 | 
			
		||||
      name = "CST816S";
 | 
			
		||||
      break;
 | 
			
		||||
 
 | 
			
		||||
@@ -24,6 +24,7 @@ static const uint8_t REG_SLEEP = 0xE5;
 | 
			
		||||
static const uint8_t REG_IRQ_CTL = 0xFA;
 | 
			
		||||
static const uint8_t IRQ_EN_MOTION = 0x70;
 | 
			
		||||
 | 
			
		||||
static const uint8_t CST826_CHIP_ID = 0x11;
 | 
			
		||||
static const uint8_t CST820_CHIP_ID = 0xB7;
 | 
			
		||||
static const uint8_t CST816S_CHIP_ID = 0xB4;
 | 
			
		||||
static const uint8_t CST816D_CHIP_ID = 0xB6;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
#include "ct_clamp_sensor.h"
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include <cinttypes>
 | 
			
		||||
#include <cmath>
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
@@ -37,8 +38,8 @@ void CTClampSensor::update() {
 | 
			
		||||
    float rms_ac = 0;
 | 
			
		||||
    if (rms_ac_squared > 0)
 | 
			
		||||
      rms_ac = std::sqrt(rms_ac_squared);
 | 
			
		||||
    ESP_LOGD(TAG, "'%s' - Raw AC Value: %.3fA after %d different samples (%d SPS)", this->name_.c_str(), rms_ac,
 | 
			
		||||
             this->num_samples_, 1000 * this->num_samples_ / this->sample_duration_);
 | 
			
		||||
    ESP_LOGD(TAG, "'%s' - Raw AC Value: %.3fA after %" PRIu32 " different samples (%" PRIu32 " SPS)",
 | 
			
		||||
             this->name_.c_str(), rms_ac, this->num_samples_, 1000 * this->num_samples_ / this->sample_duration_);
 | 
			
		||||
    this->publish_state(rms_ac);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,25 +1,7 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome import pins
 | 
			
		||||
from esphome.const import CONF_ID, CONF_PIN
 | 
			
		||||
 | 
			
		||||
MULTI_CONF = True
 | 
			
		||||
AUTO_LOAD = ["sensor"]
 | 
			
		||||
 | 
			
		||||
dallas_ns = cg.esphome_ns.namespace("dallas")
 | 
			
		||||
DallasComponent = dallas_ns.class_("DallasComponent", cg.PollingComponent)
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(): cv.declare_id(DallasComponent),
 | 
			
		||||
        cv.Required(CONF_PIN): pins.internal_gpio_output_pin_schema,
 | 
			
		||||
    }
 | 
			
		||||
).extend(cv.polling_component_schema("60s"))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
 | 
			
		||||
    pin = await cg.gpio_pin_expression(config[CONF_PIN])
 | 
			
		||||
    cg.add(var.set_pin(pin))
 | 
			
		||||
CONFIG_SCHEMA = cv.invalid(
 | 
			
		||||
    'The "dallas" component has been replaced by the "one_wire" component.\nhttps://esphome.io/components/one_wire'
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,287 +0,0 @@
 | 
			
		||||
#include "dallas_component.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace dallas {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "dallas.sensor";
 | 
			
		||||
 | 
			
		||||
static const uint8_t DALLAS_MODEL_DS18S20 = 0x10;
 | 
			
		||||
static const uint8_t DALLAS_MODEL_DS1822 = 0x22;
 | 
			
		||||
static const uint8_t DALLAS_MODEL_DS18B20 = 0x28;
 | 
			
		||||
static const uint8_t DALLAS_MODEL_DS1825 = 0x3B;
 | 
			
		||||
static const uint8_t DALLAS_MODEL_DS28EA00 = 0x42;
 | 
			
		||||
static const uint8_t DALLAS_COMMAND_START_CONVERSION = 0x44;
 | 
			
		||||
static const uint8_t DALLAS_COMMAND_READ_SCRATCH_PAD = 0xBE;
 | 
			
		||||
static const uint8_t DALLAS_COMMAND_WRITE_SCRATCH_PAD = 0x4E;
 | 
			
		||||
 | 
			
		||||
uint16_t DallasTemperatureSensor::millis_to_wait_for_conversion() const {
 | 
			
		||||
  switch (this->resolution_) {
 | 
			
		||||
    case 9:
 | 
			
		||||
      return 94;
 | 
			
		||||
    case 10:
 | 
			
		||||
      return 188;
 | 
			
		||||
    case 11:
 | 
			
		||||
      return 375;
 | 
			
		||||
    default:
 | 
			
		||||
      return 750;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DallasComponent::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up DallasComponent...");
 | 
			
		||||
 | 
			
		||||
  pin_->setup();
 | 
			
		||||
 | 
			
		||||
  // clear bus with 480µs high, otherwise initial reset in search_vec() fails
 | 
			
		||||
  pin_->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP);
 | 
			
		||||
  delayMicroseconds(480);
 | 
			
		||||
 | 
			
		||||
  one_wire_ = new ESPOneWire(pin_);  // NOLINT(cppcoreguidelines-owning-memory)
 | 
			
		||||
 | 
			
		||||
  std::vector<uint64_t> raw_sensors;
 | 
			
		||||
  raw_sensors = this->one_wire_->search_vec();
 | 
			
		||||
 | 
			
		||||
  for (auto &address : raw_sensors) {
 | 
			
		||||
    auto *address8 = reinterpret_cast<uint8_t *>(&address);
 | 
			
		||||
    if (crc8(address8, 7) != address8[7]) {
 | 
			
		||||
      ESP_LOGW(TAG, "Dallas device 0x%s has invalid CRC.", format_hex(address).c_str());
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    if (address8[0] != DALLAS_MODEL_DS18S20 && address8[0] != DALLAS_MODEL_DS1822 &&
 | 
			
		||||
        address8[0] != DALLAS_MODEL_DS18B20 && address8[0] != DALLAS_MODEL_DS1825 &&
 | 
			
		||||
        address8[0] != DALLAS_MODEL_DS28EA00) {
 | 
			
		||||
      ESP_LOGW(TAG, "Unknown device type 0x%02X.", address8[0]);
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    this->found_sensors_.push_back(address);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for (auto *sensor : this->sensors_) {
 | 
			
		||||
    if (sensor->get_index().has_value()) {
 | 
			
		||||
      if (*sensor->get_index() >= this->found_sensors_.size()) {
 | 
			
		||||
        this->status_set_error("Sensor configured by index but not found");
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
      sensor->set_address(this->found_sensors_[*sensor->get_index()]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!sensor->setup_sensor()) {
 | 
			
		||||
      this->status_set_error();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void DallasComponent::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "DallasComponent:");
 | 
			
		||||
  LOG_PIN("  Pin: ", this->pin_);
 | 
			
		||||
  LOG_UPDATE_INTERVAL(this);
 | 
			
		||||
 | 
			
		||||
  if (this->found_sensors_.empty()) {
 | 
			
		||||
    ESP_LOGW(TAG, "  Found no sensors!");
 | 
			
		||||
  } else {
 | 
			
		||||
    ESP_LOGD(TAG, "  Found sensors:");
 | 
			
		||||
    for (auto &address : this->found_sensors_) {
 | 
			
		||||
      ESP_LOGD(TAG, "    0x%s", format_hex(address).c_str());
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for (auto *sensor : this->sensors_) {
 | 
			
		||||
    LOG_SENSOR("  ", "Device", sensor);
 | 
			
		||||
    if (sensor->get_index().has_value()) {
 | 
			
		||||
      ESP_LOGCONFIG(TAG, "    Index %u", *sensor->get_index());
 | 
			
		||||
      if (*sensor->get_index() >= this->found_sensors_.size()) {
 | 
			
		||||
        ESP_LOGE(TAG, "Couldn't find sensor by index - not connected. Proceeding without it.");
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "    Address: %s", sensor->get_address_name().c_str());
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "    Resolution: %u", sensor->get_resolution());
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DallasComponent::register_sensor(DallasTemperatureSensor *sensor) { this->sensors_.push_back(sensor); }
 | 
			
		||||
void DallasComponent::update() {
 | 
			
		||||
  this->status_clear_warning();
 | 
			
		||||
 | 
			
		||||
  bool result;
 | 
			
		||||
  {
 | 
			
		||||
    InterruptLock lock;
 | 
			
		||||
    result = this->one_wire_->reset();
 | 
			
		||||
  }
 | 
			
		||||
  if (!result) {
 | 
			
		||||
    if (!this->found_sensors_.empty()) {
 | 
			
		||||
      // Only log error if at the start sensors were found (and thus are disconnected during uptime)
 | 
			
		||||
      ESP_LOGE(TAG, "Requesting conversion failed");
 | 
			
		||||
      this->status_set_warning();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (auto *sensor : this->sensors_) {
 | 
			
		||||
      sensor->publish_state(NAN);
 | 
			
		||||
    }
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  {
 | 
			
		||||
    InterruptLock lock;
 | 
			
		||||
    this->one_wire_->skip();
 | 
			
		||||
    this->one_wire_->write8(DALLAS_COMMAND_START_CONVERSION);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for (auto *sensor : this->sensors_) {
 | 
			
		||||
    if (sensor->get_address() == 0) {
 | 
			
		||||
      ESP_LOGV(TAG, "'%s' - Indexed sensor not found at startup, skipping update", sensor->get_name().c_str());
 | 
			
		||||
      sensor->publish_state(NAN);
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this->set_timeout(sensor->get_address_name(), sensor->millis_to_wait_for_conversion(), [this, sensor] {
 | 
			
		||||
      bool res = sensor->read_scratch_pad();
 | 
			
		||||
 | 
			
		||||
      if (!res) {
 | 
			
		||||
        ESP_LOGW(TAG, "'%s' - Resetting bus for read failed!", sensor->get_name().c_str());
 | 
			
		||||
        sensor->publish_state(NAN);
 | 
			
		||||
        this->status_set_warning();
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      if (!sensor->check_scratch_pad()) {
 | 
			
		||||
        sensor->publish_state(NAN);
 | 
			
		||||
        this->status_set_warning();
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      float tempc = sensor->get_temp_c();
 | 
			
		||||
      ESP_LOGD(TAG, "'%s': Got Temperature=%.1f°C", sensor->get_name().c_str(), tempc);
 | 
			
		||||
      sensor->publish_state(tempc);
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DallasTemperatureSensor::set_address(uint64_t address) { this->address_ = address; }
 | 
			
		||||
uint8_t DallasTemperatureSensor::get_resolution() const { return this->resolution_; }
 | 
			
		||||
void DallasTemperatureSensor::set_resolution(uint8_t resolution) { this->resolution_ = resolution; }
 | 
			
		||||
optional<uint8_t> DallasTemperatureSensor::get_index() const { return this->index_; }
 | 
			
		||||
void DallasTemperatureSensor::set_index(uint8_t index) { this->index_ = index; }
 | 
			
		||||
uint8_t *DallasTemperatureSensor::get_address8() { return reinterpret_cast<uint8_t *>(&this->address_); }
 | 
			
		||||
uint64_t DallasTemperatureSensor::get_address() { return this->address_; }
 | 
			
		||||
 | 
			
		||||
const std::string &DallasTemperatureSensor::get_address_name() {
 | 
			
		||||
  if (this->address_name_.empty()) {
 | 
			
		||||
    this->address_name_ = std::string("0x") + format_hex(this->address_);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return this->address_name_;
 | 
			
		||||
}
 | 
			
		||||
bool IRAM_ATTR DallasTemperatureSensor::read_scratch_pad() {
 | 
			
		||||
  auto *wire = this->parent_->one_wire_;
 | 
			
		||||
 | 
			
		||||
  {
 | 
			
		||||
    InterruptLock lock;
 | 
			
		||||
 | 
			
		||||
    if (!wire->reset()) {
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    wire->select(this->address_);
 | 
			
		||||
    wire->write8(DALLAS_COMMAND_READ_SCRATCH_PAD);
 | 
			
		||||
 | 
			
		||||
    for (unsigned char &i : this->scratch_pad_) {
 | 
			
		||||
      i = wire->read8();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
bool DallasTemperatureSensor::setup_sensor() {
 | 
			
		||||
  bool r = this->read_scratch_pad();
 | 
			
		||||
 | 
			
		||||
  if (!r) {
 | 
			
		||||
    ESP_LOGE(TAG, "Reading scratchpad failed: reset");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  if (!this->check_scratch_pad())
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  if (this->scratch_pad_[4] == this->resolution_)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  if (this->get_address8()[0] == DALLAS_MODEL_DS18S20) {
 | 
			
		||||
    // DS18S20 doesn't support resolution.
 | 
			
		||||
    ESP_LOGW(TAG, "DS18S20 doesn't support setting resolution.");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  switch (this->resolution_) {
 | 
			
		||||
    case 12:
 | 
			
		||||
      this->scratch_pad_[4] = 0x7F;
 | 
			
		||||
      break;
 | 
			
		||||
    case 11:
 | 
			
		||||
      this->scratch_pad_[4] = 0x5F;
 | 
			
		||||
      break;
 | 
			
		||||
    case 10:
 | 
			
		||||
      this->scratch_pad_[4] = 0x3F;
 | 
			
		||||
      break;
 | 
			
		||||
    case 9:
 | 
			
		||||
    default:
 | 
			
		||||
      this->scratch_pad_[4] = 0x1F;
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto *wire = this->parent_->one_wire_;
 | 
			
		||||
  {
 | 
			
		||||
    InterruptLock lock;
 | 
			
		||||
    if (wire->reset()) {
 | 
			
		||||
      wire->select(this->address_);
 | 
			
		||||
      wire->write8(DALLAS_COMMAND_WRITE_SCRATCH_PAD);
 | 
			
		||||
      wire->write8(this->scratch_pad_[2]);  // high alarm temp
 | 
			
		||||
      wire->write8(this->scratch_pad_[3]);  // low alarm temp
 | 
			
		||||
      wire->write8(this->scratch_pad_[4]);  // resolution
 | 
			
		||||
      wire->reset();
 | 
			
		||||
 | 
			
		||||
      // write value to EEPROM
 | 
			
		||||
      wire->select(this->address_);
 | 
			
		||||
      wire->write8(0x48);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  delay(20);  // allow it to finish operation
 | 
			
		||||
  wire->reset();
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
bool DallasTemperatureSensor::check_scratch_pad() {
 | 
			
		||||
  bool chksum_validity = (crc8(this->scratch_pad_, 8) == this->scratch_pad_[8]);
 | 
			
		||||
  bool config_validity = false;
 | 
			
		||||
 | 
			
		||||
  switch (this->get_address8()[0]) {
 | 
			
		||||
    case DALLAS_MODEL_DS18B20:
 | 
			
		||||
      config_validity = ((this->scratch_pad_[4] & 0x9F) == 0x1F);
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      config_validity = ((this->scratch_pad_[4] & 0x10) == 0x10);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
#ifdef ESPHOME_LOG_LEVEL_VERY_VERBOSE
 | 
			
		||||
  ESP_LOGVV(TAG, "Scratch pad: %02X.%02X.%02X.%02X.%02X.%02X.%02X.%02X.%02X (%02X)", this->scratch_pad_[0],
 | 
			
		||||
            this->scratch_pad_[1], this->scratch_pad_[2], this->scratch_pad_[3], this->scratch_pad_[4],
 | 
			
		||||
            this->scratch_pad_[5], this->scratch_pad_[6], this->scratch_pad_[7], this->scratch_pad_[8],
 | 
			
		||||
            crc8(this->scratch_pad_, 8));
 | 
			
		||||
#endif
 | 
			
		||||
  if (!chksum_validity) {
 | 
			
		||||
    ESP_LOGW(TAG, "'%s' - Scratch pad checksum invalid!", this->get_name().c_str());
 | 
			
		||||
  } else if (!config_validity) {
 | 
			
		||||
    ESP_LOGW(TAG, "'%s' - Scratch pad config register invalid!", this->get_name().c_str());
 | 
			
		||||
  }
 | 
			
		||||
  return chksum_validity && config_validity;
 | 
			
		||||
}
 | 
			
		||||
float DallasTemperatureSensor::get_temp_c() {
 | 
			
		||||
  int16_t temp = (int16_t(this->scratch_pad_[1]) << 11) | (int16_t(this->scratch_pad_[0]) << 3);
 | 
			
		||||
  if (this->get_address8()[0] == DALLAS_MODEL_DS18S20) {
 | 
			
		||||
    int diff = (this->scratch_pad_[7] - this->scratch_pad_[6]) << 7;
 | 
			
		||||
    temp = ((temp & 0xFFF0) << 3) - 16 + (diff / this->scratch_pad_[7]);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return temp / 128.0f;
 | 
			
		||||
}
 | 
			
		||||
std::string DallasTemperatureSensor::unique_id() { return "dallas-" + str_lower_case(format_hex(this->address_)); }
 | 
			
		||||
 | 
			
		||||
}  // namespace dallas
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -1,79 +0,0 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/components/sensor/sensor.h"
 | 
			
		||||
#include "esp_one_wire.h"
 | 
			
		||||
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace dallas {
 | 
			
		||||
 | 
			
		||||
class DallasTemperatureSensor;
 | 
			
		||||
 | 
			
		||||
class DallasComponent : public PollingComponent {
 | 
			
		||||
 public:
 | 
			
		||||
  void set_pin(InternalGPIOPin *pin) { pin_ = pin; }
 | 
			
		||||
  void register_sensor(DallasTemperatureSensor *sensor);
 | 
			
		||||
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
  float get_setup_priority() const override { return setup_priority::DATA; }
 | 
			
		||||
 | 
			
		||||
  void update() override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  friend DallasTemperatureSensor;
 | 
			
		||||
 | 
			
		||||
  InternalGPIOPin *pin_;
 | 
			
		||||
  ESPOneWire *one_wire_;
 | 
			
		||||
  std::vector<DallasTemperatureSensor *> sensors_;
 | 
			
		||||
  std::vector<uint64_t> found_sensors_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Internal class that helps us create multiple sensors for one Dallas hub.
 | 
			
		||||
class DallasTemperatureSensor : public sensor::Sensor {
 | 
			
		||||
 public:
 | 
			
		||||
  void set_parent(DallasComponent *parent) { parent_ = parent; }
 | 
			
		||||
  /// Helper to get a pointer to the address as uint8_t.
 | 
			
		||||
  uint8_t *get_address8();
 | 
			
		||||
  uint64_t get_address();
 | 
			
		||||
  /// Helper to create (and cache) the name for this sensor. For example "0xfe0000031f1eaf29".
 | 
			
		||||
  const std::string &get_address_name();
 | 
			
		||||
 | 
			
		||||
  /// Set the 64-bit unsigned address for this sensor.
 | 
			
		||||
  void set_address(uint64_t address);
 | 
			
		||||
  /// Get the index of this sensor. (0 if using address.)
 | 
			
		||||
  optional<uint8_t> get_index() const;
 | 
			
		||||
  /// Set the index of this sensor. If using index, address will be set after setup.
 | 
			
		||||
  void set_index(uint8_t index);
 | 
			
		||||
  /// Get the set resolution for this sensor.
 | 
			
		||||
  uint8_t get_resolution() const;
 | 
			
		||||
  /// Set the resolution for this sensor.
 | 
			
		||||
  void set_resolution(uint8_t resolution);
 | 
			
		||||
  /// Get the number of milliseconds we have to wait for the conversion phase.
 | 
			
		||||
  uint16_t millis_to_wait_for_conversion() const;
 | 
			
		||||
 | 
			
		||||
  bool setup_sensor();
 | 
			
		||||
  bool read_scratch_pad();
 | 
			
		||||
 | 
			
		||||
  bool check_scratch_pad();
 | 
			
		||||
 | 
			
		||||
  float get_temp_c();
 | 
			
		||||
 | 
			
		||||
  std::string unique_id() override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  DallasComponent *parent_;
 | 
			
		||||
  uint64_t address_;
 | 
			
		||||
  optional<uint8_t> index_;
 | 
			
		||||
 | 
			
		||||
  uint8_t resolution_;
 | 
			
		||||
  std::string address_name_;
 | 
			
		||||
  uint8_t scratch_pad_[9] = {
 | 
			
		||||
      0,
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace dallas
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -1,252 +0,0 @@
 | 
			
		||||
#include "esp_one_wire.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace dallas {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "dallas.one_wire";
 | 
			
		||||
 | 
			
		||||
const uint8_t ONE_WIRE_ROM_SELECT = 0x55;
 | 
			
		||||
const int ONE_WIRE_ROM_SEARCH = 0xF0;
 | 
			
		||||
 | 
			
		||||
ESPOneWire::ESPOneWire(InternalGPIOPin *pin) { pin_ = pin->to_isr(); }
 | 
			
		||||
 | 
			
		||||
bool HOT IRAM_ATTR ESPOneWire::reset() {
 | 
			
		||||
  // See reset here:
 | 
			
		||||
  // https://www.maximintegrated.com/en/design/technical-documents/app-notes/1/126.html
 | 
			
		||||
  // Wait for communication to clear (delay G)
 | 
			
		||||
  pin_.pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP);
 | 
			
		||||
  uint8_t retries = 125;
 | 
			
		||||
  do {
 | 
			
		||||
    if (--retries == 0)
 | 
			
		||||
      return false;
 | 
			
		||||
    delayMicroseconds(2);
 | 
			
		||||
  } while (!pin_.digital_read());
 | 
			
		||||
 | 
			
		||||
  // Send 480µs LOW TX reset pulse (drive bus low, delay H)
 | 
			
		||||
  pin_.pin_mode(gpio::FLAG_OUTPUT);
 | 
			
		||||
  pin_.digital_write(false);
 | 
			
		||||
  delayMicroseconds(480);
 | 
			
		||||
 | 
			
		||||
  // Release the bus, delay I
 | 
			
		||||
  pin_.pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP);
 | 
			
		||||
  delayMicroseconds(70);
 | 
			
		||||
 | 
			
		||||
  // sample bus, 0=device(s) present, 1=no device present
 | 
			
		||||
  bool r = !pin_.digital_read();
 | 
			
		||||
  // delay J
 | 
			
		||||
  delayMicroseconds(410);
 | 
			
		||||
  return r;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HOT IRAM_ATTR ESPOneWire::write_bit(bool bit) {
 | 
			
		||||
  // drive bus low
 | 
			
		||||
  pin_.pin_mode(gpio::FLAG_OUTPUT);
 | 
			
		||||
  pin_.digital_write(false);
 | 
			
		||||
 | 
			
		||||
  // from datasheet:
 | 
			
		||||
  // write 0 low time: t_low0: min=60µs, max=120µs
 | 
			
		||||
  // write 1 low time: t_low1: min=1µs, max=15µs
 | 
			
		||||
  // time slot: t_slot: min=60µs, max=120µs
 | 
			
		||||
  // recovery time: t_rec: min=1µs
 | 
			
		||||
  // ds18b20 appears to read the bus after roughly 14µs
 | 
			
		||||
  uint32_t delay0 = bit ? 6 : 60;
 | 
			
		||||
  uint32_t delay1 = bit ? 54 : 5;
 | 
			
		||||
 | 
			
		||||
  // delay A/C
 | 
			
		||||
  delayMicroseconds(delay0);
 | 
			
		||||
  // release bus
 | 
			
		||||
  pin_.digital_write(true);
 | 
			
		||||
  // delay B/D
 | 
			
		||||
  delayMicroseconds(delay1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool HOT IRAM_ATTR ESPOneWire::read_bit() {
 | 
			
		||||
  // drive bus low
 | 
			
		||||
  pin_.pin_mode(gpio::FLAG_OUTPUT);
 | 
			
		||||
  pin_.digital_write(false);
 | 
			
		||||
 | 
			
		||||
  // note: for reading we'll need very accurate timing, as the
 | 
			
		||||
  // timing for the digital_read() is tight; according to the datasheet,
 | 
			
		||||
  // we should read at the end of 16µs starting from the bus low
 | 
			
		||||
  // typically, the ds18b20 pulls the line high after 11µs for a logical 1
 | 
			
		||||
  // and 29µs for a logical 0
 | 
			
		||||
 | 
			
		||||
  uint32_t start = micros();
 | 
			
		||||
  // datasheet says >1µs
 | 
			
		||||
  delayMicroseconds(3);
 | 
			
		||||
 | 
			
		||||
  // release bus, delay E
 | 
			
		||||
  pin_.pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP);
 | 
			
		||||
 | 
			
		||||
  // Unfortunately some frameworks have different characteristics than others
 | 
			
		||||
  // esp32 arduino appears to pull the bus low only after the digital_write(false),
 | 
			
		||||
  // whereas on esp-idf it already happens during the pin_mode(OUTPUT)
 | 
			
		||||
  // manually correct for this with these constants.
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
  uint32_t timing_constant = 12;
 | 
			
		||||
#else
 | 
			
		||||
  uint32_t timing_constant = 14;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  // measure from start value directly, to get best accurate timing no matter
 | 
			
		||||
  // how long pin_mode/delayMicroseconds took
 | 
			
		||||
  while (micros() - start < timing_constant)
 | 
			
		||||
    ;
 | 
			
		||||
 | 
			
		||||
  // sample bus to read bit from peer
 | 
			
		||||
  bool r = pin_.digital_read();
 | 
			
		||||
 | 
			
		||||
  // read slot is at least 60µs; get as close to 60µs to spend less time with interrupts locked
 | 
			
		||||
  uint32_t now = micros();
 | 
			
		||||
  if (now - start < 60)
 | 
			
		||||
    delayMicroseconds(60 - (now - start));
 | 
			
		||||
 | 
			
		||||
  return r;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void IRAM_ATTR ESPOneWire::write8(uint8_t val) {
 | 
			
		||||
  for (uint8_t i = 0; i < 8; i++) {
 | 
			
		||||
    this->write_bit(bool((1u << i) & val));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void IRAM_ATTR ESPOneWire::write64(uint64_t val) {
 | 
			
		||||
  for (uint8_t i = 0; i < 64; i++) {
 | 
			
		||||
    this->write_bit(bool((1ULL << i) & val));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t IRAM_ATTR ESPOneWire::read8() {
 | 
			
		||||
  uint8_t ret = 0;
 | 
			
		||||
  for (uint8_t i = 0; i < 8; i++) {
 | 
			
		||||
    ret |= (uint8_t(this->read_bit()) << i);
 | 
			
		||||
  }
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
uint64_t IRAM_ATTR ESPOneWire::read64() {
 | 
			
		||||
  uint64_t ret = 0;
 | 
			
		||||
  for (uint8_t i = 0; i < 8; i++) {
 | 
			
		||||
    ret |= (uint64_t(this->read_bit()) << i);
 | 
			
		||||
  }
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
void IRAM_ATTR ESPOneWire::select(uint64_t address) {
 | 
			
		||||
  this->write8(ONE_WIRE_ROM_SELECT);
 | 
			
		||||
  this->write64(address);
 | 
			
		||||
}
 | 
			
		||||
void IRAM_ATTR ESPOneWire::reset_search() {
 | 
			
		||||
  this->last_discrepancy_ = 0;
 | 
			
		||||
  this->last_device_flag_ = false;
 | 
			
		||||
  this->rom_number_ = 0;
 | 
			
		||||
}
 | 
			
		||||
uint64_t IRAM_ATTR ESPOneWire::search() {
 | 
			
		||||
  if (this->last_device_flag_) {
 | 
			
		||||
    return 0u;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  {
 | 
			
		||||
    InterruptLock lock;
 | 
			
		||||
    if (!this->reset()) {
 | 
			
		||||
      // Reset failed or no devices present
 | 
			
		||||
      this->reset_search();
 | 
			
		||||
      return 0u;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint8_t id_bit_number = 1;
 | 
			
		||||
  uint8_t last_zero = 0;
 | 
			
		||||
  uint8_t rom_byte_number = 0;
 | 
			
		||||
  bool search_result = false;
 | 
			
		||||
  uint8_t rom_byte_mask = 1;
 | 
			
		||||
 | 
			
		||||
  {
 | 
			
		||||
    InterruptLock lock;
 | 
			
		||||
    // Initiate search
 | 
			
		||||
    this->write8(ONE_WIRE_ROM_SEARCH);
 | 
			
		||||
    do {
 | 
			
		||||
      // read bit
 | 
			
		||||
      bool id_bit = this->read_bit();
 | 
			
		||||
      // read its complement
 | 
			
		||||
      bool cmp_id_bit = this->read_bit();
 | 
			
		||||
 | 
			
		||||
      if (id_bit && cmp_id_bit) {
 | 
			
		||||
        // No devices participating in search
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      bool branch;
 | 
			
		||||
 | 
			
		||||
      if (id_bit != cmp_id_bit) {
 | 
			
		||||
        // only chose one branch, the other one doesn't have any devices.
 | 
			
		||||
        branch = id_bit;
 | 
			
		||||
      } else {
 | 
			
		||||
        // there are devices with both 0s and 1s at this bit
 | 
			
		||||
        if (id_bit_number < this->last_discrepancy_) {
 | 
			
		||||
          branch = (this->rom_number8_()[rom_byte_number] & rom_byte_mask) > 0;
 | 
			
		||||
        } else {
 | 
			
		||||
          branch = id_bit_number == this->last_discrepancy_;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!branch) {
 | 
			
		||||
          last_zero = id_bit_number;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (branch) {
 | 
			
		||||
        // set bit
 | 
			
		||||
        this->rom_number8_()[rom_byte_number] |= rom_byte_mask;
 | 
			
		||||
      } else {
 | 
			
		||||
        // clear bit
 | 
			
		||||
        this->rom_number8_()[rom_byte_number] &= ~rom_byte_mask;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // choose/announce branch
 | 
			
		||||
      this->write_bit(branch);
 | 
			
		||||
      id_bit_number++;
 | 
			
		||||
      rom_byte_mask <<= 1;
 | 
			
		||||
      if (rom_byte_mask == 0u) {
 | 
			
		||||
        // go to next byte
 | 
			
		||||
        rom_byte_number++;
 | 
			
		||||
        rom_byte_mask = 1;
 | 
			
		||||
      }
 | 
			
		||||
    } while (rom_byte_number < 8);  // loop through all bytes
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (id_bit_number >= 65) {
 | 
			
		||||
    this->last_discrepancy_ = last_zero;
 | 
			
		||||
    if (this->last_discrepancy_ == 0) {
 | 
			
		||||
      // we're at root and have no choices left, so this was the last one.
 | 
			
		||||
      this->last_device_flag_ = true;
 | 
			
		||||
    }
 | 
			
		||||
    search_result = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  search_result = search_result && (this->rom_number8_()[0] != 0);
 | 
			
		||||
  if (!search_result) {
 | 
			
		||||
    this->reset_search();
 | 
			
		||||
    return 0u;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return this->rom_number_;
 | 
			
		||||
}
 | 
			
		||||
std::vector<uint64_t> ESPOneWire::search_vec() {
 | 
			
		||||
  std::vector<uint64_t> res;
 | 
			
		||||
 | 
			
		||||
  this->reset_search();
 | 
			
		||||
  uint64_t address;
 | 
			
		||||
  while ((address = this->search()) != 0u)
 | 
			
		||||
    res.push_back(address);
 | 
			
		||||
 | 
			
		||||
  return res;
 | 
			
		||||
}
 | 
			
		||||
void IRAM_ATTR ESPOneWire::skip() {
 | 
			
		||||
  this->write8(0xCC);  // skip ROM
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t IRAM_ATTR *ESPOneWire::rom_number8_() { return reinterpret_cast<uint8_t *>(&this->rom_number_); }
 | 
			
		||||
 | 
			
		||||
}  // namespace dallas
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -1,68 +0,0 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/hal.h"
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace dallas {
 | 
			
		||||
 | 
			
		||||
extern const uint8_t ONE_WIRE_ROM_SELECT;
 | 
			
		||||
extern const int ONE_WIRE_ROM_SEARCH;
 | 
			
		||||
 | 
			
		||||
class ESPOneWire {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit ESPOneWire(InternalGPIOPin *pin);
 | 
			
		||||
 | 
			
		||||
  /** Reset the bus, should be done before all write operations.
 | 
			
		||||
   *
 | 
			
		||||
   * Takes approximately 1ms.
 | 
			
		||||
   *
 | 
			
		||||
   * @return Whether the operation was successful.
 | 
			
		||||
   */
 | 
			
		||||
  bool reset();
 | 
			
		||||
 | 
			
		||||
  /// Write a single bit to the bus, takes about 70µs.
 | 
			
		||||
  void write_bit(bool bit);
 | 
			
		||||
 | 
			
		||||
  /// Read a single bit from the bus, takes about 70µs
 | 
			
		||||
  bool read_bit();
 | 
			
		||||
 | 
			
		||||
  /// Write a word to the bus. LSB first.
 | 
			
		||||
  void write8(uint8_t val);
 | 
			
		||||
 | 
			
		||||
  /// Write a 64 bit unsigned integer to the bus. LSB first.
 | 
			
		||||
  void write64(uint64_t val);
 | 
			
		||||
 | 
			
		||||
  /// Write a command to the bus that addresses all devices by skipping the ROM.
 | 
			
		||||
  void skip();
 | 
			
		||||
 | 
			
		||||
  /// Read an 8 bit word from the bus.
 | 
			
		||||
  uint8_t read8();
 | 
			
		||||
 | 
			
		||||
  /// Read an 64-bit unsigned integer from the bus.
 | 
			
		||||
  uint64_t read64();
 | 
			
		||||
 | 
			
		||||
  /// Select a specific address on the bus for the following command.
 | 
			
		||||
  void select(uint64_t address);
 | 
			
		||||
 | 
			
		||||
  /// Reset the device search.
 | 
			
		||||
  void reset_search();
 | 
			
		||||
 | 
			
		||||
  /// Search for a 1-Wire device on the bus. Returns 0 if all devices have been found.
 | 
			
		||||
  uint64_t search();
 | 
			
		||||
 | 
			
		||||
  /// Helper that wraps search in a std::vector.
 | 
			
		||||
  std::vector<uint64_t> search_vec();
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  /// Helper to get the internal 64-bit unsigned rom number as a 8-bit integer pointer.
 | 
			
		||||
  inline uint8_t *rom_number8_();
 | 
			
		||||
 | 
			
		||||
  ISRInternalGPIOPin pin_;
 | 
			
		||||
  uint8_t last_discrepancy_{0};
 | 
			
		||||
  bool last_device_flag_{false};
 | 
			
		||||
  uint64_t rom_number_{0};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace dallas
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -1,50 +1,5 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import sensor
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_ADDRESS,
 | 
			
		||||
    CONF_DALLAS_ID,
 | 
			
		||||
    CONF_INDEX,
 | 
			
		||||
    CONF_RESOLUTION,
 | 
			
		||||
    DEVICE_CLASS_TEMPERATURE,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    UNIT_CELSIUS,
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.invalid(
 | 
			
		||||
    'The "dallas" sensor is now "dallas_temp"\nhttps://esphome.io/components/sensor/dallas_temp'
 | 
			
		||||
)
 | 
			
		||||
from . import DallasComponent, dallas_ns
 | 
			
		||||
 | 
			
		||||
DallasTemperatureSensor = dallas_ns.class_("DallasTemperatureSensor", sensor.Sensor)
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.All(
 | 
			
		||||
    sensor.sensor_schema(
 | 
			
		||||
        DallasTemperatureSensor,
 | 
			
		||||
        unit_of_measurement=UNIT_CELSIUS,
 | 
			
		||||
        accuracy_decimals=1,
 | 
			
		||||
        device_class=DEVICE_CLASS_TEMPERATURE,
 | 
			
		||||
        state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    ).extend(
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(CONF_DALLAS_ID): cv.use_id(DallasComponent),
 | 
			
		||||
            cv.Optional(CONF_ADDRESS): cv.hex_uint64_t,
 | 
			
		||||
            cv.Optional(CONF_INDEX): cv.positive_int,
 | 
			
		||||
            cv.Optional(CONF_RESOLUTION, default=12): cv.int_range(min=9, max=12),
 | 
			
		||||
        }
 | 
			
		||||
    ),
 | 
			
		||||
    cv.has_exactly_one_key(CONF_ADDRESS, CONF_INDEX),
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    hub = await cg.get_variable(config[CONF_DALLAS_ID])
 | 
			
		||||
    var = await sensor.new_sensor(config)
 | 
			
		||||
 | 
			
		||||
    if CONF_ADDRESS in config:
 | 
			
		||||
        cg.add(var.set_address(config[CONF_ADDRESS]))
 | 
			
		||||
    else:
 | 
			
		||||
        cg.add(var.set_index(config[CONF_INDEX]))
 | 
			
		||||
 | 
			
		||||
    if CONF_RESOLUTION in config:
 | 
			
		||||
        cg.add(var.set_resolution(config[CONF_RESOLUTION]))
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_parent(hub))
 | 
			
		||||
 | 
			
		||||
    cg.add(hub.register_sensor(var))
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								esphome/components/dallas_temp/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								esphome/components/dallas_temp/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
CODEOWNERS = ["@ssieb"]
 | 
			
		||||
							
								
								
									
										169
									
								
								esphome/components/dallas_temp/dallas_temp.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										169
									
								
								esphome/components/dallas_temp/dallas_temp.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,169 @@
 | 
			
		||||
#include "dallas_temp.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace dallas_temp {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "dallas.temp.sensor";
 | 
			
		||||
 | 
			
		||||
static const uint8_t DALLAS_MODEL_DS18S20 = 0x10;
 | 
			
		||||
static const uint8_t DALLAS_COMMAND_START_CONVERSION = 0x44;
 | 
			
		||||
static const uint8_t DALLAS_COMMAND_READ_SCRATCH_PAD = 0xBE;
 | 
			
		||||
static const uint8_t DALLAS_COMMAND_WRITE_SCRATCH_PAD = 0x4E;
 | 
			
		||||
static const uint8_t DALLAS_COMMAND_COPY_SCRATCH_PAD = 0x48;
 | 
			
		||||
 | 
			
		||||
uint16_t DallasTemperatureSensor::millis_to_wait_for_conversion_() const {
 | 
			
		||||
  switch (this->resolution_) {
 | 
			
		||||
    case 9:
 | 
			
		||||
      return 94;
 | 
			
		||||
    case 10:
 | 
			
		||||
      return 188;
 | 
			
		||||
    case 11:
 | 
			
		||||
      return 375;
 | 
			
		||||
    default:
 | 
			
		||||
      return 750;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DallasTemperatureSensor::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Dallas Temperature Sensor:");
 | 
			
		||||
  if (this->address_ == 0) {
 | 
			
		||||
    ESP_LOGW(TAG, "  Unable to select an address");
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  LOG_ONE_WIRE_DEVICE(this);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Resolution: %u bits", this->resolution_);
 | 
			
		||||
  LOG_UPDATE_INTERVAL(this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DallasTemperatureSensor::update() {
 | 
			
		||||
  if (this->address_ == 0)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  this->status_clear_warning();
 | 
			
		||||
 | 
			
		||||
  this->send_command_(DALLAS_COMMAND_START_CONVERSION);
 | 
			
		||||
 | 
			
		||||
  this->set_timeout(this->get_address_name(), this->millis_to_wait_for_conversion_(), [this] {
 | 
			
		||||
    if (!this->read_scratch_pad_() || !this->check_scratch_pad_()) {
 | 
			
		||||
      this->publish_state(NAN);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    float tempc = this->get_temp_c_();
 | 
			
		||||
    ESP_LOGD(TAG, "'%s': Got Temperature=%.1f°C", this->get_name().c_str(), tempc);
 | 
			
		||||
    this->publish_state(tempc);
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void IRAM_ATTR DallasTemperatureSensor::read_scratch_pad_int_() {
 | 
			
		||||
  for (uint8_t &i : this->scratch_pad_) {
 | 
			
		||||
    i = this->bus_->read8();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool DallasTemperatureSensor::read_scratch_pad_() {
 | 
			
		||||
  bool success;
 | 
			
		||||
  {
 | 
			
		||||
    InterruptLock lock;
 | 
			
		||||
    success = this->send_command_(DALLAS_COMMAND_READ_SCRATCH_PAD);
 | 
			
		||||
    if (success)
 | 
			
		||||
      this->read_scratch_pad_int_();
 | 
			
		||||
  }
 | 
			
		||||
  if (!success) {
 | 
			
		||||
    ESP_LOGW(TAG, "'%s' - reading scratch pad failed bus reset", this->get_name().c_str());
 | 
			
		||||
    this->status_set_warning("bus reset failed");
 | 
			
		||||
  }
 | 
			
		||||
  return success;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DallasTemperatureSensor::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "setting up Dallas temperature sensor...");
 | 
			
		||||
  if (!this->check_address_())
 | 
			
		||||
    return;
 | 
			
		||||
  if (!this->read_scratch_pad_())
 | 
			
		||||
    return;
 | 
			
		||||
  if (!this->check_scratch_pad_())
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  if ((this->address_ & 0xff) == DALLAS_MODEL_DS18S20) {
 | 
			
		||||
    // DS18S20 doesn't support resolution.
 | 
			
		||||
    ESP_LOGW(TAG, "DS18S20 doesn't support setting resolution.");
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint8_t res;
 | 
			
		||||
  switch (this->resolution_) {
 | 
			
		||||
    case 12:
 | 
			
		||||
      res = 0x7F;
 | 
			
		||||
      break;
 | 
			
		||||
    case 11:
 | 
			
		||||
      res = 0x5F;
 | 
			
		||||
      break;
 | 
			
		||||
    case 10:
 | 
			
		||||
      res = 0x3F;
 | 
			
		||||
      break;
 | 
			
		||||
    case 9:
 | 
			
		||||
    default:
 | 
			
		||||
      res = 0x1F;
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (this->scratch_pad_[4] == res)
 | 
			
		||||
    return;
 | 
			
		||||
  this->scratch_pad_[4] = res;
 | 
			
		||||
 | 
			
		||||
  {
 | 
			
		||||
    InterruptLock lock;
 | 
			
		||||
    if (this->send_command_(DALLAS_COMMAND_WRITE_SCRATCH_PAD)) {
 | 
			
		||||
      this->bus_->write8(this->scratch_pad_[2]);  // high alarm temp
 | 
			
		||||
      this->bus_->write8(this->scratch_pad_[3]);  // low alarm temp
 | 
			
		||||
      this->bus_->write8(this->scratch_pad_[4]);  // resolution
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // write value to EEPROM
 | 
			
		||||
    this->send_command_(DALLAS_COMMAND_COPY_SCRATCH_PAD);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool DallasTemperatureSensor::check_scratch_pad_() {
 | 
			
		||||
  bool chksum_validity = (crc8(this->scratch_pad_, 8) == this->scratch_pad_[8]);
 | 
			
		||||
 | 
			
		||||
#ifdef ESPHOME_LOG_LEVEL_VERY_VERBOSE
 | 
			
		||||
  ESP_LOGVV(TAG, "Scratch pad: %02X.%02X.%02X.%02X.%02X.%02X.%02X.%02X.%02X (%02X)", this->scratch_pad_[0],
 | 
			
		||||
            this->scratch_pad_[1], this->scratch_pad_[2], this->scratch_pad_[3], this->scratch_pad_[4],
 | 
			
		||||
            this->scratch_pad_[5], this->scratch_pad_[6], this->scratch_pad_[7], this->scratch_pad_[8],
 | 
			
		||||
            crc8(this->scratch_pad_, 8));
 | 
			
		||||
#endif
 | 
			
		||||
  if (!chksum_validity) {
 | 
			
		||||
    ESP_LOGW(TAG, "'%s' - Scratch pad checksum invalid!", this->get_name().c_str());
 | 
			
		||||
    this->status_set_warning("scratch pad checksum invalid");
 | 
			
		||||
  }
 | 
			
		||||
  return chksum_validity;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float DallasTemperatureSensor::get_temp_c_() {
 | 
			
		||||
  int16_t temp = (this->scratch_pad_[1] << 8) | this->scratch_pad_[0];
 | 
			
		||||
  if ((this->address_ & 0xff) == DALLAS_MODEL_DS18S20) {
 | 
			
		||||
    return (temp >> 1) + (this->scratch_pad_[7] - this->scratch_pad_[6]) / float(this->scratch_pad_[7]) - 0.25;
 | 
			
		||||
  }
 | 
			
		||||
  switch (this->resolution_) {
 | 
			
		||||
    case 9:
 | 
			
		||||
      temp &= 0xfff8;
 | 
			
		||||
      break;
 | 
			
		||||
    case 10:
 | 
			
		||||
      temp &= 0xfffc;
 | 
			
		||||
      break;
 | 
			
		||||
    case 11:
 | 
			
		||||
      temp &= 0xfffe;
 | 
			
		||||
      break;
 | 
			
		||||
    case 12:
 | 
			
		||||
    default:
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return temp / 16.0f;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace dallas_temp
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										32
									
								
								esphome/components/dallas_temp/dallas_temp.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								esphome/components/dallas_temp/dallas_temp.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/components/sensor/sensor.h"
 | 
			
		||||
#include "esphome/components/one_wire/one_wire.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace dallas_temp {
 | 
			
		||||
 | 
			
		||||
class DallasTemperatureSensor : public PollingComponent, public sensor::Sensor, public one_wire::OneWireDevice {
 | 
			
		||||
 public:
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void update() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
 | 
			
		||||
  /// Set the resolution for this sensor.
 | 
			
		||||
  void set_resolution(uint8_t resolution) { this->resolution_ = resolution; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  uint8_t resolution_;
 | 
			
		||||
  uint8_t scratch_pad_[9] = {0};
 | 
			
		||||
 | 
			
		||||
  /// Get the number of milliseconds we have to wait for the conversion phase.
 | 
			
		||||
  uint16_t millis_to_wait_for_conversion_() const;
 | 
			
		||||
  bool read_scratch_pad_();
 | 
			
		||||
  void read_scratch_pad_int_();
 | 
			
		||||
  bool check_scratch_pad_();
 | 
			
		||||
  float get_temp_c_();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace dallas_temp
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										43
									
								
								esphome/components/dallas_temp/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								esphome/components/dallas_temp/sensor.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import one_wire, sensor
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_RESOLUTION,
 | 
			
		||||
    DEVICE_CLASS_TEMPERATURE,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    UNIT_CELSIUS,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
dallas_temp_ns = cg.esphome_ns.namespace("dallas_temp")
 | 
			
		||||
 | 
			
		||||
DallasTemperatureSensor = dallas_temp_ns.class_(
 | 
			
		||||
    "DallasTemperatureSensor",
 | 
			
		||||
    cg.PollingComponent,
 | 
			
		||||
    sensor.Sensor,
 | 
			
		||||
    one_wire.OneWireDevice,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = (
 | 
			
		||||
    sensor.sensor_schema(
 | 
			
		||||
        DallasTemperatureSensor,
 | 
			
		||||
        unit_of_measurement=UNIT_CELSIUS,
 | 
			
		||||
        accuracy_decimals=1,
 | 
			
		||||
        device_class=DEVICE_CLASS_TEMPERATURE,
 | 
			
		||||
        state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    )
 | 
			
		||||
    .extend(
 | 
			
		||||
        {
 | 
			
		||||
            cv.Optional(CONF_RESOLUTION, default=12): cv.int_range(min=9, max=12),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
    .extend(one_wire.one_wire_device_schema())
 | 
			
		||||
    .extend(cv.polling_component_schema("60s"))
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = await sensor.new_sensor(config)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await one_wire.register_one_wire_device(var, config)
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_resolution(config[CONF_RESOLUTION]))
 | 
			
		||||
@@ -2,7 +2,7 @@ import esphome.codegen as cg
 | 
			
		||||
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome import automation
 | 
			
		||||
from esphome.components import mqtt, time
 | 
			
		||||
from esphome.components import mqtt, web_server, time
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_ON_TIME,
 | 
			
		||||
@@ -11,6 +11,7 @@ from esphome.const import (
 | 
			
		||||
    CONF_TRIGGER_ID,
 | 
			
		||||
    CONF_TYPE,
 | 
			
		||||
    CONF_MQTT_ID,
 | 
			
		||||
    CONF_WEB_SERVER_ID,
 | 
			
		||||
    CONF_DATE,
 | 
			
		||||
    CONF_DATETIME,
 | 
			
		||||
    CONF_TIME,
 | 
			
		||||
@@ -63,16 +64,20 @@ DATETIME_MODES = [
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
_DATETIME_SCHEMA = cv.Schema(
 | 
			
		||||
    {
 | 
			
		||||
        cv.Optional(CONF_ON_VALUE): automation.validate_automation(
 | 
			
		||||
            {
 | 
			
		||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DateTimeStateTrigger),
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
        cv.GenerateID(CONF_TIME_ID): cv.use_id(time.RealTimeClock),
 | 
			
		||||
    }
 | 
			
		||||
).extend(cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA))
 | 
			
		||||
_DATETIME_SCHEMA = (
 | 
			
		||||
    cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
 | 
			
		||||
    .extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA)
 | 
			
		||||
    .extend(
 | 
			
		||||
        {
 | 
			
		||||
            cv.Optional(CONF_ON_VALUE): automation.validate_automation(
 | 
			
		||||
                {
 | 
			
		||||
                    cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DateTimeStateTrigger),
 | 
			
		||||
                }
 | 
			
		||||
            ),
 | 
			
		||||
            cv.GenerateID(CONF_TIME_ID): cv.use_id(time.RealTimeClock),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def date_schema(class_: MockObjClass) -> cv.Schema:
 | 
			
		||||
@@ -128,6 +133,9 @@ async def setup_datetime_core_(var, config):
 | 
			
		||||
    if (mqtt_id := config.get(CONF_MQTT_ID)) is not None:
 | 
			
		||||
        mqtt_ = cg.new_Pvariable(mqtt_id, var)
 | 
			
		||||
        await mqtt.register_mqtt_component(mqtt_, config)
 | 
			
		||||
    if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None:
 | 
			
		||||
        web_server_ = await cg.get_variable(webserver_id)
 | 
			
		||||
        web_server.add_entity_to_sorting_list(web_server_, var, config)
 | 
			
		||||
    for conf in config.get(CONF_ON_VALUE, []):
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
			
		||||
        await automation.build_automation(trigger, [(cg.ESPTime, "x")], conf)
 | 
			
		||||
@@ -156,7 +164,7 @@ async def new_datetime(config, *args):
 | 
			
		||||
    return var
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@coroutine_with_priority(40.0)
 | 
			
		||||
@coroutine_with_priority(100.0)
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    cg.add_define("USE_DATETIME")
 | 
			
		||||
    cg.add_global(datetime_ns.using)
 | 
			
		||||
 
 | 
			
		||||
@@ -80,6 +80,17 @@ void DateCall::validate_() {
 | 
			
		||||
 | 
			
		||||
void DateCall::perform() {
 | 
			
		||||
  this->validate_();
 | 
			
		||||
  ESP_LOGD(TAG, "'%s' - Setting", this->parent_->get_name().c_str());
 | 
			
		||||
 | 
			
		||||
  if (this->year_.has_value()) {
 | 
			
		||||
    ESP_LOGD(TAG, " Year: %d", *this->year_);
 | 
			
		||||
  }
 | 
			
		||||
  if (this->month_.has_value()) {
 | 
			
		||||
    ESP_LOGD(TAG, " Month: %d", *this->month_);
 | 
			
		||||
  }
 | 
			
		||||
  if (this->day_.has_value()) {
 | 
			
		||||
    ESP_LOGD(TAG, " Day: %d", *this->day_);
 | 
			
		||||
  }
 | 
			
		||||
  this->parent_->control(*this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -8,62 +8,16 @@
 | 
			
		||||
#include <cinttypes>
 | 
			
		||||
#include <climits>
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
 | 
			
		||||
#include <esp_heap_caps.h>
 | 
			
		||||
#include <esp_system.h>
 | 
			
		||||
 | 
			
		||||
#include <esp_chip_info.h>
 | 
			
		||||
#if defined(USE_ESP32_VARIANT_ESP32)
 | 
			
		||||
#include <esp32/rom/rtc.h>
 | 
			
		||||
#elif defined(USE_ESP32_VARIANT_ESP32C3)
 | 
			
		||||
#include <esp32c3/rom/rtc.h>
 | 
			
		||||
#elif defined(USE_ESP32_VARIANT_ESP32C6)
 | 
			
		||||
#include <esp32c6/rom/rtc.h>
 | 
			
		||||
#elif defined(USE_ESP32_VARIANT_ESP32S2)
 | 
			
		||||
#include <esp32s2/rom/rtc.h>
 | 
			
		||||
#elif defined(USE_ESP32_VARIANT_ESP32S3)
 | 
			
		||||
#include <esp32s3/rom/rtc.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif  // USE_ESP32
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ARDUINO
 | 
			
		||||
#ifdef USE_RP2040
 | 
			
		||||
#include <Arduino.h>
 | 
			
		||||
#elif defined(USE_ESP32) || defined(USE_ESP8266)
 | 
			
		||||
#include <Esp.h>
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace debug {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "debug";
 | 
			
		||||
 | 
			
		||||
static uint32_t get_free_heap() {
 | 
			
		||||
#if defined(USE_ESP8266)
 | 
			
		||||
  return ESP.getFreeHeap();  // NOLINT(readability-static-accessed-through-instance)
 | 
			
		||||
#elif defined(USE_ESP32)
 | 
			
		||||
  return heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
 | 
			
		||||
#elif defined(USE_RP2040)
 | 
			
		||||
  return rp2040.getFreeHeap();
 | 
			
		||||
#elif defined(USE_LIBRETINY)
 | 
			
		||||
  return lt_heap_get_free();
 | 
			
		||||
#elif defined(USE_HOST)
 | 
			
		||||
  return INT_MAX;
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DebugComponent::dump_config() {
 | 
			
		||||
#ifndef ESPHOME_LOG_HAS_DEBUG
 | 
			
		||||
  return;  // Can't log below if debug logging is disabled
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  std::string device_info;
 | 
			
		||||
  std::string reset_reason;
 | 
			
		||||
  device_info.reserve(256);
 | 
			
		||||
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Debug component:");
 | 
			
		||||
#ifdef USE_TEXT_SENSOR
 | 
			
		||||
  LOG_TEXT_SENSOR("  ", "Device info", this->device_info_);
 | 
			
		||||
@@ -76,305 +30,15 @@ void DebugComponent::dump_config() {
 | 
			
		||||
#endif  // defined(USE_ESP8266) && USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2)
 | 
			
		||||
#endif  // USE_SENSOR
 | 
			
		||||
 | 
			
		||||
  std::string device_info;
 | 
			
		||||
  device_info.reserve(256);
 | 
			
		||||
  ESP_LOGD(TAG, "ESPHome version %s", ESPHOME_VERSION);
 | 
			
		||||
  device_info += ESPHOME_VERSION;
 | 
			
		||||
 | 
			
		||||
  this->free_heap_ = get_free_heap();
 | 
			
		||||
  this->free_heap_ = get_free_heap_();
 | 
			
		||||
  ESP_LOGD(TAG, "Free Heap Size: %" PRIu32 " bytes", this->free_heap_);
 | 
			
		||||
 | 
			
		||||
#if defined(USE_ARDUINO) && (defined(USE_ESP32) || defined(USE_ESP8266))
 | 
			
		||||
  const char *flash_mode;
 | 
			
		||||
  switch (ESP.getFlashChipMode()) {  // NOLINT(readability-static-accessed-through-instance)
 | 
			
		||||
    case FM_QIO:
 | 
			
		||||
      flash_mode = "QIO";
 | 
			
		||||
      break;
 | 
			
		||||
    case FM_QOUT:
 | 
			
		||||
      flash_mode = "QOUT";
 | 
			
		||||
      break;
 | 
			
		||||
    case FM_DIO:
 | 
			
		||||
      flash_mode = "DIO";
 | 
			
		||||
      break;
 | 
			
		||||
    case FM_DOUT:
 | 
			
		||||
      flash_mode = "DOUT";
 | 
			
		||||
      break;
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
    case FM_FAST_READ:
 | 
			
		||||
      flash_mode = "FAST_READ";
 | 
			
		||||
      break;
 | 
			
		||||
    case FM_SLOW_READ:
 | 
			
		||||
      flash_mode = "SLOW_READ";
 | 
			
		||||
      break;
 | 
			
		||||
#endif
 | 
			
		||||
    default:
 | 
			
		||||
      flash_mode = "UNKNOWN";
 | 
			
		||||
  }
 | 
			
		||||
  ESP_LOGD(TAG, "Flash Chip: Size=%ukB Speed=%uMHz Mode=%s",
 | 
			
		||||
           ESP.getFlashChipSize() / 1024,                                                   // NOLINT
 | 
			
		||||
           ESP.getFlashChipSpeed() / 1000000, flash_mode);                                  // NOLINT
 | 
			
		||||
  device_info += "|Flash: " + to_string(ESP.getFlashChipSize() / 1024) +                    // NOLINT
 | 
			
		||||
                 "kB Speed:" + to_string(ESP.getFlashChipSpeed() / 1000000) + "MHz Mode:";  // NOLINT
 | 
			
		||||
  device_info += flash_mode;
 | 
			
		||||
#endif  // USE_ARDUINO && (USE_ESP32 || USE_ESP8266)
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
  esp_chip_info_t info;
 | 
			
		||||
  esp_chip_info(&info);
 | 
			
		||||
  const char *model;
 | 
			
		||||
#if defined(USE_ESP32_VARIANT_ESP32)
 | 
			
		||||
  model = "ESP32";
 | 
			
		||||
#elif defined(USE_ESP32_VARIANT_ESP32C3)
 | 
			
		||||
  model = "ESP32-C3";
 | 
			
		||||
#elif defined(USE_ESP32_VARIANT_ESP32C6)
 | 
			
		||||
  model = "ESP32-C6";
 | 
			
		||||
#elif defined(USE_ESP32_VARIANT_ESP32S2)
 | 
			
		||||
  model = "ESP32-S2";
 | 
			
		||||
#elif defined(USE_ESP32_VARIANT_ESP32S3)
 | 
			
		||||
  model = "ESP32-S3";
 | 
			
		||||
#elif defined(USE_ESP32_VARIANT_ESP32H2)
 | 
			
		||||
  model = "ESP32-H2";
 | 
			
		||||
#else
 | 
			
		||||
  model = "UNKNOWN";
 | 
			
		||||
#endif
 | 
			
		||||
  std::string features;
 | 
			
		||||
  if (info.features & CHIP_FEATURE_EMB_FLASH) {
 | 
			
		||||
    features += "EMB_FLASH,";
 | 
			
		||||
    info.features &= ~CHIP_FEATURE_EMB_FLASH;
 | 
			
		||||
  }
 | 
			
		||||
  if (info.features & CHIP_FEATURE_WIFI_BGN) {
 | 
			
		||||
    features += "WIFI_BGN,";
 | 
			
		||||
    info.features &= ~CHIP_FEATURE_WIFI_BGN;
 | 
			
		||||
  }
 | 
			
		||||
  if (info.features & CHIP_FEATURE_BLE) {
 | 
			
		||||
    features += "BLE,";
 | 
			
		||||
    info.features &= ~CHIP_FEATURE_BLE;
 | 
			
		||||
  }
 | 
			
		||||
  if (info.features & CHIP_FEATURE_BT) {
 | 
			
		||||
    features += "BT,";
 | 
			
		||||
    info.features &= ~CHIP_FEATURE_BT;
 | 
			
		||||
  }
 | 
			
		||||
  if (info.features & CHIP_FEATURE_EMB_PSRAM) {
 | 
			
		||||
    features += "EMB_PSRAM,";
 | 
			
		||||
    info.features &= ~CHIP_FEATURE_EMB_PSRAM;
 | 
			
		||||
  }
 | 
			
		||||
  if (info.features)
 | 
			
		||||
    features += "Other:" + format_hex(info.features);
 | 
			
		||||
  ESP_LOGD(TAG, "Chip: Model=%s, Features=%s Cores=%u, Revision=%u", model, features.c_str(), info.cores,
 | 
			
		||||
           info.revision);
 | 
			
		||||
  device_info += "|Chip: ";
 | 
			
		||||
  device_info += model;
 | 
			
		||||
  device_info += " Features:";
 | 
			
		||||
  device_info += features;
 | 
			
		||||
  device_info += " Cores:" + to_string(info.cores);
 | 
			
		||||
  device_info += " Revision:" + to_string(info.revision);
 | 
			
		||||
 | 
			
		||||
  ESP_LOGD(TAG, "ESP-IDF Version: %s", esp_get_idf_version());
 | 
			
		||||
  device_info += "|ESP-IDF: ";
 | 
			
		||||
  device_info += esp_get_idf_version();
 | 
			
		||||
 | 
			
		||||
  std::string mac = get_mac_address_pretty();
 | 
			
		||||
  ESP_LOGD(TAG, "EFuse MAC: %s", mac.c_str());
 | 
			
		||||
  device_info += "|EFuse MAC: ";
 | 
			
		||||
  device_info += mac;
 | 
			
		||||
 | 
			
		||||
  switch (rtc_get_reset_reason(0)) {
 | 
			
		||||
    case POWERON_RESET:
 | 
			
		||||
      reset_reason = "Power On Reset";
 | 
			
		||||
      break;
 | 
			
		||||
#if defined(USE_ESP32_VARIANT_ESP32)
 | 
			
		||||
    case SW_RESET:
 | 
			
		||||
#elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
 | 
			
		||||
    case RTC_SW_SYS_RESET:
 | 
			
		||||
#endif
 | 
			
		||||
      reset_reason = "Software Reset Digital Core";
 | 
			
		||||
      break;
 | 
			
		||||
#if defined(USE_ESP32_VARIANT_ESP32)
 | 
			
		||||
    case OWDT_RESET:
 | 
			
		||||
      reset_reason = "Watch Dog Reset Digital Core";
 | 
			
		||||
      break;
 | 
			
		||||
#endif
 | 
			
		||||
    case DEEPSLEEP_RESET:
 | 
			
		||||
      reset_reason = "Deep Sleep Reset Digital Core";
 | 
			
		||||
      break;
 | 
			
		||||
#if defined(USE_ESP32_VARIANT_ESP32)
 | 
			
		||||
    case SDIO_RESET:
 | 
			
		||||
      reset_reason = "SLC Module Reset Digital Core";
 | 
			
		||||
      break;
 | 
			
		||||
#endif
 | 
			
		||||
    case TG0WDT_SYS_RESET:
 | 
			
		||||
      reset_reason = "Timer Group 0 Watch Dog Reset Digital Core";
 | 
			
		||||
      break;
 | 
			
		||||
    case TG1WDT_SYS_RESET:
 | 
			
		||||
      reset_reason = "Timer Group 1 Watch Dog Reset Digital Core";
 | 
			
		||||
      break;
 | 
			
		||||
    case RTCWDT_SYS_RESET:
 | 
			
		||||
      reset_reason = "RTC Watch Dog Reset Digital Core";
 | 
			
		||||
      break;
 | 
			
		||||
#if !defined(USE_ESP32_VARIANT_ESP32C6)
 | 
			
		||||
    case INTRUSION_RESET:
 | 
			
		||||
      reset_reason = "Intrusion Reset CPU";
 | 
			
		||||
      break;
 | 
			
		||||
#endif
 | 
			
		||||
#if defined(USE_ESP32_VARIANT_ESP32)
 | 
			
		||||
    case TGWDT_CPU_RESET:
 | 
			
		||||
      reset_reason = "Timer Group Reset CPU";
 | 
			
		||||
      break;
 | 
			
		||||
#elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
 | 
			
		||||
    case TG0WDT_CPU_RESET:
 | 
			
		||||
      reset_reason = "Timer Group 0 Reset CPU";
 | 
			
		||||
      break;
 | 
			
		||||
#endif
 | 
			
		||||
#if defined(USE_ESP32_VARIANT_ESP32)
 | 
			
		||||
    case SW_CPU_RESET:
 | 
			
		||||
#elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
 | 
			
		||||
    case RTC_SW_CPU_RESET:
 | 
			
		||||
#endif
 | 
			
		||||
      reset_reason = "Software Reset CPU";
 | 
			
		||||
      break;
 | 
			
		||||
    case RTCWDT_CPU_RESET:
 | 
			
		||||
      reset_reason = "RTC Watch Dog Reset CPU";
 | 
			
		||||
      break;
 | 
			
		||||
#if defined(USE_ESP32_VARIANT_ESP32)
 | 
			
		||||
    case EXT_CPU_RESET:
 | 
			
		||||
      reset_reason = "External CPU Reset";
 | 
			
		||||
      break;
 | 
			
		||||
#endif
 | 
			
		||||
    case RTCWDT_BROWN_OUT_RESET:
 | 
			
		||||
      reset_reason = "Voltage Unstable Reset";
 | 
			
		||||
      break;
 | 
			
		||||
    case RTCWDT_RTC_RESET:
 | 
			
		||||
      reset_reason = "RTC Watch Dog Reset Digital Core And RTC Module";
 | 
			
		||||
      break;
 | 
			
		||||
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
 | 
			
		||||
    case TG1WDT_CPU_RESET:
 | 
			
		||||
      reset_reason = "Timer Group 1 Reset CPU";
 | 
			
		||||
      break;
 | 
			
		||||
    case SUPER_WDT_RESET:
 | 
			
		||||
      reset_reason = "Super Watchdog Reset Digital Core And RTC Module";
 | 
			
		||||
      break;
 | 
			
		||||
    case GLITCH_RTC_RESET:
 | 
			
		||||
      reset_reason = "Glitch Reset Digital Core And RTC Module";
 | 
			
		||||
      break;
 | 
			
		||||
    case EFUSE_RESET:
 | 
			
		||||
      reset_reason = "eFuse Reset Digital Core";
 | 
			
		||||
      break;
 | 
			
		||||
#endif
 | 
			
		||||
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S3)
 | 
			
		||||
    case USB_UART_CHIP_RESET:
 | 
			
		||||
      reset_reason = "USB UART Reset Digital Core";
 | 
			
		||||
      break;
 | 
			
		||||
    case USB_JTAG_CHIP_RESET:
 | 
			
		||||
      reset_reason = "USB JTAG Reset Digital Core";
 | 
			
		||||
      break;
 | 
			
		||||
    case POWER_GLITCH_RESET:
 | 
			
		||||
      reset_reason = "Power Glitch Reset Digital Core And RTC Module";
 | 
			
		||||
      break;
 | 
			
		||||
#endif
 | 
			
		||||
    default:
 | 
			
		||||
      reset_reason = "Unknown Reset Reason";
 | 
			
		||||
  }
 | 
			
		||||
  ESP_LOGD(TAG, "Reset Reason: %s", reset_reason.c_str());
 | 
			
		||||
  device_info += "|Reset: ";
 | 
			
		||||
  device_info += reset_reason;
 | 
			
		||||
 | 
			
		||||
  const char *wakeup_reason;
 | 
			
		||||
  switch (rtc_get_wakeup_cause()) {
 | 
			
		||||
    case NO_SLEEP:
 | 
			
		||||
      wakeup_reason = "No Sleep";
 | 
			
		||||
      break;
 | 
			
		||||
    case EXT_EVENT0_TRIG:
 | 
			
		||||
      wakeup_reason = "External Event 0";
 | 
			
		||||
      break;
 | 
			
		||||
    case EXT_EVENT1_TRIG:
 | 
			
		||||
      wakeup_reason = "External Event 1";
 | 
			
		||||
      break;
 | 
			
		||||
    case GPIO_TRIG:
 | 
			
		||||
      wakeup_reason = "GPIO";
 | 
			
		||||
      break;
 | 
			
		||||
    case TIMER_EXPIRE:
 | 
			
		||||
      wakeup_reason = "Wakeup Timer";
 | 
			
		||||
      break;
 | 
			
		||||
    case SDIO_TRIG:
 | 
			
		||||
      wakeup_reason = "SDIO";
 | 
			
		||||
      break;
 | 
			
		||||
    case MAC_TRIG:
 | 
			
		||||
      wakeup_reason = "MAC";
 | 
			
		||||
      break;
 | 
			
		||||
    case UART0_TRIG:
 | 
			
		||||
      wakeup_reason = "UART0";
 | 
			
		||||
      break;
 | 
			
		||||
    case UART1_TRIG:
 | 
			
		||||
      wakeup_reason = "UART1";
 | 
			
		||||
      break;
 | 
			
		||||
    case TOUCH_TRIG:
 | 
			
		||||
      wakeup_reason = "Touch";
 | 
			
		||||
      break;
 | 
			
		||||
    case SAR_TRIG:
 | 
			
		||||
      wakeup_reason = "SAR";
 | 
			
		||||
      break;
 | 
			
		||||
    case BT_TRIG:
 | 
			
		||||
      wakeup_reason = "BT";
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      wakeup_reason = "Unknown";
 | 
			
		||||
  }
 | 
			
		||||
  ESP_LOGD(TAG, "Wakeup Reason: %s", wakeup_reason);
 | 
			
		||||
  device_info += "|Wakeup: ";
 | 
			
		||||
  device_info += wakeup_reason;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if defined(USE_ESP8266) && !defined(CLANG_TIDY)
 | 
			
		||||
  ESP_LOGD(TAG, "Chip ID: 0x%08X", ESP.getChipId());
 | 
			
		||||
  ESP_LOGD(TAG, "SDK Version: %s", ESP.getSdkVersion());
 | 
			
		||||
  ESP_LOGD(TAG, "Core Version: %s", ESP.getCoreVersion().c_str());
 | 
			
		||||
  ESP_LOGD(TAG, "Boot Version=%u Mode=%u", ESP.getBootVersion(), ESP.getBootMode());
 | 
			
		||||
  ESP_LOGD(TAG, "CPU Frequency: %u", ESP.getCpuFreqMHz());
 | 
			
		||||
  ESP_LOGD(TAG, "Flash Chip ID=0x%08X", ESP.getFlashChipId());
 | 
			
		||||
  ESP_LOGD(TAG, "Reset Reason: %s", ESP.getResetReason().c_str());
 | 
			
		||||
  ESP_LOGD(TAG, "Reset Info: %s", ESP.getResetInfo().c_str());
 | 
			
		||||
 | 
			
		||||
  device_info += "|Chip: 0x" + format_hex(ESP.getChipId());
 | 
			
		||||
  device_info += "|SDK: ";
 | 
			
		||||
  device_info += ESP.getSdkVersion();
 | 
			
		||||
  device_info += "|Core: ";
 | 
			
		||||
  device_info += ESP.getCoreVersion().c_str();
 | 
			
		||||
  device_info += "|Boot: ";
 | 
			
		||||
  device_info += to_string(ESP.getBootVersion());
 | 
			
		||||
  device_info += "|Mode: " + to_string(ESP.getBootMode());
 | 
			
		||||
  device_info += "|CPU: " + to_string(ESP.getCpuFreqMHz());
 | 
			
		||||
  device_info += "|Flash: 0x" + format_hex(ESP.getFlashChipId());
 | 
			
		||||
  device_info += "|Reset: ";
 | 
			
		||||
  device_info += ESP.getResetReason().c_str();
 | 
			
		||||
  device_info += "|";
 | 
			
		||||
  device_info += ESP.getResetInfo().c_str();
 | 
			
		||||
 | 
			
		||||
  reset_reason = ESP.getResetReason().c_str();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_RP2040
 | 
			
		||||
  ESP_LOGD(TAG, "CPU Frequency: %u", rp2040.f_cpu());
 | 
			
		||||
  device_info += "CPU Frequency: " + to_string(rp2040.f_cpu());
 | 
			
		||||
#endif  // USE_RP2040
 | 
			
		||||
 | 
			
		||||
#ifdef USE_LIBRETINY
 | 
			
		||||
  ESP_LOGD(TAG, "LibreTiny Version: %s", lt_get_version());
 | 
			
		||||
  ESP_LOGD(TAG, "Chip: %s (%04x) @ %u MHz", lt_cpu_get_model_name(), lt_cpu_get_model(), lt_cpu_get_freq_mhz());
 | 
			
		||||
  ESP_LOGD(TAG, "Chip ID: 0x%06X", lt_cpu_get_mac_id());
 | 
			
		||||
  ESP_LOGD(TAG, "Board: %s", lt_get_board_code());
 | 
			
		||||
  ESP_LOGD(TAG, "Flash: %u KiB / RAM: %u KiB", lt_flash_get_size() / 1024, lt_ram_get_size() / 1024);
 | 
			
		||||
  ESP_LOGD(TAG, "Reset Reason: %s", lt_get_reboot_reason_name(lt_get_reboot_reason()));
 | 
			
		||||
 | 
			
		||||
  device_info += "|Version: ";
 | 
			
		||||
  device_info += LT_BANNER_STR + 10;
 | 
			
		||||
  device_info += "|Reset Reason: ";
 | 
			
		||||
  device_info += lt_get_reboot_reason_name(lt_get_reboot_reason());
 | 
			
		||||
  device_info += "|Chip Name: ";
 | 
			
		||||
  device_info += lt_cpu_get_model_name();
 | 
			
		||||
  device_info += "|Chip ID: 0x" + format_hex(lt_cpu_get_mac_id());
 | 
			
		||||
  device_info += "|Flash: " + to_string(lt_flash_get_size() / 1024) + " KiB";
 | 
			
		||||
  device_info += "|RAM: " + to_string(lt_ram_get_size() / 1024) + " KiB";
 | 
			
		||||
 | 
			
		||||
  reset_reason = lt_get_reboot_reason_name(lt_get_reboot_reason());
 | 
			
		||||
#endif  // USE_LIBRETINY
 | 
			
		||||
  get_device_info_(device_info);
 | 
			
		||||
 | 
			
		||||
#ifdef USE_TEXT_SENSOR
 | 
			
		||||
  if (this->device_info_ != nullptr) {
 | 
			
		||||
@@ -383,14 +47,14 @@ void DebugComponent::dump_config() {
 | 
			
		||||
    this->device_info_->publish_state(device_info);
 | 
			
		||||
  }
 | 
			
		||||
  if (this->reset_reason_ != nullptr) {
 | 
			
		||||
    this->reset_reason_->publish_state(reset_reason);
 | 
			
		||||
    this->reset_reason_->publish_state(get_reset_reason_());
 | 
			
		||||
  }
 | 
			
		||||
#endif  // USE_TEXT_SENSOR
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DebugComponent::loop() {
 | 
			
		||||
  // log when free heap space has halved
 | 
			
		||||
  uint32_t new_free_heap = get_free_heap();
 | 
			
		||||
  uint32_t new_free_heap = get_free_heap_();
 | 
			
		||||
  if (new_free_heap < this->free_heap_ / 2) {
 | 
			
		||||
    this->free_heap_ = new_free_heap;
 | 
			
		||||
    ESP_LOGD(TAG, "Free Heap Size: %" PRIu32 " bytes", this->free_heap_);
 | 
			
		||||
@@ -411,38 +75,16 @@ void DebugComponent::loop() {
 | 
			
		||||
void DebugComponent::update() {
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
  if (this->free_sensor_ != nullptr) {
 | 
			
		||||
    this->free_sensor_->publish_state(get_free_heap());
 | 
			
		||||
    this->free_sensor_->publish_state(get_free_heap_());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (this->block_sensor_ != nullptr) {
 | 
			
		||||
#if defined(USE_ESP8266)
 | 
			
		||||
    // NOLINTNEXTLINE(readability-static-accessed-through-instance)
 | 
			
		||||
    this->block_sensor_->publish_state(ESP.getMaxFreeBlockSize());
 | 
			
		||||
#elif defined(USE_ESP32)
 | 
			
		||||
    this->block_sensor_->publish_state(heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL));
 | 
			
		||||
#elif defined(USE_LIBRETINY)
 | 
			
		||||
    this->block_sensor_->publish_state(lt_heap_get_max_alloc());
 | 
			
		||||
#endif
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
#if defined(USE_ESP8266) && USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2)
 | 
			
		||||
  if (this->fragmentation_sensor_ != nullptr) {
 | 
			
		||||
    // NOLINTNEXTLINE(readability-static-accessed-through-instance)
 | 
			
		||||
    this->fragmentation_sensor_->publish_state(ESP.getHeapFragmentation());
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  if (this->loop_time_sensor_ != nullptr) {
 | 
			
		||||
    this->loop_time_sensor_->publish_state(this->max_loop_time_);
 | 
			
		||||
    this->max_loop_time_ = 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
  if (this->psram_sensor_ != nullptr) {
 | 
			
		||||
    this->psram_sensor_->publish_state(heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
 | 
			
		||||
  }
 | 
			
		||||
#endif  // USE_ESP32
 | 
			
		||||
#endif  // USE_SENSOR
 | 
			
		||||
  update_platform_();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float DebugComponent::get_setup_priority() const { return setup_priority::LATE; }
 | 
			
		||||
 
 | 
			
		||||
@@ -59,6 +59,11 @@ class DebugComponent : public PollingComponent {
 | 
			
		||||
  text_sensor::TextSensor *device_info_{nullptr};
 | 
			
		||||
  text_sensor::TextSensor *reset_reason_{nullptr};
 | 
			
		||||
#endif  // USE_TEXT_SENSOR
 | 
			
		||||
 | 
			
		||||
  std::string get_reset_reason_();
 | 
			
		||||
  uint32_t get_free_heap_();
 | 
			
		||||
  void get_device_info_(std::string &device_info);
 | 
			
		||||
  void update_platform_();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace debug
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										287
									
								
								esphome/components/debug/debug_esp32.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										287
									
								
								esphome/components/debug/debug_esp32.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,287 @@
 | 
			
		||||
#include "debug_component.h"
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
#include <esp_heap_caps.h>
 | 
			
		||||
#include <esp_system.h>
 | 
			
		||||
#include <esp_chip_info.h>
 | 
			
		||||
 | 
			
		||||
#if defined(USE_ESP32_VARIANT_ESP32)
 | 
			
		||||
#include <esp32/rom/rtc.h>
 | 
			
		||||
#elif defined(USE_ESP32_VARIANT_ESP32C3)
 | 
			
		||||
#include <esp32c3/rom/rtc.h>
 | 
			
		||||
#elif defined(USE_ESP32_VARIANT_ESP32C6)
 | 
			
		||||
#include <esp32c6/rom/rtc.h>
 | 
			
		||||
#elif defined(USE_ESP32_VARIANT_ESP32S2)
 | 
			
		||||
#include <esp32s2/rom/rtc.h>
 | 
			
		||||
#elif defined(USE_ESP32_VARIANT_ESP32S3)
 | 
			
		||||
#include <esp32s3/rom/rtc.h>
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_ARDUINO
 | 
			
		||||
#include <Esp.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace debug {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "debug";
 | 
			
		||||
 | 
			
		||||
std::string DebugComponent::get_reset_reason_() {
 | 
			
		||||
  std::string reset_reason;
 | 
			
		||||
  switch (rtc_get_reset_reason(0)) {
 | 
			
		||||
    case POWERON_RESET:
 | 
			
		||||
      reset_reason = "Power On Reset";
 | 
			
		||||
      break;
 | 
			
		||||
#if defined(USE_ESP32_VARIANT_ESP32)
 | 
			
		||||
    case SW_RESET:
 | 
			
		||||
#elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
 | 
			
		||||
    case RTC_SW_SYS_RESET:
 | 
			
		||||
#endif
 | 
			
		||||
      reset_reason = "Software Reset Digital Core";
 | 
			
		||||
      break;
 | 
			
		||||
#if defined(USE_ESP32_VARIANT_ESP32)
 | 
			
		||||
    case OWDT_RESET:
 | 
			
		||||
      reset_reason = "Watch Dog Reset Digital Core";
 | 
			
		||||
      break;
 | 
			
		||||
#endif
 | 
			
		||||
    case DEEPSLEEP_RESET:
 | 
			
		||||
      reset_reason = "Deep Sleep Reset Digital Core";
 | 
			
		||||
      break;
 | 
			
		||||
#if defined(USE_ESP32_VARIANT_ESP32)
 | 
			
		||||
    case SDIO_RESET:
 | 
			
		||||
      reset_reason = "SLC Module Reset Digital Core";
 | 
			
		||||
      break;
 | 
			
		||||
#endif
 | 
			
		||||
    case TG0WDT_SYS_RESET:
 | 
			
		||||
      reset_reason = "Timer Group 0 Watch Dog Reset Digital Core";
 | 
			
		||||
      break;
 | 
			
		||||
    case TG1WDT_SYS_RESET:
 | 
			
		||||
      reset_reason = "Timer Group 1 Watch Dog Reset Digital Core";
 | 
			
		||||
      break;
 | 
			
		||||
    case RTCWDT_SYS_RESET:
 | 
			
		||||
      reset_reason = "RTC Watch Dog Reset Digital Core";
 | 
			
		||||
      break;
 | 
			
		||||
#if !defined(USE_ESP32_VARIANT_ESP32C6)
 | 
			
		||||
    case INTRUSION_RESET:
 | 
			
		||||
      reset_reason = "Intrusion Reset CPU";
 | 
			
		||||
      break;
 | 
			
		||||
#endif
 | 
			
		||||
#if defined(USE_ESP32_VARIANT_ESP32)
 | 
			
		||||
    case TGWDT_CPU_RESET:
 | 
			
		||||
      reset_reason = "Timer Group Reset CPU";
 | 
			
		||||
      break;
 | 
			
		||||
#elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
 | 
			
		||||
    case TG0WDT_CPU_RESET:
 | 
			
		||||
      reset_reason = "Timer Group 0 Reset CPU";
 | 
			
		||||
      break;
 | 
			
		||||
#endif
 | 
			
		||||
#if defined(USE_ESP32_VARIANT_ESP32)
 | 
			
		||||
    case SW_CPU_RESET:
 | 
			
		||||
#elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
 | 
			
		||||
    case RTC_SW_CPU_RESET:
 | 
			
		||||
#endif
 | 
			
		||||
      reset_reason = "Software Reset CPU";
 | 
			
		||||
      break;
 | 
			
		||||
    case RTCWDT_CPU_RESET:
 | 
			
		||||
      reset_reason = "RTC Watch Dog Reset CPU";
 | 
			
		||||
      break;
 | 
			
		||||
#if defined(USE_ESP32_VARIANT_ESP32)
 | 
			
		||||
    case EXT_CPU_RESET:
 | 
			
		||||
      reset_reason = "External CPU Reset";
 | 
			
		||||
      break;
 | 
			
		||||
#endif
 | 
			
		||||
    case RTCWDT_BROWN_OUT_RESET:
 | 
			
		||||
      reset_reason = "Voltage Unstable Reset";
 | 
			
		||||
      break;
 | 
			
		||||
    case RTCWDT_RTC_RESET:
 | 
			
		||||
      reset_reason = "RTC Watch Dog Reset Digital Core And RTC Module";
 | 
			
		||||
      break;
 | 
			
		||||
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
 | 
			
		||||
    case TG1WDT_CPU_RESET:
 | 
			
		||||
      reset_reason = "Timer Group 1 Reset CPU";
 | 
			
		||||
      break;
 | 
			
		||||
    case SUPER_WDT_RESET:
 | 
			
		||||
      reset_reason = "Super Watchdog Reset Digital Core And RTC Module";
 | 
			
		||||
      break;
 | 
			
		||||
    case GLITCH_RTC_RESET:
 | 
			
		||||
      reset_reason = "Glitch Reset Digital Core And RTC Module";
 | 
			
		||||
      break;
 | 
			
		||||
    case EFUSE_RESET:
 | 
			
		||||
      reset_reason = "eFuse Reset Digital Core";
 | 
			
		||||
      break;
 | 
			
		||||
#endif
 | 
			
		||||
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S3)
 | 
			
		||||
    case USB_UART_CHIP_RESET:
 | 
			
		||||
      reset_reason = "USB UART Reset Digital Core";
 | 
			
		||||
      break;
 | 
			
		||||
    case USB_JTAG_CHIP_RESET:
 | 
			
		||||
      reset_reason = "USB JTAG Reset Digital Core";
 | 
			
		||||
      break;
 | 
			
		||||
    case POWER_GLITCH_RESET:
 | 
			
		||||
      reset_reason = "Power Glitch Reset Digital Core And RTC Module";
 | 
			
		||||
      break;
 | 
			
		||||
#endif
 | 
			
		||||
    default:
 | 
			
		||||
      reset_reason = "Unknown Reset Reason";
 | 
			
		||||
  }
 | 
			
		||||
  ESP_LOGD(TAG, "Reset Reason: %s", reset_reason.c_str());
 | 
			
		||||
  return reset_reason;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t DebugComponent::get_free_heap_() { return heap_caps_get_free_size(MALLOC_CAP_INTERNAL); }
 | 
			
		||||
 | 
			
		||||
void DebugComponent::get_device_info_(std::string &device_info) {
 | 
			
		||||
#if defined(USE_ARDUINO)
 | 
			
		||||
  const char *flash_mode;
 | 
			
		||||
  switch (ESP.getFlashChipMode()) {  // NOLINT(readability-static-accessed-through-instance)
 | 
			
		||||
    case FM_QIO:
 | 
			
		||||
      flash_mode = "QIO";
 | 
			
		||||
      break;
 | 
			
		||||
    case FM_QOUT:
 | 
			
		||||
      flash_mode = "QOUT";
 | 
			
		||||
      break;
 | 
			
		||||
    case FM_DIO:
 | 
			
		||||
      flash_mode = "DIO";
 | 
			
		||||
      break;
 | 
			
		||||
    case FM_DOUT:
 | 
			
		||||
      flash_mode = "DOUT";
 | 
			
		||||
      break;
 | 
			
		||||
    case FM_FAST_READ:
 | 
			
		||||
      flash_mode = "FAST_READ";
 | 
			
		||||
      break;
 | 
			
		||||
    case FM_SLOW_READ:
 | 
			
		||||
      flash_mode = "SLOW_READ";
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      flash_mode = "UNKNOWN";
 | 
			
		||||
  }
 | 
			
		||||
  ESP_LOGD(TAG, "Flash Chip: Size=%ukB Speed=%uMHz Mode=%s",
 | 
			
		||||
           ESP.getFlashChipSize() / 1024,                                                   // NOLINT
 | 
			
		||||
           ESP.getFlashChipSpeed() / 1000000, flash_mode);                                  // NOLINT
 | 
			
		||||
  device_info += "|Flash: " + to_string(ESP.getFlashChipSize() / 1024) +                    // NOLINT
 | 
			
		||||
                 "kB Speed:" + to_string(ESP.getFlashChipSpeed() / 1000000) + "MHz Mode:";  // NOLINT
 | 
			
		||||
  device_info += flash_mode;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  esp_chip_info_t info;
 | 
			
		||||
  esp_chip_info(&info);
 | 
			
		||||
  const char *model;
 | 
			
		||||
#if defined(USE_ESP32_VARIANT_ESP32)
 | 
			
		||||
  model = "ESP32";
 | 
			
		||||
#elif defined(USE_ESP32_VARIANT_ESP32C3)
 | 
			
		||||
  model = "ESP32-C3";
 | 
			
		||||
#elif defined(USE_ESP32_VARIANT_ESP32C6)
 | 
			
		||||
  model = "ESP32-C6";
 | 
			
		||||
#elif defined(USE_ESP32_VARIANT_ESP32S2)
 | 
			
		||||
  model = "ESP32-S2";
 | 
			
		||||
#elif defined(USE_ESP32_VARIANT_ESP32S3)
 | 
			
		||||
  model = "ESP32-S3";
 | 
			
		||||
#elif defined(USE_ESP32_VARIANT_ESP32H2)
 | 
			
		||||
  model = "ESP32-H2";
 | 
			
		||||
#else
 | 
			
		||||
  model = "UNKNOWN";
 | 
			
		||||
#endif
 | 
			
		||||
  std::string features;
 | 
			
		||||
  if (info.features & CHIP_FEATURE_EMB_FLASH) {
 | 
			
		||||
    features += "EMB_FLASH,";
 | 
			
		||||
    info.features &= ~CHIP_FEATURE_EMB_FLASH;
 | 
			
		||||
  }
 | 
			
		||||
  if (info.features & CHIP_FEATURE_WIFI_BGN) {
 | 
			
		||||
    features += "WIFI_BGN,";
 | 
			
		||||
    info.features &= ~CHIP_FEATURE_WIFI_BGN;
 | 
			
		||||
  }
 | 
			
		||||
  if (info.features & CHIP_FEATURE_BLE) {
 | 
			
		||||
    features += "BLE,";
 | 
			
		||||
    info.features &= ~CHIP_FEATURE_BLE;
 | 
			
		||||
  }
 | 
			
		||||
  if (info.features & CHIP_FEATURE_BT) {
 | 
			
		||||
    features += "BT,";
 | 
			
		||||
    info.features &= ~CHIP_FEATURE_BT;
 | 
			
		||||
  }
 | 
			
		||||
  if (info.features & CHIP_FEATURE_EMB_PSRAM) {
 | 
			
		||||
    features += "EMB_PSRAM,";
 | 
			
		||||
    info.features &= ~CHIP_FEATURE_EMB_PSRAM;
 | 
			
		||||
  }
 | 
			
		||||
  if (info.features)
 | 
			
		||||
    features += "Other:" + format_hex(info.features);
 | 
			
		||||
  ESP_LOGD(TAG, "Chip: Model=%s, Features=%s Cores=%u, Revision=%u", model, features.c_str(), info.cores,
 | 
			
		||||
           info.revision);
 | 
			
		||||
  device_info += "|Chip: ";
 | 
			
		||||
  device_info += model;
 | 
			
		||||
  device_info += " Features:";
 | 
			
		||||
  device_info += features;
 | 
			
		||||
  device_info += " Cores:" + to_string(info.cores);
 | 
			
		||||
  device_info += " Revision:" + to_string(info.revision);
 | 
			
		||||
 | 
			
		||||
  ESP_LOGD(TAG, "ESP-IDF Version: %s", esp_get_idf_version());
 | 
			
		||||
  device_info += "|ESP-IDF: ";
 | 
			
		||||
  device_info += esp_get_idf_version();
 | 
			
		||||
 | 
			
		||||
  std::string mac = get_mac_address_pretty();
 | 
			
		||||
  ESP_LOGD(TAG, "EFuse MAC: %s", mac.c_str());
 | 
			
		||||
  device_info += "|EFuse MAC: ";
 | 
			
		||||
  device_info += mac;
 | 
			
		||||
 | 
			
		||||
  device_info += "|Reset: ";
 | 
			
		||||
  device_info += get_reset_reason_();
 | 
			
		||||
 | 
			
		||||
  const char *wakeup_reason;
 | 
			
		||||
  switch (rtc_get_wakeup_cause()) {
 | 
			
		||||
    case NO_SLEEP:
 | 
			
		||||
      wakeup_reason = "No Sleep";
 | 
			
		||||
      break;
 | 
			
		||||
    case EXT_EVENT0_TRIG:
 | 
			
		||||
      wakeup_reason = "External Event 0";
 | 
			
		||||
      break;
 | 
			
		||||
    case EXT_EVENT1_TRIG:
 | 
			
		||||
      wakeup_reason = "External Event 1";
 | 
			
		||||
      break;
 | 
			
		||||
    case GPIO_TRIG:
 | 
			
		||||
      wakeup_reason = "GPIO";
 | 
			
		||||
      break;
 | 
			
		||||
    case TIMER_EXPIRE:
 | 
			
		||||
      wakeup_reason = "Wakeup Timer";
 | 
			
		||||
      break;
 | 
			
		||||
    case SDIO_TRIG:
 | 
			
		||||
      wakeup_reason = "SDIO";
 | 
			
		||||
      break;
 | 
			
		||||
    case MAC_TRIG:
 | 
			
		||||
      wakeup_reason = "MAC";
 | 
			
		||||
      break;
 | 
			
		||||
    case UART0_TRIG:
 | 
			
		||||
      wakeup_reason = "UART0";
 | 
			
		||||
      break;
 | 
			
		||||
    case UART1_TRIG:
 | 
			
		||||
      wakeup_reason = "UART1";
 | 
			
		||||
      break;
 | 
			
		||||
    case TOUCH_TRIG:
 | 
			
		||||
      wakeup_reason = "Touch";
 | 
			
		||||
      break;
 | 
			
		||||
    case SAR_TRIG:
 | 
			
		||||
      wakeup_reason = "SAR";
 | 
			
		||||
      break;
 | 
			
		||||
    case BT_TRIG:
 | 
			
		||||
      wakeup_reason = "BT";
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      wakeup_reason = "Unknown";
 | 
			
		||||
  }
 | 
			
		||||
  ESP_LOGD(TAG, "Wakeup Reason: %s", wakeup_reason);
 | 
			
		||||
  device_info += "|Wakeup: ";
 | 
			
		||||
  device_info += wakeup_reason;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DebugComponent::update_platform_() {
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
  if (this->block_sensor_ != nullptr) {
 | 
			
		||||
    this->block_sensor_->publish_state(heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL));
 | 
			
		||||
  }
 | 
			
		||||
  if (this->psram_sensor_ != nullptr) {
 | 
			
		||||
    this->psram_sensor_->publish_state(heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace debug
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										94
									
								
								esphome/components/debug/debug_esp8266.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								esphome/components/debug/debug_esp8266.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,94 @@
 | 
			
		||||
#include "debug_component.h"
 | 
			
		||||
#ifdef USE_ESP8266
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include <Esp.h>
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace debug {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "debug";
 | 
			
		||||
 | 
			
		||||
std::string DebugComponent::get_reset_reason_() {
 | 
			
		||||
#if !defined(CLANG_TIDY)
 | 
			
		||||
  return ESP.getResetReason().c_str();
 | 
			
		||||
#else
 | 
			
		||||
  return "";
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t DebugComponent::get_free_heap_() {
 | 
			
		||||
  return ESP.getFreeHeap();  // NOLINT(readability-static-accessed-through-instance)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DebugComponent::get_device_info_(std::string &device_info) {
 | 
			
		||||
  const char *flash_mode;
 | 
			
		||||
  switch (ESP.getFlashChipMode()) {  // NOLINT(readability-static-accessed-through-instance)
 | 
			
		||||
    case FM_QIO:
 | 
			
		||||
      flash_mode = "QIO";
 | 
			
		||||
      break;
 | 
			
		||||
    case FM_QOUT:
 | 
			
		||||
      flash_mode = "QOUT";
 | 
			
		||||
      break;
 | 
			
		||||
    case FM_DIO:
 | 
			
		||||
      flash_mode = "DIO";
 | 
			
		||||
      break;
 | 
			
		||||
    case FM_DOUT:
 | 
			
		||||
      flash_mode = "DOUT";
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      flash_mode = "UNKNOWN";
 | 
			
		||||
  }
 | 
			
		||||
  ESP_LOGD(TAG, "Flash Chip: Size=%ukB Speed=%uMHz Mode=%s",
 | 
			
		||||
           ESP.getFlashChipSize() / 1024,                                                   // NOLINT
 | 
			
		||||
           ESP.getFlashChipSpeed() / 1000000, flash_mode);                                  // NOLINT
 | 
			
		||||
  device_info += "|Flash: " + to_string(ESP.getFlashChipSize() / 1024) +                    // NOLINT
 | 
			
		||||
                 "kB Speed:" + to_string(ESP.getFlashChipSpeed() / 1000000) + "MHz Mode:";  // NOLINT
 | 
			
		||||
  device_info += flash_mode;
 | 
			
		||||
 | 
			
		||||
#if !defined(CLANG_TIDY)
 | 
			
		||||
  auto reset_reason = get_reset_reason_();
 | 
			
		||||
  ESP_LOGD(TAG, "Chip ID: 0x%08X", ESP.getChipId());
 | 
			
		||||
  ESP_LOGD(TAG, "SDK Version: %s", ESP.getSdkVersion());
 | 
			
		||||
  ESP_LOGD(TAG, "Core Version: %s", ESP.getCoreVersion().c_str());
 | 
			
		||||
  ESP_LOGD(TAG, "Boot Version=%u Mode=%u", ESP.getBootVersion(), ESP.getBootMode());
 | 
			
		||||
  ESP_LOGD(TAG, "CPU Frequency: %u", ESP.getCpuFreqMHz());
 | 
			
		||||
  ESP_LOGD(TAG, "Flash Chip ID=0x%08X", ESP.getFlashChipId());
 | 
			
		||||
  ESP_LOGD(TAG, "Reset Reason: %s", reset_reason.c_str());
 | 
			
		||||
  ESP_LOGD(TAG, "Reset Info: %s", ESP.getResetInfo().c_str());
 | 
			
		||||
 | 
			
		||||
  device_info += "|Chip: 0x" + format_hex(ESP.getChipId());
 | 
			
		||||
  device_info += "|SDK: ";
 | 
			
		||||
  device_info += ESP.getSdkVersion();
 | 
			
		||||
  device_info += "|Core: ";
 | 
			
		||||
  device_info += ESP.getCoreVersion().c_str();
 | 
			
		||||
  device_info += "|Boot: ";
 | 
			
		||||
  device_info += to_string(ESP.getBootVersion());
 | 
			
		||||
  device_info += "|Mode: " + to_string(ESP.getBootMode());
 | 
			
		||||
  device_info += "|CPU: " + to_string(ESP.getCpuFreqMHz());
 | 
			
		||||
  device_info += "|Flash: 0x" + format_hex(ESP.getFlashChipId());
 | 
			
		||||
  device_info += "|Reset: ";
 | 
			
		||||
  device_info += reset_reason;
 | 
			
		||||
  device_info += "|";
 | 
			
		||||
  device_info += ESP.getResetInfo().c_str();
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DebugComponent::update_platform_() {
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
  if (this->block_sensor_ != nullptr) {
 | 
			
		||||
    // NOLINTNEXTLINE(readability-static-accessed-through-instance)
 | 
			
		||||
    this->block_sensor_->publish_state(ESP.getMaxFreeBlockSize());
 | 
			
		||||
  }
 | 
			
		||||
#if USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2)
 | 
			
		||||
  if (this->fragmentation_sensor_ != nullptr) {
 | 
			
		||||
    // NOLINTNEXTLINE(readability-static-accessed-through-instance)
 | 
			
		||||
    this->fragmentation_sensor_->publish_state(ESP.getHeapFragmentation());
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace debug
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										18
									
								
								esphome/components/debug/debug_host.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								esphome/components/debug/debug_host.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
#include "debug_component.h"
 | 
			
		||||
#ifdef USE_HOST
 | 
			
		||||
#include <climits>
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace debug {
 | 
			
		||||
 | 
			
		||||
std::string DebugComponent::get_reset_reason_() { return ""; }
 | 
			
		||||
 | 
			
		||||
uint32_t DebugComponent::get_free_heap_() { return INT_MAX; }
 | 
			
		||||
 | 
			
		||||
void DebugComponent::get_device_info_(std::string &device_info) {}
 | 
			
		||||
 | 
			
		||||
void DebugComponent::update_platform_() {}
 | 
			
		||||
 | 
			
		||||
}  // namespace debug
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										44
									
								
								esphome/components/debug/debug_libretiny.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								esphome/components/debug/debug_libretiny.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
			
		||||
#include "debug_component.h"
 | 
			
		||||
#ifdef USE_LIBRETINY
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace debug {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "debug";
 | 
			
		||||
 | 
			
		||||
std::string DebugComponent::get_reset_reason_() { return lt_get_reboot_reason_name(lt_get_reboot_reason()); }
 | 
			
		||||
 | 
			
		||||
uint32_t DebugComponent::get_free_heap_() { return lt_heap_get_free(); }
 | 
			
		||||
 | 
			
		||||
void DebugComponent::get_device_info_(std::string &device_info) {
 | 
			
		||||
  std::string reset_reason = get_reset_reason_();
 | 
			
		||||
  ESP_LOGD(TAG, "LibreTiny Version: %s", lt_get_version());
 | 
			
		||||
  ESP_LOGD(TAG, "Chip: %s (%04x) @ %u MHz", lt_cpu_get_model_name(), lt_cpu_get_model(), lt_cpu_get_freq_mhz());
 | 
			
		||||
  ESP_LOGD(TAG, "Chip ID: 0x%06X", lt_cpu_get_mac_id());
 | 
			
		||||
  ESP_LOGD(TAG, "Board: %s", lt_get_board_code());
 | 
			
		||||
  ESP_LOGD(TAG, "Flash: %u KiB / RAM: %u KiB", lt_flash_get_size() / 1024, lt_ram_get_size() / 1024);
 | 
			
		||||
  ESP_LOGD(TAG, "Reset Reason: %s", reset_reason.c_str());
 | 
			
		||||
 | 
			
		||||
  device_info += "|Version: ";
 | 
			
		||||
  device_info += LT_BANNER_STR + 10;
 | 
			
		||||
  device_info += "|Reset Reason: ";
 | 
			
		||||
  device_info += reset_reason;
 | 
			
		||||
  device_info += "|Chip Name: ";
 | 
			
		||||
  device_info += lt_cpu_get_model_name();
 | 
			
		||||
  device_info += "|Chip ID: 0x" + format_hex(lt_cpu_get_mac_id());
 | 
			
		||||
  device_info += "|Flash: " + to_string(lt_flash_get_size() / 1024) + " KiB";
 | 
			
		||||
  device_info += "|RAM: " + to_string(lt_ram_get_size() / 1024) + " KiB";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DebugComponent::update_platform_() {
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
  if (this->block_sensor_ != nullptr) {
 | 
			
		||||
    this->block_sensor_->publish_state(lt_heap_get_max_alloc());
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace debug
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										23
									
								
								esphome/components/debug/debug_rp2040.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								esphome/components/debug/debug_rp2040.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
#include "debug_component.h"
 | 
			
		||||
#ifdef USE_RP2040
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include <Arduino.h>
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace debug {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "debug";
 | 
			
		||||
 | 
			
		||||
std::string DebugComponent::get_reset_reason_() { return ""; }
 | 
			
		||||
 | 
			
		||||
uint32_t DebugComponent::get_free_heap_() { return rp2040.getFreeHeap(); }
 | 
			
		||||
 | 
			
		||||
void DebugComponent::get_device_info_(std::string &device_info) {
 | 
			
		||||
  ESP_LOGD(TAG, "CPU Frequency: %u", rp2040.f_cpu());
 | 
			
		||||
  device_info += "CPU Frequency: " + to_string(rp2040.f_cpu());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DebugComponent::update_platform_() {}
 | 
			
		||||
 | 
			
		||||
}  // namespace debug
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
#endif
 | 
			
		||||
@@ -1,12 +1,7 @@
 | 
			
		||||
#include "deep_sleep_component.h"
 | 
			
		||||
#include <cinttypes>
 | 
			
		||||
#include "esphome/core/application.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP8266
 | 
			
		||||
#include <Esp.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace deep_sleep {
 | 
			
		||||
 | 
			
		||||
@@ -14,25 +9,6 @@ static const char *const TAG = "deep_sleep";
 | 
			
		||||
 | 
			
		||||
bool global_has_deep_sleep = false;  // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
 | 
			
		||||
 | 
			
		||||
optional<uint32_t> DeepSleepComponent::get_run_duration_() const {
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
  if (this->wakeup_cause_to_run_duration_.has_value()) {
 | 
			
		||||
    esp_sleep_wakeup_cause_t wakeup_cause = esp_sleep_get_wakeup_cause();
 | 
			
		||||
    switch (wakeup_cause) {
 | 
			
		||||
      case ESP_SLEEP_WAKEUP_EXT0:
 | 
			
		||||
      case ESP_SLEEP_WAKEUP_EXT1:
 | 
			
		||||
      case ESP_SLEEP_WAKEUP_GPIO:
 | 
			
		||||
        return this->wakeup_cause_to_run_duration_->gpio_cause;
 | 
			
		||||
      case ESP_SLEEP_WAKEUP_TOUCHPAD:
 | 
			
		||||
        return this->wakeup_cause_to_run_duration_->touch_cause;
 | 
			
		||||
      default:
 | 
			
		||||
        return this->wakeup_cause_to_run_duration_->default_cause;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
  return this->run_duration_;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DeepSleepComponent::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up Deep Sleep...");
 | 
			
		||||
  global_has_deep_sleep = true;
 | 
			
		||||
@@ -45,6 +21,7 @@ void DeepSleepComponent::setup() {
 | 
			
		||||
    ESP_LOGD(TAG, "Not scheduling Deep Sleep, as no run duration is configured.");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DeepSleepComponent::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up Deep Sleep...");
 | 
			
		||||
  if (this->sleep_duration_.has_value()) {
 | 
			
		||||
@@ -54,65 +31,31 @@ void DeepSleepComponent::dump_config() {
 | 
			
		||||
  if (this->run_duration_.has_value()) {
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  Run Duration: %" PRIu32 " ms", *this->run_duration_);
 | 
			
		||||
  }
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
  if (wakeup_pin_ != nullptr) {
 | 
			
		||||
    LOG_PIN("  Wakeup Pin: ", this->wakeup_pin_);
 | 
			
		||||
  }
 | 
			
		||||
  if (this->wakeup_cause_to_run_duration_.has_value()) {
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  Default Wakeup Run Duration: %" PRIu32 " ms",
 | 
			
		||||
                  this->wakeup_cause_to_run_duration_->default_cause);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  Touch Wakeup Run Duration: %" PRIu32 " ms", this->wakeup_cause_to_run_duration_->touch_cause);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  GPIO Wakeup Run Duration: %" PRIu32 " ms", this->wakeup_cause_to_run_duration_->gpio_cause);
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
  this->dump_config_platform_();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DeepSleepComponent::loop() {
 | 
			
		||||
  if (this->next_enter_deep_sleep_)
 | 
			
		||||
    this->begin_sleep();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float DeepSleepComponent::get_loop_priority() const {
 | 
			
		||||
  return -100.0f;  // run after everything else is ready
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DeepSleepComponent::set_sleep_duration(uint32_t time_ms) { this->sleep_duration_ = uint64_t(time_ms) * 1000; }
 | 
			
		||||
#if defined(USE_ESP32)
 | 
			
		||||
void DeepSleepComponent::set_wakeup_pin_mode(WakeupPinMode wakeup_pin_mode) {
 | 
			
		||||
  this->wakeup_pin_mode_ = wakeup_pin_mode;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if defined(USE_ESP32)
 | 
			
		||||
#if !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32C6)
 | 
			
		||||
 | 
			
		||||
void DeepSleepComponent::set_ext1_wakeup(Ext1Wakeup ext1_wakeup) { this->ext1_wakeup_ = ext1_wakeup; }
 | 
			
		||||
 | 
			
		||||
void DeepSleepComponent::set_touch_wakeup(bool touch_wakeup) { this->touch_wakeup_ = touch_wakeup; }
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void DeepSleepComponent::set_run_duration(WakeupCauseToRunDuration wakeup_cause_to_run_duration) {
 | 
			
		||||
  wakeup_cause_to_run_duration_ = wakeup_cause_to_run_duration;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void DeepSleepComponent::set_run_duration(uint32_t time_ms) { this->run_duration_ = time_ms; }
 | 
			
		||||
 | 
			
		||||
void DeepSleepComponent::begin_sleep(bool manual) {
 | 
			
		||||
  if (this->prevent_ && !manual) {
 | 
			
		||||
    this->next_enter_deep_sleep_ = true;
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
  if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_KEEP_AWAKE && this->wakeup_pin_ != nullptr &&
 | 
			
		||||
      !this->sleep_duration_.has_value() && this->wakeup_pin_->digital_read()) {
 | 
			
		||||
    // Defer deep sleep until inactive
 | 
			
		||||
    if (!this->next_enter_deep_sleep_) {
 | 
			
		||||
      this->status_set_warning();
 | 
			
		||||
      ESP_LOGW(TAG, "Waiting for pin_ to switch state to enter deep sleep...");
 | 
			
		||||
    }
 | 
			
		||||
    this->next_enter_deep_sleep_ = true;
 | 
			
		||||
 | 
			
		||||
  if (!this->prepare_to_sleep_()) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  ESP_LOGI(TAG, "Beginning Deep Sleep");
 | 
			
		||||
  if (this->sleep_duration_.has_value()) {
 | 
			
		||||
@@ -120,47 +63,13 @@ void DeepSleepComponent::begin_sleep(bool manual) {
 | 
			
		||||
  }
 | 
			
		||||
  App.run_safe_shutdown_hooks();
 | 
			
		||||
 | 
			
		||||
#if defined(USE_ESP32)
 | 
			
		||||
#if !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32C6)
 | 
			
		||||
  if (this->sleep_duration_.has_value())
 | 
			
		||||
    esp_sleep_enable_timer_wakeup(*this->sleep_duration_);
 | 
			
		||||
  if (this->wakeup_pin_ != nullptr) {
 | 
			
		||||
    bool level = !this->wakeup_pin_->is_inverted();
 | 
			
		||||
    if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_INVERT_WAKEUP && this->wakeup_pin_->digital_read()) {
 | 
			
		||||
      level = !level;
 | 
			
		||||
    }
 | 
			
		||||
    esp_sleep_enable_ext0_wakeup(gpio_num_t(this->wakeup_pin_->get_pin()), level);
 | 
			
		||||
  }
 | 
			
		||||
  if (this->ext1_wakeup_.has_value()) {
 | 
			
		||||
    esp_sleep_enable_ext1_wakeup(this->ext1_wakeup_->mask, this->ext1_wakeup_->wakeup_mode);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (this->touch_wakeup_.has_value() && *(this->touch_wakeup_)) {
 | 
			
		||||
    esp_sleep_enable_touchpad_wakeup();
 | 
			
		||||
    esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6)
 | 
			
		||||
  if (this->sleep_duration_.has_value())
 | 
			
		||||
    esp_sleep_enable_timer_wakeup(*this->sleep_duration_);
 | 
			
		||||
  if (this->wakeup_pin_ != nullptr) {
 | 
			
		||||
    bool level = !this->wakeup_pin_->is_inverted();
 | 
			
		||||
    if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_INVERT_WAKEUP && this->wakeup_pin_->digital_read()) {
 | 
			
		||||
      level = !level;
 | 
			
		||||
    }
 | 
			
		||||
    esp_deep_sleep_enable_gpio_wakeup(1 << this->wakeup_pin_->get_pin(),
 | 
			
		||||
                                      static_cast<esp_deepsleep_gpio_wake_up_mode_t>(level));
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
  esp_deep_sleep_start();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP8266
 | 
			
		||||
  ESP.deepSleep(*this->sleep_duration_);  // NOLINT(readability-static-accessed-through-instance)
 | 
			
		||||
#endif
 | 
			
		||||
  this->deep_sleep_();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float DeepSleepComponent::get_setup_priority() const { return setup_priority::LATE; }
 | 
			
		||||
 | 
			
		||||
void DeepSleepComponent::prevent_deep_sleep() { this->prevent_ = true; }
 | 
			
		||||
 | 
			
		||||
void DeepSleepComponent::allow_deep_sleep() { this->prevent_ = false; }
 | 
			
		||||
 | 
			
		||||
}  // namespace deep_sleep
 | 
			
		||||
 
 | 
			
		||||
@@ -34,10 +34,12 @@ enum WakeupPinMode {
 | 
			
		||||
  WAKEUP_PIN_MODE_INVERT_WAKEUP,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#if defined(USE_ESP32) && !defined(USE_ESP32_VARIANT_ESP32C3)
 | 
			
		||||
struct Ext1Wakeup {
 | 
			
		||||
  uint64_t mask;
 | 
			
		||||
  esp_sleep_ext1_wakeup_mode_t wakeup_mode;
 | 
			
		||||
};
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
struct WakeupCauseToRunDuration {
 | 
			
		||||
  // Run duration if woken up by timer or any other reason besides those below.
 | 
			
		||||
@@ -106,11 +108,19 @@ class DeepSleepComponent : public Component {
 | 
			
		||||
  // duration before entering deep sleep.
 | 
			
		||||
  optional<uint32_t> get_run_duration_() const;
 | 
			
		||||
 | 
			
		||||
  void dump_config_platform_();
 | 
			
		||||
  bool prepare_to_sleep_();
 | 
			
		||||
  void deep_sleep_();
 | 
			
		||||
 | 
			
		||||
  optional<uint64_t> sleep_duration_;
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
  InternalGPIOPin *wakeup_pin_;
 | 
			
		||||
  WakeupPinMode wakeup_pin_mode_{WAKEUP_PIN_MODE_IGNORE};
 | 
			
		||||
 | 
			
		||||
#if !defined(USE_ESP32_VARIANT_ESP32C3)
 | 
			
		||||
  optional<Ext1Wakeup> ext1_wakeup_;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  optional<bool> touch_wakeup_;
 | 
			
		||||
  optional<WakeupCauseToRunDuration> wakeup_cause_to_run_duration_;
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										104
									
								
								esphome/components/deep_sleep/deep_sleep_esp32.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								esphome/components/deep_sleep/deep_sleep_esp32.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,104 @@
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
#include "deep_sleep_component.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace deep_sleep {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "deep_sleep";
 | 
			
		||||
 | 
			
		||||
optional<uint32_t> DeepSleepComponent::get_run_duration_() const {
 | 
			
		||||
  if (this->wakeup_cause_to_run_duration_.has_value()) {
 | 
			
		||||
    esp_sleep_wakeup_cause_t wakeup_cause = esp_sleep_get_wakeup_cause();
 | 
			
		||||
    switch (wakeup_cause) {
 | 
			
		||||
      case ESP_SLEEP_WAKEUP_EXT0:
 | 
			
		||||
      case ESP_SLEEP_WAKEUP_EXT1:
 | 
			
		||||
      case ESP_SLEEP_WAKEUP_GPIO:
 | 
			
		||||
        return this->wakeup_cause_to_run_duration_->gpio_cause;
 | 
			
		||||
      case ESP_SLEEP_WAKEUP_TOUCHPAD:
 | 
			
		||||
        return this->wakeup_cause_to_run_duration_->touch_cause;
 | 
			
		||||
      default:
 | 
			
		||||
        return this->wakeup_cause_to_run_duration_->default_cause;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return this->run_duration_;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DeepSleepComponent::set_wakeup_pin_mode(WakeupPinMode wakeup_pin_mode) {
 | 
			
		||||
  this->wakeup_pin_mode_ = wakeup_pin_mode;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32C6)
 | 
			
		||||
void DeepSleepComponent::set_ext1_wakeup(Ext1Wakeup ext1_wakeup) { this->ext1_wakeup_ = ext1_wakeup; }
 | 
			
		||||
 | 
			
		||||
void DeepSleepComponent::set_touch_wakeup(bool touch_wakeup) { this->touch_wakeup_ = touch_wakeup; }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void DeepSleepComponent::set_run_duration(WakeupCauseToRunDuration wakeup_cause_to_run_duration) {
 | 
			
		||||
  wakeup_cause_to_run_duration_ = wakeup_cause_to_run_duration;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DeepSleepComponent::dump_config_platform_() {
 | 
			
		||||
  if (wakeup_pin_ != nullptr) {
 | 
			
		||||
    LOG_PIN("  Wakeup Pin: ", this->wakeup_pin_);
 | 
			
		||||
  }
 | 
			
		||||
  if (this->wakeup_cause_to_run_duration_.has_value()) {
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  Default Wakeup Run Duration: %" PRIu32 " ms",
 | 
			
		||||
                  this->wakeup_cause_to_run_duration_->default_cause);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  Touch Wakeup Run Duration: %" PRIu32 " ms", this->wakeup_cause_to_run_duration_->touch_cause);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  GPIO Wakeup Run Duration: %" PRIu32 " ms", this->wakeup_cause_to_run_duration_->gpio_cause);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool DeepSleepComponent::prepare_to_sleep_() {
 | 
			
		||||
  if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_KEEP_AWAKE && this->wakeup_pin_ != nullptr &&
 | 
			
		||||
      !this->sleep_duration_.has_value() && this->wakeup_pin_->digital_read()) {
 | 
			
		||||
    // Defer deep sleep until inactive
 | 
			
		||||
    if (!this->next_enter_deep_sleep_) {
 | 
			
		||||
      this->status_set_warning();
 | 
			
		||||
      ESP_LOGW(TAG, "Waiting for pin_ to switch state to enter deep sleep...");
 | 
			
		||||
    }
 | 
			
		||||
    this->next_enter_deep_sleep_ = true;
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DeepSleepComponent::deep_sleep_() {
 | 
			
		||||
#if !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32C6)
 | 
			
		||||
  if (this->sleep_duration_.has_value())
 | 
			
		||||
    esp_sleep_enable_timer_wakeup(*this->sleep_duration_);
 | 
			
		||||
  if (this->wakeup_pin_ != nullptr) {
 | 
			
		||||
    bool level = !this->wakeup_pin_->is_inverted();
 | 
			
		||||
    if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_INVERT_WAKEUP && this->wakeup_pin_->digital_read()) {
 | 
			
		||||
      level = !level;
 | 
			
		||||
    }
 | 
			
		||||
    esp_sleep_enable_ext0_wakeup(gpio_num_t(this->wakeup_pin_->get_pin()), level);
 | 
			
		||||
  }
 | 
			
		||||
  if (this->ext1_wakeup_.has_value()) {
 | 
			
		||||
    esp_sleep_enable_ext1_wakeup(this->ext1_wakeup_->mask, this->ext1_wakeup_->wakeup_mode);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (this->touch_wakeup_.has_value() && *(this->touch_wakeup_)) {
 | 
			
		||||
    esp_sleep_enable_touchpad_wakeup();
 | 
			
		||||
    esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6)
 | 
			
		||||
  if (this->sleep_duration_.has_value())
 | 
			
		||||
    esp_sleep_enable_timer_wakeup(*this->sleep_duration_);
 | 
			
		||||
  if (this->wakeup_pin_ != nullptr) {
 | 
			
		||||
    bool level = !this->wakeup_pin_->is_inverted();
 | 
			
		||||
    if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_INVERT_WAKEUP && this->wakeup_pin_->digital_read()) {
 | 
			
		||||
      level = !level;
 | 
			
		||||
    }
 | 
			
		||||
    esp_deep_sleep_enable_gpio_wakeup(1 << this->wakeup_pin_->get_pin(),
 | 
			
		||||
                                      static_cast<esp_deepsleep_gpio_wake_up_mode_t>(level));
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
  esp_deep_sleep_start();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace deep_sleep
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										23
									
								
								esphome/components/deep_sleep/deep_sleep_esp8266.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								esphome/components/deep_sleep/deep_sleep_esp8266.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
#ifdef USE_ESP8266
 | 
			
		||||
#include "deep_sleep_component.h"
 | 
			
		||||
 | 
			
		||||
#include <Esp.h>
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace deep_sleep {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "deep_sleep";
 | 
			
		||||
 | 
			
		||||
optional<uint32_t> DeepSleepComponent::get_run_duration_() const { return this->run_duration_; }
 | 
			
		||||
 | 
			
		||||
void DeepSleepComponent::dump_config_platform_() {}
 | 
			
		||||
 | 
			
		||||
bool DeepSleepComponent::prepare_to_sleep_() { return true; }
 | 
			
		||||
 | 
			
		||||
void DeepSleepComponent::deep_sleep_() {
 | 
			
		||||
  ESP.deepSleep(*this->sleep_duration_);  // NOLINT(readability-static-accessed-through-instance)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace deep_sleep
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
#endif
 | 
			
		||||
@@ -86,9 +86,14 @@ bool HOT IRAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool r
 | 
			
		||||
  if (this->model_ == DHT_MODEL_DHT11) {
 | 
			
		||||
    delayMicroseconds(18000);
 | 
			
		||||
  } else if (this->model_ == DHT_MODEL_SI7021) {
 | 
			
		||||
#ifdef USE_ESP8266
 | 
			
		||||
    delayMicroseconds(500);
 | 
			
		||||
    this->pin_->digital_write(true);
 | 
			
		||||
    delayMicroseconds(40);
 | 
			
		||||
#else
 | 
			
		||||
    delayMicroseconds(400);
 | 
			
		||||
    this->pin_->digital_write(true);
 | 
			
		||||
#endif
 | 
			
		||||
  } else if (this->model_ == DHT_MODEL_DHT22_TYPE2) {
 | 
			
		||||
    delayMicroseconds(2000);
 | 
			
		||||
  } else if (this->model_ == DHT_MODEL_AM2120 || this->model_ == DHT_MODEL_AM2302) {
 | 
			
		||||
 
 | 
			
		||||
@@ -37,14 +37,18 @@ void DS1307Component::read_time() {
 | 
			
		||||
    ESP_LOGW(TAG, "RTC halted, not syncing to system clock.");
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  ESPTime rtc_time{.second = uint8_t(ds1307_.reg.second + 10 * ds1307_.reg.second_10),
 | 
			
		||||
                   .minute = uint8_t(ds1307_.reg.minute + 10u * ds1307_.reg.minute_10),
 | 
			
		||||
                   .hour = uint8_t(ds1307_.reg.hour + 10u * ds1307_.reg.hour_10),
 | 
			
		||||
                   .day_of_week = uint8_t(ds1307_.reg.weekday),
 | 
			
		||||
                   .day_of_month = uint8_t(ds1307_.reg.day + 10u * ds1307_.reg.day_10),
 | 
			
		||||
                   .day_of_year = 1,  // ignored by recalc_timestamp_utc(false)
 | 
			
		||||
                   .month = uint8_t(ds1307_.reg.month + 10u * ds1307_.reg.month_10),
 | 
			
		||||
                   .year = uint16_t(ds1307_.reg.year + 10u * ds1307_.reg.year_10 + 2000)};
 | 
			
		||||
  ESPTime rtc_time{
 | 
			
		||||
      .second = uint8_t(ds1307_.reg.second + 10 * ds1307_.reg.second_10),
 | 
			
		||||
      .minute = uint8_t(ds1307_.reg.minute + 10u * ds1307_.reg.minute_10),
 | 
			
		||||
      .hour = uint8_t(ds1307_.reg.hour + 10u * ds1307_.reg.hour_10),
 | 
			
		||||
      .day_of_week = uint8_t(ds1307_.reg.weekday),
 | 
			
		||||
      .day_of_month = uint8_t(ds1307_.reg.day + 10u * ds1307_.reg.day_10),
 | 
			
		||||
      .day_of_year = 1,  // ignored by recalc_timestamp_utc(false)
 | 
			
		||||
      .month = uint8_t(ds1307_.reg.month + 10u * ds1307_.reg.month_10),
 | 
			
		||||
      .year = uint16_t(ds1307_.reg.year + 10u * ds1307_.reg.year_10 + 2000),
 | 
			
		||||
      .is_dst = false,  // not used
 | 
			
		||||
      .timestamp = 0    // overwritten by recalc_timestamp_utc(false)
 | 
			
		||||
  };
 | 
			
		||||
  rtc_time.recalc_timestamp_utc(false);
 | 
			
		||||
  if (!rtc_time.is_valid()) {
 | 
			
		||||
    ESP_LOGE(TAG, "Invalid RTC time, not syncing to system clock.");
 | 
			
		||||
 
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
CODEOWNERS = ["@vincentscode"]
 | 
			
		||||
 
 | 
			
		||||
@@ -1,87 +1,7 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import i2c, sensor
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_COMPENSATION,
 | 
			
		||||
    CONF_ECO2,
 | 
			
		||||
    CONF_HUMIDITY,
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_TEMPERATURE,
 | 
			
		||||
    CONF_TVOC,
 | 
			
		||||
    DEVICE_CLASS_AQI,
 | 
			
		||||
    DEVICE_CLASS_CARBON_DIOXIDE,
 | 
			
		||||
    DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS,
 | 
			
		||||
    ICON_CHEMICAL_WEAPON,
 | 
			
		||||
    ICON_MOLECULE_CO2,
 | 
			
		||||
    ICON_RADIATOR,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    UNIT_PARTS_PER_BILLION,
 | 
			
		||||
    UNIT_PARTS_PER_MILLION,
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ["@latonita"]
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = CONFIG_SCHEMA = cv.invalid(
 | 
			
		||||
    "The ens160 sensor component has been renamed to ens160_i2c."
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ["@vincentscode"]
 | 
			
		||||
DEPENDENCIES = ["i2c"]
 | 
			
		||||
 | 
			
		||||
ens160_ns = cg.esphome_ns.namespace("ens160")
 | 
			
		||||
ENS160Component = ens160_ns.class_(
 | 
			
		||||
    "ENS160Component", cg.PollingComponent, i2c.I2CDevice, sensor.Sensor
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CONF_AQI = "aqi"
 | 
			
		||||
UNIT_INDEX = "index"
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = (
 | 
			
		||||
    cv.Schema(
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(ENS160Component),
 | 
			
		||||
            cv.Required(CONF_ECO2): sensor.sensor_schema(
 | 
			
		||||
                unit_of_measurement=UNIT_PARTS_PER_MILLION,
 | 
			
		||||
                icon=ICON_MOLECULE_CO2,
 | 
			
		||||
                accuracy_decimals=0,
 | 
			
		||||
                device_class=DEVICE_CLASS_CARBON_DIOXIDE,
 | 
			
		||||
                state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Required(CONF_TVOC): sensor.sensor_schema(
 | 
			
		||||
                unit_of_measurement=UNIT_PARTS_PER_BILLION,
 | 
			
		||||
                icon=ICON_RADIATOR,
 | 
			
		||||
                accuracy_decimals=0,
 | 
			
		||||
                device_class=DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS,
 | 
			
		||||
                state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Required(CONF_AQI): sensor.sensor_schema(
 | 
			
		||||
                icon=ICON_CHEMICAL_WEAPON,
 | 
			
		||||
                accuracy_decimals=0,
 | 
			
		||||
                device_class=DEVICE_CLASS_AQI,
 | 
			
		||||
                state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_COMPENSATION): cv.Schema(
 | 
			
		||||
                {
 | 
			
		||||
                    cv.Required(CONF_TEMPERATURE): cv.use_id(sensor.Sensor),
 | 
			
		||||
                    cv.Required(CONF_HUMIDITY): cv.use_id(sensor.Sensor),
 | 
			
		||||
                }
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
    .extend(cv.polling_component_schema("60s"))
 | 
			
		||||
    .extend(i2c.i2c_device_schema(0x53))
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await i2c.register_i2c_device(var, config)
 | 
			
		||||
 | 
			
		||||
    sens = await sensor.new_sensor(config[CONF_ECO2])
 | 
			
		||||
    cg.add(var.set_co2(sens))
 | 
			
		||||
    sens = await sensor.new_sensor(config[CONF_TVOC])
 | 
			
		||||
    cg.add(var.set_tvoc(sens))
 | 
			
		||||
    sens = await sensor.new_sensor(config[CONF_AQI])
 | 
			
		||||
    cg.add(var.set_aqi(sens))
 | 
			
		||||
 | 
			
		||||
    if CONF_COMPENSATION in config:
 | 
			
		||||
        compensation_config = config[CONF_COMPENSATION]
 | 
			
		||||
        sens = await cg.get_variable(compensation_config[CONF_TEMPERATURE])
 | 
			
		||||
        cg.add(var.set_temperature(sens))
 | 
			
		||||
        sens = await cg.get_variable(compensation_config[CONF_HUMIDITY])
 | 
			
		||||
        cg.add(var.set_humidity(sens))
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user