mirror of
				https://github.com/esphome/esphome.git
				synced 2025-11-04 09:01:49 +00:00 
			
		
		
		
	Compare commits
	
		
			496 Commits
		
	
	
		
			bluetooth-
			...
			2024.8.0
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					1f21e419aa | ||
| 
						 | 
					5d4bf5f8e5 | ||
| 
						 | 
					813d517076 | ||
| 
						 | 
					4ed6a64869 | ||
| 
						 | 
					aaae8f4a87 | ||
| 
						 | 
					436c6282da | ||
| 
						 | 
					c043bbe598 | ||
| 
						 | 
					8fae609316 | ||
| 
						 | 
					c4d225a6f2 | ||
| 
						 | 
					409e84090e | ||
| 
						 | 
					c96784f591 | ||
| 
						 | 
					0f82114e64 | ||
| 
						 | 
					5c7d070307 | ||
| 
						 | 
					7464b440c0 | ||
| 
						 | 
					28bb0ddfeb | ||
| 
						 | 
					e779a09586 | ||
| 
						 | 
					343650e37d | ||
| 
						 | 
					2c47eb62a7 | ||
| 
						 | 
					033ab55206 | ||
| 
						 | 
					e17c7124f4 | ||
| 
						 | 
					e3bfbebb8f | ||
| 
						 | 
					bc20fd57fe | ||
| 
						 | 
					b654dea55e | ||
| 
						 | 
					7b233d6871 | ||
| 
						 | 
					ccf57488c5 | ||
| 
						 | 
					cf6ea7cb2c | ||
| 
						 | 
					d6f130e35a | ||
| 
						 | 
					8f09382367 | ||
| 
						 | 
					b2b23f2a4f | ||
| 
						 | 
					8756b41b63 | ||
| 
						 | 
					4cb174585c | ||
| 
						 | 
					56e05998ef | ||
| 
						 | 
					bec2d42c79 | ||
| 
						 | 
					a0eff08f39 | ||
| 
						 | 
					a5fdcb31fc | ||
| 
						 | 
					1d25db491c | ||
| 
						 | 
					c5b1a8eb81 | ||
| 
						 | 
					68c56b3e03 | ||
| 
						 | 
					0c567adf63 | ||
| 
						 | 
					9ec61cbff3 | ||
| 
						 | 
					2e58297a16 | ||
| 
						 | 
					f81ce2c707 | ||
| 
						 | 
					4bd7ba0d30 | ||
| 
						 | 
					9663b7d67c | ||
| 
						 | 
					b082a64d32 | ||
| 
						 | 
					c9979ad90c | ||
| 
						 | 
					3598560472 | ||
| 
						 | 
					506e69addf | ||
| 
						 | 
					2a70ef05d1 | ||
| 
						 | 
					8696f922d1 | ||
| 
						 | 
					2b25daa199 | ||
| 
						 | 
					ab51bbd8f7 | ||
| 
						 | 
					390d5f2f93 | ||
| 
						 | 
					8d106e97a2 | ||
| 
						 | 
					fc146dabed | ||
| 
						 | 
					8d5be27746 | ||
| 
						 | 
					f24fd34d86 | ||
| 
						 | 
					64ee40d370 | ||
| 
						 | 
					5f3f106283 | ||
| 
						 | 
					8148eae134 | ||
| 
						 | 
					f13cf1f7a0 | ||
| 
						 | 
					8a076cc906 | ||
| 
						 | 
					82c5cd18de | ||
| 
						 | 
					e769804fe6 | ||
| 
						 | 
					f2e99fa319 | ||
| 
						 | 
					34d435c996 | ||
| 
						 | 
					d04e706295 | ||
| 
						 | 
					442e765187 | ||
| 
						 | 
					15602b0664 | ||
| 
						 | 
					b43c5b851a | ||
| 
						 | 
					a47a17d7e7 | ||
| 
						 | 
					b71c03424e | ||
| 
						 | 
					a3d5b69a9c | ||
| 
						 | 
					3f1d2c0caf | ||
| 
						 | 
					7fd65987d3 | ||
| 
						 | 
					24b6c1d3eb | ||
| 
						 | 
					9a9757ddeb | ||
| 
						 | 
					4b91ef5123 | ||
| 
						 | 
					2a8424a7f2 | ||
| 
						 | 
					132269c5b8 | ||
| 
						 | 
					ddd8027238 | ||
| 
						 | 
					c348efa401 | ||
| 
						 | 
					9b0c2234d8 | ||
| 
						 | 
					73f786c606 | ||
| 
						 | 
					1e63fddf36 | ||
| 
						 | 
					da0dbe8753 | ||
| 
						 | 
					eccc5a3ea3 | ||
| 
						 | 
					8667f51cf0 | ||
| 
						 | 
					455df35e50 | ||
| 
						 | 
					9188836f70 | ||
| 
						 | 
					b0d9800817 | ||
| 
						 | 
					e6b1780a31 | ||
| 
						 | 
					71ea2cec1f | ||
| 
						 | 
					7074fa06ae | ||
| 
						 | 
					3ba9caa118 | ||
| 
						 | 
					6b141102d6 | ||
| 
						 | 
					acaec41bb7 | ||
| 
						 | 
					f737ca6e28 | ||
| 
						 | 
					e02319dcff | ||
| 
						 | 
					d18bb34f87 | ||
| 
						 | 
					87944f0c1b | ||
| 
						 | 
					38c25dec93 | ||
| 
						 | 
					81ac9391d1 | ||
| 
						 | 
					61c6581123 | ||
| 
						 | 
					4a7570770b | ||
| 
						 | 
					aedfb32482 | ||
| 
						 | 
					a5f18dfe7f | ||
| 
						 | 
					cb9906b921 | ||
| 
						 | 
					144f1d3663 | ||
| 
						 | 
					546bfe6db5 | ||
| 
						 | 
					0af10c58f5 | ||
| 
						 | 
					5ac9d301ea | ||
| 
						 | 
					a70f926971 | ||
| 
						 | 
					dfacf1bbfe | ||
| 
						 | 
					3920029aff | ||
| 
						 | 
					8849443bf6 | ||
| 
						 | 
					dd3dd7a136 | ||
| 
						 | 
					dff6884bed | ||
| 
						 | 
					d7231fadb1 | ||
| 
						 | 
					caa2ea64e3 | ||
| 
						 | 
					83bb7d0266 | ||
| 
						 | 
					6e21d79bde | ||
| 
						 | 
					7c1aa771aa | ||
| 
						 | 
					12e840ee88 | ||
| 
						 | 
					25c8676d80 | ||
| 
						 | 
					341fc65958 | ||
| 
						 | 
					5b6b7c0d15 | ||
| 
						 | 
					24515546fd | ||
| 
						 | 
					b3728697cc | ||
| 
						 | 
					e64709c37e | ||
| 
						 | 
					20c2246533 | ||
| 
						 | 
					acf690c87d | ||
| 
						 | 
					adfec578cf | ||
| 
						 | 
					39c0019534 | ||
| 
						 | 
					f61582f826 | ||
| 
						 | 
					e343aca9bc | ||
| 
						 | 
					038f24fcea | ||
| 
						 | 
					d3f2434c57 | ||
| 
						 | 
					23ffc3ddfb | ||
| 
						 | 
					ad0118dd4a | ||
| 
						 | 
					7c24f1ba6d | ||
| 
						 | 
					6e863305aa | ||
| 
						 | 
					75635956cd | ||
| 
						 | 
					1f3754684a | ||
| 
						 | 
					da10de9ea8 | ||
| 
						 | 
					39de179e21 | ||
| 
						 | 
					2cc14055cf | ||
| 
						 | 
					19a787c235 | ||
| 
						 | 
					e88e32bf23 | ||
| 
						 | 
					f0d4b5f740 | ||
| 
						 | 
					2b2a83273f | ||
| 
						 | 
					ae476bb400 | ||
| 
						 | 
					dc24eefe08 | ||
| 
						 | 
					f1aa254e48 | ||
| 
						 | 
					5d5f3276e9 | ||
| 
						 | 
					172a358d01 | ||
| 
						 | 
					0ac549d208 | ||
| 
						 | 
					8fc42694f6 | ||
| 
						 | 
					0a7d883633 | ||
| 
						 | 
					41813b0a1f | ||
| 
						 | 
					4690e227b8 | ||
| 
						 | 
					5bec0a6534 | ||
| 
						 | 
					626ed815fb | ||
| 
						 | 
					74aee1d453 | ||
| 
						 | 
					d187340fc4 | ||
| 
						 | 
					a464e46d4d | ||
| 
						 | 
					f322ec8f3d | ||
| 
						 | 
					1f4829598a | ||
| 
						 | 
					40e79299d5 | ||
| 
						 | 
					368662969e | ||
| 
						 | 
					fbc830176f | ||
| 
						 | 
					cfb20abb9f | ||
| 
						 | 
					43b818f2b1 | ||
| 
						 | 
					32b927de7e | ||
| 
						 | 
					c5b77f4590 | ||
| 
						 | 
					0fb89d1869 | ||
| 
						 | 
					b32078a5fe | ||
| 
						 | 
					dd20c5eab0 | ||
| 
						 | 
					0ef73c6dd6 | ||
| 
						 | 
					de0e549187 | ||
| 
						 | 
					e15d0ee150 | ||
| 
						 | 
					331d556799 | ||
| 
						 | 
					93e0c71c2f | ||
| 
						 | 
					c512d5ebb6 | ||
| 
						 | 
					f153a7b0fd | ||
| 
						 | 
					10205e06cb | ||
| 
						 | 
					aa490e3726 | ||
| 
						 | 
					de43c4e6ab | ||
| 
						 | 
					193db50668 | ||
| 
						 | 
					659fdefccb | ||
| 
						 | 
					8980996b1a | ||
| 
						 | 
					0b3fe73b74 | ||
| 
						 | 
					4af8230b4f | ||
| 
						 | 
					0bbefb5b2a | ||
| 
						 | 
					41baf70660 | ||
| 
						 | 
					eaf2bb70d9 | ||
| 
						 | 
					c910fdf7e5 | ||
| 
						 | 
					f1d19416be | ||
| 
						 | 
					07b78fea76 | ||
| 
						 | 
					896af84acc | ||
| 
						 | 
					44d609b205 | ||
| 
						 | 
					72cbfd8fea | ||
| 
						 | 
					71236b170d | ||
| 
						 | 
					bb92ab01d7 | ||
| 
						 | 
					316a0e1c96 | ||
| 
						 | 
					0c2f9b9dbb | ||
| 
						 | 
					c6c1d3a3ad | ||
| 
						 | 
					fbab0aceb0 | ||
| 
						 | 
					54b77a1174 | ||
| 
						 | 
					a34cec217e | ||
| 
						 | 
					91bb38553d | ||
| 
						 | 
					531f33a158 | ||
| 
						 | 
					2d826768b0 | ||
| 
						 | 
					d7f6d4436e | ||
| 
						 | 
					bdd0a36aa3 | ||
| 
						 | 
					8a89dac5d5 | ||
| 
						 | 
					8d28c53fd3 | ||
| 
						 | 
					114476d8b1 | ||
| 
						 | 
					d1bfad9890 | ||
| 
						 | 
					feae794787 | ||
| 
						 | 
					8a3f0e3b93 | ||
| 
						 | 
					4a80a09db3 | ||
| 
						 | 
					7f83bcfdd9 | ||
| 
						 | 
					99cba0ae7f | ||
| 
						 | 
					2e8a2fdbd4 | ||
| 
						 | 
					d209a2b45a | ||
| 
						 | 
					d071b05249 | ||
| 
						 | 
					66b36afe90 | ||
| 
						 | 
					c6b81eff9a | ||
| 
						 | 
					04b268e319 | ||
| 
						 | 
					5ac875545f | ||
| 
						 | 
					6e624ff797 | ||
| 
						 | 
					08b8ab837a | ||
| 
						 | 
					1b57d8511b | ||
| 
						 | 
					ee4d5178d6 | ||
| 
						 | 
					dea1e9a1e0 | ||
| 
						 | 
					fa4fbf9d73 | ||
| 
						 | 
					fb6c2aef59 | ||
| 
						 | 
					6417f1f907 | ||
| 
						 | 
					d1b0e6b5fe | ||
| 
						 | 
					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 | 
@@ -1,7 +1,9 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "name": "ESPHome Dev",
 | 
					  "name": "ESPHome Dev",
 | 
				
			||||||
  "image": "ghcr.io/esphome/esphome-lint:dev",
 | 
					  "image": "ghcr.io/esphome/esphome-lint:dev",
 | 
				
			||||||
  "postCreateCommand": ["script/devcontainer-post-create"],
 | 
					  "postCreateCommand": [
 | 
				
			||||||
 | 
					    "script/devcontainer-post-create"
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
  "containerEnv": {
 | 
					  "containerEnv": {
 | 
				
			||||||
    "DEVCONTAINER": "1",
 | 
					    "DEVCONTAINER": "1",
 | 
				
			||||||
    "PIP_BREAK_SYSTEM_PACKAGES": "1",
 | 
					    "PIP_BREAK_SYSTEM_PACKAGES": "1",
 | 
				
			||||||
@@ -27,6 +29,9 @@
 | 
				
			|||||||
      "extensions": [
 | 
					      "extensions": [
 | 
				
			||||||
        // python
 | 
					        // python
 | 
				
			||||||
        "ms-python.python",
 | 
					        "ms-python.python",
 | 
				
			||||||
 | 
					        "ms-python.pylint",
 | 
				
			||||||
 | 
					        "ms-python.flake8",
 | 
				
			||||||
 | 
					        "ms-python.black-formatter",
 | 
				
			||||||
        "visualstudioexptteam.vscodeintellicode",
 | 
					        "visualstudioexptteam.vscodeintellicode",
 | 
				
			||||||
        // yaml
 | 
					        // yaml
 | 
				
			||||||
        "redhat.vscode-yaml",
 | 
					        "redhat.vscode-yaml",
 | 
				
			||||||
@@ -38,9 +43,21 @@
 | 
				
			|||||||
      "settings": {
 | 
					      "settings": {
 | 
				
			||||||
        "python.languageServer": "Pylance",
 | 
					        "python.languageServer": "Pylance",
 | 
				
			||||||
        "python.pythonPath": "/usr/bin/python3",
 | 
					        "python.pythonPath": "/usr/bin/python3",
 | 
				
			||||||
        "python.linting.pylintEnabled": true,
 | 
					        "pylint.args": [
 | 
				
			||||||
        "python.linting.enabled": true,
 | 
					          "--rcfile=${workspaceFolder}/pyproject.toml"
 | 
				
			||||||
        "python.formatting.provider": "black",
 | 
					        ],
 | 
				
			||||||
 | 
					        "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.formatOnPaste": false,
 | 
				
			||||||
        "editor.formatOnSave": true,
 | 
					        "editor.formatOnSave": true,
 | 
				
			||||||
        "editor.formatOnType": true,
 | 
					        "editor.formatOnType": true,
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										4
									
								
								.github/actions/build-image/action.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/actions/build-image/action.yaml
									
									
									
									
										vendored
									
									
								
							@@ -46,7 +46,7 @@ runs:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    - name: Build and push to ghcr by digest
 | 
					    - name: Build and push to ghcr by digest
 | 
				
			||||||
      id: build-ghcr
 | 
					      id: build-ghcr
 | 
				
			||||||
      uses: docker/build-push-action@v5.3.0
 | 
					      uses: docker/build-push-action@v6.7.0
 | 
				
			||||||
      with:
 | 
					      with:
 | 
				
			||||||
        context: .
 | 
					        context: .
 | 
				
			||||||
        file: ./docker/Dockerfile
 | 
					        file: ./docker/Dockerfile
 | 
				
			||||||
@@ -69,7 +69,7 @@ runs:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    - name: Build and push to dockerhub by digest
 | 
					    - name: Build and push to dockerhub by digest
 | 
				
			||||||
      id: build-dockerhub
 | 
					      id: build-dockerhub
 | 
				
			||||||
      uses: docker/build-push-action@v5.3.0
 | 
					      uses: docker/build-push-action@v6.7.0
 | 
				
			||||||
      with:
 | 
					      with:
 | 
				
			||||||
        context: .
 | 
					        context: .
 | 
				
			||||||
        file: ./docker/Dockerfile
 | 
					        file: ./docker/Dockerfile
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								.github/actions/restore-python/action.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/actions/restore-python/action.yml
									
									
									
									
										vendored
									
									
								
							@@ -17,7 +17,7 @@ runs:
 | 
				
			|||||||
  steps:
 | 
					  steps:
 | 
				
			||||||
    - name: Set up Python ${{ inputs.python-version }}
 | 
					    - name: Set up Python ${{ inputs.python-version }}
 | 
				
			||||||
      id: python
 | 
					      id: python
 | 
				
			||||||
      uses: actions/setup-python@v5.1.0
 | 
					      uses: actions/setup-python@v5.1.1
 | 
				
			||||||
      with:
 | 
					      with:
 | 
				
			||||||
        python-version: ${{ inputs.python-version }}
 | 
					        python-version: ${{ inputs.python-version }}
 | 
				
			||||||
    - name: Restore Python virtual environment
 | 
					    - name: Restore Python virtual environment
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										7
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
								
							@@ -13,6 +13,13 @@ updates:
 | 
				
			|||||||
    schedule:
 | 
					    schedule:
 | 
				
			||||||
      interval: daily
 | 
					      interval: daily
 | 
				
			||||||
    open-pull-requests-limit: 10
 | 
					    open-pull-requests-limit: 10
 | 
				
			||||||
 | 
					    groups:
 | 
				
			||||||
 | 
					      docker-actions:
 | 
				
			||||||
 | 
					        applies-to: version-updates
 | 
				
			||||||
 | 
					        patterns:
 | 
				
			||||||
 | 
					          - "docker/setup-qemu-action"
 | 
				
			||||||
 | 
					          - "docker/login-action"
 | 
				
			||||||
 | 
					          - "docker/setup-buildx-action"
 | 
				
			||||||
  - package-ecosystem: github-actions
 | 
					  - package-ecosystem: github-actions
 | 
				
			||||||
    directory: "/.github/actions/build-image"
 | 
					    directory: "/.github/actions/build-image"
 | 
				
			||||||
    schedule:
 | 
					    schedule:
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										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
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
      - name: Checkout
 | 
					      - name: Checkout
 | 
				
			||||||
        uses: actions/checkout@v4.1.6
 | 
					        uses: actions/checkout@v4.1.7
 | 
				
			||||||
      - name: Set up Python
 | 
					      - name: Set up Python
 | 
				
			||||||
        uses: actions/setup-python@v5.1.0
 | 
					        uses: actions/setup-python@v5.1.0
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										6
									
								
								.github/workflows/ci-docker.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.github/workflows/ci-docker.yml
									
									
									
									
										vendored
									
									
								
							@@ -40,15 +40,15 @@ jobs:
 | 
				
			|||||||
        arch: [amd64, armv7, aarch64]
 | 
					        arch: [amd64, armv7, aarch64]
 | 
				
			||||||
        build_type: ["ha-addon", "docker", "lint"]
 | 
					        build_type: ["ha-addon", "docker", "lint"]
 | 
				
			||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
      - uses: actions/checkout@v4.1.6
 | 
					      - uses: actions/checkout@v4.1.7
 | 
				
			||||||
      - name: Set up Python
 | 
					      - name: Set up Python
 | 
				
			||||||
        uses: actions/setup-python@v5.1.0
 | 
					        uses: actions/setup-python@v5.1.0
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
          python-version: "3.9"
 | 
					          python-version: "3.9"
 | 
				
			||||||
      - name: Set up Docker Buildx
 | 
					      - name: Set up Docker Buildx
 | 
				
			||||||
        uses: docker/setup-buildx-action@v3.3.0
 | 
					        uses: docker/setup-buildx-action@v3.6.1
 | 
				
			||||||
      - name: Set up QEMU
 | 
					      - name: Set up QEMU
 | 
				
			||||||
        uses: docker/setup-qemu-action@v3.0.0
 | 
					        uses: docker/setup-qemu-action@v3.2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      - name: Set TAG
 | 
					      - name: Set TAG
 | 
				
			||||||
        run: |
 | 
					        run: |
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										111
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										111
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							@@ -9,6 +9,7 @@ on:
 | 
				
			|||||||
    paths:
 | 
					    paths:
 | 
				
			||||||
      - "**"
 | 
					      - "**"
 | 
				
			||||||
      - "!.github/workflows/*.yml"
 | 
					      - "!.github/workflows/*.yml"
 | 
				
			||||||
 | 
					      - "!.github/actions/build-image/*"
 | 
				
			||||||
      - ".github/workflows/ci.yml"
 | 
					      - ".github/workflows/ci.yml"
 | 
				
			||||||
      - "!.yamllint"
 | 
					      - "!.yamllint"
 | 
				
			||||||
      - "!.github/dependabot.yml"
 | 
					      - "!.github/dependabot.yml"
 | 
				
			||||||
@@ -34,7 +35,7 @@ jobs:
 | 
				
			|||||||
      cache-key: ${{ steps.cache-key.outputs.key }}
 | 
					      cache-key: ${{ steps.cache-key.outputs.key }}
 | 
				
			||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
      - name: Check out code from GitHub
 | 
					      - name: Check out code from GitHub
 | 
				
			||||||
        uses: actions/checkout@v4.1.6
 | 
					        uses: actions/checkout@v4.1.7
 | 
				
			||||||
      - name: Generate cache-key
 | 
					      - name: Generate cache-key
 | 
				
			||||||
        id: cache-key
 | 
					        id: cache-key
 | 
				
			||||||
        run: echo key="${{ hashFiles('requirements.txt', 'requirements_optional.txt', 'requirements_test.txt') }}" >> $GITHUB_OUTPUT
 | 
					        run: echo key="${{ hashFiles('requirements.txt', 'requirements_optional.txt', 'requirements_test.txt') }}" >> $GITHUB_OUTPUT
 | 
				
			||||||
@@ -66,7 +67,7 @@ jobs:
 | 
				
			|||||||
      - common
 | 
					      - common
 | 
				
			||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
      - name: Check out code from GitHub
 | 
					      - name: Check out code from GitHub
 | 
				
			||||||
        uses: actions/checkout@v4.1.6
 | 
					        uses: actions/checkout@v4.1.7
 | 
				
			||||||
      - name: Restore Python
 | 
					      - name: Restore Python
 | 
				
			||||||
        uses: ./.github/actions/restore-python
 | 
					        uses: ./.github/actions/restore-python
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
@@ -87,7 +88,7 @@ jobs:
 | 
				
			|||||||
      - common
 | 
					      - common
 | 
				
			||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
      - name: Check out code from GitHub
 | 
					      - name: Check out code from GitHub
 | 
				
			||||||
        uses: actions/checkout@v4.1.6
 | 
					        uses: actions/checkout@v4.1.7
 | 
				
			||||||
      - name: Restore Python
 | 
					      - name: Restore Python
 | 
				
			||||||
        uses: ./.github/actions/restore-python
 | 
					        uses: ./.github/actions/restore-python
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
@@ -108,7 +109,7 @@ jobs:
 | 
				
			|||||||
      - common
 | 
					      - common
 | 
				
			||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
      - name: Check out code from GitHub
 | 
					      - name: Check out code from GitHub
 | 
				
			||||||
        uses: actions/checkout@v4.1.6
 | 
					        uses: actions/checkout@v4.1.7
 | 
				
			||||||
      - name: Restore Python
 | 
					      - name: Restore Python
 | 
				
			||||||
        uses: ./.github/actions/restore-python
 | 
					        uses: ./.github/actions/restore-python
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
@@ -129,7 +130,7 @@ jobs:
 | 
				
			|||||||
      - common
 | 
					      - common
 | 
				
			||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
      - name: Check out code from GitHub
 | 
					      - name: Check out code from GitHub
 | 
				
			||||||
        uses: actions/checkout@v4.1.6
 | 
					        uses: actions/checkout@v4.1.7
 | 
				
			||||||
      - name: Restore Python
 | 
					      - name: Restore Python
 | 
				
			||||||
        uses: ./.github/actions/restore-python
 | 
					        uses: ./.github/actions/restore-python
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
@@ -150,7 +151,7 @@ jobs:
 | 
				
			|||||||
      - common
 | 
					      - common
 | 
				
			||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
      - name: Check out code from GitHub
 | 
					      - name: Check out code from GitHub
 | 
				
			||||||
        uses: actions/checkout@v4.1.6
 | 
					        uses: actions/checkout@v4.1.7
 | 
				
			||||||
      - name: Restore Python
 | 
					      - name: Restore Python
 | 
				
			||||||
        uses: ./.github/actions/restore-python
 | 
					        uses: ./.github/actions/restore-python
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
@@ -199,7 +200,7 @@ jobs:
 | 
				
			|||||||
      - common
 | 
					      - common
 | 
				
			||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
      - name: Check out code from GitHub
 | 
					      - name: Check out code from GitHub
 | 
				
			||||||
        uses: actions/checkout@v4.1.6
 | 
					        uses: actions/checkout@v4.1.7
 | 
				
			||||||
      - name: Restore Python
 | 
					      - name: Restore Python
 | 
				
			||||||
        uses: ./.github/actions/restore-python
 | 
					        uses: ./.github/actions/restore-python
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
@@ -229,7 +230,7 @@ jobs:
 | 
				
			|||||||
      - common
 | 
					      - common
 | 
				
			||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
      - name: Check out code from GitHub
 | 
					      - name: Check out code from GitHub
 | 
				
			||||||
        uses: actions/checkout@v4.1.6
 | 
					        uses: actions/checkout@v4.1.7
 | 
				
			||||||
      - name: Restore Python
 | 
					      - name: Restore Python
 | 
				
			||||||
        uses: ./.github/actions/restore-python
 | 
					        uses: ./.github/actions/restore-python
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
@@ -248,72 +249,6 @@ jobs:
 | 
				
			|||||||
        run: script/ci-suggest-changes
 | 
					        run: script/ci-suggest-changes
 | 
				
			||||||
        if: always()
 | 
					        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.6
 | 
					 | 
				
			||||||
      - 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.6
 | 
					 | 
				
			||||||
      - 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.6
 | 
					 | 
				
			||||||
      - 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:
 | 
					  clang-tidy:
 | 
				
			||||||
    name: ${{ matrix.name }}
 | 
					    name: ${{ matrix.name }}
 | 
				
			||||||
    runs-on: ubuntu-latest
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
@@ -358,7 +293,7 @@ jobs:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
      - name: Check out code from GitHub
 | 
					      - name: Check out code from GitHub
 | 
				
			||||||
        uses: actions/checkout@v4.1.6
 | 
					        uses: actions/checkout@v4.1.7
 | 
				
			||||||
      - name: Restore Python
 | 
					      - name: Restore Python
 | 
				
			||||||
        uses: ./.github/actions/restore-python
 | 
					        uses: ./.github/actions/restore-python
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
@@ -387,6 +322,13 @@ jobs:
 | 
				
			|||||||
          echo "::add-matcher::.github/workflows/matchers/gcc.json"
 | 
					          echo "::add-matcher::.github/workflows/matchers/gcc.json"
 | 
				
			||||||
          echo "::add-matcher::.github/workflows/matchers/clang-tidy.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
 | 
					      - name: Run clang-tidy
 | 
				
			||||||
        run: |
 | 
					        run: |
 | 
				
			||||||
          . venv/bin/activate
 | 
					          . venv/bin/activate
 | 
				
			||||||
@@ -410,7 +352,7 @@ jobs:
 | 
				
			|||||||
      count: ${{ steps.list-components.outputs.count }}
 | 
					      count: ${{ steps.list-components.outputs.count }}
 | 
				
			||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
      - name: Check out code from GitHub
 | 
					      - name: Check out code from GitHub
 | 
				
			||||||
        uses: actions/checkout@v4.1.6
 | 
					        uses: actions/checkout@v4.1.7
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
          # Fetch enough history so `git merge-base refs/remotes/origin/dev HEAD` works.
 | 
					          # Fetch enough history so `git merge-base refs/remotes/origin/dev HEAD` works.
 | 
				
			||||||
          fetch-depth: 500
 | 
					          fetch-depth: 500
 | 
				
			||||||
@@ -454,11 +396,11 @@ jobs:
 | 
				
			|||||||
      matrix:
 | 
					      matrix:
 | 
				
			||||||
        file: ${{ fromJson(needs.list-components.outputs.components) }}
 | 
					        file: ${{ fromJson(needs.list-components.outputs.components) }}
 | 
				
			||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
      - name: Install libsodium
 | 
					      - name: Install dependencies
 | 
				
			||||||
        run: sudo apt-get install libsodium-dev
 | 
					        run: sudo apt-get install libsdl2-dev
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      - name: Check out code from GitHub
 | 
					      - name: Check out code from GitHub
 | 
				
			||||||
        uses: actions/checkout@v4.1.6
 | 
					        uses: actions/checkout@v4.1.7
 | 
				
			||||||
      - name: Restore Python
 | 
					      - name: Restore Python
 | 
				
			||||||
        uses: ./.github/actions/restore-python
 | 
					        uses: ./.github/actions/restore-python
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
@@ -484,7 +426,7 @@ jobs:
 | 
				
			|||||||
      matrix: ${{ steps.split.outputs.components }}
 | 
					      matrix: ${{ steps.split.outputs.components }}
 | 
				
			||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
      - name: Check out code from GitHub
 | 
					      - name: Check out code from GitHub
 | 
				
			||||||
        uses: actions/checkout@v4.1.6
 | 
					        uses: actions/checkout@v4.1.7
 | 
				
			||||||
      - name: Split components into 20 groups
 | 
					      - name: Split components into 20 groups
 | 
				
			||||||
        id: split
 | 
					        id: split
 | 
				
			||||||
        run: |
 | 
					        run: |
 | 
				
			||||||
@@ -508,11 +450,11 @@ jobs:
 | 
				
			|||||||
      - name: List components
 | 
					      - name: List components
 | 
				
			||||||
        run: echo ${{ matrix.components }}
 | 
					        run: echo ${{ matrix.components }}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      - name: Install libsodium
 | 
					      - name: Install dependencies
 | 
				
			||||||
        run: sudo apt-get install libsodium-dev
 | 
					        run: sudo apt-get install libsdl2-dev
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      - name: Check out code from GitHub
 | 
					      - name: Check out code from GitHub
 | 
				
			||||||
        uses: actions/checkout@v4.1.6
 | 
					        uses: actions/checkout@v4.1.7
 | 
				
			||||||
      - name: Restore Python
 | 
					      - name: Restore Python
 | 
				
			||||||
        uses: ./.github/actions/restore-python
 | 
					        uses: ./.github/actions/restore-python
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
@@ -527,6 +469,8 @@ jobs:
 | 
				
			|||||||
      - name: Compile config
 | 
					      - name: Compile config
 | 
				
			||||||
        run: |
 | 
					        run: |
 | 
				
			||||||
          . venv/bin/activate
 | 
					          . venv/bin/activate
 | 
				
			||||||
 | 
					          mkdir build_cache
 | 
				
			||||||
 | 
					          export PLATFORMIO_BUILD_CACHE_DIR=$PWD/build_cache
 | 
				
			||||||
          for component in ${{ matrix.components }}; do
 | 
					          for component in ${{ matrix.components }}; do
 | 
				
			||||||
            ./script/test_build_components -e compile -c $component
 | 
					            ./script/test_build_components -e compile -c $component
 | 
				
			||||||
          done
 | 
					          done
 | 
				
			||||||
@@ -543,7 +487,6 @@ jobs:
 | 
				
			|||||||
      - pylint
 | 
					      - pylint
 | 
				
			||||||
      - pytest
 | 
					      - pytest
 | 
				
			||||||
      - pyupgrade
 | 
					      - pyupgrade
 | 
				
			||||||
      - compile-tests
 | 
					 | 
				
			||||||
      - clang-tidy
 | 
					      - clang-tidy
 | 
				
			||||||
      - list-components
 | 
					      - list-components
 | 
				
			||||||
      - test-build-components
 | 
					      - test-build-components
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										28
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										28
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							@@ -19,7 +19,7 @@ jobs:
 | 
				
			|||||||
      tag: ${{ steps.tag.outputs.tag }}
 | 
					      tag: ${{ steps.tag.outputs.tag }}
 | 
				
			||||||
      branch_build: ${{ steps.tag.outputs.branch_build }}
 | 
					      branch_build: ${{ steps.tag.outputs.branch_build }}
 | 
				
			||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
      - uses: actions/checkout@v4.1.6
 | 
					      - uses: actions/checkout@v4.1.7
 | 
				
			||||||
      - name: Get tag
 | 
					      - name: Get tag
 | 
				
			||||||
        id: tag
 | 
					        id: tag
 | 
				
			||||||
        # yamllint disable rule:line-length
 | 
					        # yamllint disable rule:line-length
 | 
				
			||||||
@@ -51,7 +51,7 @@ jobs:
 | 
				
			|||||||
      contents: read
 | 
					      contents: read
 | 
				
			||||||
      id-token: write
 | 
					      id-token: write
 | 
				
			||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
      - uses: actions/checkout@v4.1.6
 | 
					      - uses: actions/checkout@v4.1.7
 | 
				
			||||||
      - name: Set up Python
 | 
					      - name: Set up Python
 | 
				
			||||||
        uses: actions/setup-python@v5.1.0
 | 
					        uses: actions/setup-python@v5.1.0
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
@@ -65,7 +65,7 @@ jobs:
 | 
				
			|||||||
          pip3 install build
 | 
					          pip3 install build
 | 
				
			||||||
          python3 -m build
 | 
					          python3 -m build
 | 
				
			||||||
      - name: Publish
 | 
					      - name: Publish
 | 
				
			||||||
        uses: pypa/gh-action-pypi-publish@v1.8.14
 | 
					        uses: pypa/gh-action-pypi-publish@v1.9.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  deploy-docker:
 | 
					  deploy-docker:
 | 
				
			||||||
    name: Build ESPHome ${{ matrix.platform }}
 | 
					    name: Build ESPHome ${{ matrix.platform }}
 | 
				
			||||||
@@ -83,25 +83,25 @@ jobs:
 | 
				
			|||||||
          - linux/arm/v7
 | 
					          - linux/arm/v7
 | 
				
			||||||
          - linux/arm64
 | 
					          - linux/arm64
 | 
				
			||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
      - uses: actions/checkout@v4.1.6
 | 
					      - uses: actions/checkout@v4.1.7
 | 
				
			||||||
      - name: Set up Python
 | 
					      - name: Set up Python
 | 
				
			||||||
        uses: actions/setup-python@v5.1.0
 | 
					        uses: actions/setup-python@v5.1.0
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
          python-version: "3.9"
 | 
					          python-version: "3.9"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      - name: Set up Docker Buildx
 | 
					      - name: Set up Docker Buildx
 | 
				
			||||||
        uses: docker/setup-buildx-action@v3.3.0
 | 
					        uses: docker/setup-buildx-action@v3.6.1
 | 
				
			||||||
      - name: Set up QEMU
 | 
					      - name: Set up QEMU
 | 
				
			||||||
        if: matrix.platform != 'linux/amd64'
 | 
					        if: matrix.platform != 'linux/amd64'
 | 
				
			||||||
        uses: docker/setup-qemu-action@v3.0.0
 | 
					        uses: docker/setup-qemu-action@v3.2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      - name: Log in to docker hub
 | 
					      - name: Log in to docker hub
 | 
				
			||||||
        uses: docker/login-action@v3.1.0
 | 
					        uses: docker/login-action@v3.3.0
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
          username: ${{ secrets.DOCKER_USER }}
 | 
					          username: ${{ secrets.DOCKER_USER }}
 | 
				
			||||||
          password: ${{ secrets.DOCKER_PASSWORD }}
 | 
					          password: ${{ secrets.DOCKER_PASSWORD }}
 | 
				
			||||||
      - name: Log in to the GitHub container registry
 | 
					      - name: Log in to the GitHub container registry
 | 
				
			||||||
        uses: docker/login-action@v3.1.0
 | 
					        uses: docker/login-action@v3.3.0
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
          registry: ghcr.io
 | 
					          registry: ghcr.io
 | 
				
			||||||
          username: ${{ github.actor }}
 | 
					          username: ${{ github.actor }}
 | 
				
			||||||
@@ -141,7 +141,7 @@ jobs:
 | 
				
			|||||||
          echo name=$(cat /tmp/platform) >> $GITHUB_OUTPUT
 | 
					          echo name=$(cat /tmp/platform) >> $GITHUB_OUTPUT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      - name: Upload digests
 | 
					      - name: Upload digests
 | 
				
			||||||
        uses: actions/upload-artifact@v4.3.3
 | 
					        uses: actions/upload-artifact@v4.3.4
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
          name: digests-${{ steps.sanitize.outputs.name }}
 | 
					          name: digests-${{ steps.sanitize.outputs.name }}
 | 
				
			||||||
          path: /tmp/digests
 | 
					          path: /tmp/digests
 | 
				
			||||||
@@ -174,27 +174,27 @@ jobs:
 | 
				
			|||||||
          - ghcr
 | 
					          - ghcr
 | 
				
			||||||
          - dockerhub
 | 
					          - dockerhub
 | 
				
			||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
      - uses: actions/checkout@v4.1.6
 | 
					      - uses: actions/checkout@v4.1.7
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      - name: Download digests
 | 
					      - name: Download digests
 | 
				
			||||||
        uses: actions/download-artifact@v4.1.7
 | 
					        uses: actions/download-artifact@v4.1.8
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
          pattern: digests-*
 | 
					          pattern: digests-*
 | 
				
			||||||
          path: /tmp/digests
 | 
					          path: /tmp/digests
 | 
				
			||||||
          merge-multiple: true
 | 
					          merge-multiple: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      - name: Set up Docker Buildx
 | 
					      - name: Set up Docker Buildx
 | 
				
			||||||
        uses: docker/setup-buildx-action@v3.3.0
 | 
					        uses: docker/setup-buildx-action@v3.6.1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      - name: Log in to docker hub
 | 
					      - name: Log in to docker hub
 | 
				
			||||||
        if: matrix.registry == 'dockerhub'
 | 
					        if: matrix.registry == 'dockerhub'
 | 
				
			||||||
        uses: docker/login-action@v3.1.0
 | 
					        uses: docker/login-action@v3.3.0
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
          username: ${{ secrets.DOCKER_USER }}
 | 
					          username: ${{ secrets.DOCKER_USER }}
 | 
				
			||||||
          password: ${{ secrets.DOCKER_PASSWORD }}
 | 
					          password: ${{ secrets.DOCKER_PASSWORD }}
 | 
				
			||||||
      - name: Log in to the GitHub container registry
 | 
					      - name: Log in to the GitHub container registry
 | 
				
			||||||
        if: matrix.registry == 'ghcr'
 | 
					        if: matrix.registry == 'ghcr'
 | 
				
			||||||
        uses: docker/login-action@v3.1.0
 | 
					        uses: docker/login-action@v3.3.0
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
          registry: ghcr.io
 | 
					          registry: ghcr.io
 | 
				
			||||||
          username: ${{ github.actor }}
 | 
					          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'
 | 
					    if: github.repository == 'esphome/esphome'
 | 
				
			||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
      - name: Checkout
 | 
					      - name: Checkout
 | 
				
			||||||
        uses: actions/checkout@v4.1.6
 | 
					        uses: actions/checkout@v4.1.7
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      - name: Checkout Home Assistant
 | 
					      - name: Checkout Home Assistant
 | 
				
			||||||
        uses: actions/checkout@v4.1.6
 | 
					        uses: actions/checkout@v4.1.7
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
          repository: home-assistant/core
 | 
					          repository: home-assistant/core
 | 
				
			||||||
          path: lib/home-assistant
 | 
					          path: lib/home-assistant
 | 
				
			||||||
@@ -36,7 +36,7 @@ jobs:
 | 
				
			|||||||
          python ./script/sync-device_class.py
 | 
					          python ./script/sync-device_class.py
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      - name: Commit changes
 | 
					      - name: Commit changes
 | 
				
			||||||
        uses: peter-evans/create-pull-request@v6.0.4
 | 
					        uses: peter-evans/create-pull-request@v6.1.0
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
          commit-message: "Synchronise Device Classes from Home Assistant"
 | 
					          commit-message: "Synchronise Device Classes from Home Assistant"
 | 
				
			||||||
          committer: esphomebot <esphome@nabucasa.com>
 | 
					          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
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
      - name: Check out code from GitHub
 | 
					      - name: Check out code from GitHub
 | 
				
			||||||
        uses: actions/checkout@v4.1.6
 | 
					        uses: actions/checkout@v4.1.7
 | 
				
			||||||
      - name: Run yamllint
 | 
					      - name: Run yamllint
 | 
				
			||||||
        uses: frenck/action-yamllint@v1.5.0
 | 
					        uses: frenck/action-yamllint@v1.5.0
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -138,3 +138,5 @@ sdkconfig.*
 | 
				
			|||||||
.tests/
 | 
					.tests/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/components
 | 
					/components
 | 
				
			||||||
 | 
					/managed_components
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,8 +2,17 @@
 | 
				
			|||||||
# See https://pre-commit.com for more information
 | 
					# See https://pre-commit.com for more information
 | 
				
			||||||
# See https://pre-commit.com/hooks.html for more hooks
 | 
					# See https://pre-commit.com/hooks.html for more hooks
 | 
				
			||||||
repos:
 | 
					repos:
 | 
				
			||||||
 | 
					  - repo: https://github.com/astral-sh/ruff-pre-commit
 | 
				
			||||||
 | 
					    # Ruff version.
 | 
				
			||||||
 | 
					    rev: v0.5.4
 | 
				
			||||||
 | 
					    hooks:
 | 
				
			||||||
 | 
					      # Run the linter.
 | 
				
			||||||
 | 
					      - id: ruff
 | 
				
			||||||
 | 
					        args: [--fix]
 | 
				
			||||||
 | 
					      # Run the formatter.
 | 
				
			||||||
 | 
					      - id: ruff-format
 | 
				
			||||||
  - repo: https://github.com/psf/black-pre-commit-mirror
 | 
					  - repo: https://github.com/psf/black-pre-commit-mirror
 | 
				
			||||||
    rev: 24.2.0
 | 
					    rev: 24.4.2
 | 
				
			||||||
    hooks:
 | 
					    hooks:
 | 
				
			||||||
      - id: black
 | 
					      - id: black
 | 
				
			||||||
        args:
 | 
					        args:
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										28
									
								
								CODEOWNERS
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								CODEOWNERS
									
									
									
									
									
								
							@@ -37,6 +37,7 @@ esphome/components/am43/sensor/* @buxtronix
 | 
				
			|||||||
esphome/components/analog_threshold/* @ianchi
 | 
					esphome/components/analog_threshold/* @ianchi
 | 
				
			||||||
esphome/components/animation/* @syndlex
 | 
					esphome/components/animation/* @syndlex
 | 
				
			||||||
esphome/components/anova/* @buxtronix
 | 
					esphome/components/anova/* @buxtronix
 | 
				
			||||||
 | 
					esphome/components/apds9306/* @aodrenah
 | 
				
			||||||
esphome/components/api/* @OttoWinter
 | 
					esphome/components/api/* @OttoWinter
 | 
				
			||||||
esphome/components/as5600/* @ammmze
 | 
					esphome/components/as5600/* @ammmze
 | 
				
			||||||
esphome/components/as5600/sensor/* @ammmze
 | 
					esphome/components/as5600/sensor/* @ammmze
 | 
				
			||||||
@@ -45,6 +46,7 @@ esphome/components/async_tcp/* @OttoWinter
 | 
				
			|||||||
esphome/components/at581x/* @X-Ryl669
 | 
					esphome/components/at581x/* @X-Ryl669
 | 
				
			||||||
esphome/components/atc_mithermometer/* @ahpohl
 | 
					esphome/components/atc_mithermometer/* @ahpohl
 | 
				
			||||||
esphome/components/atm90e26/* @danieltwagner
 | 
					esphome/components/atm90e26/* @danieltwagner
 | 
				
			||||||
 | 
					esphome/components/atm90e32/* @circuitsetup @descipher
 | 
				
			||||||
esphome/components/b_parasite/* @rbaron
 | 
					esphome/components/b_parasite/* @rbaron
 | 
				
			||||||
esphome/components/ballu/* @bazuchan
 | 
					esphome/components/ballu/* @bazuchan
 | 
				
			||||||
esphome/components/bang_bang/* @OttoWinter
 | 
					esphome/components/bang_bang/* @OttoWinter
 | 
				
			||||||
@@ -64,6 +66,8 @@ esphome/components/bluetooth_proxy/* @jesserockz
 | 
				
			|||||||
esphome/components/bme280_base/* @esphome/core
 | 
					esphome/components/bme280_base/* @esphome/core
 | 
				
			||||||
esphome/components/bme280_spi/* @apbodrov
 | 
					esphome/components/bme280_spi/* @apbodrov
 | 
				
			||||||
esphome/components/bme680_bsec/* @trvrnrth
 | 
					esphome/components/bme680_bsec/* @trvrnrth
 | 
				
			||||||
 | 
					esphome/components/bme68x_bsec2/* @kbx81 @neffs
 | 
				
			||||||
 | 
					esphome/components/bme68x_bsec2_i2c/* @kbx81 @neffs
 | 
				
			||||||
esphome/components/bmi160/* @flaviut
 | 
					esphome/components/bmi160/* @flaviut
 | 
				
			||||||
esphome/components/bmp3xx/* @latonita
 | 
					esphome/components/bmp3xx/* @latonita
 | 
				
			||||||
esphome/components/bmp3xx_base/* @latonita @martgras
 | 
					esphome/components/bmp3xx_base/* @latonita @martgras
 | 
				
			||||||
@@ -94,6 +98,7 @@ esphome/components/current_based/* @djwmarcx
 | 
				
			|||||||
esphome/components/dac7678/* @NickB1
 | 
					esphome/components/dac7678/* @NickB1
 | 
				
			||||||
esphome/components/daikin_arc/* @MagicBear
 | 
					esphome/components/daikin_arc/* @MagicBear
 | 
				
			||||||
esphome/components/daikin_brc/* @hagak
 | 
					esphome/components/daikin_brc/* @hagak
 | 
				
			||||||
 | 
					esphome/components/dallas_temp/* @ssieb
 | 
				
			||||||
esphome/components/daly_bms/* @s1lvi0
 | 
					esphome/components/daly_bms/* @s1lvi0
 | 
				
			||||||
esphome/components/dashboard_import/* @esphome/core
 | 
					esphome/components/dashboard_import/* @esphome/core
 | 
				
			||||||
esphome/components/datetime/* @jesserockz @rfdarter
 | 
					esphome/components/datetime/* @jesserockz @rfdarter
 | 
				
			||||||
@@ -144,6 +149,7 @@ esphome/components/gdk101/* @Szewcson
 | 
				
			|||||||
esphome/components/globals/* @esphome/core
 | 
					esphome/components/globals/* @esphome/core
 | 
				
			||||||
esphome/components/gp8403/* @jesserockz
 | 
					esphome/components/gp8403/* @jesserockz
 | 
				
			||||||
esphome/components/gpio/* @esphome/core
 | 
					esphome/components/gpio/* @esphome/core
 | 
				
			||||||
 | 
					esphome/components/gpio/one_wire/* @ssieb
 | 
				
			||||||
esphome/components/gps/* @coogle
 | 
					esphome/components/gps/* @coogle
 | 
				
			||||||
esphome/components/graph/* @synco
 | 
					esphome/components/graph/* @synco
 | 
				
			||||||
esphome/components/graphical_display_menu/* @MrMDavidson
 | 
					esphome/components/graphical_display_menu/* @MrMDavidson
 | 
				
			||||||
@@ -163,13 +169,18 @@ esphome/components/he60r/* @clydebarrow
 | 
				
			|||||||
esphome/components/heatpumpir/* @rob-deutsch
 | 
					esphome/components/heatpumpir/* @rob-deutsch
 | 
				
			||||||
esphome/components/hitachi_ac424/* @sourabhjaiswal
 | 
					esphome/components/hitachi_ac424/* @sourabhjaiswal
 | 
				
			||||||
esphome/components/hm3301/* @freekode
 | 
					esphome/components/hm3301/* @freekode
 | 
				
			||||||
esphome/components/homeassistant/* @OttoWinter
 | 
					esphome/components/homeassistant/* @OttoWinter @esphome/core
 | 
				
			||||||
 | 
					esphome/components/homeassistant/number/* @landonr
 | 
				
			||||||
 | 
					esphome/components/homeassistant/switch/* @Links2004
 | 
				
			||||||
esphome/components/honeywell_hih_i2c/* @Benichou34
 | 
					esphome/components/honeywell_hih_i2c/* @Benichou34
 | 
				
			||||||
esphome/components/honeywellabp/* @RubyBailey
 | 
					esphome/components/honeywellabp/* @RubyBailey
 | 
				
			||||||
esphome/components/honeywellabp2_i2c/* @jpfaff
 | 
					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/hrxl_maxsonar_wr/* @netmikey
 | 
				
			||||||
esphome/components/hte501/* @Stock-M
 | 
					esphome/components/hte501/* @Stock-M
 | 
				
			||||||
 | 
					esphome/components/http_request/ota/* @oarcher
 | 
				
			||||||
 | 
					esphome/components/http_request/update/* @jesserockz
 | 
				
			||||||
esphome/components/htu31d/* @betterengineering
 | 
					esphome/components/htu31d/* @betterengineering
 | 
				
			||||||
esphome/components/hydreon_rgxx/* @functionpointer
 | 
					esphome/components/hydreon_rgxx/* @functionpointer
 | 
				
			||||||
esphome/components/hyt271/* @Philippe12
 | 
					esphome/components/hyt271/* @Philippe12
 | 
				
			||||||
@@ -209,7 +220,10 @@ esphome/components/lightwaverf/* @max246
 | 
				
			|||||||
esphome/components/lilygo_t5_47/touchscreen/* @jesserockz
 | 
					esphome/components/lilygo_t5_47/touchscreen/* @jesserockz
 | 
				
			||||||
esphome/components/lock/* @esphome/core
 | 
					esphome/components/lock/* @esphome/core
 | 
				
			||||||
esphome/components/logger/* @esphome/core
 | 
					esphome/components/logger/* @esphome/core
 | 
				
			||||||
esphome/components/ltr390/* @sjtrny
 | 
					esphome/components/ltr390/* @latonita @sjtrny
 | 
				
			||||||
 | 
					esphome/components/ltr_als_ps/* @latonita
 | 
				
			||||||
 | 
					esphome/components/lvgl/* @clydebarrow
 | 
				
			||||||
 | 
					esphome/components/m5stack_8angle/* @rnauber
 | 
				
			||||||
esphome/components/matrix_keypad/* @ssieb
 | 
					esphome/components/matrix_keypad/* @ssieb
 | 
				
			||||||
esphome/components/max31865/* @DAVe3283
 | 
					esphome/components/max31865/* @DAVe3283
 | 
				
			||||||
esphome/components/max44009/* @berfenger
 | 
					esphome/components/max44009/* @berfenger
 | 
				
			||||||
@@ -266,6 +280,8 @@ esphome/components/nextion/text_sensor/* @senexcrenshaw
 | 
				
			|||||||
esphome/components/nfc/* @jesserockz @kbx81
 | 
					esphome/components/nfc/* @jesserockz @kbx81
 | 
				
			||||||
esphome/components/noblex/* @AGalfra
 | 
					esphome/components/noblex/* @AGalfra
 | 
				
			||||||
esphome/components/number/* @esphome/core
 | 
					esphome/components/number/* @esphome/core
 | 
				
			||||||
 | 
					esphome/components/one_wire/* @ssieb
 | 
				
			||||||
 | 
					esphome/components/online_image/* @guillempages
 | 
				
			||||||
esphome/components/ota/* @esphome/core
 | 
					esphome/components/ota/* @esphome/core
 | 
				
			||||||
esphome/components/output/* @esphome/core
 | 
					esphome/components/output/* @esphome/core
 | 
				
			||||||
esphome/components/pca6416a/* @Mat931
 | 
					esphome/components/pca6416a/* @Mat931
 | 
				
			||||||
@@ -313,6 +329,7 @@ esphome/components/rtttl/* @glmnet
 | 
				
			|||||||
esphome/components/safe_mode/* @jsuanet @kbx81 @paulmonigatti
 | 
					esphome/components/safe_mode/* @jsuanet @kbx81 @paulmonigatti
 | 
				
			||||||
esphome/components/scd4x/* @martgras @sjtrny
 | 
					esphome/components/scd4x/* @martgras @sjtrny
 | 
				
			||||||
esphome/components/script/* @esphome/core
 | 
					esphome/components/script/* @esphome/core
 | 
				
			||||||
 | 
					esphome/components/sdl/* @clydebarrow
 | 
				
			||||||
esphome/components/sdm_meter/* @jesserockz @polyfaces
 | 
					esphome/components/sdm_meter/* @jesserockz @polyfaces
 | 
				
			||||||
esphome/components/sdp3x/* @Azimath
 | 
					esphome/components/sdp3x/* @Azimath
 | 
				
			||||||
esphome/components/seeed_mr24hpc1/* @limengdu
 | 
					esphome/components/seeed_mr24hpc1/* @limengdu
 | 
				
			||||||
@@ -407,6 +424,7 @@ esphome/components/uart/button/* @ssieb
 | 
				
			|||||||
esphome/components/ufire_ec/* @pvizeli
 | 
					esphome/components/ufire_ec/* @pvizeli
 | 
				
			||||||
esphome/components/ufire_ise/* @pvizeli
 | 
					esphome/components/ufire_ise/* @pvizeli
 | 
				
			||||||
esphome/components/ultrasonic/* @OttoWinter
 | 
					esphome/components/ultrasonic/* @OttoWinter
 | 
				
			||||||
 | 
					esphome/components/update/* @jesserockz
 | 
				
			||||||
esphome/components/uponor_smatrix/* @kroimon
 | 
					esphome/components/uponor_smatrix/* @kroimon
 | 
				
			||||||
esphome/components/valve/* @esphome/core
 | 
					esphome/components/valve/* @esphome/core
 | 
				
			||||||
esphome/components/vbus/* @ssieb
 | 
					esphome/components/vbus/* @ssieb
 | 
				
			||||||
@@ -414,7 +432,8 @@ esphome/components/veml3235/* @kbx81
 | 
				
			|||||||
esphome/components/veml7700/* @latonita
 | 
					esphome/components/veml7700/* @latonita
 | 
				
			||||||
esphome/components/version/* @esphome/core
 | 
					esphome/components/version/* @esphome/core
 | 
				
			||||||
esphome/components/voice_assistant/* @jesserockz
 | 
					esphome/components/voice_assistant/* @jesserockz
 | 
				
			||||||
esphome/components/wake_on_lan/* @willwill2will54
 | 
					esphome/components/wake_on_lan/* @clydebarrow @willwill2will54
 | 
				
			||||||
 | 
					esphome/components/watchdog/* @oarcher
 | 
				
			||||||
esphome/components/waveshare_epaper/* @clydebarrow
 | 
					esphome/components/waveshare_epaper/* @clydebarrow
 | 
				
			||||||
esphome/components/web_server_base/* @OttoWinter
 | 
					esphome/components/web_server_base/* @OttoWinter
 | 
				
			||||||
esphome/components/web_server_idf/* @dentra
 | 
					esphome/components/web_server_idf/* @dentra
 | 
				
			||||||
@@ -437,6 +456,7 @@ esphome/components/wl_134/* @hobbypunk90
 | 
				
			|||||||
esphome/components/x9c/* @EtienneMD
 | 
					esphome/components/x9c/* @EtienneMD
 | 
				
			||||||
esphome/components/xgzp68xx/* @gcormier
 | 
					esphome/components/xgzp68xx/* @gcormier
 | 
				
			||||||
esphome/components/xiaomi_hhccjcy10/* @fariouche
 | 
					esphome/components/xiaomi_hhccjcy10/* @fariouche
 | 
				
			||||||
 | 
					esphome/components/xiaomi_lywsd02mmc/* @juanluss31
 | 
				
			||||||
esphome/components/xiaomi_lywsd03mmc/* @ahpohl
 | 
					esphome/components/xiaomi_lywsd03mmc/* @ahpohl
 | 
				
			||||||
esphome/components/xiaomi_mhoc303/* @drug123
 | 
					esphome/components/xiaomi_mhoc303/* @drug123
 | 
				
			||||||
esphome/components/xiaomi_mhoc401/* @vevsvevs
 | 
					esphome/components/xiaomi_mhoc401/* @vevsvevs
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -34,28 +34,32 @@ RUN \
 | 
				
			|||||||
        python3-wheel=0.38.4-2 \
 | 
					        python3-wheel=0.38.4-2 \
 | 
				
			||||||
        iputils-ping=3:20221126-1 \
 | 
					        iputils-ping=3:20221126-1 \
 | 
				
			||||||
        git=1:2.39.2-1.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 \
 | 
					        openssh-client=1:9.2p1-2+deb12u2 \
 | 
				
			||||||
        python3-cffi=1.15.1-5 \
 | 
					        python3-cffi=1.15.1-5 \
 | 
				
			||||||
        libcairo2=1.16.0-7 \
 | 
					        libcairo2=1.16.0-7 \
 | 
				
			||||||
        libmagic1=1:5.44-3 \
 | 
					        libmagic1=1:5.44-3 \
 | 
				
			||||||
        patch=2.7.6-7; \
 | 
					        patch=2.7.6-7 \
 | 
				
			||||||
    if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \
 | 
					    && ( \
 | 
				
			||||||
        apt-get install -y --no-install-recommends \
 | 
					        ( \
 | 
				
			||||||
          build-essential=12.9 \
 | 
					            [ "$TARGETARCH$TARGETVARIANT" = "armv7" ] && \
 | 
				
			||||||
          python3-dev=3.11.2-1+b1 \
 | 
					                apt-get install -y --no-install-recommends \
 | 
				
			||||||
          zlib1g-dev=1:1.2.13.dfsg-1 \
 | 
					                build-essential=12.9 \
 | 
				
			||||||
          libjpeg-dev=1:2.1.5-2 \
 | 
					                python3-dev=3.11.2-1+b1 \
 | 
				
			||||||
          libfreetype-dev=2.12.1+dfsg-5 \
 | 
					                zlib1g-dev=1:1.2.13.dfsg-1 \
 | 
				
			||||||
          libssl-dev=3.0.11-1~deb12u2 \
 | 
					                libjpeg-dev=1:2.1.5-2 \
 | 
				
			||||||
          libffi-dev=3.4.4-1 \
 | 
					                libfreetype-dev=2.12.1+dfsg-5+deb12u3 \
 | 
				
			||||||
          libopenjp2-7=2.5.0-2 \
 | 
					                libssl-dev=3.0.13-1~deb12u1 \
 | 
				
			||||||
          libtiff6=4.5.0-6+deb12u1 \
 | 
					                libffi-dev=3.4.4-1 \
 | 
				
			||||||
          cargo=0.66.0+ds1-1 \
 | 
					                libopenjp2-7=2.5.0-2 \
 | 
				
			||||||
          pkg-config=1.8.1-1 \
 | 
					                libtiff6=4.5.0-6+deb12u1 \
 | 
				
			||||||
          gcc-arm-linux-gnueabihf=4:12.2.0-3; \
 | 
					                cargo=0.66.0+ds1-1 \
 | 
				
			||||||
    fi; \
 | 
					                pkg-config=1.8.1-1 \
 | 
				
			||||||
    rm -rf \
 | 
					                gcc-arm-linux-gnueabihf=4:12.2.0-3 \
 | 
				
			||||||
 | 
					        ) \
 | 
				
			||||||
 | 
					        || [ "$TARGETARCH$TARGETVARIANT" != "armv7" ] \
 | 
				
			||||||
 | 
					    ) \
 | 
				
			||||||
 | 
					    && rm -rf \
 | 
				
			||||||
        /tmp/* \
 | 
					        /tmp/* \
 | 
				
			||||||
        /var/{cache,log}/* \
 | 
					        /var/{cache,log}/* \
 | 
				
			||||||
        /var/lib/apt/lists/*
 | 
					        /var/lib/apt/lists/*
 | 
				
			||||||
@@ -81,7 +85,8 @@ RUN \
 | 
				
			|||||||
    fi; \
 | 
					    fi; \
 | 
				
			||||||
    pip3 install \
 | 
					    pip3 install \
 | 
				
			||||||
    --break-system-packages --no-cache-dir \
 | 
					    --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
 | 
					    # Change some platformio settings
 | 
				
			||||||
    && platformio settings set enable_telemetry No \
 | 
					    && platformio settings set enable_telemetry No \
 | 
				
			||||||
    && platformio settings set check_platformio_interval 1000000 \
 | 
					    && 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 \
 | 
					    --break-system-packages --no-cache-dir -r /requirements.txt -r /requirements_optional.txt \
 | 
				
			||||||
    && /platformio_install_deps.py /platformio.ini --libraries
 | 
					    && /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 =======================
 | 
					# ======================= docker-type image =======================
 | 
				
			||||||
FROM base AS docker
 | 
					FROM base AS docker
 | 
				
			||||||
@@ -186,8 +194,8 @@ RUN \
 | 
				
			|||||||
        clang-format-13=1:13.0.1-11+b2 \
 | 
					        clang-format-13=1:13.0.1-11+b2 \
 | 
				
			||||||
        clang-tidy-14=1:14.0.6-12 \
 | 
					        clang-tidy-14=1:14.0.6-12 \
 | 
				
			||||||
        patch=2.7.6-7 \
 | 
					        patch=2.7.6-7 \
 | 
				
			||||||
        software-properties-common=0.99.30-4 \
 | 
					        software-properties-common=0.99.30-4.1~deb12u1 \
 | 
				
			||||||
        nano=7.2-1 \
 | 
					        nano=7.2-1+deb12u1 \
 | 
				
			||||||
        build-essential=12.9 \
 | 
					        build-essential=12.9 \
 | 
				
			||||||
        python3-dev=3.11.2-1+b1 \
 | 
					        python3-dev=3.11.2-1+b1 \
 | 
				
			||||||
    && rm -rf \
 | 
					    && rm -rf \
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
#!/bin/bash
 | 
					#!/usr/bin/env bash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# If /cache is mounted, use that as PIO's coredir
 | 
					# If /cache is mounted, use that as PIO's coredir
 | 
				
			||||||
# otherwise use path in /config (so that PIO packages aren't downloaded on each compile)
 | 
					# otherwise use path in /config (so that PIO packages aren't downloaded on each compile)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,12 +1,12 @@
 | 
				
			|||||||
# PYTHON_ARGCOMPLETE_OK
 | 
					# PYTHON_ARGCOMPLETE_OK
 | 
				
			||||||
import argparse
 | 
					import argparse
 | 
				
			||||||
 | 
					from datetime import datetime
 | 
				
			||||||
import functools
 | 
					import functools
 | 
				
			||||||
import logging
 | 
					import logging
 | 
				
			||||||
import os
 | 
					import os
 | 
				
			||||||
import re
 | 
					import re
 | 
				
			||||||
import sys
 | 
					import sys
 | 
				
			||||||
import time
 | 
					import time
 | 
				
			||||||
from datetime import datetime
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import argcomplete
 | 
					import argcomplete
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -39,14 +39,14 @@ from esphome.const import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
from esphome.core import CORE, EsphomeError, coroutine
 | 
					from esphome.core import CORE, EsphomeError, coroutine
 | 
				
			||||||
from esphome.helpers import indent, is_ip_address
 | 
					from esphome.helpers import indent, is_ip_address
 | 
				
			||||||
 | 
					from esphome.log import Fore, color, setup_log
 | 
				
			||||||
from esphome.util import (
 | 
					from esphome.util import (
 | 
				
			||||||
 | 
					    get_serial_ports,
 | 
				
			||||||
 | 
					    list_yaml_files,
 | 
				
			||||||
    run_external_command,
 | 
					    run_external_command,
 | 
				
			||||||
    run_external_process,
 | 
					    run_external_process,
 | 
				
			||||||
    safe_print,
 | 
					    safe_print,
 | 
				
			||||||
    list_yaml_files,
 | 
					 | 
				
			||||||
    get_serial_ports,
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from esphome.log import color, setup_log, Fore
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
_LOGGER = logging.getLogger(__name__)
 | 
					_LOGGER = logging.getLogger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -116,6 +116,7 @@ def get_port_type(port):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
def run_miniterm(config, port):
 | 
					def run_miniterm(config, port):
 | 
				
			||||||
    import serial
 | 
					    import serial
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    from esphome import platformio_api
 | 
					    from esphome import platformio_api
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if CONF_LOGGER not in config:
 | 
					    if CONF_LOGGER not in config:
 | 
				
			||||||
@@ -488,6 +489,15 @@ def command_run(args, config):
 | 
				
			|||||||
    if exit_code != 0:
 | 
					    if exit_code != 0:
 | 
				
			||||||
        return exit_code
 | 
					        return exit_code
 | 
				
			||||||
    _LOGGER.info("Successfully compiled program.")
 | 
					    _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(
 | 
					    port = choose_upload_log_host(
 | 
				
			||||||
        default=args.device,
 | 
					        default=args.device,
 | 
				
			||||||
        check_default=None,
 | 
					        check_default=None,
 | 
				
			||||||
@@ -587,9 +597,10 @@ def command_update_all(args):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def command_idedata(args, config):
 | 
					def command_idedata(args, config):
 | 
				
			||||||
    from esphome import platformio_api
 | 
					 | 
				
			||||||
    import json
 | 
					    import json
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    from esphome import platformio_api
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    logging.disable(logging.INFO)
 | 
					    logging.disable(logging.INFO)
 | 
				
			||||||
    logging.disable(logging.WARNING)
 | 
					    logging.disable(logging.WARNING)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -686,7 +697,8 @@ def command_rename(args, config):
 | 
				
			|||||||
        os.remove(new_path)
 | 
					        os.remove(new_path)
 | 
				
			||||||
        return 1
 | 
					        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(color(Fore.BOLD_GREEN, "SUCCESS"))
 | 
				
			||||||
    print()
 | 
					    print()
 | 
				
			||||||
@@ -737,7 +749,14 @@ def parse_args(argv):
 | 
				
			|||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    parser = argparse.ArgumentParser(
 | 
					    parser = argparse.ArgumentParser(
 | 
				
			||||||
        description=f"ESPHome v{const.__version__}", parents=[options_parser]
 | 
					        description=f"ESPHome {const.__version__}", parents=[options_parser]
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    parser.add_argument(
 | 
				
			||||||
 | 
					        "--version",
 | 
				
			||||||
 | 
					        action="version",
 | 
				
			||||||
 | 
					        version=f"Version: {const.__version__}",
 | 
				
			||||||
 | 
					        help="Print the ESPHome version and exit.",
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    mqtt_options = argparse.ArgumentParser(add_help=False)
 | 
					    mqtt_options = argparse.ArgumentParser(add_help=False)
 | 
				
			||||||
@@ -938,67 +957,6 @@ def parse_args(argv):
 | 
				
			|||||||
    # a deprecation warning).
 | 
					    # a deprecation warning).
 | 
				
			||||||
    arguments = argv[1:]
 | 
					    arguments = argv[1:]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # On Python 3.9+ we can simply set exit_on_error=False in the constructor
 | 
					 | 
				
			||||||
    def _raise(x):
 | 
					 | 
				
			||||||
        raise argparse.ArgumentError(None, x)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # First, try new-style parsing, but don't exit in case of failure
 | 
					 | 
				
			||||||
    try:
 | 
					 | 
				
			||||||
        # duplicate parser so that we can use the original one to raise errors later on
 | 
					 | 
				
			||||||
        current_parser = argparse.ArgumentParser(add_help=False, parents=[parser])
 | 
					 | 
				
			||||||
        current_parser.set_defaults(deprecated_argv_suggestion=None)
 | 
					 | 
				
			||||||
        current_parser.error = _raise
 | 
					 | 
				
			||||||
        return current_parser.parse_args(arguments)
 | 
					 | 
				
			||||||
    except argparse.ArgumentError:
 | 
					 | 
				
			||||||
        pass
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Second, try compat parsing and rearrange the command-line if it succeeds
 | 
					 | 
				
			||||||
    # Disable argparse's built-in help option and add it manually to prevent this
 | 
					 | 
				
			||||||
    # parser from printing the help messagefor the old format when invoked with -h.
 | 
					 | 
				
			||||||
    compat_parser = argparse.ArgumentParser(parents=[options_parser], add_help=False)
 | 
					 | 
				
			||||||
    compat_parser.add_argument("-h", "--help", action="store_true")
 | 
					 | 
				
			||||||
    compat_parser.add_argument("configuration", nargs="*")
 | 
					 | 
				
			||||||
    compat_parser.add_argument(
 | 
					 | 
				
			||||||
        "command",
 | 
					 | 
				
			||||||
        choices=[
 | 
					 | 
				
			||||||
            "config",
 | 
					 | 
				
			||||||
            "compile",
 | 
					 | 
				
			||||||
            "upload",
 | 
					 | 
				
			||||||
            "logs",
 | 
					 | 
				
			||||||
            "run",
 | 
					 | 
				
			||||||
            "clean-mqtt",
 | 
					 | 
				
			||||||
            "wizard",
 | 
					 | 
				
			||||||
            "mqtt-fingerprint",
 | 
					 | 
				
			||||||
            "version",
 | 
					 | 
				
			||||||
            "clean",
 | 
					 | 
				
			||||||
            "dashboard",
 | 
					 | 
				
			||||||
            "vscode",
 | 
					 | 
				
			||||||
            "update-all",
 | 
					 | 
				
			||||||
        ],
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    try:
 | 
					 | 
				
			||||||
        compat_parser.error = _raise
 | 
					 | 
				
			||||||
        result, unparsed = compat_parser.parse_known_args(argv[1:])
 | 
					 | 
				
			||||||
        last_option = len(arguments) - len(unparsed) - 1 - len(result.configuration)
 | 
					 | 
				
			||||||
        unparsed = [
 | 
					 | 
				
			||||||
            "--device" if arg in ("--upload-port", "--serial-port") else arg
 | 
					 | 
				
			||||||
            for arg in unparsed
 | 
					 | 
				
			||||||
        ]
 | 
					 | 
				
			||||||
        arguments = (
 | 
					 | 
				
			||||||
            arguments[0:last_option]
 | 
					 | 
				
			||||||
            + [result.command]
 | 
					 | 
				
			||||||
            + result.configuration
 | 
					 | 
				
			||||||
            + unparsed
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        deprecated_argv_suggestion = arguments
 | 
					 | 
				
			||||||
    except argparse.ArgumentError:
 | 
					 | 
				
			||||||
        # old-style parsing failed, don't suggest any argument
 | 
					 | 
				
			||||||
        deprecated_argv_suggestion = None
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Finally, run the new-style parser again with the possibly swapped arguments,
 | 
					 | 
				
			||||||
    # and let it error out if the command is unparsable.
 | 
					 | 
				
			||||||
    parser.set_defaults(deprecated_argv_suggestion=deprecated_argv_suggestion)
 | 
					 | 
				
			||||||
    argcomplete.autocomplete(parser)
 | 
					    argcomplete.autocomplete(parser)
 | 
				
			||||||
    return parser.parse_args(arguments)
 | 
					    return parser.parse_args(arguments)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1013,20 +971,6 @@ def run_esphome(argv):
 | 
				
			|||||||
        # Show timestamp for dashboard access logs
 | 
					        # Show timestamp for dashboard access logs
 | 
				
			||||||
        args.command == "dashboard",
 | 
					        args.command == "dashboard",
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    if args.deprecated_argv_suggestion is not None and args.command != "vscode":
 | 
					 | 
				
			||||||
        _LOGGER.warning(
 | 
					 | 
				
			||||||
            "Calling ESPHome with the configuration before the command is deprecated "
 | 
					 | 
				
			||||||
            "and will be removed in the future. "
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        _LOGGER.warning("Please instead use:")
 | 
					 | 
				
			||||||
        _LOGGER.warning("   esphome %s", " ".join(args.deprecated_argv_suggestion))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if sys.version_info < (3, 8, 0):
 | 
					 | 
				
			||||||
        _LOGGER.error(
 | 
					 | 
				
			||||||
            "You're running ESPHome with Python <3.8. ESPHome is no longer compatible "
 | 
					 | 
				
			||||||
            "with this Python version. Please reinstall ESPHome with Python 3.8+"
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        return 1
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if args.command in PRE_CONFIG_ACTIONS:
 | 
					    if args.command in PRE_CONFIG_ACTIONS:
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,10 +7,10 @@ from esphome.const import (
 | 
				
			|||||||
    CONF_ELSE,
 | 
					    CONF_ELSE,
 | 
				
			||||||
    CONF_ID,
 | 
					    CONF_ID,
 | 
				
			||||||
    CONF_THEN,
 | 
					    CONF_THEN,
 | 
				
			||||||
 | 
					    CONF_TIME,
 | 
				
			||||||
    CONF_TIMEOUT,
 | 
					    CONF_TIMEOUT,
 | 
				
			||||||
    CONF_TRIGGER_ID,
 | 
					    CONF_TRIGGER_ID,
 | 
				
			||||||
    CONF_TYPE_ID,
 | 
					    CONF_TYPE_ID,
 | 
				
			||||||
    CONF_TIME,
 | 
					 | 
				
			||||||
    CONF_UPDATE_INTERVAL,
 | 
					    CONF_UPDATE_INTERVAL,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor
 | 
					from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,84 +8,86 @@
 | 
				
			|||||||
# want to break suddenly due to a rename (this file will get backports for features).
 | 
					# want to break suddenly due to a rename (this file will get backports for features).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# pylint: disable=unused-import
 | 
					# pylint: disable=unused-import
 | 
				
			||||||
from esphome.cpp_generator import (  # noqa
 | 
					from esphome.cpp_generator import (  # noqa: F401
 | 
				
			||||||
 | 
					    ArrayInitializer,
 | 
				
			||||||
    Expression,
 | 
					    Expression,
 | 
				
			||||||
 | 
					    LineComment,
 | 
				
			||||||
 | 
					    MockObj,
 | 
				
			||||||
 | 
					    MockObjClass,
 | 
				
			||||||
 | 
					    Pvariable,
 | 
				
			||||||
    RawExpression,
 | 
					    RawExpression,
 | 
				
			||||||
    RawStatement,
 | 
					    RawStatement,
 | 
				
			||||||
    TemplateArguments,
 | 
					 | 
				
			||||||
    StructInitializer,
 | 
					 | 
				
			||||||
    ArrayInitializer,
 | 
					 | 
				
			||||||
    safe_exp,
 | 
					 | 
				
			||||||
    Statement,
 | 
					    Statement,
 | 
				
			||||||
    LineComment,
 | 
					    StructInitializer,
 | 
				
			||||||
    progmem_array,
 | 
					    TemplateArguments,
 | 
				
			||||||
    static_const_array,
 | 
					 | 
				
			||||||
    statement,
 | 
					 | 
				
			||||||
    variable,
 | 
					 | 
				
			||||||
    with_local_variable,
 | 
					 | 
				
			||||||
    new_variable,
 | 
					 | 
				
			||||||
    Pvariable,
 | 
					 | 
				
			||||||
    new_Pvariable,
 | 
					 | 
				
			||||||
    add,
 | 
					    add,
 | 
				
			||||||
    add_global,
 | 
					 | 
				
			||||||
    add_library,
 | 
					 | 
				
			||||||
    add_build_flag,
 | 
					    add_build_flag,
 | 
				
			||||||
    add_define,
 | 
					    add_define,
 | 
				
			||||||
 | 
					    add_global,
 | 
				
			||||||
 | 
					    add_library,
 | 
				
			||||||
    add_platformio_option,
 | 
					    add_platformio_option,
 | 
				
			||||||
    get_variable,
 | 
					    get_variable,
 | 
				
			||||||
    get_variable_with_full_id,
 | 
					    get_variable_with_full_id,
 | 
				
			||||||
    process_lambda,
 | 
					 | 
				
			||||||
    is_template,
 | 
					    is_template,
 | 
				
			||||||
 | 
					    new_Pvariable,
 | 
				
			||||||
 | 
					    new_variable,
 | 
				
			||||||
 | 
					    process_lambda,
 | 
				
			||||||
 | 
					    progmem_array,
 | 
				
			||||||
 | 
					    safe_exp,
 | 
				
			||||||
 | 
					    statement,
 | 
				
			||||||
 | 
					    static_const_array,
 | 
				
			||||||
    templatable,
 | 
					    templatable,
 | 
				
			||||||
    MockObj,
 | 
					    variable,
 | 
				
			||||||
    MockObjClass,
 | 
					    with_local_variable,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from esphome.cpp_helpers import (  # noqa
 | 
					from esphome.cpp_helpers import (  # noqa: F401
 | 
				
			||||||
    gpio_pin_expression,
 | 
					 | 
				
			||||||
    register_component,
 | 
					 | 
				
			||||||
    build_registry_entry,
 | 
					    build_registry_entry,
 | 
				
			||||||
    build_registry_list,
 | 
					    build_registry_list,
 | 
				
			||||||
    extract_registry_entry_config,
 | 
					    extract_registry_entry_config,
 | 
				
			||||||
    register_parented,
 | 
					    gpio_pin_expression,
 | 
				
			||||||
    past_safe_mode,
 | 
					    past_safe_mode,
 | 
				
			||||||
 | 
					    register_component,
 | 
				
			||||||
 | 
					    register_parented,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from esphome.cpp_types import (  # noqa
 | 
					from esphome.cpp_types import (  # noqa: F401
 | 
				
			||||||
    global_ns,
 | 
					    NAN,
 | 
				
			||||||
    void,
 | 
					    App,
 | 
				
			||||||
    nullptr,
 | 
					    Application,
 | 
				
			||||||
    float_,
 | 
					    Component,
 | 
				
			||||||
    double,
 | 
					    ComponentPtr,
 | 
				
			||||||
 | 
					    Controller,
 | 
				
			||||||
 | 
					    EntityBase,
 | 
				
			||||||
 | 
					    EntityCategory,
 | 
				
			||||||
 | 
					    ESPTime,
 | 
				
			||||||
 | 
					    GPIOPin,
 | 
				
			||||||
 | 
					    InternalGPIOPin,
 | 
				
			||||||
 | 
					    JsonObject,
 | 
				
			||||||
 | 
					    JsonObjectConst,
 | 
				
			||||||
 | 
					    Parented,
 | 
				
			||||||
 | 
					    PollingComponent,
 | 
				
			||||||
 | 
					    arduino_json_ns,
 | 
				
			||||||
    bool_,
 | 
					    bool_,
 | 
				
			||||||
 | 
					    const_char_ptr,
 | 
				
			||||||
 | 
					    double,
 | 
				
			||||||
 | 
					    esphome_ns,
 | 
				
			||||||
 | 
					    float_,
 | 
				
			||||||
 | 
					    global_ns,
 | 
				
			||||||
 | 
					    gpio_Flags,
 | 
				
			||||||
 | 
					    int16,
 | 
				
			||||||
 | 
					    int32,
 | 
				
			||||||
 | 
					    int64,
 | 
				
			||||||
    int_,
 | 
					    int_,
 | 
				
			||||||
 | 
					    nullptr,
 | 
				
			||||||
 | 
					    optional,
 | 
				
			||||||
 | 
					    size_t,
 | 
				
			||||||
    std_ns,
 | 
					    std_ns,
 | 
				
			||||||
 | 
					    std_shared_ptr,
 | 
				
			||||||
    std_string,
 | 
					    std_string,
 | 
				
			||||||
 | 
					    std_string_ref,
 | 
				
			||||||
    std_vector,
 | 
					    std_vector,
 | 
				
			||||||
    uint8,
 | 
					    uint8,
 | 
				
			||||||
    uint16,
 | 
					    uint16,
 | 
				
			||||||
    uint32,
 | 
					    uint32,
 | 
				
			||||||
    uint64,
 | 
					    uint64,
 | 
				
			||||||
    int16,
 | 
					    void,
 | 
				
			||||||
    int32,
 | 
					 | 
				
			||||||
    int64,
 | 
					 | 
				
			||||||
    size_t,
 | 
					 | 
				
			||||||
    const_char_ptr,
 | 
					 | 
				
			||||||
    NAN,
 | 
					 | 
				
			||||||
    esphome_ns,
 | 
					 | 
				
			||||||
    App,
 | 
					 | 
				
			||||||
    EntityBase,
 | 
					 | 
				
			||||||
    Component,
 | 
					 | 
				
			||||||
    ComponentPtr,
 | 
					 | 
				
			||||||
    PollingComponent,
 | 
					 | 
				
			||||||
    Application,
 | 
					 | 
				
			||||||
    optional,
 | 
					 | 
				
			||||||
    arduino_json_ns,
 | 
					 | 
				
			||||||
    JsonObject,
 | 
					 | 
				
			||||||
    JsonObjectConst,
 | 
					 | 
				
			||||||
    Controller,
 | 
					 | 
				
			||||||
    GPIOPin,
 | 
					 | 
				
			||||||
    InternalGPIOPin,
 | 
					 | 
				
			||||||
    gpio_Flags,
 | 
					 | 
				
			||||||
    EntityCategory,
 | 
					 | 
				
			||||||
    Parented,
 | 
					 | 
				
			||||||
    ESPTime,
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,11 +4,11 @@ from esphome.const import (
 | 
				
			|||||||
    STATE_CLASS_MEASUREMENT,
 | 
					    STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
    ICON_ARROW_EXPAND_VERTICAL,
 | 
					    ICON_ARROW_EXPAND_VERTICAL,
 | 
				
			||||||
    DEVICE_CLASS_DISTANCE,
 | 
					    DEVICE_CLASS_DISTANCE,
 | 
				
			||||||
 | 
					    UNIT_MILLIMETER,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CODEOWNERS = ["@TH-Braemer"]
 | 
					CODEOWNERS = ["@TH-Braemer"]
 | 
				
			||||||
DEPENDENCIES = ["uart"]
 | 
					DEPENDENCIES = ["uart"]
 | 
				
			||||||
UNIT_MILLIMETERS = "mm"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
a02yyuw_ns = cg.esphome_ns.namespace("a02yyuw")
 | 
					a02yyuw_ns = cg.esphome_ns.namespace("a02yyuw")
 | 
				
			||||||
A02yyuwComponent = a02yyuw_ns.class_(
 | 
					A02yyuwComponent = a02yyuw_ns.class_(
 | 
				
			||||||
@@ -17,7 +17,7 @@ A02yyuwComponent = a02yyuw_ns.class_(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
CONFIG_SCHEMA = sensor.sensor_schema(
 | 
					CONFIG_SCHEMA = sensor.sensor_schema(
 | 
				
			||||||
    A02yyuwComponent,
 | 
					    A02yyuwComponent,
 | 
				
			||||||
    unit_of_measurement=UNIT_MILLIMETERS,
 | 
					    unit_of_measurement=UNIT_MILLIMETER,
 | 
				
			||||||
    icon=ICON_ARROW_EXPAND_VERTICAL,
 | 
					    icon=ICON_ARROW_EXPAND_VERTICAL,
 | 
				
			||||||
    accuracy_decimals=0,
 | 
					    accuracy_decimals=0,
 | 
				
			||||||
    state_class=STATE_CLASS_MEASUREMENT,
 | 
					    state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,6 +11,8 @@
 | 
				
			|||||||
#include "ade7880_registers.h"
 | 
					#include "ade7880_registers.h"
 | 
				
			||||||
#include "esphome/core/log.h"
 | 
					#include "esphome/core/log.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <cinttypes>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace esphome {
 | 
					namespace esphome {
 | 
				
			||||||
namespace ade7880 {
 | 
					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() {
 | 
					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("    ", "Forward Active Energy", this->channel_a_->forward_active_energy);
 | 
				
			||||||
    LOG_SENSOR("    ", "Reverse Active Energy", this->channel_a_->reverse_active_energy);
 | 
					    LOG_SENSOR("    ", "Reverse Active Energy", this->channel_a_->reverse_active_energy);
 | 
				
			||||||
    ESP_LOGCONFIG(TAG, "    Calibration:");
 | 
					    ESP_LOGCONFIG(TAG, "    Calibration:");
 | 
				
			||||||
    ESP_LOGCONFIG(TAG, "     Current: %u", this->channel_a_->current_gain_calibration);
 | 
					    ESP_LOGCONFIG(TAG, "     Current: %" PRId32, this->channel_a_->current_gain_calibration);
 | 
				
			||||||
    ESP_LOGCONFIG(TAG, "     Voltage: %d", this->channel_a_->voltage_gain_calibration);
 | 
					    ESP_LOGCONFIG(TAG, "     Voltage: %" PRId32, this->channel_a_->voltage_gain_calibration);
 | 
				
			||||||
    ESP_LOGCONFIG(TAG, "     Power: %d", this->channel_a_->power_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);
 | 
					    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("    ", "Forward Active Energy", this->channel_b_->forward_active_energy);
 | 
				
			||||||
    LOG_SENSOR("    ", "Reverse Active Energy", this->channel_b_->reverse_active_energy);
 | 
					    LOG_SENSOR("    ", "Reverse Active Energy", this->channel_b_->reverse_active_energy);
 | 
				
			||||||
    ESP_LOGCONFIG(TAG, "    Calibration:");
 | 
					    ESP_LOGCONFIG(TAG, "    Calibration:");
 | 
				
			||||||
    ESP_LOGCONFIG(TAG, "     Current: %u", this->channel_b_->current_gain_calibration);
 | 
					    ESP_LOGCONFIG(TAG, "     Current: %" PRId32, this->channel_b_->current_gain_calibration);
 | 
				
			||||||
    ESP_LOGCONFIG(TAG, "     Voltage: %d", this->channel_b_->voltage_gain_calibration);
 | 
					    ESP_LOGCONFIG(TAG, "     Voltage: %" PRId32, this->channel_b_->voltage_gain_calibration);
 | 
				
			||||||
    ESP_LOGCONFIG(TAG, "     Power: %d", this->channel_b_->power_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);
 | 
					    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("    ", "Forward Active Energy", this->channel_c_->forward_active_energy);
 | 
				
			||||||
    LOG_SENSOR("    ", "Reverse Active Energy", this->channel_c_->reverse_active_energy);
 | 
					    LOG_SENSOR("    ", "Reverse Active Energy", this->channel_c_->reverse_active_energy);
 | 
				
			||||||
    ESP_LOGCONFIG(TAG, "    Calibration:");
 | 
					    ESP_LOGCONFIG(TAG, "    Calibration:");
 | 
				
			||||||
    ESP_LOGCONFIG(TAG, "     Current: %u", this->channel_c_->current_gain_calibration);
 | 
					    ESP_LOGCONFIG(TAG, "     Current: %" PRId32, this->channel_c_->current_gain_calibration);
 | 
				
			||||||
    ESP_LOGCONFIG(TAG, "     Voltage: %d", this->channel_c_->voltage_gain_calibration);
 | 
					    ESP_LOGCONFIG(TAG, "     Voltage: %" PRId32, this->channel_c_->voltage_gain_calibration);
 | 
				
			||||||
    ESP_LOGCONFIG(TAG, "     Power: %d", this->channel_c_->power_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);
 | 
					    ESP_LOGCONFIG(TAG, "     Phase Angle: %u", this->channel_c_->phase_angle_calibration);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -218,7 +220,7 @@ void ADE7880::dump_config() {
 | 
				
			|||||||
    ESP_LOGCONFIG(TAG, "  Neutral:");
 | 
					    ESP_LOGCONFIG(TAG, "  Neutral:");
 | 
				
			||||||
    LOG_SENSOR("    ", "Current", this->channel_n_->current);
 | 
					    LOG_SENSOR("    ", "Current", this->channel_n_->current);
 | 
				
			||||||
    ESP_LOGCONFIG(TAG, "    Calibration:");
 | 
					    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);
 | 
					  LOG_I2C_DEVICE(this);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
import esphome.config_validation as cv
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CONFIG_SCHEMA = CONFIG_SCHEMA = cv.invalid(
 | 
					CONFIG_SCHEMA = cv.invalid(
 | 
				
			||||||
    "The ade7953 sensor component has been renamed to ade7953_i2c."
 | 
					    "The ade7953 sensor component has been renamed to ade7953_i2c."
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,8 @@
 | 
				
			|||||||
#include "ade7953_base.h"
 | 
					#include "ade7953_base.h"
 | 
				
			||||||
#include "esphome/core/log.h"
 | 
					#include "esphome/core/log.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <cinttypes>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace esphome {
 | 
					namespace esphome {
 | 
				
			||||||
namespace ade7953_base {
 | 
					namespace ade7953_base {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -105,7 +107,7 @@ void ADE7953::update() {
 | 
				
			|||||||
    this->last_update_ = now;
 | 
					    this->last_update_ = now;
 | 
				
			||||||
    // prevent DIV/0
 | 
					    // prevent DIV/0
 | 
				
			||||||
    pf = ADE_WATTSEC_POWER_FACTOR * (diff < 10 ? 10 : diff) / 1000;
 | 
					    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
 | 
					  // Apparent power
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -60,7 +60,7 @@ bool AdE7953Spi::ade_read_16(uint16_t reg, uint16_t *value) {
 | 
				
			|||||||
  this->write_byte16(reg);
 | 
					  this->write_byte16(reg);
 | 
				
			||||||
  this->transfer_byte(0x80);
 | 
					  this->transfer_byte(0x80);
 | 
				
			||||||
  uint8_t recv[2];
 | 
					  uint8_t recv[2];
 | 
				
			||||||
  this->read_array(recv, 4);
 | 
					  this->read_array(recv, 2);
 | 
				
			||||||
  *value = encode_uint16(recv[0], recv[1]);
 | 
					  *value = encode_uint16(recv[0], recv[1]);
 | 
				
			||||||
  this->disable();
 | 
					  this->disable();
 | 
				
			||||||
  return false;
 | 
					  return false;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,7 @@
 | 
				
			|||||||
#include "ags10.h"
 | 
					#include "ags10.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <cinttypes>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace esphome {
 | 
					namespace esphome {
 | 
				
			||||||
namespace ags10 {
 | 
					namespace ags10 {
 | 
				
			||||||
static const char *const TAG = "ags10";
 | 
					static const char *const TAG = "ags10";
 | 
				
			||||||
@@ -35,7 +37,7 @@ void AGS10Component::setup() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  auto resistance = this->read_resistance_();
 | 
					  auto resistance = this->read_resistance_();
 | 
				
			||||||
  if (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) {
 | 
					    if (this->resistance_ != nullptr) {
 | 
				
			||||||
      this->resistance_->publish_state(*resistance);
 | 
					      this->resistance_->publish_state(*resistance);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -93,8 +93,9 @@ void AHT10Component::restart_read_() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void AHT10Component::read_data_() {
 | 
					void AHT10Component::read_data_() {
 | 
				
			||||||
  uint8_t data[6];
 | 
					  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_));
 | 
					    ESP_LOGD(TAG, "Read attempt %d at %ums", this->read_count_, (unsigned) (millis() - this->start_time_));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  if (this->read(data, 6) != i2c::ERROR_OK) {
 | 
					  if (this->read(data, 6) != i2c::ERROR_OK) {
 | 
				
			||||||
    this->status_set_warning("AHT10 read failed, retrying soon");
 | 
					    this->status_set_warning("AHT10 read failed, retrying soon");
 | 
				
			||||||
    this->restart_read_();
 | 
					    this->restart_read_();
 | 
				
			||||||
@@ -119,8 +120,9 @@ void AHT10Component::read_data_() {
 | 
				
			|||||||
      return;
 | 
					      return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  if (this->read_count_ > 1)
 | 
					  if (this->read_count_ > 1) {
 | 
				
			||||||
    ESP_LOGD(TAG, "Success at %ums", (unsigned) (millis() - this->start_time_));
 | 
					    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_temperature = ((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5];
 | 
				
			||||||
  uint32_t raw_humidity = ((data[1] << 16) | (data[2] << 8) | data[3]) >> 4;
 | 
					  uint32_t raw_humidity = ((data[1] << 16) | (data[2] << 8) | data[3]) >> 4;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,8 +14,6 @@ void AirthingsWavePlus::read_sensors(uint8_t *raw_value, uint16_t value_len) {
 | 
				
			|||||||
    ESP_LOGD(TAG, "version = %d", value->version);
 | 
					    ESP_LOGD(TAG, "version = %d", value->version);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (value->version == 1) {
 | 
					    if (value->version == 1) {
 | 
				
			||||||
      ESP_LOGD(TAG, "ambient light = %d", value->ambientLight);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      if (this->humidity_sensor_ != nullptr) {
 | 
					      if (this->humidity_sensor_ != nullptr) {
 | 
				
			||||||
        this->humidity_sensor_->publish_state(value->humidity / 2.0f);
 | 
					        this->humidity_sensor_->publish_state(value->humidity / 2.0f);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@@ -43,6 +41,10 @@ void AirthingsWavePlus::read_sensors(uint8_t *raw_value, uint16_t value_len) {
 | 
				
			|||||||
      if ((this->tvoc_sensor_ != nullptr) && this->is_valid_voc_value_(value->voc)) {
 | 
					      if ((this->tvoc_sensor_ != nullptr) && this->is_valid_voc_value_(value->voc)) {
 | 
				
			||||||
        this->tvoc_sensor_->publish_state(value->voc);
 | 
					        this->tvoc_sensor_->publish_state(value->voc);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (this->illuminance_sensor_ != nullptr) {
 | 
				
			||||||
 | 
					        this->illuminance_sensor_->publish_state(value->ambientLight);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      ESP_LOGE(TAG, "Invalid payload version (%d != 1, newer version or not a Wave Plus?)", value->version);
 | 
					      ESP_LOGE(TAG, "Invalid payload version (%d != 1, newer version or not a Wave Plus?)", value->version);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -68,6 +70,7 @@ void AirthingsWavePlus::dump_config() {
 | 
				
			|||||||
  LOG_SENSOR("  ", "Radon", this->radon_sensor_);
 | 
					  LOG_SENSOR("  ", "Radon", this->radon_sensor_);
 | 
				
			||||||
  LOG_SENSOR("  ", "Radon Long Term", this->radon_long_term_sensor_);
 | 
					  LOG_SENSOR("  ", "Radon Long Term", this->radon_long_term_sensor_);
 | 
				
			||||||
  LOG_SENSOR("  ", "CO2", this->co2_sensor_);
 | 
					  LOG_SENSOR("  ", "CO2", this->co2_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Illuminance", this->illuminance_sensor_);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
AirthingsWavePlus::AirthingsWavePlus() {
 | 
					AirthingsWavePlus::AirthingsWavePlus() {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,6 +22,7 @@ class AirthingsWavePlus : public airthings_wave_base::AirthingsWaveBase {
 | 
				
			|||||||
  void set_radon(sensor::Sensor *radon) { radon_sensor_ = radon; }
 | 
					  void set_radon(sensor::Sensor *radon) { radon_sensor_ = radon; }
 | 
				
			||||||
  void set_radon_long_term(sensor::Sensor *radon_long_term) { radon_long_term_sensor_ = radon_long_term; }
 | 
					  void set_radon_long_term(sensor::Sensor *radon_long_term) { radon_long_term_sensor_ = radon_long_term; }
 | 
				
			||||||
  void set_co2(sensor::Sensor *co2) { co2_sensor_ = co2; }
 | 
					  void set_co2(sensor::Sensor *co2) { co2_sensor_ = co2; }
 | 
				
			||||||
 | 
					  void set_illuminance(sensor::Sensor *illuminance) { illuminance_sensor_ = illuminance; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 protected:
 | 
					 protected:
 | 
				
			||||||
  bool is_valid_radon_value_(uint16_t radon);
 | 
					  bool is_valid_radon_value_(uint16_t radon);
 | 
				
			||||||
@@ -32,6 +33,7 @@ class AirthingsWavePlus : public airthings_wave_base::AirthingsWaveBase {
 | 
				
			|||||||
  sensor::Sensor *radon_sensor_{nullptr};
 | 
					  sensor::Sensor *radon_sensor_{nullptr};
 | 
				
			||||||
  sensor::Sensor *radon_long_term_sensor_{nullptr};
 | 
					  sensor::Sensor *radon_long_term_sensor_{nullptr};
 | 
				
			||||||
  sensor::Sensor *co2_sensor_{nullptr};
 | 
					  sensor::Sensor *co2_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *illuminance_sensor_{nullptr};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  struct WavePlusReadings {
 | 
					  struct WavePlusReadings {
 | 
				
			||||||
    uint8_t version;
 | 
					    uint8_t version;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,6 +12,9 @@ from esphome.const import (
 | 
				
			|||||||
    CONF_CO2,
 | 
					    CONF_CO2,
 | 
				
			||||||
    UNIT_BECQUEREL_PER_CUBIC_METER,
 | 
					    UNIT_BECQUEREL_PER_CUBIC_METER,
 | 
				
			||||||
    UNIT_PARTS_PER_MILLION,
 | 
					    UNIT_PARTS_PER_MILLION,
 | 
				
			||||||
 | 
					    CONF_ILLUMINANCE,
 | 
				
			||||||
 | 
					    UNIT_LUX,
 | 
				
			||||||
 | 
					    DEVICE_CLASS_ILLUMINANCE,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
DEPENDENCIES = airthings_wave_base.DEPENDENCIES
 | 
					DEPENDENCIES = airthings_wave_base.DEPENDENCIES
 | 
				
			||||||
@@ -45,6 +48,12 @@ CONFIG_SCHEMA = airthings_wave_base.BASE_SCHEMA.extend(
 | 
				
			|||||||
            device_class=DEVICE_CLASS_CARBON_DIOXIDE,
 | 
					            device_class=DEVICE_CLASS_CARBON_DIOXIDE,
 | 
				
			||||||
            state_class=STATE_CLASS_MEASUREMENT,
 | 
					            state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
 | 
					        cv.Optional(CONF_ILLUMINANCE): sensor.sensor_schema(
 | 
				
			||||||
 | 
					            unit_of_measurement=UNIT_LUX,
 | 
				
			||||||
 | 
					            accuracy_decimals=0,
 | 
				
			||||||
 | 
					            device_class=DEVICE_CLASS_ILLUMINANCE,
 | 
				
			||||||
 | 
					            state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -62,3 +71,6 @@ async def to_code(config):
 | 
				
			|||||||
    if config_co2 := config.get(CONF_CO2):
 | 
					    if config_co2 := config.get(CONF_CO2):
 | 
				
			||||||
        sens = await sensor.new_sensor(config_co2)
 | 
					        sens = await sensor.new_sensor(config_co2)
 | 
				
			||||||
        cg.add(var.set_co2(sens))
 | 
					        cg.add(var.set_co2(sens))
 | 
				
			||||||
 | 
					    if config_illuminance := config.get(CONF_ILLUMINANCE):
 | 
				
			||||||
 | 
					        sens = await sensor.new_sensor(config_illuminance)
 | 
				
			||||||
 | 
					        cg.add(var.set_illuminance(sens))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,14 +1,17 @@
 | 
				
			|||||||
import esphome.codegen as cg
 | 
					 | 
				
			||||||
import esphome.config_validation as cv
 | 
					 | 
				
			||||||
from esphome import automation
 | 
					from esphome import automation
 | 
				
			||||||
from esphome.automation import maybe_simple_id
 | 
					from esphome.automation import maybe_simple_id
 | 
				
			||||||
from esphome.core import CORE, coroutine_with_priority
 | 
					import esphome.codegen as cg
 | 
				
			||||||
 | 
					from esphome.components import mqtt, web_server
 | 
				
			||||||
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
from esphome.const import (
 | 
					from esphome.const import (
 | 
				
			||||||
 | 
					    CONF_CODE,
 | 
				
			||||||
    CONF_ID,
 | 
					    CONF_ID,
 | 
				
			||||||
 | 
					    CONF_MQTT_ID,
 | 
				
			||||||
    CONF_ON_STATE,
 | 
					    CONF_ON_STATE,
 | 
				
			||||||
    CONF_TRIGGER_ID,
 | 
					    CONF_TRIGGER_ID,
 | 
				
			||||||
    CONF_CODE,
 | 
					    CONF_WEB_SERVER_ID,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					from esphome.core import CORE, coroutine_with_priority
 | 
				
			||||||
from esphome.cpp_helpers import setup_entity
 | 
					from esphome.cpp_helpers import setup_entity
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CODEOWNERS = ["@grahambrown11", "@hwstar"]
 | 
					CODEOWNERS = ["@grahambrown11", "@hwstar"]
 | 
				
			||||||
@@ -75,65 +78,72 @@ AlarmControlPanelCondition = alarm_control_panel_ns.class_(
 | 
				
			|||||||
    "AlarmControlPanelCondition", automation.Condition
 | 
					    "AlarmControlPanelCondition", automation.Condition
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ALARM_CONTROL_PANEL_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(
 | 
					ALARM_CONTROL_PANEL_SCHEMA = (
 | 
				
			||||||
    {
 | 
					    cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
 | 
				
			||||||
        cv.GenerateID(): cv.declare_id(AlarmControlPanel),
 | 
					    .extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA)
 | 
				
			||||||
        cv.Optional(CONF_ON_STATE): automation.validate_automation(
 | 
					    .extend(
 | 
				
			||||||
            {
 | 
					        {
 | 
				
			||||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateTrigger),
 | 
					            cv.GenerateID(): cv.declare_id(AlarmControlPanel),
 | 
				
			||||||
            }
 | 
					            cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(
 | 
				
			||||||
        ),
 | 
					                mqtt.MQTTAlarmControlPanelComponent
 | 
				
			||||||
        cv.Optional(CONF_ON_TRIGGERED): automation.validate_automation(
 | 
					            ),
 | 
				
			||||||
            {
 | 
					            cv.Optional(CONF_ON_STATE): automation.validate_automation(
 | 
				
			||||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(TriggeredTrigger),
 | 
					                {
 | 
				
			||||||
            }
 | 
					                    cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateTrigger),
 | 
				
			||||||
        ),
 | 
					                }
 | 
				
			||||||
        cv.Optional(CONF_ON_ARMING): automation.validate_automation(
 | 
					            ),
 | 
				
			||||||
            {
 | 
					            cv.Optional(CONF_ON_TRIGGERED): automation.validate_automation(
 | 
				
			||||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ArmingTrigger),
 | 
					                {
 | 
				
			||||||
            }
 | 
					                    cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(TriggeredTrigger),
 | 
				
			||||||
        ),
 | 
					                }
 | 
				
			||||||
        cv.Optional(CONF_ON_PENDING): automation.validate_automation(
 | 
					            ),
 | 
				
			||||||
            {
 | 
					            cv.Optional(CONF_ON_ARMING): automation.validate_automation(
 | 
				
			||||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PendingTrigger),
 | 
					                {
 | 
				
			||||||
            }
 | 
					                    cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ArmingTrigger),
 | 
				
			||||||
        ),
 | 
					                }
 | 
				
			||||||
        cv.Optional(CONF_ON_ARMED_HOME): automation.validate_automation(
 | 
					            ),
 | 
				
			||||||
            {
 | 
					            cv.Optional(CONF_ON_PENDING): automation.validate_automation(
 | 
				
			||||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ArmedHomeTrigger),
 | 
					                {
 | 
				
			||||||
            }
 | 
					                    cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PendingTrigger),
 | 
				
			||||||
        ),
 | 
					                }
 | 
				
			||||||
        cv.Optional(CONF_ON_ARMED_NIGHT): automation.validate_automation(
 | 
					            ),
 | 
				
			||||||
            {
 | 
					            cv.Optional(CONF_ON_ARMED_HOME): automation.validate_automation(
 | 
				
			||||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ArmedNightTrigger),
 | 
					                {
 | 
				
			||||||
            }
 | 
					                    cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ArmedHomeTrigger),
 | 
				
			||||||
        ),
 | 
					                }
 | 
				
			||||||
        cv.Optional(CONF_ON_ARMED_AWAY): automation.validate_automation(
 | 
					            ),
 | 
				
			||||||
            {
 | 
					            cv.Optional(CONF_ON_ARMED_NIGHT): automation.validate_automation(
 | 
				
			||||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ArmedAwayTrigger),
 | 
					                {
 | 
				
			||||||
            }
 | 
					                    cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ArmedNightTrigger),
 | 
				
			||||||
        ),
 | 
					                }
 | 
				
			||||||
        cv.Optional(CONF_ON_DISARMED): automation.validate_automation(
 | 
					            ),
 | 
				
			||||||
            {
 | 
					            cv.Optional(CONF_ON_ARMED_AWAY): automation.validate_automation(
 | 
				
			||||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DisarmedTrigger),
 | 
					                {
 | 
				
			||||||
            }
 | 
					                    cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ArmedAwayTrigger),
 | 
				
			||||||
        ),
 | 
					                }
 | 
				
			||||||
        cv.Optional(CONF_ON_CLEARED): automation.validate_automation(
 | 
					            ),
 | 
				
			||||||
            {
 | 
					            cv.Optional(CONF_ON_DISARMED): automation.validate_automation(
 | 
				
			||||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ClearedTrigger),
 | 
					                {
 | 
				
			||||||
            }
 | 
					                    cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DisarmedTrigger),
 | 
				
			||||||
        ),
 | 
					                }
 | 
				
			||||||
        cv.Optional(CONF_ON_CHIME): automation.validate_automation(
 | 
					            ),
 | 
				
			||||||
            {
 | 
					            cv.Optional(CONF_ON_CLEARED): automation.validate_automation(
 | 
				
			||||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ChimeTrigger),
 | 
					                {
 | 
				
			||||||
            }
 | 
					                    cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ClearedTrigger),
 | 
				
			||||||
        ),
 | 
					                }
 | 
				
			||||||
        cv.Optional(CONF_ON_READY): automation.validate_automation(
 | 
					            ),
 | 
				
			||||||
            {
 | 
					            cv.Optional(CONF_ON_CHIME): automation.validate_automation(
 | 
				
			||||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ReadyTrigger),
 | 
					                {
 | 
				
			||||||
            }
 | 
					                    cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ChimeTrigger),
 | 
				
			||||||
        ),
 | 
					                }
 | 
				
			||||||
    }
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_ON_READY): automation.validate_automation(
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ReadyTrigger),
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ALARM_CONTROL_PANEL_ACTION_SCHEMA = maybe_simple_id(
 | 
					ALARM_CONTROL_PANEL_ACTION_SCHEMA = maybe_simple_id(
 | 
				
			||||||
@@ -185,6 +195,12 @@ async def setup_alarm_control_panel_core_(var, config):
 | 
				
			|||||||
    for conf in config.get(CONF_ON_READY, []):
 | 
					    for conf in config.get(CONF_ON_READY, []):
 | 
				
			||||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
					        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
				
			||||||
        await automation.build_automation(trigger, [], conf)
 | 
					        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):
 | 
				
			||||||
 | 
					        mqtt_ = cg.new_Pvariable(mqtt_id, var)
 | 
				
			||||||
 | 
					        await mqtt.register_mqtt_component(mqtt_, config)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async def register_alarm_control_panel(var, config):
 | 
					async def register_alarm_control_panel(var, config):
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,7 +3,13 @@ import logging
 | 
				
			|||||||
from esphome import automation, core
 | 
					from esphome import automation, core
 | 
				
			||||||
from esphome.components import font
 | 
					from esphome.components import font
 | 
				
			||||||
import esphome.components.image as espImage
 | 
					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.config_validation as cv
 | 
				
			||||||
import esphome.codegen as cg
 | 
					import esphome.codegen as cg
 | 
				
			||||||
from esphome.const import (
 | 
					from esphome.const import (
 | 
				
			||||||
@@ -13,6 +19,9 @@ from esphome.const import (
 | 
				
			|||||||
    CONF_REPEAT,
 | 
					    CONF_REPEAT,
 | 
				
			||||||
    CONF_RESIZE,
 | 
					    CONF_RESIZE,
 | 
				
			||||||
    CONF_TYPE,
 | 
					    CONF_TYPE,
 | 
				
			||||||
 | 
					    CONF_SOURCE,
 | 
				
			||||||
 | 
					    CONF_PATH,
 | 
				
			||||||
 | 
					    CONF_URL,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from esphome.core import CORE, HexInt
 | 
					from esphome.core import CORE, HexInt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -43,6 +52,40 @@ SetFrameAction = animation_ns.class_(
 | 
				
			|||||||
    "AnimationSetFrameAction", automation.Action, cg.Parented.template(Animation_)
 | 
					    "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):
 | 
					def validate_cross_dependencies(config):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
@@ -67,7 +110,7 @@ ANIMATION_SCHEMA = cv.Schema(
 | 
				
			|||||||
    cv.All(
 | 
					    cv.All(
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            cv.Required(CONF_ID): cv.declare_id(Animation_),
 | 
					            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_RESIZE): cv.dimensions,
 | 
				
			||||||
            cv.Optional(CONF_TYPE, default="BINARY"): cv.enum(
 | 
					            cv.Optional(CONF_TYPE, default="BINARY"): cv.enum(
 | 
				
			||||||
                espImage.IMAGE_TYPE, upper=True
 | 
					                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):
 | 
					async def to_code(config):
 | 
				
			||||||
    from PIL import Image
 | 
					    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:
 | 
					    try:
 | 
				
			||||||
        image = Image.open(path)
 | 
					        image = Image.open(path)
 | 
				
			||||||
    except Exception as e:
 | 
					    except Exception as e:
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										4
									
								
								esphome/components/apds9306/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								esphome/components/apds9306/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					# Based on this datasheet:
 | 
				
			||||||
 | 
					# https://www.mouser.ca/datasheet/2/678/AVGO_S_A0002854364_1-2574547.pdf
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CODEOWNERS = ["@aodrenah"]
 | 
				
			||||||
							
								
								
									
										151
									
								
								esphome/components/apds9306/apds9306.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								esphome/components/apds9306/apds9306.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,151 @@
 | 
				
			|||||||
 | 
					// Based on this datasheet:
 | 
				
			||||||
 | 
					// https://www.mouser.ca/datasheet/2/678/AVGO_S_A0002854364_1-2574547.pdf
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "apds9306.h"
 | 
				
			||||||
 | 
					#include "esphome/core/helpers.h"
 | 
				
			||||||
 | 
					#include "esphome/core/log.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace esphome {
 | 
				
			||||||
 | 
					namespace apds9306 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char *const TAG = "apds9306";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum {  // APDS9306 registers
 | 
				
			||||||
 | 
					  APDS9306_MAIN_CTRL = 0x00,
 | 
				
			||||||
 | 
					  APDS9306_ALS_MEAS_RATE = 0x04,
 | 
				
			||||||
 | 
					  APDS9306_ALS_GAIN = 0x05,
 | 
				
			||||||
 | 
					  APDS9306_PART_ID = 0x06,
 | 
				
			||||||
 | 
					  APDS9306_MAIN_STATUS = 0x07,
 | 
				
			||||||
 | 
					  APDS9306_CLEAR_DATA_0 = 0x0A,  // LSB
 | 
				
			||||||
 | 
					  APDS9306_CLEAR_DATA_1 = 0x0B,
 | 
				
			||||||
 | 
					  APDS9306_CLEAR_DATA_2 = 0x0C,  // MSB
 | 
				
			||||||
 | 
					  APDS9306_ALS_DATA_0 = 0x0D,    // LSB
 | 
				
			||||||
 | 
					  APDS9306_ALS_DATA_1 = 0x0E,
 | 
				
			||||||
 | 
					  APDS9306_ALS_DATA_2 = 0x0F,  // MSB
 | 
				
			||||||
 | 
					  APDS9306_INT_CFG = 0x19,
 | 
				
			||||||
 | 
					  APDS9306_INT_PERSISTENCE = 0x1A,
 | 
				
			||||||
 | 
					  APDS9306_ALS_THRES_UP_0 = 0x21,  // LSB
 | 
				
			||||||
 | 
					  APDS9306_ALS_THRES_UP_1 = 0x22,
 | 
				
			||||||
 | 
					  APDS9306_ALS_THRES_UP_2 = 0x23,   // MSB
 | 
				
			||||||
 | 
					  APDS9306_ALS_THRES_LOW_0 = 0x24,  // LSB
 | 
				
			||||||
 | 
					  APDS9306_ALS_THRES_LOW_1 = 0x25,
 | 
				
			||||||
 | 
					  APDS9306_ALS_THRES_LOW_2 = 0x26,  // MSB
 | 
				
			||||||
 | 
					  APDS9306_ALS_THRES_VAR = 0x27
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define APDS9306_ERROR_CHECK(func, error) \
 | 
				
			||||||
 | 
					  if (!(func)) { \
 | 
				
			||||||
 | 
					    ESP_LOGE(TAG, error); \
 | 
				
			||||||
 | 
					    this->mark_failed(); \
 | 
				
			||||||
 | 
					    return; \
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					#define APDS9306_WARNING_CHECK(func, warning) \
 | 
				
			||||||
 | 
					  if (!(func)) { \
 | 
				
			||||||
 | 
					    ESP_LOGW(TAG, warning); \
 | 
				
			||||||
 | 
					    this->status_set_warning(); \
 | 
				
			||||||
 | 
					    return; \
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					#define APDS9306_WRITE_BYTE(reg, value) \
 | 
				
			||||||
 | 
					  ESP_LOGV(TAG, "Writing 0x%02x to 0x%02x", value, reg); \
 | 
				
			||||||
 | 
					  if (!this->write_byte(reg, value)) { \
 | 
				
			||||||
 | 
					    ESP_LOGE(TAG, "Failed writing 0x%02x to 0x%02x", value, reg); \
 | 
				
			||||||
 | 
					    this->mark_failed(); \
 | 
				
			||||||
 | 
					    return; \
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void APDS9306::setup() {
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "Setting up APDS9306...");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  uint8_t id;
 | 
				
			||||||
 | 
					  if (!this->read_byte(APDS9306_PART_ID, &id)) {  // Part ID register
 | 
				
			||||||
 | 
					    this->error_code_ = COMMUNICATION_FAILED;
 | 
				
			||||||
 | 
					    this->mark_failed();
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (id != 0xB1 && id != 0xB3) {  // 0xB1 for APDS9306 0xB3 for APDS9306-065
 | 
				
			||||||
 | 
					    this->error_code_ = WRONG_ID;
 | 
				
			||||||
 | 
					    this->mark_failed();
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // ALS resolution and measurement, see datasheet or init.py for options
 | 
				
			||||||
 | 
					  uint8_t als_meas_rate = ((this->bit_width_ & 0x07) << 4) | (this->measurement_rate_ & 0x07);
 | 
				
			||||||
 | 
					  APDS9306_WRITE_BYTE(APDS9306_ALS_MEAS_RATE, als_meas_rate);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // ALS gain, see datasheet or init.py for options
 | 
				
			||||||
 | 
					  uint8_t als_gain = (this->gain_ & 0x07);
 | 
				
			||||||
 | 
					  APDS9306_WRITE_BYTE(APDS9306_ALS_GAIN, als_gain);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Set to standby mode
 | 
				
			||||||
 | 
					  APDS9306_WRITE_BYTE(APDS9306_MAIN_CTRL, 0x00);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Check for data, clear main status
 | 
				
			||||||
 | 
					  uint8_t status;
 | 
				
			||||||
 | 
					  APDS9306_WARNING_CHECK(this->read_byte(APDS9306_MAIN_STATUS, &status), "Reading MAIN STATUS failed.");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Set to active mode
 | 
				
			||||||
 | 
					  APDS9306_WRITE_BYTE(APDS9306_MAIN_CTRL, 0x02);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "APDS9306 setup complete");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void APDS9306::dump_config() {
 | 
				
			||||||
 | 
					  LOG_SENSOR("", "APDS9306", this);
 | 
				
			||||||
 | 
					  LOG_I2C_DEVICE(this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (this->is_failed()) {
 | 
				
			||||||
 | 
					    switch (this->error_code_) {
 | 
				
			||||||
 | 
					      case COMMUNICATION_FAILED:
 | 
				
			||||||
 | 
					        ESP_LOGE(TAG, "Communication with APDS9306 failed!");
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      case WRONG_ID:
 | 
				
			||||||
 | 
					        ESP_LOGE(TAG, "APDS9306 has invalid id!");
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      default:
 | 
				
			||||||
 | 
					        ESP_LOGE(TAG, "Setting up APDS9306 registers failed!");
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "  Gain: %u", AMBIENT_LIGHT_GAIN_VALUES[this->gain_]);
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "  Measurement rate: %u", MEASUREMENT_RATE_VALUES[this->measurement_rate_]);
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "  Measurement Resolution/Bit width: %d", MEASUREMENT_BIT_WIDTH_VALUES[this->bit_width_]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  LOG_UPDATE_INTERVAL(this);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void APDS9306::update() {
 | 
				
			||||||
 | 
					  // Check for new data
 | 
				
			||||||
 | 
					  uint8_t status;
 | 
				
			||||||
 | 
					  APDS9306_WARNING_CHECK(this->read_byte(APDS9306_MAIN_STATUS, &status), "Reading MAIN STATUS failed.");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  this->status_clear_warning();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!(status &= 0b00001000)) {  // No new data
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Set to standby mode
 | 
				
			||||||
 | 
					  APDS9306_WRITE_BYTE(APDS9306_MAIN_CTRL, 0x00);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Clear MAIN STATUS
 | 
				
			||||||
 | 
					  APDS9306_WARNING_CHECK(this->read_byte(APDS9306_MAIN_STATUS, &status), "Reading MAIN STATUS failed.");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  uint8_t als_data[3];
 | 
				
			||||||
 | 
					  APDS9306_WARNING_CHECK(this->read_bytes(APDS9306_ALS_DATA_0, als_data, 3), "Reading ALS data has failed.");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Set to active mode
 | 
				
			||||||
 | 
					  APDS9306_WRITE_BYTE(APDS9306_MAIN_CTRL, 0x02);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  uint32_t light_level = 0x00 | encode_uint24(als_data[2], als_data[1], als_data[0]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  float lux = ((float) light_level / AMBIENT_LIGHT_GAIN_VALUES[this->gain_]) *
 | 
				
			||||||
 | 
					              (100.0f / MEASUREMENT_RATE_VALUES[this->measurement_rate_]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ESP_LOGD(TAG, "Got illuminance=%.1flx from", lux);
 | 
				
			||||||
 | 
					  this->publish_state(lux);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace apds9306
 | 
				
			||||||
 | 
					}  // namespace esphome
 | 
				
			||||||
							
								
								
									
										66
									
								
								esphome/components/apds9306/apds9306.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								esphome/components/apds9306/apds9306.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,66 @@
 | 
				
			|||||||
 | 
					// Based on this datasheet:
 | 
				
			||||||
 | 
					// https://www.mouser.ca/datasheet/2/678/AVGO_S_A0002854364_1-2574547.pdf
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "esphome/components/i2c/i2c.h"
 | 
				
			||||||
 | 
					#include "esphome/components/sensor/sensor.h"
 | 
				
			||||||
 | 
					#include "esphome/core/component.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace esphome {
 | 
				
			||||||
 | 
					namespace apds9306 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum MeasurementBitWidth : uint8_t {
 | 
				
			||||||
 | 
					  MEASUREMENT_BIT_WIDTH_20 = 0,
 | 
				
			||||||
 | 
					  MEASUREMENT_BIT_WIDTH_19 = 1,
 | 
				
			||||||
 | 
					  MEASUREMENT_BIT_WIDTH_18 = 2,
 | 
				
			||||||
 | 
					  MEASUREMENT_BIT_WIDTH_17 = 3,
 | 
				
			||||||
 | 
					  MEASUREMENT_BIT_WIDTH_16 = 4,
 | 
				
			||||||
 | 
					  MEASUREMENT_BIT_WIDTH_13 = 5,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					static const uint8_t MEASUREMENT_BIT_WIDTH_VALUES[] = {20, 19, 18, 17, 16, 13};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum MeasurementRate : uint8_t {
 | 
				
			||||||
 | 
					  MEASUREMENT_RATE_25 = 0,
 | 
				
			||||||
 | 
					  MEASUREMENT_RATE_50 = 1,
 | 
				
			||||||
 | 
					  MEASUREMENT_RATE_100 = 2,
 | 
				
			||||||
 | 
					  MEASUREMENT_RATE_200 = 3,
 | 
				
			||||||
 | 
					  MEASUREMENT_RATE_500 = 4,
 | 
				
			||||||
 | 
					  MEASUREMENT_RATE_1000 = 5,
 | 
				
			||||||
 | 
					  MEASUREMENT_RATE_2000 = 6,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					static const uint16_t MEASUREMENT_RATE_VALUES[] = {25, 50, 100, 200, 500, 1000, 2000};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum AmbientLightGain : uint8_t {
 | 
				
			||||||
 | 
					  AMBIENT_LIGHT_GAIN_1 = 0,
 | 
				
			||||||
 | 
					  AMBIENT_LIGHT_GAIN_3 = 1,
 | 
				
			||||||
 | 
					  AMBIENT_LIGHT_GAIN_6 = 2,
 | 
				
			||||||
 | 
					  AMBIENT_LIGHT_GAIN_9 = 3,
 | 
				
			||||||
 | 
					  AMBIENT_LIGHT_GAIN_18 = 4,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					static const uint8_t AMBIENT_LIGHT_GAIN_VALUES[] = {1, 3, 6, 9, 18};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class APDS9306 : public sensor::Sensor, public PollingComponent, public i2c::I2CDevice {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  void setup() override;
 | 
				
			||||||
 | 
					  float get_setup_priority() const override { return setup_priority::BUS; }
 | 
				
			||||||
 | 
					  void dump_config() override;
 | 
				
			||||||
 | 
					  void update() override;
 | 
				
			||||||
 | 
					  void set_bit_width(MeasurementBitWidth bit_width) { this->bit_width_ = bit_width; }
 | 
				
			||||||
 | 
					  void set_measurement_rate(MeasurementRate measurement_rate) { this->measurement_rate_ = measurement_rate; }
 | 
				
			||||||
 | 
					  void set_ambient_light_gain(AmbientLightGain gain) { this->gain_ = gain; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 protected:
 | 
				
			||||||
 | 
					  enum ErrorCode {
 | 
				
			||||||
 | 
					    NONE = 0,
 | 
				
			||||||
 | 
					    COMMUNICATION_FAILED,
 | 
				
			||||||
 | 
					    WRONG_ID,
 | 
				
			||||||
 | 
					  } error_code_{NONE};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  MeasurementBitWidth bit_width_;
 | 
				
			||||||
 | 
					  MeasurementRate measurement_rate_;
 | 
				
			||||||
 | 
					  AmbientLightGain gain_;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace apds9306
 | 
				
			||||||
 | 
					}  // namespace esphome
 | 
				
			||||||
							
								
								
									
										95
									
								
								esphome/components/apds9306/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								esphome/components/apds9306/sensor.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,95 @@
 | 
				
			|||||||
 | 
					# Based on this datasheet:
 | 
				
			||||||
 | 
					# https://www.mouser.ca/datasheet/2/678/AVGO_S_A0002854364_1-2574547.pdf
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import esphome.codegen as cg
 | 
				
			||||||
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
 | 
					from esphome.components import i2c, sensor
 | 
				
			||||||
 | 
					from esphome.const import (
 | 
				
			||||||
 | 
					    CONF_GAIN,
 | 
				
			||||||
 | 
					    DEVICE_CLASS_ILLUMINANCE,
 | 
				
			||||||
 | 
					    ICON_LIGHTBULB,
 | 
				
			||||||
 | 
					    STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					    UNIT_LUX,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEPENDENCIES = ["i2c"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CONF_APDS9306_ID = "apds9306_id"
 | 
				
			||||||
 | 
					CONF_BIT_WIDTH = "bit_width"
 | 
				
			||||||
 | 
					CONF_MEASUREMENT_RATE = "measurement_rate"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					apds9306_ns = cg.esphome_ns.namespace("apds9306")
 | 
				
			||||||
 | 
					APDS9306 = apds9306_ns.class_(
 | 
				
			||||||
 | 
					    "APDS9306", sensor.Sensor, cg.PollingComponent, i2c.I2CDevice
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MeasurementBitWidth = apds9306_ns.enum("MeasurementBitWidth")
 | 
				
			||||||
 | 
					MeasurementRate = apds9306_ns.enum("MeasurementRate")
 | 
				
			||||||
 | 
					AmbientLightGain = apds9306_ns.enum("AmbientLightGain")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MEASUREMENT_BIT_WIDTHS = {
 | 
				
			||||||
 | 
					    20: MeasurementBitWidth.MEASUREMENT_BIT_WIDTH_20,
 | 
				
			||||||
 | 
					    19: MeasurementBitWidth.MEASUREMENT_BIT_WIDTH_19,
 | 
				
			||||||
 | 
					    18: MeasurementBitWidth.MEASUREMENT_BIT_WIDTH_18,
 | 
				
			||||||
 | 
					    17: MeasurementBitWidth.MEASUREMENT_BIT_WIDTH_17,
 | 
				
			||||||
 | 
					    16: MeasurementBitWidth.MEASUREMENT_BIT_WIDTH_16,
 | 
				
			||||||
 | 
					    13: MeasurementBitWidth.MEASUREMENT_BIT_WIDTH_13,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MEASUREMENT_RATES = {
 | 
				
			||||||
 | 
					    25: MeasurementRate.MEASUREMENT_RATE_25,
 | 
				
			||||||
 | 
					    50: MeasurementRate.MEASUREMENT_RATE_50,
 | 
				
			||||||
 | 
					    100: MeasurementRate.MEASUREMENT_RATE_100,
 | 
				
			||||||
 | 
					    200: MeasurementRate.MEASUREMENT_RATE_200,
 | 
				
			||||||
 | 
					    500: MeasurementRate.MEASUREMENT_RATE_500,
 | 
				
			||||||
 | 
					    1000: MeasurementRate.MEASUREMENT_RATE_1000,
 | 
				
			||||||
 | 
					    2000: MeasurementRate.MEASUREMENT_RATE_2000,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					AMBIENT_LIGHT_GAINS = {
 | 
				
			||||||
 | 
					    1: AmbientLightGain.AMBIENT_LIGHT_GAIN_1,
 | 
				
			||||||
 | 
					    3: AmbientLightGain.AMBIENT_LIGHT_GAIN_3,
 | 
				
			||||||
 | 
					    6: AmbientLightGain.AMBIENT_LIGHT_GAIN_6,
 | 
				
			||||||
 | 
					    9: AmbientLightGain.AMBIENT_LIGHT_GAIN_9,
 | 
				
			||||||
 | 
					    18: AmbientLightGain.AMBIENT_LIGHT_GAIN_18,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _validate_measurement_rate(value):
 | 
				
			||||||
 | 
					    value = cv.positive_time_period_milliseconds(value)
 | 
				
			||||||
 | 
					    return cv.enum(MEASUREMENT_RATES, int=True)(value.total_milliseconds)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CONFIG_SCHEMA = (
 | 
				
			||||||
 | 
					    sensor.sensor_schema(
 | 
				
			||||||
 | 
					        APDS9306,
 | 
				
			||||||
 | 
					        unit_of_measurement=UNIT_LUX,
 | 
				
			||||||
 | 
					        accuracy_decimals=1,
 | 
				
			||||||
 | 
					        device_class=DEVICE_CLASS_ILLUMINANCE,
 | 
				
			||||||
 | 
					        state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					        icon=ICON_LIGHTBULB,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    .extend(
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            cv.Optional(CONF_GAIN, default="1"): cv.enum(AMBIENT_LIGHT_GAINS, int=True),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_BIT_WIDTH, default="18"): cv.enum(
 | 
				
			||||||
 | 
					                MEASUREMENT_BIT_WIDTHS, int=True
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Optional(
 | 
				
			||||||
 | 
					                CONF_MEASUREMENT_RATE, default="100ms"
 | 
				
			||||||
 | 
					            ): _validate_measurement_rate,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    .extend(cv.polling_component_schema("60s"))
 | 
				
			||||||
 | 
					    .extend(i2c.i2c_device_schema(0x52))
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async def to_code(config):
 | 
				
			||||||
 | 
					    var = await sensor.new_sensor(config)
 | 
				
			||||||
 | 
					    await cg.register_component(var, config)
 | 
				
			||||||
 | 
					    await i2c.register_i2c_device(var, config)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cg.add(var.set_bit_width(config[CONF_BIT_WIDTH]))
 | 
				
			||||||
 | 
					    cg.add(var.set_measurement_rate(config[CONF_MEASUREMENT_RATE]))
 | 
				
			||||||
 | 
					    cg.add(var.set_ambient_light_gain(config[CONF_GAIN]))
 | 
				
			||||||
@@ -1,25 +1,27 @@
 | 
				
			|||||||
import base64
 | 
					import base64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import esphome.codegen as cg
 | 
					 | 
				
			||||||
import esphome.config_validation as cv
 | 
					 | 
				
			||||||
from esphome import automation
 | 
					from esphome import automation
 | 
				
			||||||
from esphome.automation import Condition
 | 
					from esphome.automation import Condition
 | 
				
			||||||
 | 
					import esphome.codegen as cg
 | 
				
			||||||
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
from esphome.const import (
 | 
					from esphome.const import (
 | 
				
			||||||
 | 
					    CONF_ACTION,
 | 
				
			||||||
 | 
					    CONF_ACTIONS,
 | 
				
			||||||
    CONF_DATA,
 | 
					    CONF_DATA,
 | 
				
			||||||
    CONF_DATA_TEMPLATE,
 | 
					    CONF_DATA_TEMPLATE,
 | 
				
			||||||
 | 
					    CONF_EVENT,
 | 
				
			||||||
    CONF_ID,
 | 
					    CONF_ID,
 | 
				
			||||||
    CONF_KEY,
 | 
					    CONF_KEY,
 | 
				
			||||||
 | 
					    CONF_ON_CLIENT_CONNECTED,
 | 
				
			||||||
 | 
					    CONF_ON_CLIENT_DISCONNECTED,
 | 
				
			||||||
    CONF_PASSWORD,
 | 
					    CONF_PASSWORD,
 | 
				
			||||||
    CONF_PORT,
 | 
					    CONF_PORT,
 | 
				
			||||||
    CONF_REBOOT_TIMEOUT,
 | 
					    CONF_REBOOT_TIMEOUT,
 | 
				
			||||||
    CONF_SERVICE,
 | 
					    CONF_SERVICE,
 | 
				
			||||||
    CONF_VARIABLES,
 | 
					 | 
				
			||||||
    CONF_SERVICES,
 | 
					    CONF_SERVICES,
 | 
				
			||||||
    CONF_TRIGGER_ID,
 | 
					 | 
				
			||||||
    CONF_EVENT,
 | 
					 | 
				
			||||||
    CONF_TAG,
 | 
					    CONF_TAG,
 | 
				
			||||||
    CONF_ON_CLIENT_CONNECTED,
 | 
					    CONF_TRIGGER_ID,
 | 
				
			||||||
    CONF_ON_CLIENT_DISCONNECTED,
 | 
					    CONF_VARIABLES,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from esphome.core import coroutine_with_priority
 | 
					from esphome.core import coroutine_with_priority
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -63,40 +65,51 @@ def validate_encryption_key(value):
 | 
				
			|||||||
    return value
 | 
					    return value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CONFIG_SCHEMA = cv.Schema(
 | 
					ACTIONS_SCHEMA = automation.validate_automation(
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        cv.GenerateID(): cv.declare_id(APIServer),
 | 
					        cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(UserServiceTrigger),
 | 
				
			||||||
        cv.Optional(CONF_PORT, default=6053): cv.port,
 | 
					        cv.Exclusive(CONF_SERVICE, group_of_exclusion=CONF_ACTION): cv.valid_name,
 | 
				
			||||||
        cv.Optional(CONF_PASSWORD, default=""): cv.string_strict,
 | 
					        cv.Exclusive(CONF_ACTION, group_of_exclusion=CONF_ACTION): cv.valid_name,
 | 
				
			||||||
        cv.Optional(
 | 
					        cv.Optional(CONF_VARIABLES, default={}): cv.Schema(
 | 
				
			||||||
            CONF_REBOOT_TIMEOUT, default="15min"
 | 
					 | 
				
			||||||
        ): cv.positive_time_period_milliseconds,
 | 
					 | 
				
			||||||
        cv.Optional(CONF_SERVICES): automation.validate_automation(
 | 
					 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(UserServiceTrigger),
 | 
					                cv.validate_id_name: cv.one_of(*SERVICE_ARG_NATIVE_TYPES, lower=True),
 | 
				
			||||||
                cv.Required(CONF_SERVICE): cv.valid_name,
 | 
					 | 
				
			||||||
                cv.Optional(CONF_VARIABLES, default={}): cv.Schema(
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        cv.validate_id_name: cv.one_of(
 | 
					 | 
				
			||||||
                            *SERVICE_ARG_NATIVE_TYPES, lower=True
 | 
					 | 
				
			||||||
                        ),
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
        cv.Optional(CONF_ENCRYPTION): cv.Schema(
 | 
					    },
 | 
				
			||||||
            {
 | 
					    cv.All(
 | 
				
			||||||
                cv.Required(CONF_KEY): validate_encryption_key,
 | 
					        cv.has_exactly_one_key(CONF_SERVICE, CONF_ACTION),
 | 
				
			||||||
            }
 | 
					        cv.rename_key(CONF_SERVICE, CONF_ACTION),
 | 
				
			||||||
        ),
 | 
					    ),
 | 
				
			||||||
        cv.Optional(CONF_ON_CLIENT_CONNECTED): automation.validate_automation(
 | 
					)
 | 
				
			||||||
            single=True
 | 
					
 | 
				
			||||||
        ),
 | 
					CONFIG_SCHEMA = cv.All(
 | 
				
			||||||
        cv.Optional(CONF_ON_CLIENT_DISCONNECTED): automation.validate_automation(
 | 
					    cv.Schema(
 | 
				
			||||||
            single=True
 | 
					        {
 | 
				
			||||||
        ),
 | 
					            cv.GenerateID(): cv.declare_id(APIServer),
 | 
				
			||||||
    }
 | 
					            cv.Optional(CONF_PORT, default=6053): cv.port,
 | 
				
			||||||
).extend(cv.COMPONENT_SCHEMA)
 | 
					            cv.Optional(CONF_PASSWORD, default=""): cv.string_strict,
 | 
				
			||||||
 | 
					            cv.Optional(
 | 
				
			||||||
 | 
					                CONF_REBOOT_TIMEOUT, default="15min"
 | 
				
			||||||
 | 
					            ): cv.positive_time_period_milliseconds,
 | 
				
			||||||
 | 
					            cv.Exclusive(
 | 
				
			||||||
 | 
					                CONF_SERVICES, group_of_exclusion=CONF_ACTIONS
 | 
				
			||||||
 | 
					            ): ACTIONS_SCHEMA,
 | 
				
			||||||
 | 
					            cv.Exclusive(CONF_ACTIONS, group_of_exclusion=CONF_ACTIONS): ACTIONS_SCHEMA,
 | 
				
			||||||
 | 
					            cv.Optional(CONF_ENCRYPTION): cv.Schema(
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    cv.Required(CONF_KEY): validate_encryption_key,
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_ON_CLIENT_CONNECTED): automation.validate_automation(
 | 
				
			||||||
 | 
					                single=True
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_ON_CLIENT_DISCONNECTED): automation.validate_automation(
 | 
				
			||||||
 | 
					                single=True
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    ).extend(cv.COMPONENT_SCHEMA),
 | 
				
			||||||
 | 
					    cv.rename_key(CONF_SERVICES, CONF_ACTIONS),
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@coroutine_with_priority(40.0)
 | 
					@coroutine_with_priority(40.0)
 | 
				
			||||||
@@ -108,7 +121,7 @@ async def to_code(config):
 | 
				
			|||||||
    cg.add(var.set_password(config[CONF_PASSWORD]))
 | 
					    cg.add(var.set_password(config[CONF_PASSWORD]))
 | 
				
			||||||
    cg.add(var.set_reboot_timeout(config[CONF_REBOOT_TIMEOUT]))
 | 
					    cg.add(var.set_reboot_timeout(config[CONF_REBOOT_TIMEOUT]))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for conf in config.get(CONF_SERVICES, []):
 | 
					    for conf in config.get(CONF_ACTIONS, []):
 | 
				
			||||||
        template_args = []
 | 
					        template_args = []
 | 
				
			||||||
        func_args = []
 | 
					        func_args = []
 | 
				
			||||||
        service_arg_names = []
 | 
					        service_arg_names = []
 | 
				
			||||||
@@ -119,7 +132,7 @@ async def to_code(config):
 | 
				
			|||||||
            service_arg_names.append(name)
 | 
					            service_arg_names.append(name)
 | 
				
			||||||
        templ = cg.TemplateArguments(*template_args)
 | 
					        templ = cg.TemplateArguments(*template_args)
 | 
				
			||||||
        trigger = cg.new_Pvariable(
 | 
					        trigger = cg.new_Pvariable(
 | 
				
			||||||
            conf[CONF_TRIGGER_ID], templ, conf[CONF_SERVICE], service_arg_names
 | 
					            conf[CONF_TRIGGER_ID], templ, conf[CONF_ACTION], service_arg_names
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        cg.add(var.register_user_service(trigger))
 | 
					        cg.add(var.register_user_service(trigger))
 | 
				
			||||||
        await automation.build_automation(trigger, func_args, conf)
 | 
					        await automation.build_automation(trigger, func_args, conf)
 | 
				
			||||||
@@ -142,7 +155,7 @@ async def to_code(config):
 | 
				
			|||||||
        decoded = base64.b64decode(encryption_config[CONF_KEY])
 | 
					        decoded = base64.b64decode(encryption_config[CONF_KEY])
 | 
				
			||||||
        cg.add(var.set_noise_psk(list(decoded)))
 | 
					        cg.add(var.set_noise_psk(list(decoded)))
 | 
				
			||||||
        cg.add_define("USE_API_NOISE")
 | 
					        cg.add_define("USE_API_NOISE")
 | 
				
			||||||
        cg.add_library("esphome/noise-c", "0.1.4")
 | 
					        cg.add_library("esphome/noise-c", "0.1.6")
 | 
				
			||||||
    else:
 | 
					    else:
 | 
				
			||||||
        cg.add_define("USE_API_PLAINTEXT")
 | 
					        cg.add_define("USE_API_PLAINTEXT")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -152,28 +165,43 @@ async def to_code(config):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
KEY_VALUE_SCHEMA = cv.Schema({cv.string: cv.templatable(cv.string_strict)})
 | 
					KEY_VALUE_SCHEMA = cv.Schema({cv.string: cv.templatable(cv.string_strict)})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
HOMEASSISTANT_SERVICE_ACTION_SCHEMA = cv.Schema(
 | 
					
 | 
				
			||||||
    {
 | 
					HOMEASSISTANT_ACTION_ACTION_SCHEMA = cv.All(
 | 
				
			||||||
        cv.GenerateID(): cv.use_id(APIServer),
 | 
					    cv.Schema(
 | 
				
			||||||
        cv.Required(CONF_SERVICE): cv.templatable(cv.string),
 | 
					        {
 | 
				
			||||||
        cv.Optional(CONF_DATA, default={}): KEY_VALUE_SCHEMA,
 | 
					            cv.GenerateID(): cv.use_id(APIServer),
 | 
				
			||||||
        cv.Optional(CONF_DATA_TEMPLATE, default={}): KEY_VALUE_SCHEMA,
 | 
					            cv.Exclusive(CONF_SERVICE, group_of_exclusion=CONF_ACTION): cv.templatable(
 | 
				
			||||||
        cv.Optional(CONF_VARIABLES, default={}): cv.Schema(
 | 
					                cv.string
 | 
				
			||||||
            {cv.string: cv.returning_lambda}
 | 
					            ),
 | 
				
			||||||
        ),
 | 
					            cv.Exclusive(CONF_ACTION, group_of_exclusion=CONF_ACTION): cv.templatable(
 | 
				
			||||||
    }
 | 
					                cv.string
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_DATA, default={}): KEY_VALUE_SCHEMA,
 | 
				
			||||||
 | 
					            cv.Optional(CONF_DATA_TEMPLATE, default={}): KEY_VALUE_SCHEMA,
 | 
				
			||||||
 | 
					            cv.Optional(CONF_VARIABLES, default={}): cv.Schema(
 | 
				
			||||||
 | 
					                {cv.string: cv.returning_lambda}
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    cv.has_exactly_one_key(CONF_SERVICE, CONF_ACTION),
 | 
				
			||||||
 | 
					    cv.rename_key(CONF_SERVICE, CONF_ACTION),
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@automation.register_action(
 | 
				
			||||||
 | 
					    "homeassistant.action",
 | 
				
			||||||
 | 
					    HomeAssistantServiceCallAction,
 | 
				
			||||||
 | 
					    HOMEASSISTANT_ACTION_ACTION_SCHEMA,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
@automation.register_action(
 | 
					@automation.register_action(
 | 
				
			||||||
    "homeassistant.service",
 | 
					    "homeassistant.service",
 | 
				
			||||||
    HomeAssistantServiceCallAction,
 | 
					    HomeAssistantServiceCallAction,
 | 
				
			||||||
    HOMEASSISTANT_SERVICE_ACTION_SCHEMA,
 | 
					    HOMEASSISTANT_ACTION_ACTION_SCHEMA,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
async def homeassistant_service_to_code(config, action_id, template_arg, args):
 | 
					async def homeassistant_service_to_code(config, action_id, template_arg, args):
 | 
				
			||||||
    serv = await cg.get_variable(config[CONF_ID])
 | 
					    serv = await cg.get_variable(config[CONF_ID])
 | 
				
			||||||
    var = cg.new_Pvariable(action_id, template_arg, serv, False)
 | 
					    var = cg.new_Pvariable(action_id, template_arg, serv, False)
 | 
				
			||||||
    templ = await cg.templatable(config[CONF_SERVICE], args, None)
 | 
					    templ = await cg.templatable(config[CONF_ACTION], args, None)
 | 
				
			||||||
    cg.add(var.set_service(templ))
 | 
					    cg.add(var.set_service(templ))
 | 
				
			||||||
    for key, value in config[CONF_DATA].items():
 | 
					    for key, value in config[CONF_DATA].items():
 | 
				
			||||||
        templ = await cg.templatable(value, args, None)
 | 
					        templ = await cg.templatable(value, args, None)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -48,6 +48,7 @@ service APIConnection {
 | 
				
			|||||||
  rpc date_command (DateCommandRequest) returns (void) {}
 | 
					  rpc date_command (DateCommandRequest) returns (void) {}
 | 
				
			||||||
  rpc time_command (TimeCommandRequest) returns (void) {}
 | 
					  rpc time_command (TimeCommandRequest) returns (void) {}
 | 
				
			||||||
  rpc datetime_command (DateTimeCommandRequest) returns (void) {}
 | 
					  rpc datetime_command (DateTimeCommandRequest) returns (void) {}
 | 
				
			||||||
 | 
					  rpc update_command (UpdateCommandRequest) returns (void) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  rpc subscribe_bluetooth_le_advertisements(SubscribeBluetoothLEAdvertisementsRequest) returns (void) {}
 | 
					  rpc subscribe_bluetooth_le_advertisements(SubscribeBluetoothLEAdvertisementsRequest) returns (void) {}
 | 
				
			||||||
  rpc bluetooth_device_request(BluetoothDeviceRequest) returns (void) {}
 | 
					  rpc bluetooth_device_request(BluetoothDeviceRequest) returns (void) {}
 | 
				
			||||||
@@ -685,6 +686,7 @@ message SubscribeHomeAssistantStateResponse {
 | 
				
			|||||||
  option (source) = SOURCE_SERVER;
 | 
					  option (source) = SOURCE_SERVER;
 | 
				
			||||||
  string entity_id = 1;
 | 
					  string entity_id = 1;
 | 
				
			||||||
  string attribute = 2;
 | 
					  string attribute = 2;
 | 
				
			||||||
 | 
					  bool once = 3;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
message HomeAssistantStateResponse {
 | 
					message HomeAssistantStateResponse {
 | 
				
			||||||
@@ -1517,6 +1519,25 @@ message VoiceAssistantAudio {
 | 
				
			|||||||
  bool end = 2;
 | 
					  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 ====================
 | 
					// ==================== ALARM CONTROL PANEL ====================
 | 
				
			||||||
enum AlarmControlPanelState {
 | 
					enum AlarmControlPanelState {
 | 
				
			||||||
@@ -1818,3 +1839,51 @@ message DateTimeCommandRequest {
 | 
				
			|||||||
  fixed32 key = 1;
 | 
					  fixed32 key = 1;
 | 
				
			||||||
  fixed32 epoch_seconds = 2;
 | 
					  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;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					enum UpdateCommand {
 | 
				
			||||||
 | 
					  UPDATE_COMMAND_NONE = 0;
 | 
				
			||||||
 | 
					  UPDATE_COMMAND_UPDATE = 1;
 | 
				
			||||||
 | 
					  UPDATE_COMMAND_CHECK = 2;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					message UpdateCommandRequest {
 | 
				
			||||||
 | 
					  option (id) = 118;
 | 
				
			||||||
 | 
					  option (source) = SOURCE_CLIENT;
 | 
				
			||||||
 | 
					  option (ifdef) = "USE_UPDATE";
 | 
				
			||||||
 | 
					  option (no_delay) = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  fixed32 key = 1;
 | 
				
			||||||
 | 
					  UpdateCommand command = 2;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1193,6 +1193,15 @@ void APIConnection::on_voice_assistant_audio(const VoiceAssistantAudio &msg) {
 | 
				
			|||||||
    voice_assistant::global_voice_assistant->on_audio(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
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1278,6 +1287,64 @@ bool APIConnection::send_event_info(event::Event *event) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  switch (msg.command) {
 | 
				
			||||||
 | 
					    case enums::UPDATE_COMMAND_UPDATE:
 | 
				
			||||||
 | 
					      update->perform();
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    case enums::UPDATE_COMMAND_CHECK:
 | 
				
			||||||
 | 
					      update->check();
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    case enums::UPDATE_COMMAND_NONE:
 | 
				
			||||||
 | 
					      ESP_LOGE(TAG, "UPDATE_COMMAND_NONE not handled. Check client is sending the correct command");
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					      ESP_LOGW(TAG, "Unknown update command: %" PRIu32, msg.command);
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool APIConnection::send_log_message(int level, const char *tag, const char *line) {
 | 
					bool APIConnection::send_log_message(int level, const char *tag, const char *line) {
 | 
				
			||||||
  if (this->log_subscription_ < level)
 | 
					  if (this->log_subscription_ < level)
 | 
				
			||||||
    return false;
 | 
					    return false;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -150,6 +150,7 @@ class APIConnection : public APIServerConnection {
 | 
				
			|||||||
  void on_voice_assistant_response(const VoiceAssistantResponse &msg) override;
 | 
					  void on_voice_assistant_response(const VoiceAssistantResponse &msg) override;
 | 
				
			||||||
  void on_voice_assistant_event_response(const VoiceAssistantEventResponse &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_audio(const VoiceAssistantAudio &msg) override;
 | 
				
			||||||
 | 
					  void on_voice_assistant_timer_event_response(const VoiceAssistantTimerEventResponse &msg) override;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef USE_ALARM_CONTROL_PANEL
 | 
					#ifdef USE_ALARM_CONTROL_PANEL
 | 
				
			||||||
@@ -163,6 +164,12 @@ class APIConnection : public APIServerConnection {
 | 
				
			|||||||
  bool send_event_info(event::Event *event);
 | 
					  bool send_event_info(event::Event *event);
 | 
				
			||||||
#endif
 | 
					#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_disconnect_response(const DisconnectResponse &value) override;
 | 
				
			||||||
  void on_ping_response(const PingResponse &value) override {
 | 
					  void on_ping_response(const PingResponse &value) override {
 | 
				
			||||||
    // we initiated ping
 | 
					    // we initiated ping
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -475,6 +475,22 @@ template<> const char *proto_enum_to_string<enums::VoiceAssistantEvent>(enums::V
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
					#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) {
 | 
					template<> const char *proto_enum_to_string<enums::AlarmControlPanelState>(enums::AlarmControlPanelState value) {
 | 
				
			||||||
  switch (value) {
 | 
					  switch (value) {
 | 
				
			||||||
    case enums::ALARM_STATE_DISARMED:
 | 
					    case enums::ALARM_STATE_DISARMED:
 | 
				
			||||||
@@ -551,6 +567,20 @@ template<> const char *proto_enum_to_string<enums::ValveOperation>(enums::ValveO
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
				
			||||||
 | 
					template<> const char *proto_enum_to_string<enums::UpdateCommand>(enums::UpdateCommand value) {
 | 
				
			||||||
 | 
					  switch (value) {
 | 
				
			||||||
 | 
					    case enums::UPDATE_COMMAND_NONE:
 | 
				
			||||||
 | 
					      return "UPDATE_COMMAND_NONE";
 | 
				
			||||||
 | 
					    case enums::UPDATE_COMMAND_UPDATE:
 | 
				
			||||||
 | 
					      return "UPDATE_COMMAND_UPDATE";
 | 
				
			||||||
 | 
					    case enums::UPDATE_COMMAND_CHECK:
 | 
				
			||||||
 | 
					      return "UPDATE_COMMAND_CHECK";
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					      return "UNKNOWN";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
bool HelloRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
					bool HelloRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
				
			||||||
  switch (field_id) {
 | 
					  switch (field_id) {
 | 
				
			||||||
    case 2: {
 | 
					    case 2: {
 | 
				
			||||||
@@ -3079,6 +3109,16 @@ void SubscribeHomeAssistantStatesRequest::dump_to(std::string &out) const {
 | 
				
			|||||||
  out.append("SubscribeHomeAssistantStatesRequest {}");
 | 
					  out.append("SubscribeHomeAssistantStatesRequest {}");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					bool SubscribeHomeAssistantStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
				
			||||||
 | 
					  switch (field_id) {
 | 
				
			||||||
 | 
					    case 3: {
 | 
				
			||||||
 | 
					      this->once = value.as_bool();
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
bool SubscribeHomeAssistantStateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
 | 
					bool SubscribeHomeAssistantStateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
 | 
				
			||||||
  switch (field_id) {
 | 
					  switch (field_id) {
 | 
				
			||||||
    case 1: {
 | 
					    case 1: {
 | 
				
			||||||
@@ -3096,6 +3136,7 @@ bool SubscribeHomeAssistantStateResponse::decode_length(uint32_t field_id, Proto
 | 
				
			|||||||
void SubscribeHomeAssistantStateResponse::encode(ProtoWriteBuffer buffer) const {
 | 
					void SubscribeHomeAssistantStateResponse::encode(ProtoWriteBuffer buffer) const {
 | 
				
			||||||
  buffer.encode_string(1, this->entity_id);
 | 
					  buffer.encode_string(1, this->entity_id);
 | 
				
			||||||
  buffer.encode_string(2, this->attribute);
 | 
					  buffer.encode_string(2, this->attribute);
 | 
				
			||||||
 | 
					  buffer.encode_bool(3, this->once);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
					#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
				
			||||||
void SubscribeHomeAssistantStateResponse::dump_to(std::string &out) const {
 | 
					void SubscribeHomeAssistantStateResponse::dump_to(std::string &out) const {
 | 
				
			||||||
@@ -3108,6 +3149,10 @@ void SubscribeHomeAssistantStateResponse::dump_to(std::string &out) const {
 | 
				
			|||||||
  out.append("  attribute: ");
 | 
					  out.append("  attribute: ");
 | 
				
			||||||
  out.append("'").append(this->attribute).append("'");
 | 
					  out.append("'").append(this->attribute).append("'");
 | 
				
			||||||
  out.append("\n");
 | 
					  out.append("\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  out.append("  once: ");
 | 
				
			||||||
 | 
					  out.append(YESNO(this->once));
 | 
				
			||||||
 | 
					  out.append("\n");
 | 
				
			||||||
  out.append("}");
 | 
					  out.append("}");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
@@ -6857,6 +6902,82 @@ void VoiceAssistantAudio::dump_to(std::string &out) const {
 | 
				
			|||||||
  out.append("}");
 | 
					  out.append("}");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#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) {
 | 
					bool ListEntitiesAlarmControlPanelResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
				
			||||||
  switch (field_id) {
 | 
					  switch (field_id) {
 | 
				
			||||||
    case 6: {
 | 
					    case 6: {
 | 
				
			||||||
@@ -8284,6 +8405,262 @@ void DateTimeCommandRequest::dump_to(std::string &out) const {
 | 
				
			|||||||
  out.append("}");
 | 
					  out.append("}");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#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->command = value.as_enum<enums::UpdateCommand>();
 | 
				
			||||||
 | 
					      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_enum<enums::UpdateCommand>(2, this->command);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#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("  command: ");
 | 
				
			||||||
 | 
					  out.append(proto_enum_to_string<enums::UpdateCommand>(this->command));
 | 
				
			||||||
 | 
					  out.append("\n");
 | 
				
			||||||
 | 
					  out.append("}");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}  // namespace api
 | 
					}  // namespace api
 | 
				
			||||||
}  // namespace esphome
 | 
					}  // namespace esphome
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -191,6 +191,12 @@ enum VoiceAssistantEvent : uint32_t {
 | 
				
			|||||||
  VOICE_ASSISTANT_TTS_STREAM_START = 98,
 | 
					  VOICE_ASSISTANT_TTS_STREAM_START = 98,
 | 
				
			||||||
  VOICE_ASSISTANT_TTS_STREAM_END = 99,
 | 
					  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 {
 | 
					enum AlarmControlPanelState : uint32_t {
 | 
				
			||||||
  ALARM_STATE_DISARMED = 0,
 | 
					  ALARM_STATE_DISARMED = 0,
 | 
				
			||||||
  ALARM_STATE_ARMED_HOME = 1,
 | 
					  ALARM_STATE_ARMED_HOME = 1,
 | 
				
			||||||
@@ -221,6 +227,11 @@ enum ValveOperation : uint32_t {
 | 
				
			|||||||
  VALVE_OPERATION_IS_OPENING = 1,
 | 
					  VALVE_OPERATION_IS_OPENING = 1,
 | 
				
			||||||
  VALVE_OPERATION_IS_CLOSING = 2,
 | 
					  VALVE_OPERATION_IS_CLOSING = 2,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					enum UpdateCommand : uint32_t {
 | 
				
			||||||
 | 
					  UPDATE_COMMAND_NONE = 0,
 | 
				
			||||||
 | 
					  UPDATE_COMMAND_UPDATE = 1,
 | 
				
			||||||
 | 
					  UPDATE_COMMAND_CHECK = 2,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}  // namespace enums
 | 
					}  // namespace enums
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -825,6 +836,7 @@ class SubscribeHomeAssistantStateResponse : public ProtoMessage {
 | 
				
			|||||||
 public:
 | 
					 public:
 | 
				
			||||||
  std::string entity_id{};
 | 
					  std::string entity_id{};
 | 
				
			||||||
  std::string attribute{};
 | 
					  std::string attribute{};
 | 
				
			||||||
 | 
					  bool once{false};
 | 
				
			||||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
					  void encode(ProtoWriteBuffer buffer) const override;
 | 
				
			||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
					#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
				
			||||||
  void dump_to(std::string &out) const override;
 | 
					  void dump_to(std::string &out) const override;
 | 
				
			||||||
@@ -832,6 +844,7 @@ class SubscribeHomeAssistantStateResponse : public ProtoMessage {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 protected:
 | 
					 protected:
 | 
				
			||||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
					  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
				
			||||||
 | 
					  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
class HomeAssistantStateResponse : public ProtoMessage {
 | 
					class HomeAssistantStateResponse : public ProtoMessage {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
@@ -1775,6 +1788,23 @@ class VoiceAssistantAudio : public ProtoMessage {
 | 
				
			|||||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
					  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
				
			||||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
					  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					class 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 {
 | 
					class ListEntitiesAlarmControlPanelResponse : public ProtoMessage {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  std::string object_id{};
 | 
					  std::string object_id{};
 | 
				
			||||||
@@ -2107,6 +2137,61 @@ class DateTimeCommandRequest : public ProtoMessage {
 | 
				
			|||||||
 protected:
 | 
					 protected:
 | 
				
			||||||
  bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
 | 
					  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};
 | 
				
			||||||
 | 
					  enums::UpdateCommand command{};
 | 
				
			||||||
 | 
					  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 api
 | 
				
			||||||
}  // namespace esphome
 | 
					}  // namespace esphome
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -484,6 +484,8 @@ bool APIServerConnectionBase::send_voice_assistant_audio(const VoiceAssistantAud
 | 
				
			|||||||
  return this->send_message_<VoiceAssistantAudio>(msg, 106);
 | 
					  return this->send_message_<VoiceAssistantAudio>(msg, 106);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					#ifdef USE_VOICE_ASSISTANT
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
#ifdef USE_ALARM_CONTROL_PANEL
 | 
					#ifdef USE_ALARM_CONTROL_PANEL
 | 
				
			||||||
bool APIServerConnectionBase::send_list_entities_alarm_control_panel_response(
 | 
					bool APIServerConnectionBase::send_list_entities_alarm_control_panel_response(
 | 
				
			||||||
    const ListEntitiesAlarmControlPanelResponse &msg) {
 | 
					    const ListEntitiesAlarmControlPanelResponse &msg) {
 | 
				
			||||||
@@ -609,6 +611,24 @@ bool APIServerConnectionBase::send_date_time_state_response(const DateTimeStateR
 | 
				
			|||||||
#endif
 | 
					#endif
 | 
				
			||||||
#ifdef USE_DATETIME_DATETIME
 | 
					#ifdef USE_DATETIME_DATETIME
 | 
				
			||||||
#endif
 | 
					#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) {
 | 
					bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {
 | 
				
			||||||
  switch (msg_type) {
 | 
					  switch (msg_type) {
 | 
				
			||||||
    case 1: {
 | 
					    case 1: {
 | 
				
			||||||
@@ -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());
 | 
					      ESP_LOGVV(TAG, "on_date_time_command_request: %s", msg.dump().c_str());
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
      this->on_date_time_command_request(msg);
 | 
					      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
 | 
					#endif
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -1421,6 +1463,19 @@ void APIServerConnection::on_date_time_command_request(const DateTimeCommandRequ
 | 
				
			|||||||
  this->datetime_command(msg);
 | 
					  this->datetime_command(msg);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#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
 | 
					#ifdef USE_BLUETOOTH_PROXY
 | 
				
			||||||
void APIServerConnection::on_subscribe_bluetooth_le_advertisements_request(
 | 
					void APIServerConnection::on_subscribe_bluetooth_le_advertisements_request(
 | 
				
			||||||
    const SubscribeBluetoothLEAdvertisementsRequest &msg) {
 | 
					    const SubscribeBluetoothLEAdvertisementsRequest &msg) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -244,6 +244,9 @@ class APIServerConnectionBase : public ProtoService {
 | 
				
			|||||||
  bool send_voice_assistant_audio(const VoiceAssistantAudio &msg);
 | 
					  bool send_voice_assistant_audio(const VoiceAssistantAudio &msg);
 | 
				
			||||||
  virtual void on_voice_assistant_audio(const VoiceAssistantAudio &value){};
 | 
					  virtual void on_voice_assistant_audio(const VoiceAssistantAudio &value){};
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					#ifdef USE_VOICE_ASSISTANT
 | 
				
			||||||
 | 
					  virtual void on_voice_assistant_timer_event_response(const VoiceAssistantTimerEventResponse &value){};
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
#ifdef USE_ALARM_CONTROL_PANEL
 | 
					#ifdef USE_ALARM_CONTROL_PANEL
 | 
				
			||||||
  bool send_list_entities_alarm_control_panel_response(const ListEntitiesAlarmControlPanelResponse &msg);
 | 
					  bool send_list_entities_alarm_control_panel_response(const ListEntitiesAlarmControlPanelResponse &msg);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
@@ -303,6 +306,15 @@ class APIServerConnectionBase : public ProtoService {
 | 
				
			|||||||
#endif
 | 
					#endif
 | 
				
			||||||
#ifdef USE_DATETIME_DATETIME
 | 
					#ifdef USE_DATETIME_DATETIME
 | 
				
			||||||
  virtual void on_date_time_command_request(const DateTimeCommandRequest &value){};
 | 
					  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
 | 
					#endif
 | 
				
			||||||
 protected:
 | 
					 protected:
 | 
				
			||||||
  bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;
 | 
					  bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;
 | 
				
			||||||
@@ -370,6 +382,9 @@ class APIServerConnection : public APIServerConnectionBase {
 | 
				
			|||||||
#ifdef USE_DATETIME_DATETIME
 | 
					#ifdef USE_DATETIME_DATETIME
 | 
				
			||||||
  virtual void datetime_command(const DateTimeCommandRequest &msg) = 0;
 | 
					  virtual void datetime_command(const DateTimeCommandRequest &msg) = 0;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					#ifdef USE_UPDATE
 | 
				
			||||||
 | 
					  virtual void update_command(const UpdateCommandRequest &msg) = 0;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
#ifdef USE_BLUETOOTH_PROXY
 | 
					#ifdef USE_BLUETOOTH_PROXY
 | 
				
			||||||
  virtual void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) = 0;
 | 
					  virtual void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) = 0;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
@@ -468,6 +483,9 @@ class APIServerConnection : public APIServerConnectionBase {
 | 
				
			|||||||
#ifdef USE_DATETIME_DATETIME
 | 
					#ifdef USE_DATETIME_DATETIME
 | 
				
			||||||
  void on_date_time_command_request(const DateTimeCommandRequest &msg) override;
 | 
					  void on_date_time_command_request(const DateTimeCommandRequest &msg) override;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					#ifdef USE_UPDATE
 | 
				
			||||||
 | 
					  void on_update_command_request(const UpdateCommandRequest &msg) override;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
#ifdef USE_BLUETOOTH_PROXY
 | 
					#ifdef USE_BLUETOOTH_PROXY
 | 
				
			||||||
  void on_subscribe_bluetooth_le_advertisements_request(const SubscribeBluetoothLEAdvertisementsRequest &msg) override;
 | 
					  void on_subscribe_bluetooth_le_advertisements_request(const SubscribeBluetoothLEAdvertisementsRequest &msg) override;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -334,6 +334,13 @@ void APIServer::on_event(event::Event *obj, const std::string &event_type) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#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; }
 | 
					float APIServer::get_setup_priority() const { return setup_priority::AFTER_WIFI; }
 | 
				
			||||||
void APIServer::set_port(uint16_t port) { this->port_ = port; }
 | 
					void APIServer::set_port(uint16_t port) { this->port_ = port; }
 | 
				
			||||||
APIServer *global_api_server = nullptr;  // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
 | 
					APIServer *global_api_server = nullptr;  // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
 | 
				
			||||||
@@ -352,8 +359,18 @@ void APIServer::subscribe_home_assistant_state(std::string entity_id, optional<s
 | 
				
			|||||||
      .entity_id = std::move(entity_id),
 | 
					      .entity_id = std::move(entity_id),
 | 
				
			||||||
      .attribute = std::move(attribute),
 | 
					      .attribute = std::move(attribute),
 | 
				
			||||||
      .callback = std::move(f),
 | 
					      .callback = std::move(f),
 | 
				
			||||||
 | 
					      .once = false,
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					void APIServer::get_home_assistant_state(std::string entity_id, optional<std::string> attribute,
 | 
				
			||||||
 | 
					                                         std::function<void(std::string)> f) {
 | 
				
			||||||
 | 
					  this->state_subs_.push_back(HomeAssistantStateSubscription{
 | 
				
			||||||
 | 
					      .entity_id = std::move(entity_id),
 | 
				
			||||||
 | 
					      .attribute = std::move(attribute),
 | 
				
			||||||
 | 
					      .callback = std::move(f),
 | 
				
			||||||
 | 
					      .once = true,
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
const std::vector<APIServer::HomeAssistantStateSubscription> &APIServer::get_state_subs() const {
 | 
					const std::vector<APIServer::HomeAssistantStateSubscription> &APIServer::get_state_subs() const {
 | 
				
			||||||
  return this->state_subs_;
 | 
					  return this->state_subs_;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -102,6 +102,9 @@ class APIServer : public Component, public Controller {
 | 
				
			|||||||
#ifdef USE_EVENT
 | 
					#ifdef USE_EVENT
 | 
				
			||||||
  void on_event(event::Event *obj, const std::string &event_type) override;
 | 
					  void on_event(event::Event *obj, const std::string &event_type) override;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					#ifdef USE_UPDATE
 | 
				
			||||||
 | 
					  void on_update(update::UpdateEntity *obj) override;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  bool is_connected() const;
 | 
					  bool is_connected() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -109,10 +112,13 @@ class APIServer : public Component, public Controller {
 | 
				
			|||||||
    std::string entity_id;
 | 
					    std::string entity_id;
 | 
				
			||||||
    optional<std::string> attribute;
 | 
					    optional<std::string> attribute;
 | 
				
			||||||
    std::function<void(std::string)> callback;
 | 
					    std::function<void(std::string)> callback;
 | 
				
			||||||
 | 
					    bool once;
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute,
 | 
					  void subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute,
 | 
				
			||||||
                                      std::function<void(std::string)> f);
 | 
					                                      std::function<void(std::string)> f);
 | 
				
			||||||
 | 
					  void get_home_assistant_state(std::string entity_id, optional<std::string> attribute,
 | 
				
			||||||
 | 
					                                std::function<void(std::string)> f);
 | 
				
			||||||
  const std::vector<HomeAssistantStateSubscription> &get_state_subs() const;
 | 
					  const std::vector<HomeAssistantStateSubscription> &get_state_subs() const;
 | 
				
			||||||
  const std::vector<UserServiceDescriptor *> &get_user_services() const { return this->user_services_; }
 | 
					  const std::vector<UserServiceDescriptor *> &get_user_services() const { return this->user_services_; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -105,7 +105,7 @@ class CustomAPIDevice {
 | 
				
			|||||||
  /** Subscribe to the state (or attribute state) of an entity from Home Assistant.
 | 
					  /** Subscribe to the state (or attribute state) of an entity from Home Assistant.
 | 
				
			||||||
   *
 | 
					   *
 | 
				
			||||||
   * Usage:
 | 
					   * Usage:
 | 
				
			||||||
   *å
 | 
					   *
 | 
				
			||||||
   * ```cpp
 | 
					   * ```cpp
 | 
				
			||||||
   * void setup() override {
 | 
					   * void setup() override {
 | 
				
			||||||
   *   subscribe_homeassistant_state(&CustomNativeAPI::on_state_changed, "sensor.weather_forecast");
 | 
					   *   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
 | 
					#ifdef USE_EVENT
 | 
				
			||||||
bool ListEntitiesIterator::on_event(event::Event *event) { return this->client_->send_event_info(event); }
 | 
					bool ListEntitiesIterator::on_event(event::Event *event) { return this->client_->send_event_info(event); }
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					#ifdef USE_UPDATE
 | 
				
			||||||
 | 
					bool ListEntitiesIterator::on_update(update::UpdateEntity *update) { return this->client_->send_update_info(update); }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}  // namespace api
 | 
					}  // namespace api
 | 
				
			||||||
}  // namespace esphome
 | 
					}  // namespace esphome
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -75,6 +75,9 @@ class ListEntitiesIterator : public ComponentIterator {
 | 
				
			|||||||
#endif
 | 
					#endif
 | 
				
			||||||
#ifdef USE_EVENT
 | 
					#ifdef USE_EVENT
 | 
				
			||||||
  bool on_event(event::Event *event) override;
 | 
					  bool on_event(event::Event *event) override;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#ifdef USE_UPDATE
 | 
				
			||||||
 | 
					  bool on_update(update::UpdateEntity *update) override;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
  bool on_end() override;
 | 
					  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);
 | 
					  return this->client_->send_alarm_control_panel_state(a_alarm_control_panel);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#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) {}
 | 
					InitialStateIterator::InitialStateIterator(APIConnection *client) : client_(client) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}  // namespace api
 | 
					}  // namespace api
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -72,6 +72,9 @@ class InitialStateIterator : public ComponentIterator {
 | 
				
			|||||||
#endif
 | 
					#endif
 | 
				
			||||||
#ifdef USE_EVENT
 | 
					#ifdef USE_EVENT
 | 
				
			||||||
  bool on_event(event::Event *event) override { return true; };
 | 
					  bool on_event(event::Event *event) override { return true; };
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#ifdef USE_UPDATE
 | 
				
			||||||
 | 
					  bool on_update(update::UpdateEntity *update) override;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 protected:
 | 
					 protected:
 | 
				
			||||||
  APIConnection *client_;
 | 
					  APIConnection *client_;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					import esphome.codegen as cg
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CODEOWNERS = ["@circuitsetup", "@descipher"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					atm90e32_ns = cg.esphome_ns.namespace("atm90e32")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CONF_ATM90E32_ID = "atm90e32_id"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -132,10 +132,77 @@ void ATM90E32Component::update() {
 | 
				
			|||||||
  this->status_clear_warning();
 | 
					  this->status_clear_warning();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ATM90E32Component::restore_calibrations_() {
 | 
				
			||||||
 | 
					  if (enable_offset_calibration_) {
 | 
				
			||||||
 | 
					    this->pref_.load(&this->offset_phase_);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ATM90E32Component::run_offset_calibrations() {
 | 
				
			||||||
 | 
					  // Run the calibrations and
 | 
				
			||||||
 | 
					  // Setup voltage and current calibration offsets for PHASE A
 | 
				
			||||||
 | 
					  this->offset_phase_[PHASEA].voltage_offset_ = calibrate_voltage_offset_phase(PHASEA);
 | 
				
			||||||
 | 
					  this->phase_[PHASEA].voltage_offset_ = this->offset_phase_[PHASEA].voltage_offset_;
 | 
				
			||||||
 | 
					  this->write16_(ATM90E32_REGISTER_UOFFSETA, this->phase_[PHASEA].voltage_offset_);  // C Voltage offset
 | 
				
			||||||
 | 
					  this->offset_phase_[PHASEA].current_offset_ = calibrate_current_offset_phase(PHASEA);
 | 
				
			||||||
 | 
					  this->phase_[PHASEA].current_offset_ = this->offset_phase_[PHASEA].current_offset_;
 | 
				
			||||||
 | 
					  this->write16_(ATM90E32_REGISTER_IOFFSETA, this->phase_[PHASEA].current_offset_);  // C Current offset
 | 
				
			||||||
 | 
					  // Setup voltage and current calibration offsets for PHASE B
 | 
				
			||||||
 | 
					  this->offset_phase_[PHASEB].voltage_offset_ = calibrate_voltage_offset_phase(PHASEB);
 | 
				
			||||||
 | 
					  this->phase_[PHASEB].voltage_offset_ = this->offset_phase_[PHASEB].voltage_offset_;
 | 
				
			||||||
 | 
					  this->write16_(ATM90E32_REGISTER_UOFFSETB, this->phase_[PHASEB].voltage_offset_);  // C Voltage offset
 | 
				
			||||||
 | 
					  this->offset_phase_[PHASEB].current_offset_ = calibrate_current_offset_phase(PHASEB);
 | 
				
			||||||
 | 
					  this->phase_[PHASEB].current_offset_ = this->offset_phase_[PHASEB].current_offset_;
 | 
				
			||||||
 | 
					  this->write16_(ATM90E32_REGISTER_IOFFSETB, this->phase_[PHASEB].current_offset_);  // C Current offset
 | 
				
			||||||
 | 
					  // Setup voltage and current calibration offsets for PHASE C
 | 
				
			||||||
 | 
					  this->offset_phase_[PHASEC].voltage_offset_ = calibrate_voltage_offset_phase(PHASEC);
 | 
				
			||||||
 | 
					  this->phase_[PHASEC].voltage_offset_ = this->offset_phase_[PHASEC].voltage_offset_;
 | 
				
			||||||
 | 
					  this->write16_(ATM90E32_REGISTER_UOFFSETC, this->phase_[PHASEC].voltage_offset_);  // C Voltage offset
 | 
				
			||||||
 | 
					  this->offset_phase_[PHASEC].current_offset_ = calibrate_current_offset_phase(PHASEC);
 | 
				
			||||||
 | 
					  this->phase_[PHASEC].current_offset_ = this->offset_phase_[PHASEC].current_offset_;
 | 
				
			||||||
 | 
					  this->write16_(ATM90E32_REGISTER_IOFFSETC, this->phase_[PHASEC].current_offset_);  // C Current offset
 | 
				
			||||||
 | 
					  this->pref_.save(&this->offset_phase_);
 | 
				
			||||||
 | 
					  ESP_LOGI(TAG, "PhaseA Vo=%5d PhaseB Vo=%5d PhaseC Vo=%5d", this->offset_phase_[PHASEA].voltage_offset_,
 | 
				
			||||||
 | 
					           this->offset_phase_[PHASEB].voltage_offset_, this->offset_phase_[PHASEC].voltage_offset_);
 | 
				
			||||||
 | 
					  ESP_LOGI(TAG, "PhaseA Io=%5d PhaseB Io=%5d PhaseC Io=%5d", this->offset_phase_[PHASEA].current_offset_,
 | 
				
			||||||
 | 
					           this->offset_phase_[PHASEB].current_offset_, this->offset_phase_[PHASEC].current_offset_);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ATM90E32Component::clear_offset_calibrations() {
 | 
				
			||||||
 | 
					  // Clear the calibrations and
 | 
				
			||||||
 | 
					  this->offset_phase_[PHASEA].voltage_offset_ = 0;
 | 
				
			||||||
 | 
					  this->phase_[PHASEA].voltage_offset_ = this->offset_phase_[PHASEA].voltage_offset_;
 | 
				
			||||||
 | 
					  this->write16_(ATM90E32_REGISTER_UOFFSETA, this->phase_[PHASEA].voltage_offset_);  // C Voltage offset
 | 
				
			||||||
 | 
					  this->offset_phase_[PHASEA].current_offset_ = 0;
 | 
				
			||||||
 | 
					  this->phase_[PHASEA].current_offset_ = this->offset_phase_[PHASEA].current_offset_;
 | 
				
			||||||
 | 
					  this->write16_(ATM90E32_REGISTER_IOFFSETA, this->phase_[PHASEA].current_offset_);  // C Current offset
 | 
				
			||||||
 | 
					  this->offset_phase_[PHASEB].voltage_offset_ = 0;
 | 
				
			||||||
 | 
					  this->phase_[PHASEB].voltage_offset_ = this->offset_phase_[PHASEB].voltage_offset_;
 | 
				
			||||||
 | 
					  this->write16_(ATM90E32_REGISTER_UOFFSETB, this->phase_[PHASEB].voltage_offset_);  // C Voltage offset
 | 
				
			||||||
 | 
					  this->offset_phase_[PHASEB].current_offset_ = 0;
 | 
				
			||||||
 | 
					  this->phase_[PHASEB].current_offset_ = this->offset_phase_[PHASEB].current_offset_;
 | 
				
			||||||
 | 
					  this->write16_(ATM90E32_REGISTER_IOFFSETB, this->phase_[PHASEB].current_offset_);  // C Current offset
 | 
				
			||||||
 | 
					  this->offset_phase_[PHASEC].voltage_offset_ = 0;
 | 
				
			||||||
 | 
					  this->phase_[PHASEC].voltage_offset_ = this->offset_phase_[PHASEC].voltage_offset_;
 | 
				
			||||||
 | 
					  this->write16_(ATM90E32_REGISTER_UOFFSETC, this->phase_[PHASEC].voltage_offset_);  // C Voltage offset
 | 
				
			||||||
 | 
					  this->offset_phase_[PHASEC].current_offset_ = 0;
 | 
				
			||||||
 | 
					  this->phase_[PHASEC].current_offset_ = this->offset_phase_[PHASEC].current_offset_;
 | 
				
			||||||
 | 
					  this->write16_(ATM90E32_REGISTER_IOFFSETC, this->phase_[PHASEC].current_offset_);  // C Current offset
 | 
				
			||||||
 | 
					  this->pref_.save(&this->offset_phase_);
 | 
				
			||||||
 | 
					  ESP_LOGI(TAG, "PhaseA Vo=%5d PhaseB Vo=%5d PhaseC Vo=%5d", this->offset_phase_[PHASEA].voltage_offset_,
 | 
				
			||||||
 | 
					           this->offset_phase_[PHASEB].voltage_offset_, this->offset_phase_[PHASEC].voltage_offset_);
 | 
				
			||||||
 | 
					  ESP_LOGI(TAG, "PhaseA Io=%5d PhaseB Io=%5d PhaseC Io=%5d", this->offset_phase_[PHASEA].current_offset_,
 | 
				
			||||||
 | 
					           this->offset_phase_[PHASEB].current_offset_, this->offset_phase_[PHASEC].current_offset_);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void ATM90E32Component::setup() {
 | 
					void ATM90E32Component::setup() {
 | 
				
			||||||
  ESP_LOGCONFIG(TAG, "Setting up ATM90E32 Component...");
 | 
					  ESP_LOGCONFIG(TAG, "Setting up ATM90E32 Component...");
 | 
				
			||||||
  this->spi_setup();
 | 
					  this->spi_setup();
 | 
				
			||||||
 | 
					  if (this->enable_offset_calibration_) {
 | 
				
			||||||
 | 
					    uint32_t hash = fnv1_hash(App.get_friendly_name());
 | 
				
			||||||
 | 
					    this->pref_ = global_preferences->make_preference<Calibration[3]>(hash, true);
 | 
				
			||||||
 | 
					    this->restore_calibrations_();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  uint16_t mmode0 = 0x87;  // 3P4W 50Hz
 | 
					  uint16_t mmode0 = 0x87;  // 3P4W 50Hz
 | 
				
			||||||
  if (line_freq_ == 60) {
 | 
					  if (line_freq_ == 60) {
 | 
				
			||||||
    mmode0 |= 1 << 12;  // sets 12th bit to 1, 60Hz
 | 
					    mmode0 |= 1 << 12;  // sets 12th bit to 1, 60Hz
 | 
				
			||||||
@@ -167,27 +234,12 @@ void ATM90E32Component::setup() {
 | 
				
			|||||||
  this->write16_(ATM90E32_REGISTER_SSTARTTH, 0x1D4C);       // All Reactive Startup Power Threshold - 50%
 | 
					  this->write16_(ATM90E32_REGISTER_SSTARTTH, 0x1D4C);       // All Reactive Startup Power Threshold - 50%
 | 
				
			||||||
  this->write16_(ATM90E32_REGISTER_PPHASETH, 0x02EE);       // Each Phase Active Phase Threshold - 0.002A/0.00032 = 750
 | 
					  this->write16_(ATM90E32_REGISTER_PPHASETH, 0x02EE);       // Each Phase Active Phase Threshold - 0.002A/0.00032 = 750
 | 
				
			||||||
  this->write16_(ATM90E32_REGISTER_QPHASETH, 0x02EE);       // Each phase Reactive Phase Threshold - 10%
 | 
					  this->write16_(ATM90E32_REGISTER_QPHASETH, 0x02EE);       // Each phase Reactive Phase Threshold - 10%
 | 
				
			||||||
  // Setup voltage and current calibration offsets for PHASE A
 | 
					 | 
				
			||||||
  this->phase_[PHASEA].voltage_offset_ = calibrate_voltage_offset_phase(PHASEA);
 | 
					 | 
				
			||||||
  this->write16_(ATM90E32_REGISTER_UOFFSETA, this->phase_[PHASEA].voltage_offset_);  // A Voltage offset
 | 
					 | 
				
			||||||
  this->phase_[PHASEA].current_offset_ = calibrate_current_offset_phase(PHASEA);
 | 
					 | 
				
			||||||
  this->write16_(ATM90E32_REGISTER_IOFFSETA, this->phase_[PHASEA].current_offset_);  // A Current offset
 | 
					 | 
				
			||||||
  // Setup voltage and current gain for PHASE A
 | 
					  // Setup voltage and current gain for PHASE A
 | 
				
			||||||
  this->write16_(ATM90E32_REGISTER_UGAINA, this->phase_[PHASEA].voltage_gain_);  // A Voltage rms gain
 | 
					  this->write16_(ATM90E32_REGISTER_UGAINA, this->phase_[PHASEA].voltage_gain_);  // A Voltage rms gain
 | 
				
			||||||
  this->write16_(ATM90E32_REGISTER_IGAINA, this->phase_[PHASEA].ct_gain_);       // A line current gain
 | 
					  this->write16_(ATM90E32_REGISTER_IGAINA, this->phase_[PHASEA].ct_gain_);       // A line current gain
 | 
				
			||||||
  // Setup voltage and current calibration offsets for PHASE B
 | 
					 | 
				
			||||||
  this->phase_[PHASEB].voltage_offset_ = calibrate_voltage_offset_phase(PHASEB);
 | 
					 | 
				
			||||||
  this->write16_(ATM90E32_REGISTER_UOFFSETB, this->phase_[PHASEB].voltage_offset_);  // B Voltage offset
 | 
					 | 
				
			||||||
  this->phase_[PHASEB].current_offset_ = calibrate_current_offset_phase(PHASEB);
 | 
					 | 
				
			||||||
  this->write16_(ATM90E32_REGISTER_IOFFSETB, this->phase_[PHASEB].current_offset_);  // B Current offset
 | 
					 | 
				
			||||||
  // Setup voltage and current gain for PHASE B
 | 
					  // Setup voltage and current gain for PHASE B
 | 
				
			||||||
  this->write16_(ATM90E32_REGISTER_UGAINB, this->phase_[PHASEB].voltage_gain_);  // B Voltage rms gain
 | 
					  this->write16_(ATM90E32_REGISTER_UGAINB, this->phase_[PHASEB].voltage_gain_);  // B Voltage rms gain
 | 
				
			||||||
  this->write16_(ATM90E32_REGISTER_IGAINB, this->phase_[PHASEB].ct_gain_);       // B line current gain
 | 
					  this->write16_(ATM90E32_REGISTER_IGAINB, this->phase_[PHASEB].ct_gain_);       // B line current gain
 | 
				
			||||||
  // Setup voltage and current calibration offsets for PHASE C
 | 
					 | 
				
			||||||
  this->phase_[PHASEC].voltage_offset_ = calibrate_voltage_offset_phase(PHASEC);
 | 
					 | 
				
			||||||
  this->write16_(ATM90E32_REGISTER_UOFFSETC, this->phase_[PHASEC].voltage_offset_);  // C Voltage offset
 | 
					 | 
				
			||||||
  this->phase_[PHASEC].current_offset_ = calibrate_current_offset_phase(PHASEC);
 | 
					 | 
				
			||||||
  this->write16_(ATM90E32_REGISTER_IOFFSETC, this->phase_[PHASEC].current_offset_);  // C Current offset
 | 
					 | 
				
			||||||
  // Setup voltage and current gain for PHASE C
 | 
					  // Setup voltage and current gain for PHASE C
 | 
				
			||||||
  this->write16_(ATM90E32_REGISTER_UGAINC, this->phase_[PHASEC].voltage_gain_);  // C Voltage rms gain
 | 
					  this->write16_(ATM90E32_REGISTER_UGAINC, this->phase_[PHASEC].voltage_gain_);  // C Voltage rms gain
 | 
				
			||||||
  this->write16_(ATM90E32_REGISTER_IGAINC, this->phase_[PHASEC].ct_gain_);       // C line current gain
 | 
					  this->write16_(ATM90E32_REGISTER_IGAINC, this->phase_[PHASEC].ct_gain_);       // C line current gain
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,12 @@
 | 
				
			|||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "esphome/core/component.h"
 | 
					#include "atm90e32_reg.h"
 | 
				
			||||||
#include "esphome/components/sensor/sensor.h"
 | 
					#include "esphome/components/sensor/sensor.h"
 | 
				
			||||||
#include "esphome/components/spi/spi.h"
 | 
					#include "esphome/components/spi/spi.h"
 | 
				
			||||||
#include "atm90e32_reg.h"
 | 
					#include "esphome/core/application.h"
 | 
				
			||||||
 | 
					#include "esphome/core/component.h"
 | 
				
			||||||
 | 
					#include "esphome/core/helpers.h"
 | 
				
			||||||
 | 
					#include "esphome/core/preferences.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace esphome {
 | 
					namespace esphome {
 | 
				
			||||||
namespace atm90e32 {
 | 
					namespace atm90e32 {
 | 
				
			||||||
@@ -20,7 +23,6 @@ class ATM90E32Component : public PollingComponent,
 | 
				
			|||||||
  void dump_config() override;
 | 
					  void dump_config() override;
 | 
				
			||||||
  float get_setup_priority() const override;
 | 
					  float get_setup_priority() const override;
 | 
				
			||||||
  void update() override;
 | 
					  void update() override;
 | 
				
			||||||
 | 
					 | 
				
			||||||
  void set_voltage_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].voltage_sensor_ = obj; }
 | 
					  void set_voltage_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].voltage_sensor_ = obj; }
 | 
				
			||||||
  void set_current_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].current_sensor_ = obj; }
 | 
					  void set_current_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].current_sensor_ = obj; }
 | 
				
			||||||
  void set_power_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].power_sensor_ = obj; }
 | 
					  void set_power_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].power_sensor_ = obj; }
 | 
				
			||||||
@@ -48,9 +50,11 @@ class ATM90E32Component : public PollingComponent,
 | 
				
			|||||||
  void set_line_freq(int freq) { line_freq_ = freq; }
 | 
					  void set_line_freq(int freq) { line_freq_ = freq; }
 | 
				
			||||||
  void set_current_phases(int phases) { current_phases_ = phases; }
 | 
					  void set_current_phases(int phases) { current_phases_ = phases; }
 | 
				
			||||||
  void set_pga_gain(uint16_t gain) { pga_gain_ = gain; }
 | 
					  void set_pga_gain(uint16_t gain) { pga_gain_ = gain; }
 | 
				
			||||||
 | 
					  void run_offset_calibrations();
 | 
				
			||||||
 | 
					  void clear_offset_calibrations();
 | 
				
			||||||
 | 
					  void set_enable_offset_calibration(bool flag) { enable_offset_calibration_ = flag; }
 | 
				
			||||||
  uint16_t calibrate_voltage_offset_phase(uint8_t /*phase*/);
 | 
					  uint16_t calibrate_voltage_offset_phase(uint8_t /*phase*/);
 | 
				
			||||||
  uint16_t calibrate_current_offset_phase(uint8_t /*phase*/);
 | 
					  uint16_t calibrate_current_offset_phase(uint8_t /*phase*/);
 | 
				
			||||||
 | 
					 | 
				
			||||||
  int32_t last_periodic_millis = millis();
 | 
					  int32_t last_periodic_millis = millis();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 protected:
 | 
					 protected:
 | 
				
			||||||
@@ -83,10 +87,11 @@ class ATM90E32Component : public PollingComponent,
 | 
				
			|||||||
  float get_chip_temperature_();
 | 
					  float get_chip_temperature_();
 | 
				
			||||||
  bool get_publish_interval_flag_() { return publish_interval_flag_; };
 | 
					  bool get_publish_interval_flag_() { return publish_interval_flag_; };
 | 
				
			||||||
  void set_publish_interval_flag_(bool flag) { publish_interval_flag_ = flag; };
 | 
					  void set_publish_interval_flag_(bool flag) { publish_interval_flag_ = flag; };
 | 
				
			||||||
 | 
					  void restore_calibrations_();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  struct ATM90E32Phase {
 | 
					  struct ATM90E32Phase {
 | 
				
			||||||
    uint16_t voltage_gain_{7305};
 | 
					    uint16_t voltage_gain_{0};
 | 
				
			||||||
    uint16_t ct_gain_{27961};
 | 
					    uint16_t ct_gain_{0};
 | 
				
			||||||
    uint16_t voltage_offset_{0};
 | 
					    uint16_t voltage_offset_{0};
 | 
				
			||||||
    uint16_t current_offset_{0};
 | 
					    uint16_t current_offset_{0};
 | 
				
			||||||
    float voltage_{0};
 | 
					    float voltage_{0};
 | 
				
			||||||
@@ -114,13 +119,21 @@ class ATM90E32Component : public PollingComponent,
 | 
				
			|||||||
    uint32_t cumulative_reverse_active_energy_{0};
 | 
					    uint32_t cumulative_reverse_active_energy_{0};
 | 
				
			||||||
  } phase_[3];
 | 
					  } phase_[3];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  struct Calibration {
 | 
				
			||||||
 | 
					    uint16_t voltage_offset_{0};
 | 
				
			||||||
 | 
					    uint16_t current_offset_{0};
 | 
				
			||||||
 | 
					  } offset_phase_[3];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ESPPreferenceObject pref_;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  sensor::Sensor *freq_sensor_{nullptr};
 | 
					  sensor::Sensor *freq_sensor_{nullptr};
 | 
				
			||||||
  sensor::Sensor *chip_temperature_sensor_{nullptr};
 | 
					  sensor::Sensor *chip_temperature_sensor_{nullptr};
 | 
				
			||||||
  uint16_t pga_gain_{0x15};
 | 
					  uint16_t pga_gain_{0x15};
 | 
				
			||||||
  int line_freq_{60};
 | 
					  int line_freq_{60};
 | 
				
			||||||
  int current_phases_{3};
 | 
					  int current_phases_{3};
 | 
				
			||||||
  bool publish_interval_flag_{true};
 | 
					  bool publish_interval_flag_{false};
 | 
				
			||||||
  bool peak_current_signed_{false};
 | 
					  bool peak_current_signed_{false};
 | 
				
			||||||
 | 
					  bool enable_offset_calibration_{false};
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}  // namespace atm90e32
 | 
					}  // namespace atm90e32
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,7 @@
 | 
				
			|||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <cinttypes>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace esphome {
 | 
					namespace esphome {
 | 
				
			||||||
namespace atm90e32 {
 | 
					namespace atm90e32 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										43
									
								
								esphome/components/atm90e32/button/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								esphome/components/atm90e32/button/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
				
			|||||||
 | 
					import esphome.codegen as cg
 | 
				
			||||||
 | 
					from esphome.components import button
 | 
				
			||||||
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
 | 
					from esphome.const import CONF_ID, ENTITY_CATEGORY_CONFIG, ICON_CHIP, ICON_SCALE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .. import atm90e32_ns
 | 
				
			||||||
 | 
					from ..sensor import ATM90E32Component
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CONF_RUN_OFFSET_CALIBRATION = "run_offset_calibration"
 | 
				
			||||||
 | 
					CONF_CLEAR_OFFSET_CALIBRATION = "clear_offset_calibration"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ATM90E32CalibrationButton = atm90e32_ns.class_(
 | 
				
			||||||
 | 
					    "ATM90E32CalibrationButton",
 | 
				
			||||||
 | 
					    button.Button,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					ATM90E32ClearCalibrationButton = atm90e32_ns.class_(
 | 
				
			||||||
 | 
					    "ATM90E32ClearCalibrationButton",
 | 
				
			||||||
 | 
					    button.Button,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CONFIG_SCHEMA = {
 | 
				
			||||||
 | 
					    cv.GenerateID(CONF_ID): cv.use_id(ATM90E32Component),
 | 
				
			||||||
 | 
					    cv.Optional(CONF_RUN_OFFSET_CALIBRATION): button.button_schema(
 | 
				
			||||||
 | 
					        ATM90E32CalibrationButton,
 | 
				
			||||||
 | 
					        entity_category=ENTITY_CATEGORY_CONFIG,
 | 
				
			||||||
 | 
					        icon=ICON_SCALE,
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    cv.Optional(CONF_CLEAR_OFFSET_CALIBRATION): button.button_schema(
 | 
				
			||||||
 | 
					        ATM90E32ClearCalibrationButton,
 | 
				
			||||||
 | 
					        entity_category=ENTITY_CATEGORY_CONFIG,
 | 
				
			||||||
 | 
					        icon=ICON_CHIP,
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async def to_code(config):
 | 
				
			||||||
 | 
					    parent = await cg.get_variable(config[CONF_ID])
 | 
				
			||||||
 | 
					    if run_offset := config.get(CONF_RUN_OFFSET_CALIBRATION):
 | 
				
			||||||
 | 
					        b = await button.new_button(run_offset)
 | 
				
			||||||
 | 
					        await cg.register_parented(b, parent)
 | 
				
			||||||
 | 
					    if clear_offset := config.get(CONF_CLEAR_OFFSET_CALIBRATION):
 | 
				
			||||||
 | 
					        b = await button.new_button(clear_offset)
 | 
				
			||||||
 | 
					        await cg.register_parented(b, parent)
 | 
				
			||||||
							
								
								
									
										20
									
								
								esphome/components/atm90e32/button/atm90e32_button.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								esphome/components/atm90e32/button/atm90e32_button.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					#include "atm90e32_button.h"
 | 
				
			||||||
 | 
					#include "esphome/core/log.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace esphome {
 | 
				
			||||||
 | 
					namespace atm90e32 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char *const TAG = "atm90e32.button";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ATM90E32CalibrationButton::press_action() {
 | 
				
			||||||
 | 
					  ESP_LOGI(TAG, "Running offset calibrations, Note: CTs and ACVs must be 0 during this process...");
 | 
				
			||||||
 | 
					  this->parent_->run_offset_calibrations();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ATM90E32ClearCalibrationButton::press_action() {
 | 
				
			||||||
 | 
					  ESP_LOGI(TAG, "Offset calibrations cleared.");
 | 
				
			||||||
 | 
					  this->parent_->clear_offset_calibrations();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace atm90e32
 | 
				
			||||||
 | 
					}  // namespace esphome
 | 
				
			||||||
							
								
								
									
										27
									
								
								esphome/components/atm90e32/button/atm90e32_button.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								esphome/components/atm90e32/button/atm90e32_button.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "esphome/core/component.h"
 | 
				
			||||||
 | 
					#include "esphome/components/atm90e32/atm90e32.h"
 | 
				
			||||||
 | 
					#include "esphome/components/button/button.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace esphome {
 | 
				
			||||||
 | 
					namespace atm90e32 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ATM90E32CalibrationButton : public button::Button, public Parented<ATM90E32Component> {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  ATM90E32CalibrationButton() = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 protected:
 | 
				
			||||||
 | 
					  void press_action() override;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ATM90E32ClearCalibrationButton : public button::Button, public Parented<ATM90E32Component> {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  ATM90E32ClearCalibrationButton() = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 protected:
 | 
				
			||||||
 | 
					  void press_action() override;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace atm90e32
 | 
				
			||||||
 | 
					}  // namespace esphome
 | 
				
			||||||
@@ -1,21 +1,21 @@
 | 
				
			|||||||
import esphome.codegen as cg
 | 
					import esphome.codegen as cg
 | 
				
			||||||
import esphome.config_validation as cv
 | 
					 | 
				
			||||||
from esphome.components import sensor, spi
 | 
					from esphome.components import sensor, spi
 | 
				
			||||||
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
from esphome.const import (
 | 
					from esphome.const import (
 | 
				
			||||||
    CONF_ID,
 | 
					    CONF_APPARENT_POWER,
 | 
				
			||||||
    CONF_REACTIVE_POWER,
 | 
					 | 
				
			||||||
    CONF_VOLTAGE,
 | 
					 | 
				
			||||||
    CONF_CURRENT,
 | 
					    CONF_CURRENT,
 | 
				
			||||||
 | 
					    CONF_FORWARD_ACTIVE_ENERGY,
 | 
				
			||||||
 | 
					    CONF_FREQUENCY,
 | 
				
			||||||
 | 
					    CONF_ID,
 | 
				
			||||||
    CONF_PHASE_A,
 | 
					    CONF_PHASE_A,
 | 
				
			||||||
 | 
					    CONF_PHASE_ANGLE,
 | 
				
			||||||
    CONF_PHASE_B,
 | 
					    CONF_PHASE_B,
 | 
				
			||||||
    CONF_PHASE_C,
 | 
					    CONF_PHASE_C,
 | 
				
			||||||
    CONF_PHASE_ANGLE,
 | 
					 | 
				
			||||||
    CONF_POWER,
 | 
					    CONF_POWER,
 | 
				
			||||||
    CONF_POWER_FACTOR,
 | 
					    CONF_POWER_FACTOR,
 | 
				
			||||||
    CONF_APPARENT_POWER,
 | 
					    CONF_REACTIVE_POWER,
 | 
				
			||||||
    CONF_FREQUENCY,
 | 
					 | 
				
			||||||
    CONF_FORWARD_ACTIVE_ENERGY,
 | 
					 | 
				
			||||||
    CONF_REVERSE_ACTIVE_ENERGY,
 | 
					    CONF_REVERSE_ACTIVE_ENERGY,
 | 
				
			||||||
 | 
					    CONF_VOLTAGE,
 | 
				
			||||||
    DEVICE_CLASS_CURRENT,
 | 
					    DEVICE_CLASS_CURRENT,
 | 
				
			||||||
    DEVICE_CLASS_ENERGY,
 | 
					    DEVICE_CLASS_ENERGY,
 | 
				
			||||||
    DEVICE_CLASS_POWER,
 | 
					    DEVICE_CLASS_POWER,
 | 
				
			||||||
@@ -23,13 +23,13 @@ from esphome.const import (
 | 
				
			|||||||
    DEVICE_CLASS_TEMPERATURE,
 | 
					    DEVICE_CLASS_TEMPERATURE,
 | 
				
			||||||
    DEVICE_CLASS_VOLTAGE,
 | 
					    DEVICE_CLASS_VOLTAGE,
 | 
				
			||||||
    ENTITY_CATEGORY_DIAGNOSTIC,
 | 
					    ENTITY_CATEGORY_DIAGNOSTIC,
 | 
				
			||||||
    ICON_LIGHTBULB,
 | 
					 | 
				
			||||||
    ICON_CURRENT_AC,
 | 
					    ICON_CURRENT_AC,
 | 
				
			||||||
 | 
					    ICON_LIGHTBULB,
 | 
				
			||||||
    STATE_CLASS_MEASUREMENT,
 | 
					    STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
    STATE_CLASS_TOTAL_INCREASING,
 | 
					    STATE_CLASS_TOTAL_INCREASING,
 | 
				
			||||||
    UNIT_AMPERE,
 | 
					    UNIT_AMPERE,
 | 
				
			||||||
    UNIT_DEGREES,
 | 
					 | 
				
			||||||
    UNIT_CELSIUS,
 | 
					    UNIT_CELSIUS,
 | 
				
			||||||
 | 
					    UNIT_DEGREES,
 | 
				
			||||||
    UNIT_HERTZ,
 | 
					    UNIT_HERTZ,
 | 
				
			||||||
    UNIT_VOLT,
 | 
					    UNIT_VOLT,
 | 
				
			||||||
    UNIT_VOLT_AMPS_REACTIVE,
 | 
					    UNIT_VOLT_AMPS_REACTIVE,
 | 
				
			||||||
@@ -37,6 +37,8 @@ from esphome.const import (
 | 
				
			|||||||
    UNIT_WATT_HOURS,
 | 
					    UNIT_WATT_HOURS,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from . import atm90e32_ns
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CONF_LINE_FREQUENCY = "line_frequency"
 | 
					CONF_LINE_FREQUENCY = "line_frequency"
 | 
				
			||||||
CONF_CHIP_TEMPERATURE = "chip_temperature"
 | 
					CONF_CHIP_TEMPERATURE = "chip_temperature"
 | 
				
			||||||
CONF_GAIN_PGA = "gain_pga"
 | 
					CONF_GAIN_PGA = "gain_pga"
 | 
				
			||||||
@@ -46,6 +48,7 @@ CONF_GAIN_CT = "gain_ct"
 | 
				
			|||||||
CONF_HARMONIC_POWER = "harmonic_power"
 | 
					CONF_HARMONIC_POWER = "harmonic_power"
 | 
				
			||||||
CONF_PEAK_CURRENT = "peak_current"
 | 
					CONF_PEAK_CURRENT = "peak_current"
 | 
				
			||||||
CONF_PEAK_CURRENT_SIGNED = "peak_current_signed"
 | 
					CONF_PEAK_CURRENT_SIGNED = "peak_current_signed"
 | 
				
			||||||
 | 
					CONF_ENABLE_OFFSET_CALIBRATION = "enable_offset_calibration"
 | 
				
			||||||
UNIT_DEG = "degrees"
 | 
					UNIT_DEG = "degrees"
 | 
				
			||||||
LINE_FREQS = {
 | 
					LINE_FREQS = {
 | 
				
			||||||
    "50HZ": 50,
 | 
					    "50HZ": 50,
 | 
				
			||||||
@@ -61,7 +64,6 @@ PGA_GAINS = {
 | 
				
			|||||||
    "4X": 0x2A,
 | 
					    "4X": 0x2A,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
atm90e32_ns = cg.esphome_ns.namespace("atm90e32")
 | 
					 | 
				
			||||||
ATM90E32Component = atm90e32_ns.class_(
 | 
					ATM90E32Component = atm90e32_ns.class_(
 | 
				
			||||||
    "ATM90E32Component", cg.PollingComponent, spi.SPIDevice
 | 
					    "ATM90E32Component", cg.PollingComponent, spi.SPIDevice
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -164,6 +166,7 @@ CONFIG_SCHEMA = (
 | 
				
			|||||||
            ),
 | 
					            ),
 | 
				
			||||||
            cv.Optional(CONF_GAIN_PGA, default="2X"): cv.enum(PGA_GAINS, upper=True),
 | 
					            cv.Optional(CONF_GAIN_PGA, default="2X"): cv.enum(PGA_GAINS, upper=True),
 | 
				
			||||||
            cv.Optional(CONF_PEAK_CURRENT_SIGNED, default=False): cv.boolean,
 | 
					            cv.Optional(CONF_PEAK_CURRENT_SIGNED, default=False): cv.boolean,
 | 
				
			||||||
 | 
					            cv.Optional(CONF_ENABLE_OFFSET_CALIBRATION, default=False): cv.boolean,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    .extend(cv.polling_component_schema("60s"))
 | 
					    .extend(cv.polling_component_schema("60s"))
 | 
				
			||||||
@@ -227,3 +230,4 @@ async def to_code(config):
 | 
				
			|||||||
    cg.add(var.set_current_phases(config[CONF_CURRENT_PHASES]))
 | 
					    cg.add(var.set_current_phases(config[CONF_CURRENT_PHASES]))
 | 
				
			||||||
    cg.add(var.set_pga_gain(config[CONF_GAIN_PGA]))
 | 
					    cg.add(var.set_pga_gain(config[CONF_GAIN_PGA]))
 | 
				
			||||||
    cg.add(var.set_peak_current_signed(config[CONF_PEAK_CURRENT_SIGNED]))
 | 
					    cg.add(var.set_peak_current_signed(config[CONF_PEAK_CURRENT_SIGNED]))
 | 
				
			||||||
 | 
					    cg.add(var.set_enable_offset_calibration(config[CONF_ENABLE_OFFSET_CALIBRATION]))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -90,7 +90,7 @@ struct BedjetStatusPacket {
 | 
				
			|||||||
    int unused_6 : 1;       // 0x4
 | 
					    int unused_6 : 1;       // 0x4
 | 
				
			||||||
    bool is_dual_zone : 1;  /// Is part of a Dual Zone configuration
 | 
					    bool is_dual_zone : 1;  /// Is part of a Dual Zone configuration
 | 
				
			||||||
    int unused_7 : 1;       // 0x1
 | 
					    int unused_7 : 1;       // 0x1
 | 
				
			||||||
  } dual_zone_flags;
 | 
					  } dual_zone_flags;        // NOLINT(clang-diagnostic-unaligned-access)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  uint8_t unused_4 : 8;  // Unknown 23-24 = 0x1310
 | 
					  uint8_t unused_4 : 8;  // Unknown 23-24 = 0x1310
 | 
				
			||||||
  uint8_t unused_5 : 8;  // Unknown 23-24 = 0x1310
 | 
					  uint8_t unused_5 : 8;  // Unknown 23-24 = 0x1310
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,10 +18,11 @@ class BinaryLightOutput : public light::LightOutput {
 | 
				
			|||||||
  void write_state(light::LightState *state) override {
 | 
					  void write_state(light::LightState *state) override {
 | 
				
			||||||
    bool binary;
 | 
					    bool binary;
 | 
				
			||||||
    state->current_values_as_binary(&binary);
 | 
					    state->current_values_as_binary(&binary);
 | 
				
			||||||
    if (binary)
 | 
					    if (binary) {
 | 
				
			||||||
      this->output_->turn_on();
 | 
					      this->output_->turn_on();
 | 
				
			||||||
    else
 | 
					    } else {
 | 
				
			||||||
      this->output_->turn_off();
 | 
					      this->output_->turn_off();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 protected:
 | 
					 protected:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,10 +1,8 @@
 | 
				
			|||||||
import esphome.codegen as cg
 | 
					 | 
				
			||||||
import esphome.config_validation as cv
 | 
					 | 
				
			||||||
from esphome.cpp_generator import MockObjClass
 | 
					 | 
				
			||||||
from esphome.cpp_helpers import setup_entity
 | 
					 | 
				
			||||||
from esphome import automation, core
 | 
					from esphome import automation, core
 | 
				
			||||||
from esphome.automation import Condition, maybe_simple_id
 | 
					from esphome.automation import Condition, maybe_simple_id
 | 
				
			||||||
from esphome.components import mqtt
 | 
					import esphome.codegen as cg
 | 
				
			||||||
 | 
					from esphome.components import mqtt, web_server
 | 
				
			||||||
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
from esphome.const import (
 | 
					from esphome.const import (
 | 
				
			||||||
    CONF_DELAY,
 | 
					    CONF_DELAY,
 | 
				
			||||||
    CONF_DEVICE_CLASS,
 | 
					    CONF_DEVICE_CLASS,
 | 
				
			||||||
@@ -16,6 +14,7 @@ from esphome.const import (
 | 
				
			|||||||
    CONF_INVERTED,
 | 
					    CONF_INVERTED,
 | 
				
			||||||
    CONF_MAX_LENGTH,
 | 
					    CONF_MAX_LENGTH,
 | 
				
			||||||
    CONF_MIN_LENGTH,
 | 
					    CONF_MIN_LENGTH,
 | 
				
			||||||
 | 
					    CONF_MQTT_ID,
 | 
				
			||||||
    CONF_ON_CLICK,
 | 
					    CONF_ON_CLICK,
 | 
				
			||||||
    CONF_ON_DOUBLE_CLICK,
 | 
					    CONF_ON_DOUBLE_CLICK,
 | 
				
			||||||
    CONF_ON_MULTI_CLICK,
 | 
					    CONF_ON_MULTI_CLICK,
 | 
				
			||||||
@@ -26,7 +25,7 @@ from esphome.const import (
 | 
				
			|||||||
    CONF_STATE,
 | 
					    CONF_STATE,
 | 
				
			||||||
    CONF_TIMING,
 | 
					    CONF_TIMING,
 | 
				
			||||||
    CONF_TRIGGER_ID,
 | 
					    CONF_TRIGGER_ID,
 | 
				
			||||||
    CONF_MQTT_ID,
 | 
					    CONF_WEB_SERVER_ID,
 | 
				
			||||||
    DEVICE_CLASS_BATTERY,
 | 
					    DEVICE_CLASS_BATTERY,
 | 
				
			||||||
    DEVICE_CLASS_BATTERY_CHARGING,
 | 
					    DEVICE_CLASS_BATTERY_CHARGING,
 | 
				
			||||||
    DEVICE_CLASS_CARBON_MONOXIDE,
 | 
					    DEVICE_CLASS_CARBON_MONOXIDE,
 | 
				
			||||||
@@ -58,6 +57,8 @@ from esphome.const import (
 | 
				
			|||||||
    DEVICE_CLASS_WINDOW,
 | 
					    DEVICE_CLASS_WINDOW,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from esphome.core import CORE, coroutine_with_priority
 | 
					from esphome.core import CORE, coroutine_with_priority
 | 
				
			||||||
 | 
					from esphome.cpp_generator import MockObjClass
 | 
				
			||||||
 | 
					from esphome.cpp_helpers import setup_entity
 | 
				
			||||||
from esphome.util import Registry
 | 
					from esphome.util import Registry
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CODEOWNERS = ["@esphome/core"]
 | 
					CODEOWNERS = ["@esphome/core"]
 | 
				
			||||||
@@ -385,70 +386,76 @@ def validate_click_timing(value):
 | 
				
			|||||||
    return value
 | 
					    return value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
BINARY_SENSOR_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend(
 | 
					BINARY_SENSOR_SCHEMA = (
 | 
				
			||||||
    {
 | 
					    cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
 | 
				
			||||||
        cv.GenerateID(): cv.declare_id(BinarySensor),
 | 
					    .extend(cv.MQTT_COMPONENT_SCHEMA)
 | 
				
			||||||
        cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(
 | 
					    .extend(
 | 
				
			||||||
            mqtt.MQTTBinarySensorComponent
 | 
					        {
 | 
				
			||||||
        ),
 | 
					            cv.GenerateID(): cv.declare_id(BinarySensor),
 | 
				
			||||||
        cv.Optional(CONF_PUBLISH_INITIAL_STATE): cv.boolean,
 | 
					            cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(
 | 
				
			||||||
        cv.Optional(CONF_DEVICE_CLASS): validate_device_class,
 | 
					                mqtt.MQTTBinarySensorComponent
 | 
				
			||||||
        cv.Optional(CONF_FILTERS): validate_filters,
 | 
					            ),
 | 
				
			||||||
        cv.Optional(CONF_ON_PRESS): automation.validate_automation(
 | 
					            cv.Optional(CONF_PUBLISH_INITIAL_STATE): cv.boolean,
 | 
				
			||||||
            {
 | 
					            cv.Optional(CONF_DEVICE_CLASS): validate_device_class,
 | 
				
			||||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PressTrigger),
 | 
					            cv.Optional(CONF_FILTERS): validate_filters,
 | 
				
			||||||
            }
 | 
					            cv.Optional(CONF_ON_PRESS): automation.validate_automation(
 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
        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(
 | 
					 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ClickTrigger),
 | 
					                    cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PressTrigger),
 | 
				
			||||||
                    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_RELEASE): automation.validate_automation(
 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
        cv.Optional(CONF_ON_DOUBLE_CLICK): cv.All(
 | 
					 | 
				
			||||||
            automation.validate_automation(
 | 
					 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DoubleClickTrigger),
 | 
					                    cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ReleaseTrigger),
 | 
				
			||||||
                    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_CLICK): cv.All(
 | 
				
			||||||
        ),
 | 
					                automation.validate_automation(
 | 
				
			||||||
        cv.Optional(CONF_ON_MULTI_CLICK): automation.validate_automation(
 | 
					                    {
 | 
				
			||||||
            {
 | 
					                        cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ClickTrigger),
 | 
				
			||||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(MultiClickTrigger),
 | 
					                        cv.Optional(
 | 
				
			||||||
                cv.Required(CONF_TIMING): cv.All(
 | 
					                            CONF_MIN_LENGTH, default="50ms"
 | 
				
			||||||
                    [parse_multi_click_timing_str], validate_multi_click_timing
 | 
					                        ): cv.positive_time_period_milliseconds,
 | 
				
			||||||
 | 
					                        cv.Optional(
 | 
				
			||||||
 | 
					                            CONF_MAX_LENGTH, default="350ms"
 | 
				
			||||||
 | 
					                        ): cv.positive_time_period_milliseconds,
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                ),
 | 
					                ),
 | 
				
			||||||
                cv.Optional(
 | 
					                validate_click_timing,
 | 
				
			||||||
                    CONF_INVALID_COOLDOWN, default="1s"
 | 
					            ),
 | 
				
			||||||
                ): cv.positive_time_period_milliseconds,
 | 
					            cv.Optional(CONF_ON_DOUBLE_CLICK): cv.All(
 | 
				
			||||||
            }
 | 
					                automation.validate_automation(
 | 
				
			||||||
        ),
 | 
					                    {
 | 
				
			||||||
        cv.Optional(CONF_ON_STATE): automation.validate_automation(
 | 
					                        cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
 | 
				
			||||||
            {
 | 
					                            DoubleClickTrigger
 | 
				
			||||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateTrigger),
 | 
					                        ),
 | 
				
			||||||
            }
 | 
					                        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()
 | 
					_UNDEF = object()
 | 
				
			||||||
@@ -536,6 +543,10 @@ async def setup_binary_sensor_core_(var, config):
 | 
				
			|||||||
        mqtt_ = cg.new_Pvariable(mqtt_id, var)
 | 
					        mqtt_ = cg.new_Pvariable(mqtt_id, var)
 | 
				
			||||||
        await mqtt.register_mqtt_component(mqtt_, config)
 | 
					        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):
 | 
					async def register_binary_sensor(var, config):
 | 
				
			||||||
    if not CORE.has_id(config[CONF_ID]):
 | 
					    if not CORE.has_id(config[CONF_ID]):
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,8 @@
 | 
				
			|||||||
import esphome.codegen as cg
 | 
					from esphome import automation
 | 
				
			||||||
import esphome.config_validation as cv
 | 
					 | 
				
			||||||
from esphome.automation import maybe_simple_id
 | 
					from esphome.automation import maybe_simple_id
 | 
				
			||||||
from esphome.components import esp32_ble_tracker, esp32_ble_client
 | 
					import esphome.codegen as cg
 | 
				
			||||||
 | 
					from esphome.components import esp32_ble_client, esp32_ble_tracker
 | 
				
			||||||
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
from esphome.const import (
 | 
					from esphome.const import (
 | 
				
			||||||
    CONF_CHARACTERISTIC_UUID,
 | 
					    CONF_CHARACTERISTIC_UUID,
 | 
				
			||||||
    CONF_ID,
 | 
					    CONF_ID,
 | 
				
			||||||
@@ -13,7 +14,6 @@ from esphome.const import (
 | 
				
			|||||||
    CONF_TRIGGER_ID,
 | 
					    CONF_TRIGGER_ID,
 | 
				
			||||||
    CONF_VALUE,
 | 
					    CONF_VALUE,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from esphome import automation
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
AUTO_LOAD = ["esp32_ble_client"]
 | 
					AUTO_LOAD = ["esp32_ble_client"]
 | 
				
			||||||
CODEOWNERS = ["@buxtronix", "@clydebarrow"]
 | 
					CODEOWNERS = ["@buxtronix", "@clydebarrow"]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
import esphome.codegen as cg
 | 
					import esphome.codegen as cg
 | 
				
			||||||
import esphome.config_validation as cv
 | 
					 | 
				
			||||||
from esphome.components import ble_client, esp32_ble_tracker, output
 | 
					from esphome.components import ble_client, esp32_ble_tracker, output
 | 
				
			||||||
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
from esphome.const import CONF_CHARACTERISTIC_UUID, CONF_ID, CONF_SERVICE_UUID
 | 
					from esphome.const import CONF_CHARACTERISTIC_UUID, CONF_ID, CONF_SERVICE_UUID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .. import ble_client_ns
 | 
					from .. import ble_client_ns
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,17 +1,18 @@
 | 
				
			|||||||
 | 
					from esphome import automation
 | 
				
			||||||
import esphome.codegen as cg
 | 
					import esphome.codegen as cg
 | 
				
			||||||
 | 
					from esphome.components import ble_client, esp32_ble_tracker, sensor
 | 
				
			||||||
import esphome.config_validation as cv
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
from esphome.components import sensor, ble_client, esp32_ble_tracker
 | 
					 | 
				
			||||||
from esphome.const import (
 | 
					from esphome.const import (
 | 
				
			||||||
    CONF_CHARACTERISTIC_UUID,
 | 
					    CONF_CHARACTERISTIC_UUID,
 | 
				
			||||||
    CONF_LAMBDA,
 | 
					    CONF_LAMBDA,
 | 
				
			||||||
 | 
					    CONF_SERVICE_UUID,
 | 
				
			||||||
    CONF_TRIGGER_ID,
 | 
					    CONF_TRIGGER_ID,
 | 
				
			||||||
    CONF_TYPE,
 | 
					    CONF_TYPE,
 | 
				
			||||||
    CONF_SERVICE_UUID,
 | 
					 | 
				
			||||||
    DEVICE_CLASS_SIGNAL_STRENGTH,
 | 
					    DEVICE_CLASS_SIGNAL_STRENGTH,
 | 
				
			||||||
    STATE_CLASS_MEASUREMENT,
 | 
					    STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
    UNIT_DECIBEL_MILLIWATT,
 | 
					    UNIT_DECIBEL_MILLIWATT,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from esphome import automation
 | 
					
 | 
				
			||||||
from .. import ble_client_ns
 | 
					from .. import ble_client_ns
 | 
				
			||||||
 | 
					
 | 
				
			||||||
DEPENDENCIES = ["ble_client"]
 | 
					DEPENDENCIES = ["ble_client"]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,8 @@
 | 
				
			|||||||
import esphome.codegen as cg
 | 
					import esphome.codegen as cg
 | 
				
			||||||
 | 
					from esphome.components import ble_client, switch
 | 
				
			||||||
import esphome.config_validation as cv
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
from esphome.components import switch, ble_client
 | 
					 | 
				
			||||||
from esphome.const import ICON_BLUETOOTH
 | 
					from esphome.const import ICON_BLUETOOTH
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .. import ble_client_ns
 | 
					from .. import ble_client_ns
 | 
				
			||||||
 | 
					
 | 
				
			||||||
BLEClientSwitch = ble_client_ns.class_(
 | 
					BLEClientSwitch = ble_client_ns.class_(
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,13 +1,14 @@
 | 
				
			|||||||
 | 
					from esphome import automation
 | 
				
			||||||
import esphome.codegen as cg
 | 
					import esphome.codegen as cg
 | 
				
			||||||
 | 
					from esphome.components import ble_client, esp32_ble_tracker, text_sensor
 | 
				
			||||||
import esphome.config_validation as cv
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
from esphome.components import text_sensor, ble_client, esp32_ble_tracker
 | 
					 | 
				
			||||||
from esphome.const import (
 | 
					from esphome.const import (
 | 
				
			||||||
    CONF_CHARACTERISTIC_UUID,
 | 
					    CONF_CHARACTERISTIC_UUID,
 | 
				
			||||||
    CONF_ID,
 | 
					    CONF_ID,
 | 
				
			||||||
    CONF_TRIGGER_ID,
 | 
					 | 
				
			||||||
    CONF_SERVICE_UUID,
 | 
					    CONF_SERVICE_UUID,
 | 
				
			||||||
 | 
					    CONF_TRIGGER_ID,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from esphome import automation
 | 
					
 | 
				
			||||||
from .. import ble_client_ns
 | 
					from .. import ble_client_ns
 | 
				
			||||||
 | 
					
 | 
				
			||||||
DEPENDENCIES = ["ble_client"]
 | 
					DEPENDENCIES = ["ble_client"]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,13 +1,13 @@
 | 
				
			|||||||
import esphome.codegen as cg
 | 
					import esphome.codegen as cg
 | 
				
			||||||
import esphome.config_validation as cv
 | 
					 | 
				
			||||||
from esphome.components import binary_sensor, esp32_ble_tracker
 | 
					from esphome.components import binary_sensor, esp32_ble_tracker
 | 
				
			||||||
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
from esphome.const import (
 | 
					from esphome.const import (
 | 
				
			||||||
    CONF_MAC_ADDRESS,
 | 
					 | 
				
			||||||
    CONF_SERVICE_UUID,
 | 
					 | 
				
			||||||
    CONF_IBEACON_MAJOR,
 | 
					    CONF_IBEACON_MAJOR,
 | 
				
			||||||
    CONF_IBEACON_MINOR,
 | 
					    CONF_IBEACON_MINOR,
 | 
				
			||||||
    CONF_IBEACON_UUID,
 | 
					    CONF_IBEACON_UUID,
 | 
				
			||||||
 | 
					    CONF_MAC_ADDRESS,
 | 
				
			||||||
    CONF_MIN_RSSI,
 | 
					    CONF_MIN_RSSI,
 | 
				
			||||||
 | 
					    CONF_SERVICE_UUID,
 | 
				
			||||||
    CONF_TIMEOUT,
 | 
					    CONF_TIMEOUT,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,12 +1,12 @@
 | 
				
			|||||||
import esphome.codegen as cg
 | 
					import esphome.codegen as cg
 | 
				
			||||||
 | 
					from esphome.components import esp32_ble_tracker, sensor
 | 
				
			||||||
import esphome.config_validation as cv
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
from esphome.components import sensor, esp32_ble_tracker
 | 
					 | 
				
			||||||
from esphome.const import (
 | 
					from esphome.const import (
 | 
				
			||||||
    CONF_IBEACON_MAJOR,
 | 
					    CONF_IBEACON_MAJOR,
 | 
				
			||||||
    CONF_IBEACON_MINOR,
 | 
					    CONF_IBEACON_MINOR,
 | 
				
			||||||
    CONF_IBEACON_UUID,
 | 
					    CONF_IBEACON_UUID,
 | 
				
			||||||
    CONF_SERVICE_UUID,
 | 
					 | 
				
			||||||
    CONF_MAC_ADDRESS,
 | 
					    CONF_MAC_ADDRESS,
 | 
				
			||||||
 | 
					    CONF_SERVICE_UUID,
 | 
				
			||||||
    DEVICE_CLASS_SIGNAL_STRENGTH,
 | 
					    DEVICE_CLASS_SIGNAL_STRENGTH,
 | 
				
			||||||
    STATE_CLASS_MEASUREMENT,
 | 
					    STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
    UNIT_DECIBEL_MILLIWATT,
 | 
					    UNIT_DECIBEL_MILLIWATT,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
import esphome.codegen as cg
 | 
					import esphome.codegen as cg
 | 
				
			||||||
 | 
					from esphome.components import esp32_ble_tracker, text_sensor
 | 
				
			||||||
import esphome.config_validation as cv
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
from esphome.components import text_sensor, esp32_ble_tracker
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
DEPENDENCIES = ["esp32_ble_tracker"]
 | 
					DEPENDENCIES = ["esp32_ble_tracker"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,8 @@
 | 
				
			|||||||
from esphome.components import esp32_ble_tracker, esp32_ble_client
 | 
					 | 
				
			||||||
import esphome.config_validation as cv
 | 
					 | 
				
			||||||
import esphome.codegen as cg
 | 
					import esphome.codegen as cg
 | 
				
			||||||
from esphome.const import CONF_ACTIVE, CONF_ID
 | 
					from esphome.components import esp32_ble_client, esp32_ble_tracker
 | 
				
			||||||
from esphome.components.esp32 import add_idf_sdkconfig_option
 | 
					from esphome.components.esp32 import add_idf_sdkconfig_option
 | 
				
			||||||
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
 | 
					from esphome.const import CONF_ACTIVE, CONF_ID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
AUTO_LOAD = ["esp32_ble_client", "esp32_ble_tracker"]
 | 
					AUTO_LOAD = ["esp32_ble_client", "esp32_ble_tracker"]
 | 
				
			||||||
DEPENDENCIES = ["api", "esp32"]
 | 
					DEPENDENCIES = ["api", "esp32"]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
import esphome.codegen as cg
 | 
					import esphome.codegen as cg
 | 
				
			||||||
import esphome.config_validation as cv
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
from esphome.components import i2c, esp32
 | 
					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"]
 | 
					CODEOWNERS = ["@trvrnrth"]
 | 
				
			||||||
DEPENDENCIES = ["i2c"]
 | 
					DEPENDENCIES = ["i2c"]
 | 
				
			||||||
@@ -11,7 +11,6 @@ MULTI_CONF = True
 | 
				
			|||||||
CONF_BME680_BSEC_ID = "bme680_bsec_id"
 | 
					CONF_BME680_BSEC_ID = "bme680_bsec_id"
 | 
				
			||||||
CONF_IAQ_MODE = "iaq_mode"
 | 
					CONF_IAQ_MODE = "iaq_mode"
 | 
				
			||||||
CONF_SUPPLY_VOLTAGE = "supply_voltage"
 | 
					CONF_SUPPLY_VOLTAGE = "supply_voltage"
 | 
				
			||||||
CONF_SAMPLE_RATE = "sample_rate"
 | 
					 | 
				
			||||||
CONF_STATE_SAVE_INTERVAL = "state_save_interval"
 | 
					CONF_STATE_SAVE_INTERVAL = "state_save_interval"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bme680_bsec_ns = cg.esphome_ns.namespace("bme680_bsec")
 | 
					bme680_bsec_ns = cg.esphome_ns.namespace("bme680_bsec")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,33 +4,33 @@ from esphome.components import sensor
 | 
				
			|||||||
from esphome.const import (
 | 
					from esphome.const import (
 | 
				
			||||||
    CONF_GAS_RESISTANCE,
 | 
					    CONF_GAS_RESISTANCE,
 | 
				
			||||||
    CONF_HUMIDITY,
 | 
					    CONF_HUMIDITY,
 | 
				
			||||||
 | 
					    CONF_IAQ_ACCURACY,
 | 
				
			||||||
    CONF_PRESSURE,
 | 
					    CONF_PRESSURE,
 | 
				
			||||||
 | 
					    CONF_SAMPLE_RATE,
 | 
				
			||||||
    CONF_TEMPERATURE,
 | 
					    CONF_TEMPERATURE,
 | 
				
			||||||
 | 
					    DEVICE_CLASS_ATMOSPHERIC_PRESSURE,
 | 
				
			||||||
    DEVICE_CLASS_CARBON_DIOXIDE,
 | 
					    DEVICE_CLASS_CARBON_DIOXIDE,
 | 
				
			||||||
    DEVICE_CLASS_HUMIDITY,
 | 
					    DEVICE_CLASS_HUMIDITY,
 | 
				
			||||||
    DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS,
 | 
					 | 
				
			||||||
    DEVICE_CLASS_ATMOSPHERIC_PRESSURE,
 | 
					 | 
				
			||||||
    DEVICE_CLASS_TEMPERATURE,
 | 
					    DEVICE_CLASS_TEMPERATURE,
 | 
				
			||||||
 | 
					    DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS,
 | 
				
			||||||
 | 
					    ICON_GAS_CYLINDER,
 | 
				
			||||||
 | 
					    ICON_GAUGE,
 | 
				
			||||||
    STATE_CLASS_MEASUREMENT,
 | 
					    STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
    UNIT_CELSIUS,
 | 
					    UNIT_CELSIUS,
 | 
				
			||||||
    UNIT_HECTOPASCAL,
 | 
					    UNIT_HECTOPASCAL,
 | 
				
			||||||
    UNIT_OHM,
 | 
					    UNIT_OHM,
 | 
				
			||||||
    UNIT_PARTS_PER_MILLION,
 | 
					    UNIT_PARTS_PER_MILLION,
 | 
				
			||||||
    UNIT_PERCENT,
 | 
					    UNIT_PERCENT,
 | 
				
			||||||
    ICON_GAS_CYLINDER,
 | 
					 | 
				
			||||||
    ICON_GAUGE,
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from . import (
 | 
					from . import (
 | 
				
			||||||
    BME680BSECComponent,
 | 
					    BME680BSECComponent,
 | 
				
			||||||
    CONF_BME680_BSEC_ID,
 | 
					    CONF_BME680_BSEC_ID,
 | 
				
			||||||
    CONF_SAMPLE_RATE,
 | 
					 | 
				
			||||||
    SAMPLE_RATE_OPTIONS,
 | 
					    SAMPLE_RATE_OPTIONS,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
DEPENDENCIES = ["bme680_bsec"]
 | 
					DEPENDENCIES = ["bme680_bsec"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CONF_IAQ = "iaq"
 | 
					CONF_IAQ = "iaq"
 | 
				
			||||||
CONF_IAQ_ACCURACY = "iaq_accuracy"
 | 
					 | 
				
			||||||
CONF_CO2_EQUIVALENT = "co2_equivalent"
 | 
					CONF_CO2_EQUIVALENT = "co2_equivalent"
 | 
				
			||||||
CONF_BREATH_VOC_EQUIVALENT = "breath_voc_equivalent"
 | 
					CONF_BREATH_VOC_EQUIVALENT = "breath_voc_equivalent"
 | 
				
			||||||
UNIT_IAQ = "IAQ"
 | 
					UNIT_IAQ = "IAQ"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,11 +1,11 @@
 | 
				
			|||||||
import esphome.codegen as cg
 | 
					import esphome.codegen as cg
 | 
				
			||||||
import esphome.config_validation as cv
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
from esphome.components import text_sensor
 | 
					from esphome.components import text_sensor
 | 
				
			||||||
 | 
					from esphome.const import CONF_IAQ_ACCURACY
 | 
				
			||||||
from . import BME680BSECComponent, CONF_BME680_BSEC_ID
 | 
					from . import BME680BSECComponent, CONF_BME680_BSEC_ID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
DEPENDENCIES = ["bme680_bsec"]
 | 
					DEPENDENCIES = ["bme680_bsec"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CONF_IAQ_ACCURACY = "iaq_accuracy"
 | 
					 | 
				
			||||||
ICON_ACCURACY = "mdi:checkbox-marked-circle-outline"
 | 
					ICON_ACCURACY = "mdi:checkbox-marked-circle-outline"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TYPES = [CONF_IAQ_ACCURACY]
 | 
					TYPES = [CONF_IAQ_ACCURACY]
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										196
									
								
								esphome/components/bme68x_bsec2/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										196
									
								
								esphome/components/bme68x_bsec2/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,196 @@
 | 
				
			|||||||
 | 
					import hashlib
 | 
				
			||||||
 | 
					from pathlib import Path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from esphome import core, external_files
 | 
				
			||||||
 | 
					import esphome.codegen as cg
 | 
				
			||||||
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
 | 
					from esphome.const import (
 | 
				
			||||||
 | 
					    CONF_ID,
 | 
				
			||||||
 | 
					    CONF_MODEL,
 | 
				
			||||||
 | 
					    CONF_RAW_DATA_ID,
 | 
				
			||||||
 | 
					    CONF_SAMPLE_RATE,
 | 
				
			||||||
 | 
					    CONF_TEMPERATURE_OFFSET,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CODEOWNERS = ["@neffs", "@kbx81"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DOMAIN = "bme68x_bsec2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					BSEC2_LIBRARY_VERSION = "v1.7.2502"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CONF_ALGORITHM_OUTPUT = "algorithm_output"
 | 
				
			||||||
 | 
					CONF_BME68X_BSEC2_ID = "bme68x_bsec2_id"
 | 
				
			||||||
 | 
					CONF_IAQ_MODE = "iaq_mode"
 | 
				
			||||||
 | 
					CONF_OPERATING_AGE = "operating_age"
 | 
				
			||||||
 | 
					CONF_STATE_SAVE_INTERVAL = "state_save_interval"
 | 
				
			||||||
 | 
					CONF_SUPPLY_VOLTAGE = "supply_voltage"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bme68x_bsec2_ns = cg.esphome_ns.namespace("bme68x_bsec2")
 | 
				
			||||||
 | 
					BME68xBSEC2Component = bme68x_bsec2_ns.class_("BME68xBSEC2Component", cg.Component)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MODEL_OPTIONS = ["bme680", "bme688"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					AlgorithmOutput = bme68x_bsec2_ns.enum("AlgorithmOutput")
 | 
				
			||||||
 | 
					ALGORITHM_OUTPUT_OPTIONS = {
 | 
				
			||||||
 | 
					    "classification": AlgorithmOutput.ALGORITHM_OUTPUT_CLASSIFICATION,
 | 
				
			||||||
 | 
					    "regression": AlgorithmOutput.ALGORITHM_OUTPUT_REGRESSION,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					OperatingAge = bme68x_bsec2_ns.enum("OperatingAge")
 | 
				
			||||||
 | 
					OPERATING_AGE_OPTIONS = {
 | 
				
			||||||
 | 
					    "4d": OperatingAge.OPERATING_AGE_4D,
 | 
				
			||||||
 | 
					    "28d": OperatingAge.OPERATING_AGE_28D,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SampleRate = bme68x_bsec2_ns.enum("SampleRate")
 | 
				
			||||||
 | 
					SAMPLE_RATE_OPTIONS = {
 | 
				
			||||||
 | 
					    "LP": SampleRate.SAMPLE_RATE_LP,
 | 
				
			||||||
 | 
					    "ULP": SampleRate.SAMPLE_RATE_ULP,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Voltage = bme68x_bsec2_ns.enum("Voltage")
 | 
				
			||||||
 | 
					VOLTAGE_OPTIONS = {
 | 
				
			||||||
 | 
					    "1.8V": Voltage.VOLTAGE_1_8V,
 | 
				
			||||||
 | 
					    "3.3V": Voltage.VOLTAGE_3_3V,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ALGORITHM_OUTPUT_FILE_NAME = {
 | 
				
			||||||
 | 
					    "classification": "sel",
 | 
				
			||||||
 | 
					    "regression": "reg",
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SAMPLE_RATE_FILE_NAME = {
 | 
				
			||||||
 | 
					    "LP": "3s",
 | 
				
			||||||
 | 
					    "ULP": "300s",
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					VOLTAGE_FILE_NAME = {
 | 
				
			||||||
 | 
					    "1.8V": "18v",
 | 
				
			||||||
 | 
					    "3.3V": "33v",
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _compute_local_file_path(url: str) -> Path:
 | 
				
			||||||
 | 
					    h = hashlib.new("sha256")
 | 
				
			||||||
 | 
					    h.update(url.encode())
 | 
				
			||||||
 | 
					    key = h.hexdigest()[:8]
 | 
				
			||||||
 | 
					    base_dir = external_files.compute_local_file_dir(DOMAIN)
 | 
				
			||||||
 | 
					    return base_dir / key
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _compute_url(config: dict) -> str:
 | 
				
			||||||
 | 
					    model = config.get(CONF_MODEL)
 | 
				
			||||||
 | 
					    operating_age = config.get(CONF_OPERATING_AGE)
 | 
				
			||||||
 | 
					    sample_rate = SAMPLE_RATE_FILE_NAME[config.get(CONF_SAMPLE_RATE)]
 | 
				
			||||||
 | 
					    volts = VOLTAGE_FILE_NAME[config.get(CONF_SUPPLY_VOLTAGE)]
 | 
				
			||||||
 | 
					    if model == "bme688":
 | 
				
			||||||
 | 
					        algo = ALGORITHM_OUTPUT_FILE_NAME[
 | 
				
			||||||
 | 
					            config.get(CONF_ALGORITHM_OUTPUT, "classification")
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					        filename = "bsec_selectivity"
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        algo = "iaq"
 | 
				
			||||||
 | 
					        filename = "bsec_iaq"
 | 
				
			||||||
 | 
					    return f"https://raw.githubusercontent.com/boschsensortec/Bosch-BSEC2-Library/{BSEC2_LIBRARY_VERSION}/src/config/{model}/{model}_{algo}_{volts}_{sample_rate}_{operating_age}/{filename}.txt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def download_bme68x_blob(config):
 | 
				
			||||||
 | 
					    url = _compute_url(config)
 | 
				
			||||||
 | 
					    path = _compute_local_file_path(url)
 | 
				
			||||||
 | 
					    external_files.download_content(url, path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def validate_bme68x(config):
 | 
				
			||||||
 | 
					    if CONF_ALGORITHM_OUTPUT not in config:
 | 
				
			||||||
 | 
					        return config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if config[CONF_MODEL] != "bme688":
 | 
				
			||||||
 | 
					        raise cv.Invalid(f"{CONF_ALGORITHM_OUTPUT} is only valid for BME688")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if config[CONF_ALGORITHM_OUTPUT] == "regression" and (
 | 
				
			||||||
 | 
					        config[CONF_OPERATING_AGE] != "4d"
 | 
				
			||||||
 | 
					        or config[CONF_SAMPLE_RATE] != "ULP"
 | 
				
			||||||
 | 
					        or config[CONF_SUPPLY_VOLTAGE] != "1.8V"
 | 
				
			||||||
 | 
					    ):
 | 
				
			||||||
 | 
					        raise cv.Invalid(
 | 
				
			||||||
 | 
					            f" To use '{CONF_ALGORITHM_OUTPUT}: regression', {CONF_OPERATING_AGE} must be '4d', {CONF_SAMPLE_RATE} must be 'ULP' and {CONF_SUPPLY_VOLTAGE} must be '1.8V'"
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    return config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CONFIG_SCHEMA_BASE = (
 | 
				
			||||||
 | 
					    cv.Schema(
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            cv.GenerateID(): cv.declare_id(BME68xBSEC2Component),
 | 
				
			||||||
 | 
					            cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8),
 | 
				
			||||||
 | 
					            cv.Required(CONF_MODEL): cv.one_of(*MODEL_OPTIONS, lower=True),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_ALGORITHM_OUTPUT): cv.enum(
 | 
				
			||||||
 | 
					                ALGORITHM_OUTPUT_OPTIONS, lower=True
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_OPERATING_AGE, default="28d"): cv.enum(
 | 
				
			||||||
 | 
					                OPERATING_AGE_OPTIONS, lower=True
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_SAMPLE_RATE, default="LP"): cv.enum(
 | 
				
			||||||
 | 
					                SAMPLE_RATE_OPTIONS, upper=True
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_SUPPLY_VOLTAGE, default="3.3V"): cv.enum(
 | 
				
			||||||
 | 
					                VOLTAGE_OPTIONS, upper=True
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_TEMPERATURE_OFFSET, default=0): cv.temperature,
 | 
				
			||||||
 | 
					            cv.Optional(
 | 
				
			||||||
 | 
					                CONF_STATE_SAVE_INTERVAL, default="6hours"
 | 
				
			||||||
 | 
					            ): cv.positive_time_period_minutes,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    .add_extra(cv.only_with_arduino)
 | 
				
			||||||
 | 
					    .add_extra(validate_bme68x)
 | 
				
			||||||
 | 
					    .add_extra(download_bme68x_blob)
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async def to_code_base(config):
 | 
				
			||||||
 | 
					    var = cg.new_Pvariable(config[CONF_ID])
 | 
				
			||||||
 | 
					    await cg.register_component(var, config)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if algo_output := config.get(CONF_ALGORITHM_OUTPUT):
 | 
				
			||||||
 | 
					        cg.add(var.set_algorithm_output(algo_output))
 | 
				
			||||||
 | 
					    cg.add(var.set_operating_age(config[CONF_OPERATING_AGE]))
 | 
				
			||||||
 | 
					    cg.add(var.set_sample_rate(config[CONF_SAMPLE_RATE]))
 | 
				
			||||||
 | 
					    cg.add(var.set_voltage(config[CONF_SUPPLY_VOLTAGE]))
 | 
				
			||||||
 | 
					    cg.add(var.set_temperature_offset(config[CONF_TEMPERATURE_OFFSET]))
 | 
				
			||||||
 | 
					    cg.add(
 | 
				
			||||||
 | 
					        var.set_state_save_interval(config[CONF_STATE_SAVE_INTERVAL].total_milliseconds)
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    path = _compute_local_file_path(_compute_url(config))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        with open(path, encoding="utf-8") as f:
 | 
				
			||||||
 | 
					            bsec2_iaq_config = f.read()
 | 
				
			||||||
 | 
					    except Exception as e:
 | 
				
			||||||
 | 
					        raise core.EsphomeError(f"Could not open binary configuration file {path}: {e}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Convert retrieved BSEC2 config to an array of ints
 | 
				
			||||||
 | 
					    rhs = [int(x) for x in bsec2_iaq_config.split(",")]
 | 
				
			||||||
 | 
					    # Create an array which will reside in program memory and configure the sensor instance to use it
 | 
				
			||||||
 | 
					    bsec2_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs)
 | 
				
			||||||
 | 
					    cg.add(var.set_bsec2_configuration(bsec2_arr, len(rhs)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Although this component does not use SPI, the BSEC2 library requires the SPI library
 | 
				
			||||||
 | 
					    cg.add_library("SPI", None)
 | 
				
			||||||
 | 
					    cg.add_library(
 | 
				
			||||||
 | 
					        "BME68x Sensor library",
 | 
				
			||||||
 | 
					        "1.1.40407",
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    cg.add_library(
 | 
				
			||||||
 | 
					        "BSEC2 Software Library",
 | 
				
			||||||
 | 
					        None,
 | 
				
			||||||
 | 
					        f"https://github.com/boschsensortec/Bosch-BSEC2-Library.git#{BSEC2_LIBRARY_VERSION}",
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cg.add_define("USE_BSEC2")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return var
 | 
				
			||||||
							
								
								
									
										523
									
								
								esphome/components/bme68x_bsec2/bme68x_bsec2.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										523
									
								
								esphome/components/bme68x_bsec2/bme68x_bsec2.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,523 @@
 | 
				
			|||||||
 | 
					#include "esphome/core/defines.h"
 | 
				
			||||||
 | 
					#include "esphome/core/helpers.h"
 | 
				
			||||||
 | 
					#include "esphome/core/log.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef USE_BSEC2
 | 
				
			||||||
 | 
					#include "bme68x_bsec2.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace esphome {
 | 
				
			||||||
 | 
					namespace bme68x_bsec2 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define BME68X_BSEC2_ALGORITHM_OUTPUT_LOG(a) (a == ALGORITHM_OUTPUT_CLASSIFICATION ? "Classification" : "Regression")
 | 
				
			||||||
 | 
					#define BME68X_BSEC2_OPERATING_AGE_LOG(o) (o == OPERATING_AGE_4D ? "4 days" : "28 days")
 | 
				
			||||||
 | 
					#define BME68X_BSEC2_SAMPLE_RATE_LOG(r) (r == SAMPLE_RATE_DEFAULT ? "Default" : (r == SAMPLE_RATE_ULP ? "ULP" : "LP"))
 | 
				
			||||||
 | 
					#define BME68X_BSEC2_VOLTAGE_LOG(v) (v == VOLTAGE_3_3V ? "3.3V" : "1.8V")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char *const TAG = "bme68x_bsec2.sensor";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const std::string IAQ_ACCURACY_STATES[4] = {"Stabilizing", "Uncertain", "Calibrating", "Calibrated"};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void BME68xBSEC2Component::setup() {
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "Setting up BME68X via BSEC2...");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  this->bsec_status_ = bsec_init_m(&this->bsec_instance_);
 | 
				
			||||||
 | 
					  if (this->bsec_status_ != BSEC_OK) {
 | 
				
			||||||
 | 
					    this->mark_failed();
 | 
				
			||||||
 | 
					    ESP_LOGE(TAG, "bsec_init_m failed: status %d", this->bsec_status_);
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  bsec_get_version_m(&this->bsec_instance_, &this->version_);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  this->bme68x_status_ = bme68x_init(&this->bme68x_);
 | 
				
			||||||
 | 
					  if (this->bme68x_status_ != BME68X_OK) {
 | 
				
			||||||
 | 
					    this->mark_failed();
 | 
				
			||||||
 | 
					    ESP_LOGE(TAG, "bme68x_init failed: status %d", this->bme68x_status_);
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (this->bsec2_configuration_ != nullptr && this->bsec2_configuration_length_) {
 | 
				
			||||||
 | 
					    this->set_config_(this->bsec2_configuration_, this->bsec2_configuration_length_);
 | 
				
			||||||
 | 
					    if (this->bsec_status_ != BSEC_OK) {
 | 
				
			||||||
 | 
					      this->mark_failed();
 | 
				
			||||||
 | 
					      ESP_LOGE(TAG, "bsec_set_configuration_m failed: status %d", this->bsec_status_);
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  this->update_subscription_();
 | 
				
			||||||
 | 
					  if (this->bsec_status_ != BSEC_OK) {
 | 
				
			||||||
 | 
					    this->mark_failed();
 | 
				
			||||||
 | 
					    ESP_LOGE(TAG, "bsec_update_subscription_m failed: status %d", this->bsec_status_);
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  this->load_state_();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void BME68xBSEC2Component::dump_config() {
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "BME68X via BSEC2:");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "  BSEC2 version: %d.%d.%d.%d", this->version_.major, this->version_.minor,
 | 
				
			||||||
 | 
					                this->version_.major_bugfix, this->version_.minor_bugfix);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "  BSEC2 configuration blob:");
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "    Configured: %s", YESNO(this->bsec2_blob_configured_));
 | 
				
			||||||
 | 
					  if (this->bsec2_configuration_ != nullptr && this->bsec2_configuration_length_) {
 | 
				
			||||||
 | 
					    ESP_LOGCONFIG(TAG, "    Size: %" PRIu32, this->bsec2_configuration_length_);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (this->is_failed()) {
 | 
				
			||||||
 | 
					    ESP_LOGE(TAG, "Communication failed (BSEC2 status: %d, BME68X status: %d)", this->bsec_status_,
 | 
				
			||||||
 | 
					             this->bme68x_status_);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (this->algorithm_output_ != ALGORITHM_OUTPUT_IAQ) {
 | 
				
			||||||
 | 
					    ESP_LOGCONFIG(TAG, "  Algorithm output: %s", BME68X_BSEC2_ALGORITHM_OUTPUT_LOG(this->algorithm_output_));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "  Operating age: %s", BME68X_BSEC2_OPERATING_AGE_LOG(this->operating_age_));
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "  Sample rate: %s", BME68X_BSEC2_SAMPLE_RATE_LOG(this->sample_rate_));
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "  Voltage: %s", BME68X_BSEC2_VOLTAGE_LOG(this->voltage_));
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "  State save interval: %ims", this->state_save_interval_ms_);
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "  Temperature offset: %.2f", this->temperature_offset_);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef USE_SENSOR
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Temperature", this->temperature_sensor_);
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "    Sample rate: %s", BME68X_BSEC2_SAMPLE_RATE_LOG(this->temperature_sample_rate_));
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Pressure", this->pressure_sensor_);
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "    Sample rate: %s", BME68X_BSEC2_SAMPLE_RATE_LOG(this->pressure_sample_rate_));
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Humidity", this->humidity_sensor_);
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "    Sample rate: %s", BME68X_BSEC2_SAMPLE_RATE_LOG(this->humidity_sample_rate_));
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Gas resistance", this->gas_resistance_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "CO2 equivalent", this->co2_equivalent_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Breath VOC equivalent", this->breath_voc_equivalent_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "IAQ", this->iaq_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "IAQ static", this->iaq_static_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Numeric IAQ accuracy", this->iaq_accuracy_sensor_);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#ifdef USE_TEXT_SENSOR
 | 
				
			||||||
 | 
					  LOG_TEXT_SENSOR("  ", "IAQ accuracy", this->iaq_accuracy_text_sensor_);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					float BME68xBSEC2Component::get_setup_priority() const { return setup_priority::DATA; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void BME68xBSEC2Component::loop() {
 | 
				
			||||||
 | 
					  this->run_();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (this->bsec_status_ < BSEC_OK || this->bme68x_status_ < BME68X_OK) {
 | 
				
			||||||
 | 
					    this->status_set_error();
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    this->status_clear_error();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (this->bsec_status_ > BSEC_OK || this->bme68x_status_ > BME68X_OK) {
 | 
				
			||||||
 | 
					    this->status_set_warning();
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    this->status_clear_warning();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  // Process a single action from the queue. These are primarily sensor state publishes
 | 
				
			||||||
 | 
					  // that in totality take too long to send in a single call.
 | 
				
			||||||
 | 
					  if (this->queue_.size()) {
 | 
				
			||||||
 | 
					    auto action = std::move(this->queue_.front());
 | 
				
			||||||
 | 
					    this->queue_.pop();
 | 
				
			||||||
 | 
					    action();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void BME68xBSEC2Component::set_config_(const uint8_t *config, uint32_t len) {
 | 
				
			||||||
 | 
					  if (len > BSEC_MAX_PROPERTY_BLOB_SIZE) {
 | 
				
			||||||
 | 
					    ESP_LOGE(TAG, "Configuration is larger than BSEC_MAX_PROPERTY_BLOB_SIZE");
 | 
				
			||||||
 | 
					    this->mark_failed();
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  uint8_t work_buffer[BSEC_MAX_PROPERTY_BLOB_SIZE];
 | 
				
			||||||
 | 
					  this->bsec_status_ = bsec_set_configuration_m(&this->bsec_instance_, config, len, work_buffer, sizeof(work_buffer));
 | 
				
			||||||
 | 
					  if (this->bsec_status_ == BSEC_OK) {
 | 
				
			||||||
 | 
					    this->bsec2_blob_configured_ = true;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					float BME68xBSEC2Component::calc_sensor_sample_rate_(SampleRate sample_rate) {
 | 
				
			||||||
 | 
					  if (sample_rate == SAMPLE_RATE_DEFAULT) {
 | 
				
			||||||
 | 
					    sample_rate = this->sample_rate_;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return sample_rate == SAMPLE_RATE_ULP ? BSEC_SAMPLE_RATE_ULP : BSEC_SAMPLE_RATE_LP;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void BME68xBSEC2Component::update_subscription_() {
 | 
				
			||||||
 | 
					  bsec_sensor_configuration_t virtual_sensors[BSEC_NUMBER_OUTPUTS];
 | 
				
			||||||
 | 
					  uint8_t num_virtual_sensors = 0;
 | 
				
			||||||
 | 
					#ifdef USE_SENSOR
 | 
				
			||||||
 | 
					  if (this->iaq_sensor_) {
 | 
				
			||||||
 | 
					    virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_IAQ;
 | 
				
			||||||
 | 
					    virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(SAMPLE_RATE_DEFAULT);
 | 
				
			||||||
 | 
					    num_virtual_sensors++;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (this->iaq_static_sensor_) {
 | 
				
			||||||
 | 
					    virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_STATIC_IAQ;
 | 
				
			||||||
 | 
					    virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(SAMPLE_RATE_DEFAULT);
 | 
				
			||||||
 | 
					    num_virtual_sensors++;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (this->co2_equivalent_sensor_) {
 | 
				
			||||||
 | 
					    virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_CO2_EQUIVALENT;
 | 
				
			||||||
 | 
					    virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(SAMPLE_RATE_DEFAULT);
 | 
				
			||||||
 | 
					    num_virtual_sensors++;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (this->breath_voc_equivalent_sensor_) {
 | 
				
			||||||
 | 
					    virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_BREATH_VOC_EQUIVALENT;
 | 
				
			||||||
 | 
					    virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(SAMPLE_RATE_DEFAULT);
 | 
				
			||||||
 | 
					    num_virtual_sensors++;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (this->pressure_sensor_) {
 | 
				
			||||||
 | 
					    virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_RAW_PRESSURE;
 | 
				
			||||||
 | 
					    virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(this->pressure_sample_rate_);
 | 
				
			||||||
 | 
					    num_virtual_sensors++;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (this->gas_resistance_sensor_) {
 | 
				
			||||||
 | 
					    virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_RAW_GAS;
 | 
				
			||||||
 | 
					    virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(SAMPLE_RATE_DEFAULT);
 | 
				
			||||||
 | 
					    num_virtual_sensors++;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (this->temperature_sensor_) {
 | 
				
			||||||
 | 
					    virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE;
 | 
				
			||||||
 | 
					    virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(this->temperature_sample_rate_);
 | 
				
			||||||
 | 
					    num_virtual_sensors++;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (this->humidity_sensor_) {
 | 
				
			||||||
 | 
					    virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY;
 | 
				
			||||||
 | 
					    virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(this->humidity_sample_rate_);
 | 
				
			||||||
 | 
					    num_virtual_sensors++;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					  bsec_sensor_configuration_t sensor_settings[BSEC_MAX_PHYSICAL_SENSOR];
 | 
				
			||||||
 | 
					  uint8_t num_sensor_settings = BSEC_MAX_PHYSICAL_SENSOR;
 | 
				
			||||||
 | 
					  this->bsec_status_ = bsec_update_subscription_m(&this->bsec_instance_, virtual_sensors, num_virtual_sensors,
 | 
				
			||||||
 | 
					                                                  sensor_settings, &num_sensor_settings);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void BME68xBSEC2Component::run_() {
 | 
				
			||||||
 | 
					  int64_t curr_time_ns = this->get_time_ns_();
 | 
				
			||||||
 | 
					  if (curr_time_ns < this->next_call_ns_) {
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  this->op_mode_ = this->bsec_settings_.op_mode;
 | 
				
			||||||
 | 
					  uint8_t status;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ESP_LOGV(TAG, "Performing sensor run");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  struct bme68x_conf bme68x_conf;
 | 
				
			||||||
 | 
					  this->bsec_status_ = bsec_sensor_control_m(&this->bsec_instance_, curr_time_ns, &this->bsec_settings_);
 | 
				
			||||||
 | 
					  if (this->bsec_status_ < BSEC_OK) {
 | 
				
			||||||
 | 
					    ESP_LOGW(TAG, "Failed to fetch sensor control settings (BSEC2 error code %d)", this->bsec_status_);
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  this->next_call_ns_ = this->bsec_settings_.next_call;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (this->bsec_settings_.trigger_measurement) {
 | 
				
			||||||
 | 
					    bme68x_get_conf(&bme68x_conf, &this->bme68x_);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bme68x_conf.os_hum = this->bsec_settings_.humidity_oversampling;
 | 
				
			||||||
 | 
					    bme68x_conf.os_temp = this->bsec_settings_.temperature_oversampling;
 | 
				
			||||||
 | 
					    bme68x_conf.os_pres = this->bsec_settings_.pressure_oversampling;
 | 
				
			||||||
 | 
					    bme68x_set_conf(&bme68x_conf, &this->bme68x_);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    switch (this->bsec_settings_.op_mode) {
 | 
				
			||||||
 | 
					      case BME68X_FORCED_MODE:
 | 
				
			||||||
 | 
					        this->bme68x_heatr_conf_.enable = BME68X_ENABLE;
 | 
				
			||||||
 | 
					        this->bme68x_heatr_conf_.heatr_temp = this->bsec_settings_.heater_temperature;
 | 
				
			||||||
 | 
					        this->bme68x_heatr_conf_.heatr_dur = this->bsec_settings_.heater_duration;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        status = bme68x_set_op_mode(this->bsec_settings_.op_mode, &this->bme68x_);
 | 
				
			||||||
 | 
					        status = bme68x_set_heatr_conf(BME68X_FORCED_MODE, &this->bme68x_heatr_conf_, &this->bme68x_);
 | 
				
			||||||
 | 
					        status = bme68x_set_op_mode(BME68X_FORCED_MODE, &this->bme68x_);
 | 
				
			||||||
 | 
					        this->op_mode_ = BME68X_FORCED_MODE;
 | 
				
			||||||
 | 
					        this->sleep_mode_ = false;
 | 
				
			||||||
 | 
					        ESP_LOGV(TAG, "Using forced mode");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      case BME68X_PARALLEL_MODE:
 | 
				
			||||||
 | 
					        if (this->op_mode_ != this->bsec_settings_.op_mode) {
 | 
				
			||||||
 | 
					          this->bme68x_heatr_conf_.enable = BME68X_ENABLE;
 | 
				
			||||||
 | 
					          this->bme68x_heatr_conf_.heatr_temp_prof = this->bsec_settings_.heater_temperature_profile;
 | 
				
			||||||
 | 
					          this->bme68x_heatr_conf_.heatr_dur_prof = this->bsec_settings_.heater_duration_profile;
 | 
				
			||||||
 | 
					          this->bme68x_heatr_conf_.profile_len = this->bsec_settings_.heater_profile_len;
 | 
				
			||||||
 | 
					          this->bme68x_heatr_conf_.shared_heatr_dur =
 | 
				
			||||||
 | 
					              BSEC_TOTAL_HEAT_DUR -
 | 
				
			||||||
 | 
					              (bme68x_get_meas_dur(BME68X_PARALLEL_MODE, &bme68x_conf, &this->bme68x_) / INT64_C(1000));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          status = bme68x_set_heatr_conf(BME68X_PARALLEL_MODE, &this->bme68x_heatr_conf_, &this->bme68x_);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          status = bme68x_set_op_mode(BME68X_PARALLEL_MODE, &this->bme68x_);
 | 
				
			||||||
 | 
					          this->op_mode_ = BME68X_PARALLEL_MODE;
 | 
				
			||||||
 | 
					          this->sleep_mode_ = false;
 | 
				
			||||||
 | 
					          ESP_LOGV(TAG, "Using parallel mode");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      case BME68X_SLEEP_MODE:
 | 
				
			||||||
 | 
					        if (!this->sleep_mode_) {
 | 
				
			||||||
 | 
					          bme68x_set_op_mode(BME68X_SLEEP_MODE, &this->bme68x_);
 | 
				
			||||||
 | 
					          this->sleep_mode_ = true;
 | 
				
			||||||
 | 
					          ESP_LOGV(TAG, "Using sleep mode");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    uint32_t meas_dur = 0;
 | 
				
			||||||
 | 
					    meas_dur = bme68x_get_meas_dur(this->op_mode_, &bme68x_conf, &this->bme68x_);
 | 
				
			||||||
 | 
					    ESP_LOGV(TAG, "Queueing read in %uus", meas_dur);
 | 
				
			||||||
 | 
					    this->set_timeout("read", meas_dur / 1000, [this, curr_time_ns]() { this->read_(curr_time_ns); });
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    ESP_LOGV(TAG, "Measurement not required");
 | 
				
			||||||
 | 
					    this->read_(curr_time_ns);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void BME68xBSEC2Component::read_(int64_t trigger_time_ns) {
 | 
				
			||||||
 | 
					  ESP_LOGV(TAG, "Reading data");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (this->bsec_settings_.trigger_measurement) {
 | 
				
			||||||
 | 
					    uint8_t current_op_mode;
 | 
				
			||||||
 | 
					    this->bme68x_status_ = bme68x_get_op_mode(¤t_op_mode, &this->bme68x_);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (current_op_mode == BME68X_SLEEP_MODE) {
 | 
				
			||||||
 | 
					      ESP_LOGV(TAG, "Still in sleep mode, doing nothing");
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!this->bsec_settings_.process_data) {
 | 
				
			||||||
 | 
					    ESP_LOGV(TAG, "Data processing not required");
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  struct bme68x_data data[3];
 | 
				
			||||||
 | 
					  uint8_t nFields = 0;
 | 
				
			||||||
 | 
					  this->bme68x_status_ = bme68x_get_data(this->op_mode_, &data[0], &nFields, &this->bme68x_);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (this->bme68x_status_ != BME68X_OK) {
 | 
				
			||||||
 | 
					    ESP_LOGW(TAG, "Failed to get sensor data (BME68X error code %d)", this->bme68x_status_);
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (nFields < 1) {
 | 
				
			||||||
 | 
					    ESP_LOGD(TAG, "BME68X did not provide new data");
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (uint8_t i = 0; i < nFields; i++) {
 | 
				
			||||||
 | 
					    bsec_input_t inputs[BSEC_MAX_PHYSICAL_SENSOR];  // Temperature, Pressure, Humidity & Gas Resistance
 | 
				
			||||||
 | 
					    uint8_t num_inputs = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (BSEC_CHECK_INPUT(this->bsec_settings_.process_data, BSEC_INPUT_TEMPERATURE)) {
 | 
				
			||||||
 | 
					      inputs[num_inputs].sensor_id = BSEC_INPUT_TEMPERATURE;
 | 
				
			||||||
 | 
					      inputs[num_inputs].signal = data[i].temperature;
 | 
				
			||||||
 | 
					      inputs[num_inputs].time_stamp = trigger_time_ns;
 | 
				
			||||||
 | 
					      num_inputs++;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (BSEC_CHECK_INPUT(this->bsec_settings_.process_data, BSEC_INPUT_HEATSOURCE)) {
 | 
				
			||||||
 | 
					      inputs[num_inputs].sensor_id = BSEC_INPUT_HEATSOURCE;
 | 
				
			||||||
 | 
					      inputs[num_inputs].signal = this->temperature_offset_;
 | 
				
			||||||
 | 
					      inputs[num_inputs].time_stamp = trigger_time_ns;
 | 
				
			||||||
 | 
					      num_inputs++;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (BSEC_CHECK_INPUT(this->bsec_settings_.process_data, BSEC_INPUT_HUMIDITY)) {
 | 
				
			||||||
 | 
					      inputs[num_inputs].sensor_id = BSEC_INPUT_HUMIDITY;
 | 
				
			||||||
 | 
					      inputs[num_inputs].signal = data[i].humidity;
 | 
				
			||||||
 | 
					      inputs[num_inputs].time_stamp = trigger_time_ns;
 | 
				
			||||||
 | 
					      num_inputs++;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (BSEC_CHECK_INPUT(this->bsec_settings_.process_data, BSEC_INPUT_PRESSURE)) {
 | 
				
			||||||
 | 
					      inputs[num_inputs].sensor_id = BSEC_INPUT_PRESSURE;
 | 
				
			||||||
 | 
					      inputs[num_inputs].signal = data[i].pressure;
 | 
				
			||||||
 | 
					      inputs[num_inputs].time_stamp = trigger_time_ns;
 | 
				
			||||||
 | 
					      num_inputs++;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (BSEC_CHECK_INPUT(this->bsec_settings_.process_data, BSEC_INPUT_GASRESISTOR)) {
 | 
				
			||||||
 | 
					      if (data[i].status & BME68X_GASM_VALID_MSK) {
 | 
				
			||||||
 | 
					        inputs[num_inputs].sensor_id = BSEC_INPUT_GASRESISTOR;
 | 
				
			||||||
 | 
					        inputs[num_inputs].signal = data[i].gas_resistance;
 | 
				
			||||||
 | 
					        inputs[num_inputs].time_stamp = trigger_time_ns;
 | 
				
			||||||
 | 
					        num_inputs++;
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        ESP_LOGD(TAG, "BME68X did not report gas data");
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (BSEC_CHECK_INPUT(this->bsec_settings_.process_data, BSEC_INPUT_PROFILE_PART) &&
 | 
				
			||||||
 | 
					        (data[i].status & BME68X_GASM_VALID_MSK)) {
 | 
				
			||||||
 | 
					      inputs[num_inputs].sensor_id = BSEC_INPUT_PROFILE_PART;
 | 
				
			||||||
 | 
					      inputs[num_inputs].signal = (this->op_mode_ == BME68X_FORCED_MODE) ? 0 : data[i].gas_index;
 | 
				
			||||||
 | 
					      inputs[num_inputs].time_stamp = trigger_time_ns;
 | 
				
			||||||
 | 
					      num_inputs++;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (num_inputs < 1) {
 | 
				
			||||||
 | 
					      ESP_LOGD(TAG, "No signal inputs available for BSEC2");
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bsec_output_t outputs[BSEC_NUMBER_OUTPUTS];
 | 
				
			||||||
 | 
					    uint8_t num_outputs = BSEC_NUMBER_OUTPUTS;
 | 
				
			||||||
 | 
					    this->bsec_status_ = bsec_do_steps_m(&this->bsec_instance_, inputs, num_inputs, outputs, &num_outputs);
 | 
				
			||||||
 | 
					    if (this->bsec_status_ != BSEC_OK) {
 | 
				
			||||||
 | 
					      ESP_LOGW(TAG, "BSEC2 failed to process signals (BSEC2 error code %d)", this->bsec_status_);
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (num_outputs < 1) {
 | 
				
			||||||
 | 
					      ESP_LOGD(TAG, "No signal outputs provided by BSEC2");
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this->publish_(outputs, num_outputs);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void BME68xBSEC2Component::publish_(const bsec_output_t *outputs, uint8_t num_outputs) {
 | 
				
			||||||
 | 
					  ESP_LOGV(TAG, "Publishing sensor states");
 | 
				
			||||||
 | 
					  bool update_accuracy = false;
 | 
				
			||||||
 | 
					  uint8_t max_accuracy = 0;
 | 
				
			||||||
 | 
					  for (uint8_t i = 0; i < num_outputs; i++) {
 | 
				
			||||||
 | 
					    float signal = outputs[i].signal;
 | 
				
			||||||
 | 
					    switch (outputs[i].sensor_id) {
 | 
				
			||||||
 | 
					      case BSEC_OUTPUT_IAQ:
 | 
				
			||||||
 | 
					        max_accuracy = std::max(outputs[i].accuracy, max_accuracy);
 | 
				
			||||||
 | 
					        update_accuracy = true;
 | 
				
			||||||
 | 
					#ifdef USE_SENSOR
 | 
				
			||||||
 | 
					        this->queue_push_([this, signal]() { this->publish_sensor_(this->iaq_sensor_, signal); });
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      case BSEC_OUTPUT_STATIC_IAQ:
 | 
				
			||||||
 | 
					        max_accuracy = std::max(outputs[i].accuracy, max_accuracy);
 | 
				
			||||||
 | 
					        update_accuracy = true;
 | 
				
			||||||
 | 
					#ifdef USE_SENSOR
 | 
				
			||||||
 | 
					        this->queue_push_([this, signal]() { this->publish_sensor_(this->iaq_static_sensor_, signal); });
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      case BSEC_OUTPUT_CO2_EQUIVALENT:
 | 
				
			||||||
 | 
					#ifdef USE_SENSOR
 | 
				
			||||||
 | 
					        this->queue_push_([this, signal]() { this->publish_sensor_(this->co2_equivalent_sensor_, signal); });
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      case BSEC_OUTPUT_BREATH_VOC_EQUIVALENT:
 | 
				
			||||||
 | 
					#ifdef USE_SENSOR
 | 
				
			||||||
 | 
					        this->queue_push_([this, signal]() { this->publish_sensor_(this->breath_voc_equivalent_sensor_, signal); });
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      case BSEC_OUTPUT_RAW_PRESSURE:
 | 
				
			||||||
 | 
					#ifdef USE_SENSOR
 | 
				
			||||||
 | 
					        this->queue_push_([this, signal]() { this->publish_sensor_(this->pressure_sensor_, signal / 100.0f); });
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      case BSEC_OUTPUT_RAW_GAS:
 | 
				
			||||||
 | 
					#ifdef USE_SENSOR
 | 
				
			||||||
 | 
					        this->queue_push_([this, signal]() { this->publish_sensor_(this->gas_resistance_sensor_, signal); });
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE:
 | 
				
			||||||
 | 
					#ifdef USE_SENSOR
 | 
				
			||||||
 | 
					        this->queue_push_([this, signal]() { this->publish_sensor_(this->temperature_sensor_, signal); });
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY:
 | 
				
			||||||
 | 
					#ifdef USE_SENSOR
 | 
				
			||||||
 | 
					        this->queue_push_([this, signal]() { this->publish_sensor_(this->humidity_sensor_, signal); });
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (update_accuracy) {
 | 
				
			||||||
 | 
					#ifdef USE_SENSOR
 | 
				
			||||||
 | 
					    this->queue_push_(
 | 
				
			||||||
 | 
					        [this, max_accuracy]() { this->publish_sensor_(this->iaq_accuracy_sensor_, max_accuracy, true); });
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#ifdef USE_TEXT_SENSOR
 | 
				
			||||||
 | 
					    this->queue_push_([this, max_accuracy]() {
 | 
				
			||||||
 | 
					      this->publish_sensor_(this->iaq_accuracy_text_sensor_, IAQ_ACCURACY_STATES[max_accuracy]);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					    // Queue up an opportunity to save state
 | 
				
			||||||
 | 
					    this->queue_push_([this, max_accuracy]() { this->save_state_(max_accuracy); });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int64_t BME68xBSEC2Component::get_time_ns_() {
 | 
				
			||||||
 | 
					  int64_t time_ms = millis();
 | 
				
			||||||
 | 
					  if (this->last_time_ms_ > time_ms) {
 | 
				
			||||||
 | 
					    this->millis_overflow_counter_++;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  this->last_time_ms_ = time_ms;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (time_ms + ((int64_t) this->millis_overflow_counter_ << 32)) * INT64_C(1000000);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef USE_SENSOR
 | 
				
			||||||
 | 
					void BME68xBSEC2Component::publish_sensor_(sensor::Sensor *sensor, float value, bool change_only) {
 | 
				
			||||||
 | 
					  if (!sensor || (change_only && sensor->has_state() && sensor->state == value)) {
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  sensor->publish_state(value);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef USE_TEXT_SENSOR
 | 
				
			||||||
 | 
					void BME68xBSEC2Component::publish_sensor_(text_sensor::TextSensor *sensor, const std::string &value) {
 | 
				
			||||||
 | 
					  if (!sensor || (sensor->has_state() && sensor->state == value)) {
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  sensor->publish_state(value);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void BME68xBSEC2Component::load_state_() {
 | 
				
			||||||
 | 
					  uint32_t hash = this->get_hash();
 | 
				
			||||||
 | 
					  this->bsec_state_ = global_preferences->make_preference<uint8_t[BSEC_MAX_STATE_BLOB_SIZE]>(hash, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  uint8_t state[BSEC_MAX_STATE_BLOB_SIZE];
 | 
				
			||||||
 | 
					  if (this->bsec_state_.load(&state)) {
 | 
				
			||||||
 | 
					    ESP_LOGV(TAG, "Loading state");
 | 
				
			||||||
 | 
					    uint8_t work_buffer[BSEC_MAX_WORKBUFFER_SIZE];
 | 
				
			||||||
 | 
					    this->bsec_status_ =
 | 
				
			||||||
 | 
					        bsec_set_state_m(&this->bsec_instance_, state, BSEC_MAX_STATE_BLOB_SIZE, work_buffer, sizeof(work_buffer));
 | 
				
			||||||
 | 
					    if (this->bsec_status_ != BSEC_OK) {
 | 
				
			||||||
 | 
					      ESP_LOGW(TAG, "Failed to load state (BSEC2 error code %d)", this->bsec_status_);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    ESP_LOGI(TAG, "Loaded state");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void BME68xBSEC2Component::save_state_(uint8_t accuracy) {
 | 
				
			||||||
 | 
					  if (accuracy < 3 || (millis() - this->last_state_save_ms_ < this->state_save_interval_ms_)) {
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ESP_LOGV(TAG, "Saving state");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  uint8_t state[BSEC_MAX_STATE_BLOB_SIZE];
 | 
				
			||||||
 | 
					  uint8_t work_buffer[BSEC_MAX_STATE_BLOB_SIZE];
 | 
				
			||||||
 | 
					  uint32_t num_serialized_state = BSEC_MAX_STATE_BLOB_SIZE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  this->bsec_status_ = bsec_get_state_m(&this->bsec_instance_, 0, state, BSEC_MAX_STATE_BLOB_SIZE, work_buffer,
 | 
				
			||||||
 | 
					                                        BSEC_MAX_STATE_BLOB_SIZE, &num_serialized_state);
 | 
				
			||||||
 | 
					  if (this->bsec_status_ != BSEC_OK) {
 | 
				
			||||||
 | 
					    ESP_LOGW(TAG, "Failed fetch state for save (BSEC2 error code %d)", this->bsec_status_);
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!this->bsec_state_.save(&state)) {
 | 
				
			||||||
 | 
					    ESP_LOGW(TAG, "Failed to save state");
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  this->last_state_save_ms_ = millis();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ESP_LOGI(TAG, "Saved state");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace bme68x_bsec2
 | 
				
			||||||
 | 
					}  // namespace esphome
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										163
									
								
								esphome/components/bme68x_bsec2/bme68x_bsec2.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								esphome/components/bme68x_bsec2/bme68x_bsec2.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,163 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "esphome/core/component.h"
 | 
				
			||||||
 | 
					#include "esphome/core/defines.h"
 | 
				
			||||||
 | 
					#include "esphome/core/preferences.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef USE_BSEC2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef USE_SENSOR
 | 
				
			||||||
 | 
					#include "esphome/components/sensor/sensor.h"
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef USE_TEXT_SENSOR
 | 
				
			||||||
 | 
					#include "esphome/components/text_sensor/text_sensor.h"
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <cinttypes>
 | 
				
			||||||
 | 
					#include <queue>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <bsec2.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace esphome {
 | 
				
			||||||
 | 
					namespace bme68x_bsec2 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum AlgorithmOutput {
 | 
				
			||||||
 | 
					  ALGORITHM_OUTPUT_IAQ,
 | 
				
			||||||
 | 
					  ALGORITHM_OUTPUT_CLASSIFICATION,
 | 
				
			||||||
 | 
					  ALGORITHM_OUTPUT_REGRESSION,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum OperatingAge {
 | 
				
			||||||
 | 
					  OPERATING_AGE_4D,
 | 
				
			||||||
 | 
					  OPERATING_AGE_28D,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum SampleRate {
 | 
				
			||||||
 | 
					  SAMPLE_RATE_LP = 0,
 | 
				
			||||||
 | 
					  SAMPLE_RATE_ULP = 1,
 | 
				
			||||||
 | 
					  SAMPLE_RATE_DEFAULT = 2,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum Voltage {
 | 
				
			||||||
 | 
					  VOLTAGE_1_8V,
 | 
				
			||||||
 | 
					  VOLTAGE_3_3V,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class BME68xBSEC2Component : public Component {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  void setup() override;
 | 
				
			||||||
 | 
					  void dump_config() override;
 | 
				
			||||||
 | 
					  float get_setup_priority() const override;
 | 
				
			||||||
 | 
					  void loop() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void set_algorithm_output(AlgorithmOutput algorithm_output) { this->algorithm_output_ = algorithm_output; }
 | 
				
			||||||
 | 
					  void set_operating_age(OperatingAge operating_age) { this->operating_age_ = operating_age; }
 | 
				
			||||||
 | 
					  void set_temperature_offset(float offset) { this->temperature_offset_ = offset; }
 | 
				
			||||||
 | 
					  void set_voltage(Voltage voltage) { this->voltage_ = voltage; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void set_sample_rate(SampleRate sample_rate) { this->sample_rate_ = sample_rate; }
 | 
				
			||||||
 | 
					  void set_temperature_sample_rate(SampleRate sample_rate) { this->temperature_sample_rate_ = sample_rate; }
 | 
				
			||||||
 | 
					  void set_pressure_sample_rate(SampleRate sample_rate) { this->pressure_sample_rate_ = sample_rate; }
 | 
				
			||||||
 | 
					  void set_humidity_sample_rate(SampleRate sample_rate) { this->humidity_sample_rate_ = sample_rate; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void set_bsec2_configuration(const uint8_t *data, const uint32_t len) {
 | 
				
			||||||
 | 
					    this->bsec2_configuration_ = data;
 | 
				
			||||||
 | 
					    this->bsec2_configuration_length_ = len;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void set_state_save_interval(uint32_t interval) { this->state_save_interval_ms_ = interval; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef USE_SENSOR
 | 
				
			||||||
 | 
					  void set_temperature_sensor(sensor::Sensor *sensor) { this->temperature_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_pressure_sensor(sensor::Sensor *sensor) { this->pressure_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_humidity_sensor(sensor::Sensor *sensor) { this->humidity_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_gas_resistance_sensor(sensor::Sensor *sensor) { this->gas_resistance_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_iaq_sensor(sensor::Sensor *sensor) { this->iaq_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_iaq_static_sensor(sensor::Sensor *sensor) { this->iaq_static_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_iaq_accuracy_sensor(sensor::Sensor *sensor) { this->iaq_accuracy_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_co2_equivalent_sensor(sensor::Sensor *sensor) { this->co2_equivalent_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_breath_voc_equivalent_sensor(sensor::Sensor *sensor) { this->breath_voc_equivalent_sensor_ = sensor; }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#ifdef USE_TEXT_SENSOR
 | 
				
			||||||
 | 
					  void set_iaq_accuracy_text_sensor(text_sensor::TextSensor *sensor) { this->iaq_accuracy_text_sensor_ = sensor; }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					  virtual uint32_t get_hash() = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 protected:
 | 
				
			||||||
 | 
					  void set_config_(const uint8_t *config, u_int32_t len);
 | 
				
			||||||
 | 
					  float calc_sensor_sample_rate_(SampleRate sample_rate);
 | 
				
			||||||
 | 
					  void update_subscription_();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void run_();
 | 
				
			||||||
 | 
					  void read_(int64_t trigger_time_ns);
 | 
				
			||||||
 | 
					  void publish_(const bsec_output_t *outputs, uint8_t num_outputs);
 | 
				
			||||||
 | 
					  int64_t get_time_ns_();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef USE_SENSOR
 | 
				
			||||||
 | 
					  void publish_sensor_(sensor::Sensor *sensor, float value, bool change_only = false);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#ifdef USE_TEXT_SENSOR
 | 
				
			||||||
 | 
					  void publish_sensor_(text_sensor::TextSensor *sensor, const std::string &value);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void load_state_();
 | 
				
			||||||
 | 
					  void save_state_(uint8_t accuracy);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void queue_push_(std::function<void()> &&f) { this->queue_.push(std::move(f)); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  struct bme68x_dev bme68x_;
 | 
				
			||||||
 | 
					  bsec_bme_settings_t bsec_settings_;
 | 
				
			||||||
 | 
					  bsec_version_t version_;
 | 
				
			||||||
 | 
					  uint8_t bsec_instance_[BSEC_INSTANCE_SIZE];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  struct bme68x_heatr_conf bme68x_heatr_conf_;
 | 
				
			||||||
 | 
					  uint8_t op_mode_;  // operating mode of sensor
 | 
				
			||||||
 | 
					  bool sleep_mode_;
 | 
				
			||||||
 | 
					  bsec_library_return_t bsec_status_{BSEC_OK};
 | 
				
			||||||
 | 
					  int8_t bme68x_status_{BME68X_OK};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  int64_t last_time_ms_{0};
 | 
				
			||||||
 | 
					  uint32_t millis_overflow_counter_{0};
 | 
				
			||||||
 | 
					  int64_t next_call_ns_{0};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  std::queue<std::function<void()>> queue_;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  uint8_t const *bsec2_configuration_{nullptr};
 | 
				
			||||||
 | 
					  uint32_t bsec2_configuration_length_{0};
 | 
				
			||||||
 | 
					  bool bsec2_blob_configured_{false};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ESPPreferenceObject bsec_state_;
 | 
				
			||||||
 | 
					  uint32_t state_save_interval_ms_{21600000};  // 6 hours - 4 times a day
 | 
				
			||||||
 | 
					  uint32_t last_state_save_ms_ = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  float temperature_offset_{0};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  AlgorithmOutput algorithm_output_{ALGORITHM_OUTPUT_IAQ};
 | 
				
			||||||
 | 
					  OperatingAge operating_age_{OPERATING_AGE_28D};
 | 
				
			||||||
 | 
					  Voltage voltage_{VOLTAGE_3_3V};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  SampleRate sample_rate_{SAMPLE_RATE_LP};  // Core/gas sample rate
 | 
				
			||||||
 | 
					  SampleRate temperature_sample_rate_{SAMPLE_RATE_DEFAULT};
 | 
				
			||||||
 | 
					  SampleRate pressure_sample_rate_{SAMPLE_RATE_DEFAULT};
 | 
				
			||||||
 | 
					  SampleRate humidity_sample_rate_{SAMPLE_RATE_DEFAULT};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef USE_SENSOR
 | 
				
			||||||
 | 
					  sensor::Sensor *temperature_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *pressure_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *humidity_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *gas_resistance_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *iaq_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *iaq_static_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *iaq_accuracy_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *co2_equivalent_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *breath_voc_equivalent_sensor_{nullptr};
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#ifdef USE_TEXT_SENSOR
 | 
				
			||||||
 | 
					  text_sensor::TextSensor *iaq_accuracy_text_sensor_{nullptr};
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace bme68x_bsec2
 | 
				
			||||||
 | 
					}  // namespace esphome
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										130
									
								
								esphome/components/bme68x_bsec2/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								esphome/components/bme68x_bsec2/sensor.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,130 @@
 | 
				
			|||||||
 | 
					import esphome.codegen as cg
 | 
				
			||||||
 | 
					from esphome.components import sensor
 | 
				
			||||||
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
 | 
					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_HUMIDITY,
 | 
				
			||||||
 | 
					    DEVICE_CLASS_TEMPERATURE,
 | 
				
			||||||
 | 
					    ICON_GAS_CYLINDER,
 | 
				
			||||||
 | 
					    ICON_GAUGE,
 | 
				
			||||||
 | 
					    ICON_THERMOMETER,
 | 
				
			||||||
 | 
					    ICON_WATER_PERCENT,
 | 
				
			||||||
 | 
					    STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					    UNIT_CELSIUS,
 | 
				
			||||||
 | 
					    UNIT_HECTOPASCAL,
 | 
				
			||||||
 | 
					    UNIT_OHM,
 | 
				
			||||||
 | 
					    UNIT_PARTS_PER_MILLION,
 | 
				
			||||||
 | 
					    UNIT_PERCENT,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from . import CONF_BME68X_BSEC2_ID, SAMPLE_RATE_OPTIONS, BME68xBSEC2Component
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEPENDENCIES = ["bme68x_bsec2"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CONF_BREATH_VOC_EQUIVALENT = "breath_voc_equivalent"
 | 
				
			||||||
 | 
					CONF_CO2_EQUIVALENT = "co2_equivalent"
 | 
				
			||||||
 | 
					CONF_IAQ = "iaq"
 | 
				
			||||||
 | 
					CONF_IAQ_STATIC = "iaq_static"
 | 
				
			||||||
 | 
					ICON_ACCURACY = "mdi:checkbox-marked-circle-outline"
 | 
				
			||||||
 | 
					ICON_TEST_TUBE = "mdi:test-tube"
 | 
				
			||||||
 | 
					UNIT_IAQ = "IAQ"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TYPES = [
 | 
				
			||||||
 | 
					    CONF_TEMPERATURE,
 | 
				
			||||||
 | 
					    CONF_PRESSURE,
 | 
				
			||||||
 | 
					    CONF_HUMIDITY,
 | 
				
			||||||
 | 
					    CONF_GAS_RESISTANCE,
 | 
				
			||||||
 | 
					    CONF_IAQ,
 | 
				
			||||||
 | 
					    CONF_IAQ_STATIC,
 | 
				
			||||||
 | 
					    CONF_IAQ_ACCURACY,
 | 
				
			||||||
 | 
					    CONF_CO2_EQUIVALENT,
 | 
				
			||||||
 | 
					    CONF_BREATH_VOC_EQUIVALENT,
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CONFIG_SCHEMA = cv.Schema(
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        cv.GenerateID(CONF_BME68X_BSEC2_ID): cv.use_id(BME68xBSEC2Component),
 | 
				
			||||||
 | 
					        cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
 | 
				
			||||||
 | 
					            unit_of_measurement=UNIT_CELSIUS,
 | 
				
			||||||
 | 
					            icon=ICON_THERMOMETER,
 | 
				
			||||||
 | 
					            accuracy_decimals=1,
 | 
				
			||||||
 | 
					            device_class=DEVICE_CLASS_TEMPERATURE,
 | 
				
			||||||
 | 
					            state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					        ).extend(
 | 
				
			||||||
 | 
					            {cv.Optional(CONF_SAMPLE_RATE): cv.enum(SAMPLE_RATE_OPTIONS, upper=True)}
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
 | 
				
			||||||
 | 
					            unit_of_measurement=UNIT_HECTOPASCAL,
 | 
				
			||||||
 | 
					            icon=ICON_GAUGE,
 | 
				
			||||||
 | 
					            accuracy_decimals=1,
 | 
				
			||||||
 | 
					            device_class=DEVICE_CLASS_ATMOSPHERIC_PRESSURE,
 | 
				
			||||||
 | 
					            state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					        ).extend(
 | 
				
			||||||
 | 
					            {cv.Optional(CONF_SAMPLE_RATE): cv.enum(SAMPLE_RATE_OPTIONS, upper=True)}
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
 | 
				
			||||||
 | 
					            unit_of_measurement=UNIT_PERCENT,
 | 
				
			||||||
 | 
					            icon=ICON_WATER_PERCENT,
 | 
				
			||||||
 | 
					            accuracy_decimals=1,
 | 
				
			||||||
 | 
					            device_class=DEVICE_CLASS_HUMIDITY,
 | 
				
			||||||
 | 
					            state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					        ).extend(
 | 
				
			||||||
 | 
					            {cv.Optional(CONF_SAMPLE_RATE): cv.enum(SAMPLE_RATE_OPTIONS, upper=True)}
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        cv.Optional(CONF_GAS_RESISTANCE): sensor.sensor_schema(
 | 
				
			||||||
 | 
					            unit_of_measurement=UNIT_OHM,
 | 
				
			||||||
 | 
					            icon=ICON_GAS_CYLINDER,
 | 
				
			||||||
 | 
					            accuracy_decimals=0,
 | 
				
			||||||
 | 
					            state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        cv.Optional(CONF_IAQ): sensor.sensor_schema(
 | 
				
			||||||
 | 
					            unit_of_measurement=UNIT_IAQ,
 | 
				
			||||||
 | 
					            icon=ICON_GAUGE,
 | 
				
			||||||
 | 
					            accuracy_decimals=0,
 | 
				
			||||||
 | 
					            state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        cv.Optional(CONF_IAQ_STATIC): sensor.sensor_schema(
 | 
				
			||||||
 | 
					            unit_of_measurement=UNIT_IAQ,
 | 
				
			||||||
 | 
					            icon=ICON_GAUGE,
 | 
				
			||||||
 | 
					            accuracy_decimals=0,
 | 
				
			||||||
 | 
					            state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        cv.Optional(CONF_IAQ_ACCURACY): sensor.sensor_schema(
 | 
				
			||||||
 | 
					            icon=ICON_ACCURACY,
 | 
				
			||||||
 | 
					            accuracy_decimals=0,
 | 
				
			||||||
 | 
					            state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        cv.Optional(CONF_CO2_EQUIVALENT): sensor.sensor_schema(
 | 
				
			||||||
 | 
					            unit_of_measurement=UNIT_PARTS_PER_MILLION,
 | 
				
			||||||
 | 
					            icon=ICON_TEST_TUBE,
 | 
				
			||||||
 | 
					            accuracy_decimals=1,
 | 
				
			||||||
 | 
					            state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        cv.Optional(CONF_BREATH_VOC_EQUIVALENT): sensor.sensor_schema(
 | 
				
			||||||
 | 
					            unit_of_measurement=UNIT_PARTS_PER_MILLION,
 | 
				
			||||||
 | 
					            icon=ICON_TEST_TUBE,
 | 
				
			||||||
 | 
					            accuracy_decimals=1,
 | 
				
			||||||
 | 
					            state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async def setup_conf(config, key, hub):
 | 
				
			||||||
 | 
					    if conf := config.get(key):
 | 
				
			||||||
 | 
					        sens = await sensor.new_sensor(conf)
 | 
				
			||||||
 | 
					        cg.add(getattr(hub, f"set_{key}_sensor")(sens))
 | 
				
			||||||
 | 
					        if sample_rate := conf.get(CONF_SAMPLE_RATE):
 | 
				
			||||||
 | 
					            cg.add(getattr(hub, f"set_{key}_sample_rate")(sample_rate))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async def to_code(config):
 | 
				
			||||||
 | 
					    hub = await cg.get_variable(config[CONF_BME68X_BSEC2_ID])
 | 
				
			||||||
 | 
					    for key in TYPES:
 | 
				
			||||||
 | 
					        await setup_conf(config, key, hub)
 | 
				
			||||||
							
								
								
									
										33
									
								
								esphome/components/bme68x_bsec2/text_sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								esphome/components/bme68x_bsec2/text_sensor.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
				
			|||||||
 | 
					import esphome.codegen as cg
 | 
				
			||||||
 | 
					from esphome.components import text_sensor
 | 
				
			||||||
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
 | 
					from esphome.const import CONF_IAQ_ACCURACY
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from . import CONF_BME68X_BSEC2_ID, BME68xBSEC2Component
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEPENDENCIES = ["bme68x_bsec2"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ICON_ACCURACY = "mdi:checkbox-marked-circle-outline"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TYPES = [CONF_IAQ_ACCURACY]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CONFIG_SCHEMA = cv.Schema(
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        cv.GenerateID(CONF_BME68X_BSEC2_ID): cv.use_id(BME68xBSEC2Component),
 | 
				
			||||||
 | 
					        cv.Optional(CONF_IAQ_ACCURACY): text_sensor.text_sensor_schema(
 | 
				
			||||||
 | 
					            icon=ICON_ACCURACY
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async def setup_conf(config, key, hub):
 | 
				
			||||||
 | 
					    if conf := config.get(key):
 | 
				
			||||||
 | 
					        sens = await text_sensor.new_text_sensor(conf)
 | 
				
			||||||
 | 
					        cg.add(getattr(hub, f"set_{key}_text_sensor")(sens))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async def to_code(config):
 | 
				
			||||||
 | 
					    hub = await cg.get_variable(config[CONF_BME68X_BSEC2_ID])
 | 
				
			||||||
 | 
					    for key in TYPES:
 | 
				
			||||||
 | 
					        await setup_conf(config, key, hub)
 | 
				
			||||||
							
								
								
									
										28
									
								
								esphome/components/bme68x_bsec2_i2c/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								esphome/components/bme68x_bsec2_i2c/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					import esphome.codegen as cg
 | 
				
			||||||
 | 
					from esphome.components import i2c
 | 
				
			||||||
 | 
					from esphome.components.bme68x_bsec2 import (
 | 
				
			||||||
 | 
					    CONFIG_SCHEMA_BASE,
 | 
				
			||||||
 | 
					    BME68xBSEC2Component,
 | 
				
			||||||
 | 
					    to_code_base,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CODEOWNERS = ["@neffs", "@kbx81"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					AUTO_LOAD = ["bme68x_bsec2"]
 | 
				
			||||||
 | 
					DEPENDENCIES = ["i2c"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bme68x_bsec2_i2c_ns = cg.esphome_ns.namespace("bme68x_bsec2_i2c")
 | 
				
			||||||
 | 
					BME68xBSEC2I2CComponent = bme68x_bsec2_i2c_ns.class_(
 | 
				
			||||||
 | 
					    "BME68xBSEC2I2CComponent", BME68xBSEC2Component, i2c.I2CDevice
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CONFIG_SCHEMA = CONFIG_SCHEMA_BASE.extend(
 | 
				
			||||||
 | 
					    cv.Schema({cv.GenerateID(): cv.declare_id(BME68xBSEC2I2CComponent)})
 | 
				
			||||||
 | 
					).extend(i2c.i2c_device_schema(0x76))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async def to_code(config):
 | 
				
			||||||
 | 
					    var = await to_code_base(config)
 | 
				
			||||||
 | 
					    await i2c.register_i2c_device(var, config)
 | 
				
			||||||
							
								
								
									
										53
									
								
								esphome/components/bme68x_bsec2_i2c/bme68x_bsec2_i2c.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								esphome/components/bme68x_bsec2_i2c/bme68x_bsec2_i2c.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,53 @@
 | 
				
			|||||||
 | 
					#include "esphome/core/defines.h"
 | 
				
			||||||
 | 
					#include "esphome/core/helpers.h"
 | 
				
			||||||
 | 
					#include "esphome/core/log.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef USE_BSEC2
 | 
				
			||||||
 | 
					#include "bme68x_bsec2_i2c.h"
 | 
				
			||||||
 | 
					#include "esphome/components/i2c/i2c.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <cinttypes>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace esphome {
 | 
				
			||||||
 | 
					namespace bme68x_bsec2_i2c {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char *const TAG = "bme68x_bsec2_i2c.sensor";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void BME68xBSEC2I2CComponent::setup() {
 | 
				
			||||||
 | 
					  // must set up our bme68x_dev instance before calling setup()
 | 
				
			||||||
 | 
					  this->bme68x_.intf_ptr = (void *) this;
 | 
				
			||||||
 | 
					  this->bme68x_.intf = BME68X_I2C_INTF;
 | 
				
			||||||
 | 
					  this->bme68x_.read = BME68xBSEC2I2CComponent::read_bytes_wrapper;
 | 
				
			||||||
 | 
					  this->bme68x_.write = BME68xBSEC2I2CComponent::write_bytes_wrapper;
 | 
				
			||||||
 | 
					  this->bme68x_.delay_us = BME68xBSEC2I2CComponent::delay_us;
 | 
				
			||||||
 | 
					  this->bme68x_.amb_temp = 25;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  BME68xBSEC2Component::setup();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void BME68xBSEC2I2CComponent::dump_config() {
 | 
				
			||||||
 | 
					  LOG_I2C_DEVICE(this);
 | 
				
			||||||
 | 
					  BME68xBSEC2Component::dump_config();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uint32_t BME68xBSEC2I2CComponent::get_hash() { return fnv1_hash("bme68x_bsec_state_" + to_string(this->address_)); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int8_t BME68xBSEC2I2CComponent::read_bytes_wrapper(uint8_t a_register, uint8_t *data, uint32_t len, void *intfPtr) {
 | 
				
			||||||
 | 
					  ESP_LOGVV(TAG, "read_bytes_wrapper: reg = %u", a_register);
 | 
				
			||||||
 | 
					  return static_cast<BME68xBSEC2I2CComponent *>(intfPtr)->read_bytes(a_register, data, len) ? 0 : -1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int8_t BME68xBSEC2I2CComponent::write_bytes_wrapper(uint8_t a_register, const uint8_t *data, uint32_t len,
 | 
				
			||||||
 | 
					                                                    void *intfPtr) {
 | 
				
			||||||
 | 
					  ESP_LOGVV(TAG, "write_bytes_wrapper: reg = %u", a_register);
 | 
				
			||||||
 | 
					  return static_cast<BME68xBSEC2I2CComponent *>(intfPtr)->write_bytes(a_register, data, len) ? 0 : -1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void BME68xBSEC2I2CComponent::delay_us(uint32_t period, void *intfPtr) {
 | 
				
			||||||
 | 
					  ESP_LOGVV(TAG, "Delaying for %" PRIu32 "us", period);
 | 
				
			||||||
 | 
					  delayMicroseconds(period);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace bme68x_bsec2_i2c
 | 
				
			||||||
 | 
					}  // namespace esphome
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										28
									
								
								esphome/components/bme68x_bsec2_i2c/bme68x_bsec2_i2c.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								esphome/components/bme68x_bsec2_i2c/bme68x_bsec2_i2c.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "esphome/core/component.h"
 | 
				
			||||||
 | 
					#include "esphome/core/defines.h"
 | 
				
			||||||
 | 
					#include "esphome/core/preferences.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef USE_BSEC2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "esphome/components/bme68x_bsec2/bme68x_bsec2.h"
 | 
				
			||||||
 | 
					#include "esphome/components/i2c/i2c.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace esphome {
 | 
				
			||||||
 | 
					namespace bme68x_bsec2_i2c {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class BME68xBSEC2I2CComponent : public bme68x_bsec2::BME68xBSEC2Component, public i2c::I2CDevice {
 | 
				
			||||||
 | 
					  void setup() override;
 | 
				
			||||||
 | 
					  void dump_config() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  uint32_t get_hash() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static int8_t read_bytes_wrapper(uint8_t a_register, uint8_t *data, uint32_t len, void *intfPtr);
 | 
				
			||||||
 | 
					  static int8_t write_bytes_wrapper(uint8_t a_register, const uint8_t *data, uint32_t len, void *intfPtr);
 | 
				
			||||||
 | 
					  static void delay_us(uint32_t period, void *intfPtr);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace bme68x_bsec2_i2c
 | 
				
			||||||
 | 
					}  // namespace esphome
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
@@ -2,6 +2,6 @@ import esphome.config_validation as cv
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
CODEOWNERS = ["@latonita"]
 | 
					CODEOWNERS = ["@latonita"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CONFIG_SCHEMA = CONFIG_SCHEMA = cv.invalid(
 | 
					CONFIG_SCHEMA = cv.invalid(
 | 
				
			||||||
    "The bmp3xx sensor component has been renamed to bmp3xx_i2c."
 | 
					    "The bmp3xx sensor component has been renamed to bmp3xx_i2c."
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,24 +1,25 @@
 | 
				
			|||||||
import esphome.codegen as cg
 | 
					 | 
				
			||||||
import esphome.config_validation as cv
 | 
					 | 
				
			||||||
from esphome import automation
 | 
					from esphome import automation
 | 
				
			||||||
from esphome.automation import maybe_simple_id
 | 
					from esphome.automation import maybe_simple_id
 | 
				
			||||||
from esphome.components import mqtt
 | 
					import esphome.codegen as cg
 | 
				
			||||||
 | 
					from esphome.components import mqtt, web_server
 | 
				
			||||||
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
from esphome.const import (
 | 
					from esphome.const import (
 | 
				
			||||||
    CONF_DEVICE_CLASS,
 | 
					    CONF_DEVICE_CLASS,
 | 
				
			||||||
    CONF_ENTITY_CATEGORY,
 | 
					    CONF_ENTITY_CATEGORY,
 | 
				
			||||||
    CONF_ICON,
 | 
					    CONF_ICON,
 | 
				
			||||||
    CONF_ID,
 | 
					    CONF_ID,
 | 
				
			||||||
 | 
					    CONF_MQTT_ID,
 | 
				
			||||||
    CONF_ON_PRESS,
 | 
					    CONF_ON_PRESS,
 | 
				
			||||||
    CONF_TRIGGER_ID,
 | 
					    CONF_TRIGGER_ID,
 | 
				
			||||||
    CONF_MQTT_ID,
 | 
					    CONF_WEB_SERVER_ID,
 | 
				
			||||||
    DEVICE_CLASS_EMPTY,
 | 
					    DEVICE_CLASS_EMPTY,
 | 
				
			||||||
    DEVICE_CLASS_IDENTIFY,
 | 
					    DEVICE_CLASS_IDENTIFY,
 | 
				
			||||||
    DEVICE_CLASS_RESTART,
 | 
					    DEVICE_CLASS_RESTART,
 | 
				
			||||||
    DEVICE_CLASS_UPDATE,
 | 
					    DEVICE_CLASS_UPDATE,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from esphome.core import CORE, coroutine_with_priority
 | 
					from esphome.core import CORE, coroutine_with_priority
 | 
				
			||||||
from esphome.cpp_helpers import setup_entity
 | 
					 | 
				
			||||||
from esphome.cpp_generator import MockObjClass
 | 
					from esphome.cpp_generator import MockObjClass
 | 
				
			||||||
 | 
					from esphome.cpp_helpers import setup_entity
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CODEOWNERS = ["@esphome/core"]
 | 
					CODEOWNERS = ["@esphome/core"]
 | 
				
			||||||
IS_PLATFORM_COMPONENT = True
 | 
					IS_PLATFORM_COMPONENT = True
 | 
				
			||||||
@@ -43,16 +44,20 @@ ButtonPressTrigger = button_ns.class_(
 | 
				
			|||||||
validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
 | 
					validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
BUTTON_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
 | 
					BUTTON_SCHEMA = (
 | 
				
			||||||
    {
 | 
					    cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
 | 
				
			||||||
        cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTButtonComponent),
 | 
					    .extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA)
 | 
				
			||||||
        cv.Optional(CONF_DEVICE_CLASS): validate_device_class,
 | 
					    .extend(
 | 
				
			||||||
        cv.Optional(CONF_ON_PRESS): automation.validate_automation(
 | 
					        {
 | 
				
			||||||
            {
 | 
					            cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTButtonComponent),
 | 
				
			||||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ButtonPressTrigger),
 | 
					            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()
 | 
					_UNDEF = object()
 | 
				
			||||||
@@ -92,6 +97,10 @@ async def setup_button_core_(var, config):
 | 
				
			|||||||
        mqtt_ = cg.new_Pvariable(mqtt_id, var)
 | 
					        mqtt_ = cg.new_Pvariable(mqtt_id, var)
 | 
				
			||||||
        await mqtt.register_mqtt_component(mqtt_, config)
 | 
					        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):
 | 
					async def register_button(var, config):
 | 
				
			||||||
    if not CORE.has_id(config[CONF_ID]):
 | 
					    if not CORE.has_id(config[CONF_ID]):
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,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 import automation
 | 
				
			||||||
from esphome.components import mqtt
 | 
					import esphome.codegen as cg
 | 
				
			||||||
 | 
					from esphome.components import mqtt, web_server
 | 
				
			||||||
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
from esphome.const import (
 | 
					from esphome.const import (
 | 
				
			||||||
    CONF_ACTION_STATE_TOPIC,
 | 
					    CONF_ACTION_STATE_TOPIC,
 | 
				
			||||||
    CONF_AWAY,
 | 
					    CONF_AWAY,
 | 
				
			||||||
@@ -21,6 +20,7 @@ from esphome.const import (
 | 
				
			|||||||
    CONF_MODE,
 | 
					    CONF_MODE,
 | 
				
			||||||
    CONF_MODE_COMMAND_TOPIC,
 | 
					    CONF_MODE_COMMAND_TOPIC,
 | 
				
			||||||
    CONF_MODE_STATE_TOPIC,
 | 
					    CONF_MODE_STATE_TOPIC,
 | 
				
			||||||
 | 
					    CONF_MQTT_ID,
 | 
				
			||||||
    CONF_ON_CONTROL,
 | 
					    CONF_ON_CONTROL,
 | 
				
			||||||
    CONF_ON_STATE,
 | 
					    CONF_ON_STATE,
 | 
				
			||||||
    CONF_PRESET,
 | 
					    CONF_PRESET,
 | 
				
			||||||
@@ -33,19 +33,20 @@ from esphome.const import (
 | 
				
			|||||||
    CONF_TARGET_HUMIDITY_STATE_TOPIC,
 | 
					    CONF_TARGET_HUMIDITY_STATE_TOPIC,
 | 
				
			||||||
    CONF_TARGET_TEMPERATURE,
 | 
					    CONF_TARGET_TEMPERATURE,
 | 
				
			||||||
    CONF_TARGET_TEMPERATURE_COMMAND_TOPIC,
 | 
					    CONF_TARGET_TEMPERATURE_COMMAND_TOPIC,
 | 
				
			||||||
    CONF_TARGET_TEMPERATURE_STATE_TOPIC,
 | 
					 | 
				
			||||||
    CONF_TARGET_TEMPERATURE_HIGH,
 | 
					    CONF_TARGET_TEMPERATURE_HIGH,
 | 
				
			||||||
    CONF_TARGET_TEMPERATURE_HIGH_COMMAND_TOPIC,
 | 
					    CONF_TARGET_TEMPERATURE_HIGH_COMMAND_TOPIC,
 | 
				
			||||||
    CONF_TARGET_TEMPERATURE_HIGH_STATE_TOPIC,
 | 
					    CONF_TARGET_TEMPERATURE_HIGH_STATE_TOPIC,
 | 
				
			||||||
    CONF_TARGET_TEMPERATURE_LOW,
 | 
					    CONF_TARGET_TEMPERATURE_LOW,
 | 
				
			||||||
    CONF_TARGET_TEMPERATURE_LOW_COMMAND_TOPIC,
 | 
					    CONF_TARGET_TEMPERATURE_LOW_COMMAND_TOPIC,
 | 
				
			||||||
    CONF_TARGET_TEMPERATURE_LOW_STATE_TOPIC,
 | 
					    CONF_TARGET_TEMPERATURE_LOW_STATE_TOPIC,
 | 
				
			||||||
 | 
					    CONF_TARGET_TEMPERATURE_STATE_TOPIC,
 | 
				
			||||||
    CONF_TEMPERATURE_STEP,
 | 
					    CONF_TEMPERATURE_STEP,
 | 
				
			||||||
    CONF_TRIGGER_ID,
 | 
					    CONF_TRIGGER_ID,
 | 
				
			||||||
    CONF_VISUAL,
 | 
					    CONF_VISUAL,
 | 
				
			||||||
    CONF_MQTT_ID,
 | 
					    CONF_WEB_SERVER_ID,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from esphome.core import CORE, coroutine_with_priority
 | 
					from esphome.core import CORE, coroutine_with_priority
 | 
				
			||||||
 | 
					from esphome.cpp_helpers import setup_entity
 | 
				
			||||||
 | 
					
 | 
				
			||||||
IS_PLATFORM_COMPONENT = True
 | 
					IS_PLATFORM_COMPONENT = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -150,93 +151,97 @@ VISUAL_TEMPERATURE_STEP_SCHEMA = cv.Any(
 | 
				
			|||||||
    ),
 | 
					    ),
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CLIMATE_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
 | 
					CLIMATE_SCHEMA = (
 | 
				
			||||||
    {
 | 
					    cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
 | 
				
			||||||
        cv.GenerateID(): cv.declare_id(Climate),
 | 
					    .extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA)
 | 
				
			||||||
        cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTClimateComponent),
 | 
					    .extend(
 | 
				
			||||||
        cv.Optional(CONF_VISUAL, default={}): cv.Schema(
 | 
					        {
 | 
				
			||||||
            {
 | 
					            cv.GenerateID(): cv.declare_id(Climate),
 | 
				
			||||||
                cv.Optional(CONF_MIN_TEMPERATURE): cv.temperature,
 | 
					            cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTClimateComponent),
 | 
				
			||||||
                cv.Optional(CONF_MAX_TEMPERATURE): cv.temperature,
 | 
					            cv.Optional(CONF_VISUAL, default={}): cv.Schema(
 | 
				
			||||||
                cv.Optional(CONF_TEMPERATURE_STEP): VISUAL_TEMPERATURE_STEP_SCHEMA,
 | 
					                {
 | 
				
			||||||
                cv.Optional(CONF_MIN_HUMIDITY): cv.percentage_int,
 | 
					                    cv.Optional(CONF_MIN_TEMPERATURE): cv.temperature,
 | 
				
			||||||
                cv.Optional(CONF_MAX_HUMIDITY): cv.percentage_int,
 | 
					                    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_ACTION_STATE_TOPIC): cv.All(
 | 
					                    cv.Optional(CONF_MAX_HUMIDITY): cv.percentage_int,
 | 
				
			||||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
					                }
 | 
				
			||||||
        ),
 | 
					            ),
 | 
				
			||||||
        cv.Optional(CONF_AWAY_COMMAND_TOPIC): cv.All(
 | 
					            cv.Optional(CONF_ACTION_STATE_TOPIC): cv.All(
 | 
				
			||||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
					                cv.requires_component("mqtt"), cv.publish_topic
 | 
				
			||||||
        ),
 | 
					            ),
 | 
				
			||||||
        cv.Optional(CONF_AWAY_STATE_TOPIC): cv.All(
 | 
					            cv.Optional(CONF_AWAY_COMMAND_TOPIC): cv.All(
 | 
				
			||||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
					                cv.requires_component("mqtt"), cv.publish_topic
 | 
				
			||||||
        ),
 | 
					            ),
 | 
				
			||||||
        cv.Optional(CONF_CURRENT_TEMPERATURE_STATE_TOPIC): cv.All(
 | 
					            cv.Optional(CONF_AWAY_STATE_TOPIC): cv.All(
 | 
				
			||||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
					                cv.requires_component("mqtt"), cv.publish_topic
 | 
				
			||||||
        ),
 | 
					            ),
 | 
				
			||||||
        cv.Optional(CONF_CURRENT_HUMIDITY_STATE_TOPIC): cv.All(
 | 
					            cv.Optional(CONF_CURRENT_TEMPERATURE_STATE_TOPIC): cv.All(
 | 
				
			||||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
					                cv.requires_component("mqtt"), cv.publish_topic
 | 
				
			||||||
        ),
 | 
					            ),
 | 
				
			||||||
        cv.Optional(CONF_FAN_MODE_COMMAND_TOPIC): cv.All(
 | 
					            cv.Optional(CONF_CURRENT_HUMIDITY_STATE_TOPIC): cv.All(
 | 
				
			||||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
					                cv.requires_component("mqtt"), cv.publish_topic
 | 
				
			||||||
        ),
 | 
					            ),
 | 
				
			||||||
        cv.Optional(CONF_FAN_MODE_STATE_TOPIC): cv.All(
 | 
					            cv.Optional(CONF_FAN_MODE_COMMAND_TOPIC): cv.All(
 | 
				
			||||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
					                cv.requires_component("mqtt"), cv.publish_topic
 | 
				
			||||||
        ),
 | 
					            ),
 | 
				
			||||||
        cv.Optional(CONF_MODE_COMMAND_TOPIC): cv.All(
 | 
					            cv.Optional(CONF_FAN_MODE_STATE_TOPIC): cv.All(
 | 
				
			||||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
					                cv.requires_component("mqtt"), cv.publish_topic
 | 
				
			||||||
        ),
 | 
					            ),
 | 
				
			||||||
        cv.Optional(CONF_MODE_STATE_TOPIC): cv.All(
 | 
					            cv.Optional(CONF_MODE_COMMAND_TOPIC): cv.All(
 | 
				
			||||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
					                cv.requires_component("mqtt"), cv.publish_topic
 | 
				
			||||||
        ),
 | 
					            ),
 | 
				
			||||||
        cv.Optional(CONF_PRESET_COMMAND_TOPIC): cv.All(
 | 
					            cv.Optional(CONF_MODE_STATE_TOPIC): cv.All(
 | 
				
			||||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
					                cv.requires_component("mqtt"), cv.publish_topic
 | 
				
			||||||
        ),
 | 
					            ),
 | 
				
			||||||
        cv.Optional(CONF_PRESET_STATE_TOPIC): cv.All(
 | 
					            cv.Optional(CONF_PRESET_COMMAND_TOPIC): cv.All(
 | 
				
			||||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
					                cv.requires_component("mqtt"), cv.publish_topic
 | 
				
			||||||
        ),
 | 
					            ),
 | 
				
			||||||
        cv.Optional(CONF_SWING_MODE_COMMAND_TOPIC): cv.All(
 | 
					            cv.Optional(CONF_PRESET_STATE_TOPIC): cv.All(
 | 
				
			||||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
					                cv.requires_component("mqtt"), cv.publish_topic
 | 
				
			||||||
        ),
 | 
					            ),
 | 
				
			||||||
        cv.Optional(CONF_SWING_MODE_STATE_TOPIC): cv.All(
 | 
					            cv.Optional(CONF_SWING_MODE_COMMAND_TOPIC): cv.All(
 | 
				
			||||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
					                cv.requires_component("mqtt"), cv.publish_topic
 | 
				
			||||||
        ),
 | 
					            ),
 | 
				
			||||||
        cv.Optional(CONF_TARGET_TEMPERATURE_COMMAND_TOPIC): cv.All(
 | 
					            cv.Optional(CONF_SWING_MODE_STATE_TOPIC): cv.All(
 | 
				
			||||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
					                cv.requires_component("mqtt"), cv.publish_topic
 | 
				
			||||||
        ),
 | 
					            ),
 | 
				
			||||||
        cv.Optional(CONF_TARGET_TEMPERATURE_STATE_TOPIC): cv.All(
 | 
					            cv.Optional(CONF_TARGET_TEMPERATURE_COMMAND_TOPIC): cv.All(
 | 
				
			||||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
					                cv.requires_component("mqtt"), cv.publish_topic
 | 
				
			||||||
        ),
 | 
					            ),
 | 
				
			||||||
        cv.Optional(CONF_TARGET_TEMPERATURE_HIGH_COMMAND_TOPIC): cv.All(
 | 
					            cv.Optional(CONF_TARGET_TEMPERATURE_STATE_TOPIC): cv.All(
 | 
				
			||||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
					                cv.requires_component("mqtt"), cv.publish_topic
 | 
				
			||||||
        ),
 | 
					            ),
 | 
				
			||||||
        cv.Optional(CONF_TARGET_TEMPERATURE_HIGH_STATE_TOPIC): cv.All(
 | 
					            cv.Optional(CONF_TARGET_TEMPERATURE_HIGH_COMMAND_TOPIC): cv.All(
 | 
				
			||||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
					                cv.requires_component("mqtt"), cv.publish_topic
 | 
				
			||||||
        ),
 | 
					            ),
 | 
				
			||||||
        cv.Optional(CONF_TARGET_TEMPERATURE_LOW_COMMAND_TOPIC): cv.All(
 | 
					            cv.Optional(CONF_TARGET_TEMPERATURE_HIGH_STATE_TOPIC): cv.All(
 | 
				
			||||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
					                cv.requires_component("mqtt"), cv.publish_topic
 | 
				
			||||||
        ),
 | 
					            ),
 | 
				
			||||||
        cv.Optional(CONF_TARGET_TEMPERATURE_LOW_STATE_TOPIC): cv.All(
 | 
					            cv.Optional(CONF_TARGET_TEMPERATURE_LOW_COMMAND_TOPIC): cv.All(
 | 
				
			||||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
					                cv.requires_component("mqtt"), cv.publish_topic
 | 
				
			||||||
        ),
 | 
					            ),
 | 
				
			||||||
        cv.Optional(CONF_TARGET_HUMIDITY_COMMAND_TOPIC): cv.All(
 | 
					            cv.Optional(CONF_TARGET_TEMPERATURE_LOW_STATE_TOPIC): cv.All(
 | 
				
			||||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
					                cv.requires_component("mqtt"), cv.publish_topic
 | 
				
			||||||
        ),
 | 
					            ),
 | 
				
			||||||
        cv.Optional(CONF_TARGET_HUMIDITY_STATE_TOPIC): cv.All(
 | 
					            cv.Optional(CONF_TARGET_HUMIDITY_COMMAND_TOPIC): cv.All(
 | 
				
			||||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
					                cv.requires_component("mqtt"), cv.publish_topic
 | 
				
			||||||
        ),
 | 
					            ),
 | 
				
			||||||
        cv.Optional(CONF_ON_CONTROL): automation.validate_automation(
 | 
					            cv.Optional(CONF_TARGET_HUMIDITY_STATE_TOPIC): cv.All(
 | 
				
			||||||
            {
 | 
					                cv.requires_component("mqtt"), cv.publish_topic
 | 
				
			||||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ControlTrigger),
 | 
					            ),
 | 
				
			||||||
            }
 | 
					            cv.Optional(CONF_ON_CONTROL): automation.validate_automation(
 | 
				
			||||||
        ),
 | 
					                {
 | 
				
			||||||
        cv.Optional(CONF_ON_STATE): automation.validate_automation(
 | 
					                    cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ControlTrigger),
 | 
				
			||||||
            {
 | 
					                }
 | 
				
			||||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateTrigger),
 | 
					            ),
 | 
				
			||||||
            }
 | 
					            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
 | 
					            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):
 | 
					async def register_climate(var, config):
 | 
				
			||||||
    if not CORE.has_id(config[CONF_ID]):
 | 
					    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, "      - Max temperature: %.1f", traits.get_visual_max_temperature());
 | 
				
			||||||
  ESP_LOGCONFIG(tag, "      - Temperature step:");
 | 
					  ESP_LOGCONFIG(tag, "      - Temperature step:");
 | 
				
			||||||
  ESP_LOGCONFIG(tag, "          Target: %.1f", traits.get_visual_target_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()) {
 | 
					  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()) {
 | 
					  if (traits.get_supports_target_humidity() || traits.get_supports_current_humidity()) {
 | 
				
			||||||
    ESP_LOGCONFIG(tag, "  [x] 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()) {
 | 
					  if (traits.get_supports_two_point_target_temperature()) {
 | 
				
			||||||
    ESP_LOGCONFIG(tag, "  [x] 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()) {
 | 
					  if (traits.get_supports_target_humidity()) {
 | 
				
			||||||
    ESP_LOGCONFIG(tag, "  [x] 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()) {
 | 
					  if (traits.get_supports_action()) {
 | 
				
			||||||
    ESP_LOGCONFIG(tag, "  [x] Supports action");
 | 
					    ESP_LOGCONFIG(tag, "  [x] Supports action");
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -141,7 +141,7 @@ struct ClimateDeviceRestoreState {
 | 
				
			|||||||
      float target_temperature_low;
 | 
					      float target_temperature_low;
 | 
				
			||||||
      float target_temperature_high;
 | 
					      float target_temperature_high;
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
  };
 | 
					  } __attribute__((packed));
 | 
				
			||||||
  float target_humidity;
 | 
					  float target_humidity;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Convert this struct to a climate call that can be performed.
 | 
					  /// Convert this struct to a climate call that can be performed.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -73,7 +73,7 @@ class ClimateTraits {
 | 
				
			|||||||
  ESPDEPRECATED("This method is deprecated, use set_supported_modes() instead", "v1.20")
 | 
					  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); }
 | 
					  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); }
 | 
					  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; }
 | 
					  void set_supports_action(bool supports_action) { supports_action_ = supports_action; }
 | 
				
			||||||
  bool get_supports_action() const { return 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); }
 | 
					  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 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(); }
 | 
					  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) {
 | 
					  void set_supported_custom_fan_modes(std::set<std::string> supported_custom_fan_modes) {
 | 
				
			||||||
    supported_custom_fan_modes_ = std::move(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 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(); }
 | 
					  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_; }
 | 
					  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; }
 | 
					  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";
 | 
					static const char *const TAG = "climate.climate_ir_lg";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const uint32_t COMMAND_ON = 0x00000;
 | 
					// Commands
 | 
				
			||||||
const uint32_t COMMAND_ON_AI = 0x03000;
 | 
					const uint32_t COMMAND_MASK = 0xFF000;
 | 
				
			||||||
const uint32_t COMMAND_COOL = 0x08000;
 | 
					 | 
				
			||||||
const uint32_t COMMAND_HEAT = 0x0C000;
 | 
					 | 
				
			||||||
const uint32_t COMMAND_OFF = 0xC0000;
 | 
					const uint32_t COMMAND_OFF = 0xC0000;
 | 
				
			||||||
const uint32_t COMMAND_SWING = 0x10000;
 | 
					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_MASK = 0xF0;
 | 
				
			||||||
const uint32_t FAN_AUTO = 0x50;
 | 
					const uint32_t FAN_AUTO = 0x50;
 | 
				
			||||||
const uint32_t FAN_MIN = 0x00;
 | 
					const uint32_t FAN_MIN = 0x00;
 | 
				
			||||||
@@ -35,69 +41,67 @@ void LgIrClimate::transmit_state() {
 | 
				
			|||||||
  uint32_t remote_state = 0x8800000;
 | 
					  uint32_t remote_state = 0x8800000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // ESP_LOGD(TAG, "climate_lg_ir mode_before_ code: 0x%02X", modeBefore_);
 | 
					  // ESP_LOGD(TAG, "climate_lg_ir mode_before_ code: 0x%02X", modeBefore_);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Set command
 | 
				
			||||||
  if (send_swing_cmd_) {
 | 
					  if (send_swing_cmd_) {
 | 
				
			||||||
    send_swing_cmd_ = false;
 | 
					    send_swing_cmd_ = false;
 | 
				
			||||||
    remote_state |= COMMAND_SWING;
 | 
					    remote_state |= COMMAND_SWING;
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    if (mode_before_ == climate::CLIMATE_MODE_OFF && this->mode == climate::CLIMATE_MODE_HEAT_COOL) {
 | 
					    bool climate_is_off = (mode_before_ == climate::CLIMATE_MODE_OFF);
 | 
				
			||||||
      remote_state |= COMMAND_ON_AI;
 | 
					    switch (this->mode) {
 | 
				
			||||||
    } else if (mode_before_ == climate::CLIMATE_MODE_OFF && this->mode != climate::CLIMATE_MODE_OFF) {
 | 
					      case climate::CLIMATE_MODE_COOL:
 | 
				
			||||||
      remote_state |= COMMAND_ON;
 | 
					        remote_state |= climate_is_off ? COMMAND_ON_COOL : COMMAND_COOL;
 | 
				
			||||||
      this->mode = climate::CLIMATE_MODE_COOL;
 | 
					        break;
 | 
				
			||||||
    } else {
 | 
					      case climate::CLIMATE_MODE_DRY:
 | 
				
			||||||
      switch (this->mode) {
 | 
					        remote_state |= climate_is_off ? COMMAND_ON_DRY : COMMAND_DRY;
 | 
				
			||||||
        case climate::CLIMATE_MODE_COOL:
 | 
					        break;
 | 
				
			||||||
          remote_state |= COMMAND_COOL;
 | 
					      case climate::CLIMATE_MODE_FAN_ONLY:
 | 
				
			||||||
          break;
 | 
					        remote_state |= climate_is_off ? COMMAND_ON_FAN_ONLY : COMMAND_FAN_ONLY;
 | 
				
			||||||
        case climate::CLIMATE_MODE_HEAT:
 | 
					        break;
 | 
				
			||||||
          remote_state |= COMMAND_HEAT;
 | 
					      case climate::CLIMATE_MODE_HEAT_COOL:
 | 
				
			||||||
          break;
 | 
					        remote_state |= climate_is_off ? COMMAND_ON_AI : COMMAND_AI;
 | 
				
			||||||
        case climate::CLIMATE_MODE_HEAT_COOL:
 | 
					        break;
 | 
				
			||||||
          remote_state |= COMMAND_AUTO;
 | 
					      case climate::CLIMATE_MODE_HEAT:
 | 
				
			||||||
          break;
 | 
					        remote_state |= climate_is_off ? COMMAND_ON_HEAT : COMMAND_HEAT;
 | 
				
			||||||
        case climate::CLIMATE_MODE_DRY:
 | 
					        break;
 | 
				
			||||||
          remote_state |= COMMAND_DRY_FAN;
 | 
					      case climate::CLIMATE_MODE_OFF:
 | 
				
			||||||
          break;
 | 
					      default:
 | 
				
			||||||
        case climate::CLIMATE_MODE_OFF:
 | 
					        remote_state |= COMMAND_OFF;
 | 
				
			||||||
        default:
 | 
					        break;
 | 
				
			||||||
          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);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  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);
 | 
					  transmit_(remote_state);
 | 
				
			||||||
  this->publish_state();
 | 
					  this->publish_state();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -125,37 +129,42 @@ bool LgIrClimate::on_receive(remote_base::RemoteReceiveData data) {
 | 
				
			|||||||
  if ((remote_state & 0xFF00000) != 0x8800000)
 | 
					  if ((remote_state & 0xFF00000) != 0x8800000)
 | 
				
			||||||
    return false;
 | 
					    return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if ((remote_state & COMMAND_MASK) == COMMAND_ON) {
 | 
					  // Get command
 | 
				
			||||||
    this->mode = climate::CLIMATE_MODE_COOL;
 | 
					 | 
				
			||||||
  } else if ((remote_state & COMMAND_MASK) == COMMAND_ON_AI) {
 | 
					 | 
				
			||||||
    this->mode = climate::CLIMATE_MODE_HEAT_COOL;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if ((remote_state & COMMAND_MASK) == COMMAND_OFF) {
 | 
					  if ((remote_state & COMMAND_MASK) == COMMAND_OFF) {
 | 
				
			||||||
    this->mode = climate::CLIMATE_MODE_OFF;
 | 
					    this->mode = climate::CLIMATE_MODE_OFF;
 | 
				
			||||||
  } else if ((remote_state & COMMAND_MASK) == COMMAND_SWING) {
 | 
					  } else if ((remote_state & COMMAND_MASK) == COMMAND_SWING) {
 | 
				
			||||||
    this->swing_mode =
 | 
					    this->swing_mode =
 | 
				
			||||||
        this->swing_mode == climate::CLIMATE_SWING_OFF ? climate::CLIMATE_SWING_VERTICAL : climate::CLIMATE_SWING_OFF;
 | 
					        this->swing_mode == climate::CLIMATE_SWING_OFF ? climate::CLIMATE_SWING_VERTICAL : climate::CLIMATE_SWING_OFF;
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    if ((remote_state & COMMAND_MASK) == COMMAND_AUTO) {
 | 
					    switch (remote_state & COMMAND_MASK) {
 | 
				
			||||||
      this->mode = climate::CLIMATE_MODE_HEAT_COOL;
 | 
					      case COMMAND_DRY:
 | 
				
			||||||
    } else if ((remote_state & COMMAND_MASK) == COMMAND_DRY_FAN) {
 | 
					      case COMMAND_ON_DRY:
 | 
				
			||||||
      this->mode = climate::CLIMATE_MODE_DRY;
 | 
					        this->mode = climate::CLIMATE_MODE_DRY;
 | 
				
			||||||
    } else if ((remote_state & COMMAND_MASK) == COMMAND_HEAT) {
 | 
					        break;
 | 
				
			||||||
      this->mode = climate::CLIMATE_MODE_HEAT;
 | 
					      case COMMAND_FAN_ONLY:
 | 
				
			||||||
    } else {
 | 
					      case COMMAND_ON_FAN_ONLY:
 | 
				
			||||||
      this->mode = climate::CLIMATE_MODE_COOL;
 | 
					        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
 | 
					    // Get fan speed
 | 
				
			||||||
    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
 | 
					 | 
				
			||||||
    if (this->mode == climate::CLIMATE_MODE_HEAT_COOL) {
 | 
					    if (this->mode == climate::CLIMATE_MODE_HEAT_COOL) {
 | 
				
			||||||
      this->fan_mode = climate::CLIMATE_FAN_AUTO;
 | 
					      this->fan_mode = climate::CLIMATE_FAN_AUTO;
 | 
				
			||||||
    } else if (this->mode == climate::CLIMATE_MODE_COOL || this->mode == climate::CLIMATE_MODE_HEAT ||
 | 
					    } else if (this->mode == climate::CLIMATE_MODE_COOL || this->mode == climate::CLIMATE_MODE_DRY ||
 | 
				
			||||||
               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) {
 | 
					      if ((remote_state & FAN_MASK) == FAN_AUTO) {
 | 
				
			||||||
        this->fan_mode = climate::CLIMATE_FAN_AUTO;
 | 
					        this->fan_mode = climate::CLIMATE_FAN_AUTO;
 | 
				
			||||||
      } else if ((remote_state & FAN_MASK) == FAN_MIN) {
 | 
					      } 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;
 | 
					        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();
 | 
					  this->publish_state();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return true;
 | 
					  return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void LgIrClimate::transmit_(uint32_t value) {
 | 
					void LgIrClimate::transmit_(uint32_t value) {
 | 
				
			||||||
  calc_checksum_(value);
 | 
					  calc_checksum_(value);
 | 
				
			||||||
  ESP_LOGD(TAG, "Sending climate_lg_ir code: 0x%02" PRIX32, 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 {
 | 
					class LgIrClimate : public climate_ir::ClimateIR {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  LgIrClimate()
 | 
					  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_AUTO, climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM,
 | 
				
			||||||
                               climate::CLIMATE_FAN_HIGH},
 | 
					                               climate::CLIMATE_FAN_HIGH},
 | 
				
			||||||
                              {climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_VERTICAL}) {}
 | 
					                              {climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_VERTICAL}) {}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,22 +1,23 @@
 | 
				
			|||||||
import esphome.codegen as cg
 | 
					 | 
				
			||||||
import esphome.config_validation as cv
 | 
					 | 
				
			||||||
from esphome import automation
 | 
					from esphome import automation
 | 
				
			||||||
from esphome.automation import maybe_simple_id, Condition
 | 
					from esphome.automation import Condition, maybe_simple_id
 | 
				
			||||||
from esphome.components import mqtt
 | 
					import esphome.codegen as cg
 | 
				
			||||||
 | 
					from esphome.components import mqtt, web_server
 | 
				
			||||||
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
from esphome.const import (
 | 
					from esphome.const import (
 | 
				
			||||||
    CONF_ID,
 | 
					 | 
				
			||||||
    CONF_DEVICE_CLASS,
 | 
					    CONF_DEVICE_CLASS,
 | 
				
			||||||
    CONF_STATE,
 | 
					    CONF_ID,
 | 
				
			||||||
 | 
					    CONF_MQTT_ID,
 | 
				
			||||||
    CONF_ON_OPEN,
 | 
					    CONF_ON_OPEN,
 | 
				
			||||||
    CONF_POSITION,
 | 
					    CONF_POSITION,
 | 
				
			||||||
    CONF_POSITION_COMMAND_TOPIC,
 | 
					    CONF_POSITION_COMMAND_TOPIC,
 | 
				
			||||||
    CONF_POSITION_STATE_TOPIC,
 | 
					    CONF_POSITION_STATE_TOPIC,
 | 
				
			||||||
 | 
					    CONF_STATE,
 | 
				
			||||||
 | 
					    CONF_STOP,
 | 
				
			||||||
    CONF_TILT,
 | 
					    CONF_TILT,
 | 
				
			||||||
    CONF_TILT_COMMAND_TOPIC,
 | 
					    CONF_TILT_COMMAND_TOPIC,
 | 
				
			||||||
    CONF_TILT_STATE_TOPIC,
 | 
					    CONF_TILT_STATE_TOPIC,
 | 
				
			||||||
    CONF_STOP,
 | 
					 | 
				
			||||||
    CONF_MQTT_ID,
 | 
					 | 
				
			||||||
    CONF_TRIGGER_ID,
 | 
					    CONF_TRIGGER_ID,
 | 
				
			||||||
 | 
					    CONF_WEB_SERVER_ID,
 | 
				
			||||||
    DEVICE_CLASS_AWNING,
 | 
					    DEVICE_CLASS_AWNING,
 | 
				
			||||||
    DEVICE_CLASS_BLIND,
 | 
					    DEVICE_CLASS_BLIND,
 | 
				
			||||||
    DEVICE_CLASS_CURTAIN,
 | 
					    DEVICE_CLASS_CURTAIN,
 | 
				
			||||||
@@ -88,34 +89,38 @@ CoverClosedTrigger = cover_ns.class_(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
CONF_ON_CLOSED = "on_closed"
 | 
					CONF_ON_CLOSED = "on_closed"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
COVER_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
 | 
					COVER_SCHEMA = (
 | 
				
			||||||
    {
 | 
					    cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
 | 
				
			||||||
        cv.GenerateID(): cv.declare_id(Cover),
 | 
					    .extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA)
 | 
				
			||||||
        cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTCoverComponent),
 | 
					    .extend(
 | 
				
			||||||
        cv.Optional(CONF_DEVICE_CLASS): cv.one_of(*DEVICE_CLASSES, lower=True),
 | 
					        {
 | 
				
			||||||
        cv.Optional(CONF_POSITION_COMMAND_TOPIC): cv.All(
 | 
					            cv.GenerateID(): cv.declare_id(Cover),
 | 
				
			||||||
            cv.requires_component("mqtt"), cv.subscribe_topic
 | 
					            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_STATE_TOPIC): cv.All(
 | 
					            cv.Optional(CONF_POSITION_COMMAND_TOPIC): cv.All(
 | 
				
			||||||
            cv.requires_component("mqtt"), cv.subscribe_topic
 | 
					                cv.requires_component("mqtt"), cv.subscribe_topic
 | 
				
			||||||
        ),
 | 
					            ),
 | 
				
			||||||
        cv.Optional(CONF_TILT_COMMAND_TOPIC): cv.All(
 | 
					            cv.Optional(CONF_POSITION_STATE_TOPIC): cv.All(
 | 
				
			||||||
            cv.requires_component("mqtt"), cv.subscribe_topic
 | 
					                cv.requires_component("mqtt"), cv.subscribe_topic
 | 
				
			||||||
        ),
 | 
					            ),
 | 
				
			||||||
        cv.Optional(CONF_TILT_STATE_TOPIC): cv.All(
 | 
					            cv.Optional(CONF_TILT_COMMAND_TOPIC): cv.All(
 | 
				
			||||||
            cv.requires_component("mqtt"), cv.subscribe_topic
 | 
					                cv.requires_component("mqtt"), cv.subscribe_topic
 | 
				
			||||||
        ),
 | 
					            ),
 | 
				
			||||||
        cv.Optional(CONF_ON_OPEN): automation.validate_automation(
 | 
					            cv.Optional(CONF_TILT_STATE_TOPIC): cv.All(
 | 
				
			||||||
            {
 | 
					                cv.requires_component("mqtt"), cv.subscribe_topic
 | 
				
			||||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CoverOpenTrigger),
 | 
					            ),
 | 
				
			||||||
            }
 | 
					            cv.Optional(CONF_ON_OPEN): automation.validate_automation(
 | 
				
			||||||
        ),
 | 
					                {
 | 
				
			||||||
        cv.Optional(CONF_ON_CLOSED): automation.validate_automation(
 | 
					                    cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CoverOpenTrigger),
 | 
				
			||||||
            {
 | 
					                }
 | 
				
			||||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CoverClosedTrigger),
 | 
					            ),
 | 
				
			||||||
            }
 | 
					            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)
 | 
					        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
				
			||||||
        await automation.build_automation(trigger, [], conf)
 | 
					        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:
 | 
					    if (mqtt_id := config.get(CONF_MQTT_ID)) is not None:
 | 
				
			||||||
        mqtt_ = cg.new_Pvariable(mqtt_id, var)
 | 
					        mqtt_ = cg.new_Pvariable(mqtt_id, var)
 | 
				
			||||||
        await mqtt.register_mqtt_component(mqtt_, config)
 | 
					        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.
 | 
					   * 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();
 | 
					  void open();
 | 
				
			||||||
  /** Close the cover.
 | 
					  /** Close the cover.
 | 
				
			||||||
   *
 | 
					   *
 | 
				
			||||||
   * This is a legacy method and may be removed later, please use `.make_call()` instead.
 | 
					   * 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();
 | 
					  void close();
 | 
				
			||||||
  /** Stop the cover.
 | 
					  /** Stop the cover.
 | 
				
			||||||
   *
 | 
					   *
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,13 +5,17 @@ namespace cst226 {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void CST226Touchscreen::setup() {
 | 
					void CST226Touchscreen::setup() {
 | 
				
			||||||
  esph_log_config(TAG, "Setting up CST226 Touchscreen...");
 | 
					  esph_log_config(TAG, "Setting up CST226 Touchscreen...");
 | 
				
			||||||
  this->reset_pin_->setup();
 | 
					  if (this->reset_pin_ != nullptr) {
 | 
				
			||||||
  this->reset_pin_->digital_write(true);
 | 
					    this->reset_pin_->setup();
 | 
				
			||||||
  delay(5);
 | 
					    this->reset_pin_->digital_write(true);
 | 
				
			||||||
  this->reset_pin_->digital_write(false);
 | 
					    delay(5);
 | 
				
			||||||
  delay(5);
 | 
					    this->reset_pin_->digital_write(false);
 | 
				
			||||||
  this->reset_pin_->digital_write(true);
 | 
					    delay(5);
 | 
				
			||||||
  this->set_timeout(30, [this] { this->continue_setup_(); });
 | 
					    this->reset_pin_->digital_write(true);
 | 
				
			||||||
 | 
					    this->set_timeout(30, [this] { this->continue_setup_(); });
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    this->continue_setup_();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void CST226Touchscreen::update_touches() {
 | 
					void CST226Touchscreen::update_touches() {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,7 +35,7 @@ class CST226Touchscreen : public touchscreen::Touchscreen, public i2c::I2CDevice
 | 
				
			|||||||
  void continue_setup_();
 | 
					  void continue_setup_();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  InternalGPIOPin *interrupt_pin_{};
 | 
					  InternalGPIOPin *interrupt_pin_{};
 | 
				
			||||||
  GPIOPin *reset_pin_{NULL_PIN};
 | 
					  GPIOPin *reset_pin_{};
 | 
				
			||||||
  uint8_t chip_id_{};
 | 
					  uint8_t chip_id_{};
 | 
				
			||||||
  bool setup_complete_{};
 | 
					  bool setup_complete_{};
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,7 @@
 | 
				
			|||||||
#include "ct_clamp_sensor.h"
 | 
					#include "ct_clamp_sensor.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "esphome/core/log.h"
 | 
					#include "esphome/core/log.h"
 | 
				
			||||||
 | 
					#include <cinttypes>
 | 
				
			||||||
#include <cmath>
 | 
					#include <cmath>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace esphome {
 | 
					namespace esphome {
 | 
				
			||||||
@@ -37,8 +38,8 @@ void CTClampSensor::update() {
 | 
				
			|||||||
    float rms_ac = 0;
 | 
					    float rms_ac = 0;
 | 
				
			||||||
    if (rms_ac_squared > 0)
 | 
					    if (rms_ac_squared > 0)
 | 
				
			||||||
      rms_ac = std::sqrt(rms_ac_squared);
 | 
					      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,
 | 
					    ESP_LOGD(TAG, "'%s' - Raw AC Value: %.3fA after %" PRIu32 " different samples (%" PRIu32 " SPS)",
 | 
				
			||||||
             this->num_samples_, 1000 * this->num_samples_ / this->sample_duration_);
 | 
					             this->name_.c_str(), rms_ac, this->num_samples_, 1000 * this->num_samples_ / this->sample_duration_);
 | 
				
			||||||
    this->publish_state(rms_ac);
 | 
					    this->publish_state(rms_ac);
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,25 +1,7 @@
 | 
				
			|||||||
import esphome.codegen as cg
 | 
					 | 
				
			||||||
import esphome.config_validation as cv
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
from esphome import pins
 | 
					 | 
				
			||||||
from esphome.const import CONF_ID, CONF_PIN
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
MULTI_CONF = True
 | 
					MULTI_CONF = True
 | 
				
			||||||
AUTO_LOAD = ["sensor"]
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
dallas_ns = cg.esphome_ns.namespace("dallas")
 | 
					CONFIG_SCHEMA = cv.invalid(
 | 
				
			||||||
DallasComponent = dallas_ns.class_("DallasComponent", cg.PollingComponent)
 | 
					    'The "dallas" component has been replaced by the "one_wire" component.\nhttps://esphome.io/components/one_wire'
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
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))
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
from esphome.components import sensor
 | 
					
 | 
				
			||||||
from esphome.const import (
 | 
					CONFIG_SCHEMA = cv.invalid(
 | 
				
			||||||
    CONF_ADDRESS,
 | 
					    'The "dallas" sensor is now "dallas_temp"\nhttps://esphome.io/components/sensor/dallas_temp'
 | 
				
			||||||
    CONF_DALLAS_ID,
 | 
					 | 
				
			||||||
    CONF_INDEX,
 | 
					 | 
				
			||||||
    CONF_RESOLUTION,
 | 
					 | 
				
			||||||
    DEVICE_CLASS_TEMPERATURE,
 | 
					 | 
				
			||||||
    STATE_CLASS_MEASUREMENT,
 | 
					 | 
				
			||||||
    UNIT_CELSIUS,
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
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"]
 | 
				
			||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user