mirror of
				https://github.com/esphome/esphome.git
				synced 2025-11-04 09:01:49 +00:00 
			
		
		
		
	Compare commits
	
		
			956 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					81959804df | ||
| 
						 | 
					75b524ddc4 | ||
| 
						 | 
					f599c36272 | ||
| 
						 | 
					9bb64315f3 | ||
| 
						 | 
					575badc690 | ||
| 
						 | 
					4b91cfb7f9 | ||
| 
						 | 
					a57a842f7b | ||
| 
						 | 
					a8c253a2a5 | ||
| 
						 | 
					8b737aabd9 | ||
| 
						 | 
					0db4815f3d | ||
| 
						 | 
					139db58a66 | ||
| 
						 | 
					c32fec7432 | ||
| 
						 | 
					8bd23dd457 | ||
| 
						 | 
					97a12c0169 | ||
| 
						 | 
					635916737b | ||
| 
						 | 
					65c50e4f01 | ||
| 
						 | 
					5cf18235e3 | ||
| 
						 | 
					b80f3fdec9 | ||
| 
						 | 
					0426be9280 | ||
| 
						 | 
					7c678659d4 | ||
| 
						 | 
					13fe9e83fa | ||
| 
						 | 
					4711f36a1f | ||
| 
						 | 
					01e2a51132 | ||
| 
						 | 
					a70a205ace | ||
| 
						 | 
					33625e2dd3 | ||
| 
						 | 
					0277218319 | ||
| 
						 | 
					18a8c727fa | ||
| 
						 | 
					80ad784a4e | ||
| 
						 | 
					ebadaa9660 | ||
| 
						 | 
					7bc51582f0 | ||
| 
						 | 
					11fb54c74e | ||
| 
						 | 
					913ac8b7e8 | ||
| 
						 | 
					2b9350ce76 | ||
| 
						 | 
					3b18f1b87f | ||
| 
						 | 
					c5c24c1989 | ||
| 
						 | 
					c3938d04f3 | ||
| 
						 | 
					f968713be8 | ||
| 
						 | 
					7b11284008 | ||
| 
						 | 
					f39c0d52ee | ||
| 
						 | 
					a3756a9600 | ||
| 
						 | 
					afa436fe8f | ||
| 
						 | 
					48b5ea9e59 | ||
| 
						 | 
					56974153f1 | ||
| 
						 | 
					9a2cd71571 | ||
| 
						 | 
					d1c6368283 | ||
| 
						 | 
					5c3268b8d4 | ||
| 
						 | 
					25af5ab7c6 | ||
| 
						 | 
					4d586b1446 | ||
| 
						 | 
					bb759d52c8 | ||
| 
						 | 
					9a2cf05c5f | ||
| 
						 | 
					c79d700d03 | ||
| 
						 | 
					482a3aebc9 | ||
| 
						 | 
					387f249363 | ||
| 
						 | 
					3d917d0b7e | ||
| 
						 | 
					824f3187ac | ||
| 
						 | 
					e3c27a483c | ||
| 
						 | 
					a33bb32874 | ||
| 
						 | 
					93d9d4b50a | ||
| 
						 | 
					2376a2c941 | ||
| 
						 | 
					b92702a312 | ||
| 
						 | 
					b11d5f6799 | ||
| 
						 | 
					072dce340e | ||
| 
						 | 
					cccb1a2c9e | ||
| 
						 | 
					063d9c47a4 | ||
| 
						 | 
					8d8d421286 | ||
| 
						 | 
					0ce57e5a39 | ||
| 
						 | 
					aebad04c0b | ||
| 
						 | 
					514d11d46f | ||
| 
						 | 
					96e46db272 | ||
| 
						 | 
					76f78877f6 | ||
| 
						 | 
					4ffa68b773 | ||
| 
						 | 
					8eaffee160 | ||
| 
						 | 
					557a622f71 | ||
| 
						 | 
					e9c6556296 | ||
| 
						 | 
					dce9d59dfe | ||
| 
						 | 
					d3e291b442 | ||
| 
						 | 
					d4686c0fb1 | ||
| 
						 | 
					ae9b247f47 | ||
| 
						 | 
					a59761d292 | ||
| 
						 | 
					030c87d142 | ||
| 
						 | 
					95ed3e9d46 | ||
| 
						 | 
					d0eaebe19f | ||
| 
						 | 
					9ecead2645 | ||
| 
						 | 
					98166dfa66 | ||
| 
						 | 
					5645be4e0f | ||
| 
						 | 
					9a7a205510 | ||
| 
						 | 
					7e3b8fd346 | ||
| 
						 | 
					3d6dcc9eee | ||
| 
						 | 
					4f6982fbc5 | ||
| 
						 | 
					00c144daeb | ||
| 
						 | 
					fddb05c845 | ||
| 
						 | 
					3b13e62d3f | ||
| 
						 | 
					9e7acd6f75 | ||
| 
						 | 
					7d9c043f1d | ||
| 
						 | 
					bafbeefb37 | ||
| 
						 | 
					54660300e9 | ||
| 
						 | 
					5dc40049be | ||
| 
						 | 
					1e46b4073f | ||
| 
						 | 
					29fc4af0fc | ||
| 
						 | 
					4030a2e253 | ||
| 
						 | 
					ea80cb751b | ||
| 
						 | 
					6aa61dbce7 | ||
| 
						 | 
					cdc9c99d40 | ||
| 
						 | 
					07f2931841 | ||
| 
						 | 
					616ad04131 | ||
| 
						 | 
					b966e58f9e | ||
| 
						 | 
					a546677b08 | ||
| 
						 | 
					5c3af1d3f6 | ||
| 
						 | 
					66b0b6feeb | ||
| 
						 | 
					7665a220a0 | ||
| 
						 | 
					4250af4dd9 | ||
| 
						 | 
					73252ccd25 | ||
| 
						 | 
					33bf78c369 | ||
| 
						 | 
					f33c2a48eb | ||
| 
						 | 
					96ded4e402 | ||
| 
						 | 
					44d5be6e7b | ||
| 
						 | 
					076124eb71 | ||
| 
						 | 
					44562dbef1 | ||
| 
						 | 
					cafdcaec29 | ||
| 
						 | 
					b660e5a7fa | ||
| 
						 | 
					3b4ea0ed0a | ||
| 
						 | 
					403b6e32e3 | ||
| 
						 | 
					229bf719a2 | ||
| 
						 | 
					2225594ee8 | ||
| 
						 | 
					b91a1aa027 | ||
| 
						 | 
					13dbdd9b16 | ||
| 
						 | 
					37bc0b3b5a | ||
| 
						 | 
					be70a96651 | ||
| 
						 | 
					d83d214497 | ||
| 
						 | 
					6ec0f80b76 | ||
| 
						 | 
					06f566346d | ||
| 
						 | 
					b680649113 | ||
| 
						 | 
					5ab2ef4079 | ||
| 
						 | 
					392ed64375 | ||
| 
						 | 
					566c129435 | ||
| 
						 | 
					c903eb2d01 | ||
| 
						 | 
					69c78651d5 | ||
| 
						 | 
					f7232b199a | ||
| 
						 | 
					ffc6fe9ca0 | ||
| 
						 | 
					06b8e4df27 | ||
| 
						 | 
					b103be99e8 | ||
| 
						 | 
					28ed42d879 | ||
| 
						 | 
					98f0d75180 | ||
| 
						 | 
					34487c9de4 | ||
| 
						 | 
					822377be8b | ||
| 
						 | 
					dd4fb85170 | ||
| 
						 | 
					07b3327102 | ||
| 
						 | 
					02aa75f68c | ||
| 
						 | 
					07db9319ad | ||
| 
						 | 
					4ae4a4ee88 | ||
| 
						 | 
					a7c648b60b | ||
| 
						 | 
					4d7c1ae143 | ||
| 
						 | 
					cc6d1e85cc | ||
| 
						 | 
					7fb116d87d | ||
| 
						 | 
					cc43e01e34 | ||
| 
						 | 
					6d3ccf4df5 | ||
| 
						 | 
					bb3d0706d3 | ||
| 
						 | 
					820dedbcd2 | ||
| 
						 | 
					aed6f2b1ea | ||
| 
						 | 
					bf1885af3f | ||
| 
						 | 
					d8e4f5d56b | ||
| 
						 | 
					af616473aa | ||
| 
						 | 
					2033ac34e5 | ||
| 
						 | 
					5e239d3d88 | ||
| 
						 | 
					30893afd67 | ||
| 
						 | 
					a39bb7b92f | ||
| 
						 | 
					b53f9f2a81 | ||
| 
						 | 
					586e36906d | ||
| 
						 | 
					e6f8e73705 | ||
| 
						 | 
					eaf9735eda | ||
| 
						 | 
					2e50e1f506 | ||
| 
						 | 
					76a6c39f25 | ||
| 
						 | 
					bf01c22e1f | ||
| 
						 | 
					99f14e03d4 | ||
| 
						 | 
					d92c8ccadf | ||
| 
						 | 
					7964b724ed | ||
| 
						 | 
					e0c5b45694 | ||
| 
						 | 
					26407e001b | ||
| 
						 | 
					3ecae3f16f | ||
| 
						 | 
					5c359856ff | ||
| 
						 | 
					2d618768d5 | ||
| 
						 | 
					808e3be324 | ||
| 
						 | 
					9e23987db8 | ||
| 
						 | 
					ad76312f66 | ||
| 
						 | 
					2028362fd5 | ||
| 
						 | 
					13e0d6b9a1 | ||
| 
						 | 
					a6255c31fe | ||
| 
						 | 
					46356cbc4a | ||
| 
						 | 
					0fe61d9ec7 | ||
| 
						 | 
					6114d331c9 | ||
| 
						 | 
					e2e074a3fd | ||
| 
						 | 
					4d340dc029 | ||
| 
						 | 
					fb6c5ebe9a | ||
| 
						 | 
					af3273d930 | ||
| 
						 | 
					8f1eb77e05 | ||
| 
						 | 
					89d0d41c5a | ||
| 
						 | 
					452ca8e4c6 | ||
| 
						 | 
					e51b0ca15e | ||
| 
						 | 
					5eeb110d74 | ||
| 
						 | 
					60b2d57dc3 | ||
| 
						 | 
					91898cb814 | ||
| 
						 | 
					818a7f1c78 | ||
| 
						 | 
					dedf343bd5 | ||
| 
						 | 
					251240cc90 | ||
| 
						 | 
					e5b45b6b4b | ||
| 
						 | 
					a77784a6da | ||
| 
						 | 
					f63f9168ff | ||
| 
						 | 
					b5b2036971 | ||
| 
						 | 
					a96b6e7002 | ||
| 
						 | 
					f34c9b33fc | ||
| 
						 | 
					faf577a9dd | ||
| 
						 | 
					7708b81ef5 | ||
| 
						 | 
					08998caabc | ||
| 
						 | 
					848a5f1680 | ||
| 
						 | 
					2e7c1d7345 | ||
| 
						 | 
					28a72fa56b | ||
| 
						 | 
					cd1353ae54 | ||
| 
						 | 
					c9ee513fa8 | ||
| 
						 | 
					2a12caa955 | ||
| 
						 | 
					5e5f8d5f9b | ||
| 
						 | 
					2c0fe49b86 | ||
| 
						 | 
					1e227e8051 | ||
| 
						 | 
					d5cf4b7eac | ||
| 
						 | 
					570ec36fe3 | ||
| 
						 | 
					69879920eb | ||
| 
						 | 
					2b60b0f1fa | ||
| 
						 | 
					c17624adab | ||
| 
						 | 
					0f151a8f6b | ||
| 
						 | 
					e62bf333a2 | ||
| 
						 | 
					88b46b7487 | ||
| 
						 | 
					fa290fbce8 | ||
| 
						 | 
					1883ce1876 | ||
| 
						 | 
					f3fe2bde38 | ||
| 
						 | 
					811c13d7d1 | ||
| 
						 | 
					521dfe08f2 | ||
| 
						 | 
					2c77d121da | ||
| 
						 | 
					c5dc736c53 | ||
| 
						 | 
					8e93735861 | ||
| 
						 | 
					ac25b138f5 | ||
| 
						 | 
					b17e0c298e | ||
| 
						 | 
					422f0ad7a9 | ||
| 
						 | 
					34d37961c3 | ||
| 
						 | 
					bdf004867d | ||
| 
						 | 
					08ecca86bc | ||
| 
						 | 
					520c4331e3 | ||
| 
						 | 
					342d5166a0 | ||
| 
						 | 
					69d39ef0cd | ||
| 
						 | 
					6588e9380e | ||
| 
						 | 
					4d6277330b | ||
| 
						 | 
					87154e9b6f | ||
| 
						 | 
					b91723344e | ||
| 
						 | 
					92b36720b6 | ||
| 
						 | 
					3d0310d0e0 | ||
| 
						 | 
					f81cddf22e | ||
| 
						 | 
					808ce6eecb | ||
| 
						 | 
					665d0fd759 | ||
| 
						 | 
					c519c02de8 | ||
| 
						 | 
					eacac78099 | ||
| 
						 | 
					a925036ff8 | ||
| 
						 | 
					1468293f3e | ||
| 
						 | 
					25924ca4e8 | ||
| 
						 | 
					6c8ace0ce8 | ||
| 
						 | 
					acc1af0f51 | ||
| 
						 | 
					c92c439d17 | ||
| 
						 | 
					410fad3b41 | ||
| 
						 | 
					dce20680d7 | ||
| 
						 | 
					f95be6a0df | ||
| 
						 | 
					a342302114 | ||
| 
						 | 
					925a992d1b | ||
| 
						 | 
					61ecbe4273 | ||
| 
						 | 
					65c7e27a43 | ||
| 
						 | 
					57b56010da | ||
| 
						 | 
					ef89249019 | ||
| 
						 | 
					bc64cf3e47 | ||
| 
						 | 
					def70dde72 | ||
| 
						 | 
					b52f7cfe86 | ||
| 
						 | 
					81b512a7b3 | ||
| 
						 | 
					57d6185374 | ||
| 
						 | 
					e288aa07fb | ||
| 
						 | 
					50006e4c42 | ||
| 
						 | 
					23cf120977 | ||
| 
						 | 
					04d8593f38 | ||
| 
						 | 
					28e39f7f76 | ||
| 
						 | 
					de3377132d | ||
| 
						 | 
					b351cd94d7 | ||
| 
						 | 
					d238e06f86 | ||
| 
						 | 
					2fc59ecc30 | ||
| 
						 | 
					0047d24698 | ||
| 
						 | 
					89a89e1785 | ||
| 
						 | 
					1952d275f7 | ||
| 
						 | 
					043095b605 | ||
| 
						 | 
					bccaa78a90 | ||
| 
						 | 
					f402c89539 | ||
| 
						 | 
					431d3578a5 | ||
| 
						 | 
					5f02d86841 | ||
| 
						 | 
					a7ec57d4be | ||
| 
						 | 
					4eeb111fa3 | ||
| 
						 | 
					1ea5cc497f | ||
| 
						 | 
					b601cf6bc6 | ||
| 
						 | 
					5057caa7fc | ||
| 
						 | 
					95bef53d37 | ||
| 
						 | 
					ea019a057b | ||
| 
						 | 
					1d378e416d | ||
| 
						 | 
					d3b758d10a | ||
| 
						 | 
					7b9c2d2978 | ||
| 
						 | 
					9d38543cb0 | ||
| 
						 | 
					b860a317b9 | ||
| 
						 | 
					9591c903f7 | ||
| 
						 | 
					65fbb8bc60 | ||
| 
						 | 
					97428f2ba2 | ||
| 
						 | 
					9ab6a7b7ff | ||
| 
						 | 
					e2ad6fe3d8 | ||
| 
						 | 
					6781d08c9b | ||
| 
						 | 
					36a2ce2c23 | ||
| 
						 | 
					c7c71089ce | ||
| 
						 | 
					52c67d715b | ||
| 
						 | 
					f084ab339b | ||
| 
						 | 
					8352f52fef | ||
| 
						 | 
					b28735d63b | ||
| 
						 | 
					4c105398f7 | ||
| 
						 | 
					828f7946ea | ||
| 
						 | 
					23c663d5d4 | ||
| 
						 | 
					6a99789c92 | ||
| 
						 | 
					52e13164b4 | ||
| 
						 | 
					28f2582256 | ||
| 
						 | 
					652f6058d1 | ||
| 
						 | 
					717aab7c8b | ||
| 
						 | 
					5e799b5284 | ||
| 
						 | 
					9f36b25d4e | ||
| 
						 | 
					d9a2651a5a | ||
| 
						 | 
					5fcd1e391d | ||
| 
						 | 
					36089a1400 | ||
| 
						 | 
					e7b1d2efaa | ||
| 
						 | 
					bf453ad8cd | ||
| 
						 | 
					fbc1b3e316 | ||
| 
						 | 
					400819175d | ||
| 
						 | 
					3c34b539b0 | ||
| 
						 | 
					c8058e9636 | ||
| 
						 | 
					f2474c5c45 | ||
| 
						 | 
					7acc36d39d | ||
| 
						 | 
					b01db991a5 | ||
| 
						 | 
					86385a1c19 | ||
| 
						 | 
					96ab6b51b8 | ||
| 
						 | 
					8c849b9002 | ||
| 
						 | 
					6a0d4cb5a9 | ||
| 
						 | 
					72002ce70e | ||
| 
						 | 
					c67539cf5b | ||
| 
						 | 
					dcd3d2084d | ||
| 
						 | 
					585bb6dac8 | ||
| 
						 | 
					02dc49c272 | ||
| 
						 | 
					5df398ec31 | ||
| 
						 | 
					699696e8d1 | ||
| 
						 | 
					93e35a53ed | ||
| 
						 | 
					a269098a0e | ||
| 
						 | 
					cac3055261 | ||
| 
						 | 
					6f7e6cc944 | ||
| 
						 | 
					0a841fcc50 | ||
| 
						 | 
					3c64c9b0e9 | ||
| 
						 | 
					34df25da39 | ||
| 
						 | 
					9586fb95d1 | ||
| 
						 | 
					3ee6348e41 | ||
| 
						 | 
					543f2c8152 | ||
| 
						 | 
					16d11be213 | ||
| 
						 | 
					e49b568fd4 | ||
| 
						 | 
					22ab830ff3 | ||
| 
						 | 
					095d3181cd | ||
| 
						 | 
					9aa14a2e83 | ||
| 
						 | 
					ac15ce576b | ||
| 
						 | 
					498b59e998 | ||
| 
						 | 
					63c420254a | ||
| 
						 | 
					765e641d08 | ||
| 
						 | 
					be16d10b7d | ||
| 
						 | 
					4b808611e9 | ||
| 
						 | 
					7afe202e20 | ||
| 
						 | 
					039810eef3 | ||
| 
						 | 
					ff43b45113 | ||
| 
						 | 
					7cd4c3bdd3 | ||
| 
						 | 
					c12c9e97c2 | ||
| 
						 | 
					b3169deda7 | ||
| 
						 | 
					0ea41e2f71 | ||
| 
						 | 
					3afb564a48 | ||
| 
						 | 
					7ff3f752e2 | ||
| 
						 | 
					d821ead92a | ||
| 
						 | 
					e42ce64127 | ||
| 
						 | 
					9d2b0b4e03 | ||
| 
						 | 
					b5e6ae0d69 | ||
| 
						 | 
					08f1eac8b2 | ||
| 
						 | 
					6ed3da33a2 | ||
| 
						 | 
					a9a00f139b | ||
| 
						 | 
					63d8071dbd | ||
| 
						 | 
					d20caa9d60 | ||
| 
						 | 
					7e40d4246c | ||
| 
						 | 
					5a2b14cfa4 | ||
| 
						 | 
					f2d218e5ad | ||
| 
						 | 
					b493d5bba5 | ||
| 
						 | 
					c9055f2aef | ||
| 
						 | 
					9fed7cab5f | ||
| 
						 | 
					eb5c4b7c4f | ||
| 
						 | 
					2ab3534a4b | ||
| 
						 | 
					9816e677a6 | ||
| 
						 | 
					fc01a70b65 | ||
| 
						 | 
					7221337442 | ||
| 
						 | 
					051a1e4772 | ||
| 
						 | 
					274741a9d5 | ||
| 
						 | 
					25c01adf51 | ||
| 
						 | 
					bf2d54c3ef | ||
| 
						 | 
					49cb8fd9d3 | ||
| 
						 | 
					e536316e3d | ||
| 
						 | 
					a6c46eb8e5 | ||
| 
						 | 
					1a270374e0 | ||
| 
						 | 
					e73eafbd88 | ||
| 
						 | 
					9fc3e05b76 | ||
| 
						 | 
					31c604331c | ||
| 
						 | 
					3fcdaaefe0 | ||
| 
						 | 
					20dd744680 | ||
| 
						 | 
					e4636b99f7 | ||
| 
						 | 
					10e7abb579 | ||
| 
						 | 
					d3f03b7acb | ||
| 
						 | 
					7e53fc9d6a | ||
| 
						 | 
					0059a6de46 | ||
| 
						 | 
					22e1758d5b | ||
| 
						 | 
					59cdc32970 | ||
| 
						 | 
					adb51cf733 | ||
| 
						 | 
					d97a9bf8e8 | ||
| 
						 | 
					f034472e2a | ||
| 
						 | 
					ed328d2df8 | ||
| 
						 | 
					1520dc8755 | ||
| 
						 | 
					bf601c3126 | ||
| 
						 | 
					ab48e4a466 | ||
| 
						 | 
					8ef0f5b047 | ||
| 
						 | 
					2c14d134be | ||
| 
						 | 
					9cd21bb5a0 | ||
| 
						 | 
					bd061ac2ee | ||
| 
						 | 
					dd3e821857 | ||
| 
						 | 
					b38b7019ea | ||
| 
						 | 
					2c71ee7853 | ||
| 
						 | 
					540c62061d | ||
| 
						 | 
					221ef07c8b | ||
| 
						 | 
					29fc7ea154 | ||
| 
						 | 
					7b157aeff1 | ||
| 
						 | 
					e50644edee | ||
| 
						 | 
					5f619e6f01 | ||
| 
						 | 
					b266fb37a3 | ||
| 
						 | 
					c9caf44c2e | ||
| 
						 | 
					0c87a9ad2c | ||
| 
						 | 
					c680b437f5 | ||
| 
						 | 
					4988349677 | ||
| 
						 | 
					e35d56defe | ||
| 
						 | 
					5c86f332b2 | ||
| 
						 | 
					002861f13b | ||
| 
						 | 
					dbec3d7c50 | ||
| 
						 | 
					89cde158d6 | ||
| 
						 | 
					d7b76aadb2 | ||
| 
						 | 
					e09fefd389 | ||
| 
						 | 
					febc485da6 | ||
| 
						 | 
					2ae709c2ba | ||
| 
						 | 
					0ccfdd4711 | ||
| 
						 | 
					0e59243b83 | ||
| 
						 | 
					01bbd04a5a | ||
| 
						 | 
					a3b2d384f5 | ||
| 
						 | 
					50238f8d72 | ||
| 
						 | 
					704470d606 | ||
| 
						 | 
					f7e6195466 | ||
| 
						 | 
					a0bb7c3ed0 | ||
| 
						 | 
					e3a6c9a6cf | ||
| 
						 | 
					99598d87a9 | ||
| 
						 | 
					b5df50893b | ||
| 
						 | 
					f46b3d15cd | ||
| 
						 | 
					041b4ec66e | ||
| 
						 | 
					703e9673c2 | ||
| 
						 | 
					e7bd93b4b0 | ||
| 
						 | 
					a401c71d3e | ||
| 
						 | 
					5bae233334 | ||
| 
						 | 
					c4edd3047f | ||
| 
						 | 
					c50da1593a | ||
| 
						 | 
					1d06426281 | ||
| 
						 | 
					9c5b693dd5 | ||
| 
						 | 
					5fecc70db1 | ||
| 
						 | 
					ff24023b39 | ||
| 
						 | 
					1a04e2d1b8 | ||
| 
						 | 
					52c4dd0e35 | ||
| 
						 | 
					ff90f6a440 | ||
| 
						 | 
					e24d5c172f | ||
| 
						 | 
					0918f452a0 | ||
| 
						 | 
					839fe49e61 | ||
| 
						 | 
					ff050d634a | ||
| 
						 | 
					228670df78 | ||
| 
						 | 
					cfe4638665 | ||
| 
						 | 
					b7352b1345 | ||
| 
						 | 
					32ae8fc2d0 | ||
| 
						 | 
					86df4c1d8d | ||
| 
						 | 
					dc4a88029c | ||
| 
						 | 
					5da9b2ede7 | ||
| 
						 | 
					29cfcfaf0f | ||
| 
						 | 
					61bfd347ea | ||
| 
						 | 
					b7436c0b22 | ||
| 
						 | 
					8a45dfac5c | ||
| 
						 | 
					69f5d8cd0f | ||
| 
						 | 
					9a57e8fcb0 | ||
| 
						 | 
					a9d75ca4f4 | ||
| 
						 | 
					ccb6fc3010 | ||
| 
						 | 
					4e9a05fe11 | ||
| 
						 | 
					8a294e4134 | ||
| 
						 | 
					aad9a539c1 | ||
| 
						 | 
					fd6ac529fb | ||
| 
						 | 
					009cea1abf | ||
| 
						 | 
					4c3c14ec32 | ||
| 
						 | 
					636c9db1e3 | ||
| 
						 | 
					71f625bbd3 | ||
| 
						 | 
					aea2e9a6bb | ||
| 
						 | 
					3f6f3c14c4 | ||
| 
						 | 
					b1d77b7c03 | ||
| 
						 | 
					49233e4734 | ||
| 
						 | 
					e2b5ecb78b | ||
| 
						 | 
					351ce67eae | ||
| 
						 | 
					44af5e439c | ||
| 
						 | 
					dbc0d500d8 | ||
| 
						 | 
					86736aa480 | ||
| 
						 | 
					57eb05c0e3 | ||
| 
						 | 
					f6fe6e6bff | ||
| 
						 | 
					18560f9430 | ||
| 
						 | 
					cb0ba647ed | ||
| 
						 | 
					f9fceb7ffc | ||
| 
						 | 
					2697c9465b | ||
| 
						 | 
					8d204655be | ||
| 
						 | 
					8414a22356 | ||
| 
						 | 
					36e4a8b444 | ||
| 
						 | 
					949c71dc97 | ||
| 
						 | 
					a6f6b8da7f | ||
| 
						 | 
					a9f123e864 | ||
| 
						 | 
					a64a505817 | ||
| 
						 | 
					352004221e | ||
| 
						 | 
					fc6a3e31c2 | ||
| 
						 | 
					a32b58fdf1 | ||
| 
						 | 
					2d50ecbecf | ||
| 
						 | 
					87f1ffec05 | ||
| 
						 | 
					f0dfde9fa1 | ||
| 
						 | 
					e36dc2d05e | ||
| 
						 | 
					08c8fa2c90 | ||
| 
						 | 
					fe6621357e | ||
| 
						 | 
					4a0067a2c5 | ||
| 
						 | 
					b270ff335d | ||
| 
						 | 
					7d2fcf59fd | ||
| 
						 | 
					d26c43103d | ||
| 
						 | 
					389889ad70 | ||
| 
						 | 
					0af73c7903 | ||
| 
						 | 
					8aa73bba10 | ||
| 
						 | 
					5396b05237 | ||
| 
						 | 
					e898345ff1 | ||
| 
						 | 
					f39bf9c1e5 | ||
| 
						 | 
					9483a9c8c4 | ||
| 
						 | 
					52639a0a7c | ||
| 
						 | 
					a1e10f384e | ||
| 
						 | 
					27d4b3b8ad | ||
| 
						 | 
					6438bd84fb | ||
| 
						 | 
					4b8e910e3f | ||
| 
						 | 
					9c0e463698 | ||
| 
						 | 
					2092939353 | ||
| 
						 | 
					b9d55fd1ed | ||
| 
						 | 
					c1748d586e | ||
| 
						 | 
					4c55b9c58c | ||
| 
						 | 
					312958c573 | ||
| 
						 | 
					65a489ebdd | ||
| 
						 | 
					bd392e2efc | ||
| 
						 | 
					25ad33a377 | ||
| 
						 | 
					abc83f6cb0 | ||
| 
						 | 
					f61a82a568 | ||
| 
						 | 
					1c3ed71d36 | ||
| 
						 | 
					8215a018e9 | ||
| 
						 | 
					bf3b678727 | ||
| 
						 | 
					f6e3070dd8 | ||
| 
						 | 
					4996967c79 | ||
| 
						 | 
					5887fe8302 | ||
| 
						 | 
					0fc8e8d483 | ||
| 
						 | 
					55388724af | ||
| 
						 | 
					32efa5d83e | ||
| 
						 | 
					275c12150e | ||
| 
						 | 
					62468198d6 | ||
| 
						 | 
					43d5e7a8cc | ||
| 
						 | 
					7524493c3c | ||
| 
						 | 
					5759de079b | ||
| 
						 | 
					f3d5d27c8f | ||
| 
						 | 
					c030be4d3f | ||
| 
						 | 
					50cf57affb | ||
| 
						 | 
					25c62319a3 | ||
| 
						 | 
					27abb1280a | ||
| 
						 | 
					c73f0eee4c | ||
| 
						 | 
					9208227d92 | ||
| 
						 | 
					725e8c69f5 | ||
| 
						 | 
					facd4f63ec | ||
| 
						 | 
					2e1d14b8b1 | ||
| 
						 | 
					e8272759be | ||
| 
						 | 
					ebbfab608c | ||
| 
						 | 
					a5e1f8fe19 | ||
| 
						 | 
					1b2de953d0 | ||
| 
						 | 
					df5d03b0bc | ||
| 
						 | 
					11c1fe8827 | ||
| 
						 | 
					f29622abe1 | ||
| 
						 | 
					10e411f8c1 | ||
| 
						 | 
					6405799cc2 | ||
| 
						 | 
					0afa41d08a | ||
| 
						 | 
					48f3dfe455 | ||
| 
						 | 
					6f0bfb286a | ||
| 
						 | 
					2e54d3f98d | ||
| 
						 | 
					67b4dcf8ae | ||
| 
						 | 
					ad91362571 | ||
| 
						 | 
					76a3e75bc7 | ||
| 
						 | 
					e88418a01b | ||
| 
						 | 
					27e08d37ea | ||
| 
						 | 
					e069687477 | ||
| 
						 | 
					ef0e611e52 | ||
| 
						 | 
					d58d0e89c7 | ||
| 
						 | 
					dfbf225403 | ||
| 
						 | 
					e962762046 | ||
| 
						 | 
					8166d0de79 | ||
| 
						 | 
					d9c33f19e2 | ||
| 
						 | 
					0cc3902ffc | ||
| 
						 | 
					4752096520 | ||
| 
						 | 
					dcadcdf056 | ||
| 
						 | 
					b6394b7c6d | ||
| 
						 | 
					3ec9bcaed6 | ||
| 
						 | 
					d5c59292c8 | ||
| 
						 | 
					a20e71b32a | ||
| 
						 | 
					1254ec2849 | ||
| 
						 | 
					ca144bae90 | ||
| 
						 | 
					850368b529 | ||
| 
						 | 
					7bbdfc5553 | ||
| 
						 | 
					ba73cb1b75 | ||
| 
						 | 
					eca36cb868 | ||
| 
						 | 
					2a14473e8c | ||
| 
						 | 
					9880a425f4 | ||
| 
						 | 
					13696b1ec4 | ||
| 
						 | 
					764eb960c6 | ||
| 
						 | 
					17b55fc23d | ||
| 
						 | 
					e20c994b82 | ||
| 
						 | 
					465cd3d1f9 | ||
| 
						 | 
					412351fd1e | ||
| 
						 | 
					5776e70d7c | ||
| 
						 | 
					1ccc6e342c | ||
| 
						 | 
					a53481e2da | ||
| 
						 | 
					01b1b688b1 | ||
| 
						 | 
					3d78248aaf | ||
| 
						 | 
					cf703f6ac4 | ||
| 
						 | 
					2012c769f6 | ||
| 
						 | 
					c8a4eb426c | ||
| 
						 | 
					351ecf9bd4 | ||
| 
						 | 
					e6f42fa6f0 | ||
| 
						 | 
					582ac4ac81 | ||
| 
						 | 
					6e30bacae3 | ||
| 
						 | 
					5d136a18af | ||
| 
						 | 
					1a47e4b524 | ||
| 
						 | 
					c296b4c348 | ||
| 
						 | 
					c52cb7bbad | ||
| 
						 | 
					1923e0312b | ||
| 
						 | 
					c8998941a5 | ||
| 
						 | 
					e41ea42074 | ||
| 
						 | 
					ed5f207eba | ||
| 
						 | 
					a76b8e745b | ||
| 
						 | 
					68d29c5af5 | ||
| 
						 | 
					c3acf08c02 | ||
| 
						 | 
					ef9e6e4d6e | ||
| 
						 | 
					f3158c8b24 | ||
| 
						 | 
					ac4a179703 | ||
| 
						 | 
					dd4ea51d1f | ||
| 
						 | 
					45f6926a5a | ||
| 
						 | 
					5ca4bac10a | ||
| 
						 | 
					ec026ab3a8 | ||
| 
						 | 
					0e5e559283 | ||
| 
						 | 
					417a3cdf51 | ||
| 
						 | 
					7fa98e288f | ||
| 
						 | 
					c693c219f4 | ||
| 
						 | 
					e5d4e12457 | ||
| 
						 | 
					33212d1abf | ||
| 
						 | 
					7a16f846eb | ||
| 
						 | 
					6873f09f57 | ||
| 
						 | 
					382793de9a | ||
| 
						 | 
					d4e76185bd | ||
| 
						 | 
					8566dd9100 | ||
| 
						 | 
					f479eae714 | ||
| 
						 | 
					31067530a5 | ||
| 
						 | 
					6505c30c2b | ||
| 
						 | 
					7487ffcbcb | ||
| 
						 | 
					f79299c240 | ||
| 
						 | 
					2f07225984 | ||
| 
						 | 
					c99b2b59c2 | ||
| 
						 | 
					78633c5768 | ||
| 
						 | 
					c041cc483c | ||
| 
						 | 
					83a12d980e | ||
| 
						 | 
					491f7e96f0 | ||
| 
						 | 
					bfb9cb6732 | ||
| 
						 | 
					3c9712d683 | ||
| 
						 | 
					ca4107d450 | ||
| 
						 | 
					148f5d9418 | ||
| 
						 | 
					51ab0f0b78 | ||
| 
						 | 
					bf0cce4ad8 | ||
| 
						 | 
					82d5d50d61 | ||
| 
						 | 
					ecb1c77f8b | ||
| 
						 | 
					f9a8629157 | ||
| 
						 | 
					e2b655a6cc | ||
| 
						 | 
					04e6f475b4 | ||
| 
						 | 
					0082c5b459 | ||
| 
						 | 
					3fd130869e | ||
| 
						 | 
					d000440927 | ||
| 
						 | 
					4fb750de43 | ||
| 
						 | 
					9632ac0e02 | ||
| 
						 | 
					35078fd52f | ||
| 
						 | 
					0bb81e5b2d | ||
| 
						 | 
					35a2258f12 | ||
| 
						 | 
					b650704877 | ||
| 
						 | 
					0c0dec2534 | ||
| 
						 | 
					d1b051a6bd | ||
| 
						 | 
					27204aa53c | ||
| 
						 | 
					8aedac81a5 | ||
| 
						 | 
					42007d03d4 | ||
| 
						 | 
					821c1a8bbd | ||
| 
						 | 
					bab562dc3a | ||
| 
						 | 
					f63fd9696f | ||
| 
						 | 
					cd7af19e7c | ||
| 
						 | 
					64bd33a94e | ||
| 
						 | 
					60e6366521 | ||
| 
						 | 
					072b2c445c | ||
| 
						 | 
					219fe41831 | ||
| 
						 | 
					dcc8bb83af | ||
| 
						 | 
					a8e3521f3c | ||
| 
						 | 
					706dc6d116 | ||
| 
						 | 
					84accb6df6 | ||
| 
						 | 
					8421570b18 | ||
| 
						 | 
					d355543ac9 | ||
| 
						 | 
					3a597c5aa6 | ||
| 
						 | 
					c7dddaded4 | ||
| 
						 | 
					aae4b2ea5d | ||
| 
						 | 
					310e2a0b20 | ||
| 
						 | 
					0b04d143ac | ||
| 
						 | 
					d1f4c2ab57 | ||
| 
						 | 
					7e4d12f880 | ||
| 
						 | 
					ecd65003d4 | ||
| 
						 | 
					fed37b48a1 | ||
| 
						 | 
					3fba3a5e2e | ||
| 
						 | 
					9e7e8ab116 | ||
| 
						 | 
					1bec1faf6d | ||
| 
						 | 
					e64246f642 | ||
| 
						 | 
					fb2b7ade41 | ||
| 
						 | 
					4de44a99eb | ||
| 
						 | 
					39fbf9c56f | ||
| 
						 | 
					021055f0b8 | ||
| 
						 | 
					c2e0ea97d8 | ||
| 
						 | 
					153aadadae | ||
| 
						 | 
					1319ff1129 | ||
| 
						 | 
					7df72ddb96 | ||
| 
						 | 
					1d9ce2afc5 | ||
| 
						 | 
					53986279d7 | ||
| 
						 | 
					26d6738abb | ||
| 
						 | 
					7fa5cab8e3 | ||
| 
						 | 
					4f2e2fa297 | ||
| 
						 | 
					0473c4c79f | ||
| 
						 | 
					a62b6548d2 | ||
| 
						 | 
					da390d32f8 | ||
| 
						 | 
					8b92456ded | ||
| 
						 | 
					14e9375262 | ||
| 
						 | 
					d49ee47018 | ||
| 
						 | 
					af66753c1b | ||
| 
						 | 
					39b35b79ba | ||
| 
						 | 
					a2a83c5004 | ||
| 
						 | 
					ba1222eae4 | ||
| 
						 | 
					31ae337931 | ||
| 
						 | 
					bab0ba9c0f | ||
| 
						 | 
					c9e224e999 | ||
| 
						 | 
					6123cb7c69 | ||
| 
						 | 
					8040e3cf95 | ||
| 
						 | 
					d447548893 | ||
| 
						 | 
					269812e781 | ||
| 
						 | 
					65f4d30fd0 | ||
| 
						 | 
					c1dfed5c08 | ||
| 
						 | 
					835079ad43 | ||
| 
						 | 
					17fd9d5107 | ||
| 
						 | 
					8613c02d5c | ||
| 
						 | 
					dea6675c21 | ||
| 
						 | 
					43cf3063e0 | ||
| 
						 | 
					3b7a47fb90 | ||
| 
						 | 
					79248e8b74 | ||
| 
						 | 
					4620ad6124 | ||
| 
						 | 
					25cdbacecc | ||
| 
						 | 
					4ec636c08f | ||
| 
						 | 
					4cb30a22ac | ||
| 
						 | 
					c632b0e1d4 | ||
| 
						 | 
					714d28a61a | ||
| 
						 | 
					c60989a7be | ||
| 
						 | 
					11b727fdf7 | ||
| 
						 | 
					a1dfd355f7 | ||
| 
						 | 
					fcb2cc2471 | ||
| 
						 | 
					177617e6e3 | ||
| 
						 | 
					e0b4226930 | ||
| 
						 | 
					426e6a1b46 | ||
| 
						 | 
					66083c5e97 | ||
| 
						 | 
					aff4f1e9e2 | ||
| 
						 | 
					3c68348868 | ||
| 
						 | 
					7f2a6e7403 | ||
| 
						 | 
					11069085e3 | ||
| 
						 | 
					854d735ab3 | ||
| 
						 | 
					a4ab52918b | ||
| 
						 | 
					eb895d2095 | ||
| 
						 | 
					67cbaabd99 | ||
| 
						 | 
					4402a6eb4c | ||
| 
						 | 
					6ae1efcf9f | ||
| 
						 | 
					1d136ab0df | ||
| 
						 | 
					7721049ed7 | ||
| 
						 | 
					e6f21873c3 | ||
| 
						 | 
					499903bd3d | ||
| 
						 | 
					2d0d794a9d | ||
| 
						 | 
					a55787f40c | ||
| 
						 | 
					990a4d4774 | ||
| 
						 | 
					6a60f01753 | ||
| 
						 | 
					30ecb58e06 | ||
| 
						 | 
					3b689ef39c | ||
| 
						 | 
					170d52e0db | ||
| 
						 | 
					92d93d658c | ||
| 
						 | 
					d7a2816c58 | ||
| 
						 | 
					a30d2f291c | ||
| 
						 | 
					d33a158585 | ||
| 
						 | 
					e21dbc4b60 | ||
| 
						 | 
					8a754421fe | ||
| 
						 | 
					a73fd55fc2 | ||
| 
						 | 
					45630d74f3 | ||
| 
						 | 
					a6d31f05ee | ||
| 
						 | 
					05f9dede70 | ||
| 
						 | 
					7c870556c6 | ||
| 
						 | 
					420c860424 | ||
| 
						 | 
					828e291538 | ||
| 
						 | 
					eea78531a1 | ||
| 
						 | 
					c8ccb06f11 | ||
| 
						 | 
					f5b7cc81d8 | ||
| 
						 | 
					ae784dc74c | ||
| 
						 | 
					056c72d50d | ||
| 
						 | 
					b5714cd70f | ||
| 
						 | 
					74aca2137b | ||
| 
						 | 
					d09dff3ae3 | ||
| 
						 | 
					d280380c8d | ||
| 
						 | 
					8a08d1fb5d | ||
| 
						 | 
					ea652e3587 | ||
| 
						 | 
					7a6df38515 | ||
| 
						 | 
					bba6d6897d | ||
| 
						 | 
					33c08812cc | ||
| 
						 | 
					f68a3a9334 | ||
| 
						 | 
					0698be3995 | ||
| 
						 | 
					064589934c | ||
| 
						 | 
					e9e92afc9e | ||
| 
						 | 
					e86f2e993f | ||
| 
						 | 
					d26cd85306 | ||
| 
						 | 
					73f80a8ea1 | ||
| 
						 | 
					b7dff4bbab | ||
| 
						 | 
					31d964c16a | ||
| 
						 | 
					7f895abc24 | ||
| 
						 | 
					0f406c38eb | ||
| 
						 | 
					6433da13a3 | ||
| 
						 | 
					fa1adfd934 | ||
| 
						 | 
					36ffef083b | ||
| 
						 | 
					fe89dcdc08 | ||
| 
						 | 
					be36eef939 | ||
| 
						 | 
					6a0268b852 | ||
| 
						 | 
					b7b23ffdb2 | ||
| 
						 | 
					ad76709d00 | ||
| 
						 | 
					072cd5b83e | ||
| 
						 | 
					cfd42ea162 | ||
| 
						 | 
					b55544b860 | ||
| 
						 | 
					5becaebdda | ||
| 
						 | 
					1814e4a46b | ||
| 
						 | 
					4f8f59f705 | ||
| 
						 | 
					aca306d120 | ||
| 
						 | 
					694395ac91 | ||
| 
						 | 
					092bca0d63 | ||
| 
						 | 
					a386bb476f | ||
| 
						 | 
					39a520f552 | ||
| 
						 | 
					663f84f8b4 | ||
| 
						 | 
					8677d47777 | ||
| 
						 | 
					4f1a28d460 | ||
| 
						 | 
					7b142525b4 | ||
| 
						 | 
					7d4f279206 | ||
| 
						 | 
					51233e1931 | ||
| 
						 | 
					907c14aa98 | ||
| 
						 | 
					fb055750df | ||
| 
						 | 
					fad05d5a2e | ||
| 
						 | 
					9580b13b9f | ||
| 
						 | 
					367ae906c3 | ||
| 
						 | 
					f8d98ac494 | ||
| 
						 | 
					3e8fd48dc0 | ||
| 
						 | 
					003326f2eb | ||
| 
						 | 
					b5af3aa048 | ||
| 
						 | 
					a919b015b4 | ||
| 
						 | 
					f94e9b6b1e | ||
| 
						 | 
					1ed8e63d59 | ||
| 
						 | 
					d97bc95798 | ||
| 
						 | 
					5c56f15c67 | ||
| 
						 | 
					3fdb68cba8 | ||
| 
						 | 
					85c46becdf | ||
| 
						 | 
					0cbd373817 | ||
| 
						 | 
					8027facb39 | ||
| 
						 | 
					16c2dc2aaf | ||
| 
						 | 
					dc2279b74f | ||
| 
						 | 
					75275c4e93 | ||
| 
						 | 
					65d3dc9cb8 | ||
| 
						 | 
					66aa02fc34 | ||
| 
						 | 
					be6b4ee47f | ||
| 
						 | 
					90f909d2ea | ||
| 
						 | 
					09fd505f08 | ||
| 
						 | 
					042ccde441 | ||
| 
						 | 
					442030b6ca | ||
| 
						 | 
					1df9ae53f8 | ||
| 
						 | 
					adf2a463fd | ||
| 
						 | 
					80aaf66963 | ||
| 
						 | 
					560251ab2a | ||
| 
						 | 
					864c5d8908 | ||
| 
						 | 
					742c21506c | ||
| 
						 | 
					a6faccb4d9 | ||
| 
						 | 
					69fd3e8937 | ||
| 
						 | 
					41233d7f25 | ||
| 
						 | 
					07286d1d76 | ||
| 
						 | 
					08148c5830 | ||
| 
						 | 
					969bdb06ce | ||
| 
						 | 
					b0bb692af4 | ||
| 
						 | 
					7bf6fd316f | ||
| 
						 | 
					c1f5e04d6c | ||
| 
						 | 
					5a67e72389 | ||
| 
						 | 
					91c9b11647 | ||
| 
						 | 
					bb2582717f | ||
| 
						 | 
					d62ef35860 | ||
| 
						 | 
					59c5956f93 | ||
| 
						 | 
					e4f055597c | ||
| 
						 | 
					4c49beb3c7 | ||
| 
						 | 
					8ff742d9ab | ||
| 
						 | 
					d63cd8b4cd | ||
| 
						 | 
					3f0503c296 | ||
| 
						 | 
					c18050bda0 | ||
| 
						 | 
					6542be5588 | ||
| 
						 | 
					9fb60b8015 | ||
| 
						 | 
					1177b856a0 | ||
| 
						 | 
					c0adaa8de8 | ||
| 
						 | 
					ae8700447e | ||
| 
						 | 
					d64a4ef2b4 | ||
| 
						 | 
					1e22b1e959 | ||
| 
						 | 
					e077ad56bd | ||
| 
						 | 
					f1e00f8c8e | ||
| 
						 | 
					9a152e588e | ||
| 
						 | 
					16f42a3d03 | ||
| 
						 | 
					6c8d0f1852 | ||
| 
						 | 
					b59cf6572b | ||
| 
						 | 
					4fa11dfa68 | ||
| 
						 | 
					352bdd9fb5 | ||
| 
						 | 
					96ff9a162c | ||
| 
						 | 
					af15a4e710 | ||
| 
						 | 
					18426b71e4 | ||
| 
						 | 
					7063aa6009 | ||
| 
						 | 
					286ca07cc8 | ||
| 
						 | 
					58b6311821 | ||
| 
						 | 
					e553c0768e | ||
| 
						 | 
					62d4b29662 | ||
| 
						 | 
					16bc60644d | 
							
								
								
									
										38
									
								
								.clang-tidy
									
									
									
									
									
								
							
							
						
						
									
										38
									
								
								.clang-tidy
									
									
									
									
									
								
							@@ -4,14 +4,35 @@ Checks: >-
 | 
				
			|||||||
  -abseil-*,
 | 
					  -abseil-*,
 | 
				
			||||||
  -android-*,
 | 
					  -android-*,
 | 
				
			||||||
  -boost-*,
 | 
					  -boost-*,
 | 
				
			||||||
 | 
					  -bugprone-branch-clone,
 | 
				
			||||||
  -bugprone-macro-parentheses,
 | 
					  -bugprone-macro-parentheses,
 | 
				
			||||||
 | 
					  -bugprone-narrowing-conversions,
 | 
				
			||||||
 | 
					  -bugprone-reserved-identifier,
 | 
				
			||||||
 | 
					  -bugprone-signed-char-misuse,
 | 
				
			||||||
 | 
					  -bugprone-suspicious-include,
 | 
				
			||||||
 | 
					  -bugprone-too-small-loop-variable,
 | 
				
			||||||
 | 
					  -bugprone-unhandled-self-assignment,
 | 
				
			||||||
 | 
					  -cert-dcl37-c,
 | 
				
			||||||
  -cert-dcl50-cpp,
 | 
					  -cert-dcl50-cpp,
 | 
				
			||||||
 | 
					  -cert-dcl51-cpp,
 | 
				
			||||||
  -cert-err58-cpp,
 | 
					  -cert-err58-cpp,
 | 
				
			||||||
 | 
					  -cert-oop54-cpp,
 | 
				
			||||||
 | 
					  -cert-oop57-cpp,
 | 
				
			||||||
 | 
					  -cert-str34-c,
 | 
				
			||||||
  -clang-analyzer-core.CallAndMessage,
 | 
					  -clang-analyzer-core.CallAndMessage,
 | 
				
			||||||
 | 
					  -clang-analyzer-optin.*,
 | 
				
			||||||
  -clang-analyzer-osx.*,
 | 
					  -clang-analyzer-osx.*,
 | 
				
			||||||
  -clang-analyzer-security.*,
 | 
					  -clang-analyzer-security.*,
 | 
				
			||||||
 | 
					  -clang-diagnostic-shadow-field,
 | 
				
			||||||
 | 
					  -cppcoreguidelines-avoid-c-arrays,
 | 
				
			||||||
  -cppcoreguidelines-avoid-goto,
 | 
					  -cppcoreguidelines-avoid-goto,
 | 
				
			||||||
 | 
					  -cppcoreguidelines-avoid-magic-numbers,
 | 
				
			||||||
 | 
					  -cppcoreguidelines-avoid-non-const-global-variables,
 | 
				
			||||||
  -cppcoreguidelines-c-copy-assignment-signature,
 | 
					  -cppcoreguidelines-c-copy-assignment-signature,
 | 
				
			||||||
 | 
					  -cppcoreguidelines-init-variables,
 | 
				
			||||||
 | 
					  -cppcoreguidelines-macro-usage,
 | 
				
			||||||
 | 
					  -cppcoreguidelines-narrowing-conversions,
 | 
				
			||||||
 | 
					  -cppcoreguidelines-non-private-member-variables-in-classes,
 | 
				
			||||||
  -cppcoreguidelines-owning-memory,
 | 
					  -cppcoreguidelines-owning-memory,
 | 
				
			||||||
  -cppcoreguidelines-pro-bounds-array-to-pointer-decay,
 | 
					  -cppcoreguidelines-pro-bounds-array-to-pointer-decay,
 | 
				
			||||||
  -cppcoreguidelines-pro-bounds-constant-array-index,
 | 
					  -cppcoreguidelines-pro-bounds-constant-array-index,
 | 
				
			||||||
@@ -37,10 +58,16 @@ Checks: >-
 | 
				
			|||||||
  -google-runtime-int,
 | 
					  -google-runtime-int,
 | 
				
			||||||
  -google-runtime-references,
 | 
					  -google-runtime-references,
 | 
				
			||||||
  -hicpp-*,
 | 
					  -hicpp-*,
 | 
				
			||||||
 | 
					  -llvm-else-after-return,
 | 
				
			||||||
  -llvm-header-guard,
 | 
					  -llvm-header-guard,
 | 
				
			||||||
  -llvm-include-order,
 | 
					  -llvm-include-order,
 | 
				
			||||||
 | 
					  -llvm-qualified-auto,
 | 
				
			||||||
 | 
					  -llvmlibc-*,
 | 
				
			||||||
 | 
					  -misc-non-private-member-variables-in-classes,
 | 
				
			||||||
 | 
					  -misc-no-recursion,
 | 
				
			||||||
  -misc-unconventional-assign-operator,
 | 
					  -misc-unconventional-assign-operator,
 | 
				
			||||||
  -misc-unused-parameters,
 | 
					  -misc-unused-parameters,
 | 
				
			||||||
 | 
					  -modernize-avoid-c-arrays,
 | 
				
			||||||
  -modernize-deprecated-headers,
 | 
					  -modernize-deprecated-headers,
 | 
				
			||||||
  -modernize-pass-by-value,
 | 
					  -modernize-pass-by-value,
 | 
				
			||||||
  -modernize-pass-by-value,
 | 
					  -modernize-pass-by-value,
 | 
				
			||||||
@@ -48,14 +75,25 @@ Checks: >-
 | 
				
			|||||||
  -modernize-use-auto,
 | 
					  -modernize-use-auto,
 | 
				
			||||||
  -modernize-use-default-member-init,
 | 
					  -modernize-use-default-member-init,
 | 
				
			||||||
  -modernize-use-equals-default,
 | 
					  -modernize-use-equals-default,
 | 
				
			||||||
 | 
					  -modernize-use-trailing-return-type,
 | 
				
			||||||
  -mpi-*,
 | 
					  -mpi-*,
 | 
				
			||||||
  -objc-*,
 | 
					  -objc-*,
 | 
				
			||||||
  -performance-unnecessary-value-param,
 | 
					  -performance-unnecessary-value-param,
 | 
				
			||||||
  -readability-braces-around-statements,
 | 
					  -readability-braces-around-statements,
 | 
				
			||||||
 | 
					  -readability-const-return-type,
 | 
				
			||||||
 | 
					  -readability-convert-member-functions-to-static,
 | 
				
			||||||
  -readability-else-after-return,
 | 
					  -readability-else-after-return,
 | 
				
			||||||
  -readability-implicit-bool-conversion,
 | 
					  -readability-implicit-bool-conversion,
 | 
				
			||||||
 | 
					  -readability-isolate-declaration,
 | 
				
			||||||
 | 
					  -readability-magic-numbers,
 | 
				
			||||||
 | 
					  -readability-make-member-function-const,
 | 
				
			||||||
  -readability-named-parameter,
 | 
					  -readability-named-parameter,
 | 
				
			||||||
 | 
					  -readability-qualified-auto,
 | 
				
			||||||
 | 
					  -readability-redundant-access-specifiers,
 | 
				
			||||||
  -readability-redundant-member-init,
 | 
					  -readability-redundant-member-init,
 | 
				
			||||||
 | 
					  -readability-redundant-string-init,
 | 
				
			||||||
 | 
					  -readability-uppercase-literal-suffix,
 | 
				
			||||||
 | 
					  -readability-use-anyofallof,
 | 
				
			||||||
  -warnings-as-errors,
 | 
					  -warnings-as-errors,
 | 
				
			||||||
  -zircon-*
 | 
					  -zircon-*
 | 
				
			||||||
WarningsAsErrors: '*'
 | 
					WarningsAsErrors: '*'
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								.coveragerc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.coveragerc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					[run]
 | 
				
			||||||
 | 
					omit = esphome/components/*
 | 
				
			||||||
							
								
								
									
										32
									
								
								.devcontainer/devcontainer.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								.devcontainer/devcontainer.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "name": "ESPHome Dev",
 | 
				
			||||||
 | 
					  "context": "..",
 | 
				
			||||||
 | 
					  "dockerFile": "../docker/Dockerfile.dev",
 | 
				
			||||||
 | 
					  "postCreateCommand": "mkdir -p config && pip3 install -e .",
 | 
				
			||||||
 | 
					  "runArgs": ["--privileged", "-e", "ESPHOME_DASHBOARD_USE_PING=1"],
 | 
				
			||||||
 | 
					  "appPort": 6052,
 | 
				
			||||||
 | 
					  "extensions": [
 | 
				
			||||||
 | 
					    "ms-python.python",
 | 
				
			||||||
 | 
					    "visualstudioexptteam.vscodeintellicode",
 | 
				
			||||||
 | 
					    "redhat.vscode-yaml"
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  "settings": {
 | 
				
			||||||
 | 
					    "python.pythonPath": "/usr/local/bin/python",
 | 
				
			||||||
 | 
					    "python.linting.pylintEnabled": true,
 | 
				
			||||||
 | 
					    "python.linting.enabled": true,
 | 
				
			||||||
 | 
					    "python.formatting.provider": "black",
 | 
				
			||||||
 | 
					    "editor.formatOnPaste": false,
 | 
				
			||||||
 | 
					    "editor.formatOnSave": true,
 | 
				
			||||||
 | 
					    "editor.formatOnType": true,
 | 
				
			||||||
 | 
					    "files.trimTrailingWhitespace": true,
 | 
				
			||||||
 | 
					    "terminal.integrated.shell.linux": "/bin/bash",
 | 
				
			||||||
 | 
					    "yaml.customTags": [
 | 
				
			||||||
 | 
					      "!secret scalar",
 | 
				
			||||||
 | 
					      "!lambda scalar",
 | 
				
			||||||
 | 
					      "!include_dir_named scalar",
 | 
				
			||||||
 | 
					      "!include_dir_list scalar",
 | 
				
			||||||
 | 
					      "!include_dir_merge_list scalar",
 | 
				
			||||||
 | 
					      "!include_dir_merge_named scalar"
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -25,3 +25,4 @@ indent_size = 2
 | 
				
			|||||||
[*.{yaml,yml}]
 | 
					[*.{yaml,yml}]
 | 
				
			||||||
indent_style = space
 | 
					indent_style = space
 | 
				
			||||||
indent_size = 2
 | 
					indent_size = 2
 | 
				
			||||||
 | 
					quote_type = single
 | 
				
			||||||
							
								
								
									
										7
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,8 +1,3 @@
 | 
				
			|||||||
# These are supported funding model platforms
 | 
					# These are supported funding model platforms
 | 
				
			||||||
 | 
					
 | 
				
			||||||
github:
 | 
					custom: https://www.nabucasa.com
 | 
				
			||||||
patreon: ottowinter
 | 
					 | 
				
			||||||
open_collective:
 | 
					 | 
				
			||||||
ko_fi:
 | 
					 | 
				
			||||||
tidelift:
 | 
					 | 
				
			||||||
custom: https://esphome.io/guides/supporters.html
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										12
									
								
								.github/ISSUE_TEMPLATE/config.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								.github/ISSUE_TEMPLATE/config.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					blank_issues_enabled: false
 | 
				
			||||||
 | 
					contact_links:
 | 
				
			||||||
 | 
					  - name: Issue Tracker
 | 
				
			||||||
 | 
					    url: https://github.com/esphome/issues
 | 
				
			||||||
 | 
					    about: Please create bug reports in the dedicated issue tracker.
 | 
				
			||||||
 | 
					  - name: Feature Request Tracker
 | 
				
			||||||
 | 
					    url: https://github.com/esphome/feature-requests
 | 
				
			||||||
 | 
					    about: Please create feature requests in the dedicated feature request tracker.
 | 
				
			||||||
 | 
					  - name: Frequently Asked Question
 | 
				
			||||||
 | 
					    url: https://esphome.io/guides/faq.html
 | 
				
			||||||
 | 
					    about: Please view the FAQ for common questions and what to include in a bug report.
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
							
								
								
									
										32
									
								
								.github/PULL_REQUEST_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										32
									
								
								.github/PULL_REQUEST_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							@@ -1,13 +1,39 @@
 | 
				
			|||||||
## Description:
 | 
					# What does this implement/fix? 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Quick description and explanation of changes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
**Related issue (if applicable):** fixes <link to issue>
 | 
					## Types of changes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- [ ] Bugfix (non-breaking change which fixes an issue)
 | 
				
			||||||
 | 
					- [ ] New feature (non-breaking change which adds functionality)
 | 
				
			||||||
 | 
					- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
 | 
				
			||||||
 | 
					- [ ] Other
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Related issue or feature (if applicable):** fixes <link to issue>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
**Pull request in [esphome-docs](https://github.com/esphome/esphome-docs) with documentation (if applicable):** esphome/esphome-docs#<esphome-docs PR number goes here>
 | 
					**Pull request in [esphome-docs](https://github.com/esphome/esphome-docs) with documentation (if applicable):** esphome/esphome-docs#<esphome-docs PR number goes here>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Test Environment
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- [ ] ESP32
 | 
				
			||||||
 | 
					- [ ] ESP8266
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Example entry for `config.yaml`:
 | 
				
			||||||
 | 
					<!--
 | 
				
			||||||
 | 
					  Supplying a configuration snippet, makes it easier for a maintainer to test
 | 
				
			||||||
 | 
					  your PR. Furthermore, for new integrations, it gives an impression of how
 | 
				
			||||||
 | 
					  the configuration would look like.
 | 
				
			||||||
 | 
					  Note: Remove this section if this PR does not have an example entry.
 | 
				
			||||||
 | 
					-->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```yaml
 | 
				
			||||||
 | 
					# Example config.yaml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Checklist:
 | 
					## Checklist:
 | 
				
			||||||
  - [ ] The code change is tested and works locally.
 | 
					  - [ ] The code change is tested and works locally.
 | 
				
			||||||
  - [ ] Tests have been added to verify that the new code works (under `tests/` folder).
 | 
					  - [ ] Tests have been added to verify that the new code works (under `tests/` folder).
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
If user exposed functionality or configuration variables are added/changed:
 | 
					If user exposed functionality or configuration variables are added/changed:
 | 
				
			||||||
  - [ ] Documentation added/updated in [esphome-docs](https://github.com/esphome/esphome-docs).
 | 
					  - [ ] Documentation added/updated in [esphome-docs](https://github.com/esphome/esphome-docs).
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										9
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					version: 2
 | 
				
			||||||
 | 
					updates:
 | 
				
			||||||
 | 
					  - package-ecosystem: "pip"
 | 
				
			||||||
 | 
					    directory: "/"
 | 
				
			||||||
 | 
					    schedule:
 | 
				
			||||||
 | 
					      interval: "daily"
 | 
				
			||||||
 | 
					    ignore:
 | 
				
			||||||
 | 
					      # Hypotehsis is only used for testing and is updated quite often
 | 
				
			||||||
 | 
					      - dependency-name: hypothesis
 | 
				
			||||||
							
								
								
									
										36
									
								
								.github/lock.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								.github/lock.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,36 @@
 | 
				
			|||||||
 | 
					# Configuration for Lock Threads - https://github.com/dessant/lock-threads
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Number of days of inactivity before a closed issue or pull request is locked
 | 
				
			||||||
 | 
					daysUntilLock: 7
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Skip issues and pull requests created before a given timestamp. Timestamp must
 | 
				
			||||||
 | 
					# follow ISO 8601 (`YYYY-MM-DD`). Set to `false` to disable
 | 
				
			||||||
 | 
					skipCreatedBefore: false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Issues and pull requests with these labels will be ignored. Set to `[]` to disable
 | 
				
			||||||
 | 
					exemptLabels:
 | 
				
			||||||
 | 
					  - keep-open
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Label to add before locking, such as `outdated`. Set to `false` to disable
 | 
				
			||||||
 | 
					lockLabel: false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Comment to post before locking. Set to `false` to disable
 | 
				
			||||||
 | 
					lockComment: false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Assign `resolved` as the reason for locking. Set to `false` to disable
 | 
				
			||||||
 | 
					setLockReason: false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Limit to only `issues` or `pulls`
 | 
				
			||||||
 | 
					# only: issues
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Optionally, specify configuration settings just for `issues` or `pulls`
 | 
				
			||||||
 | 
					# issues:
 | 
				
			||||||
 | 
					#   exemptLabels:
 | 
				
			||||||
 | 
					#     - help-wanted
 | 
				
			||||||
 | 
					#   lockLabel: outdated
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# pulls:
 | 
				
			||||||
 | 
					#   daysUntilLock: 30
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Repository to extend settings from
 | 
				
			||||||
 | 
					# _extends: repo
 | 
				
			||||||
							
								
								
									
										59
									
								
								.github/stale.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								.github/stale.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,59 @@
 | 
				
			|||||||
 | 
					# Configuration for probot-stale - https://github.com/probot/stale
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Number of days of inactivity before an Issue or Pull Request becomes stale
 | 
				
			||||||
 | 
					daysUntilStale: 60
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
 | 
				
			||||||
 | 
					# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
 | 
				
			||||||
 | 
					daysUntilClose: 7
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled)
 | 
				
			||||||
 | 
					onlyLabels: []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
 | 
				
			||||||
 | 
					exemptLabels:
 | 
				
			||||||
 | 
					  - not-stale
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Set to true to ignore issues in a project (defaults to false)
 | 
				
			||||||
 | 
					exemptProjects: false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Set to true to ignore issues in a milestone (defaults to false)
 | 
				
			||||||
 | 
					exemptMilestones: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Set to true to ignore issues with an assignee (defaults to false)
 | 
				
			||||||
 | 
					exemptAssignees: false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Label to use when marking as stale
 | 
				
			||||||
 | 
					staleLabel: stale
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Comment to post when marking as stale. Set to `false` to disable
 | 
				
			||||||
 | 
					markComment: >
 | 
				
			||||||
 | 
					  This issue has been automatically marked as stale because it has not had
 | 
				
			||||||
 | 
					  recent activity. It will be closed if no further activity occurs. Thank you
 | 
				
			||||||
 | 
					  for your contributions.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Comment to post when removing the stale label.
 | 
				
			||||||
 | 
					# unmarkComment: >
 | 
				
			||||||
 | 
					#   Your comment here.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Comment to post when closing a stale Issue or Pull Request.
 | 
				
			||||||
 | 
					# closeComment: >
 | 
				
			||||||
 | 
					#   Your comment here.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Limit the number of actions per hour, from 1-30. Default is 30
 | 
				
			||||||
 | 
					limitPerRun: 10
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Limit to only `issues` or `pulls`
 | 
				
			||||||
 | 
					only: pulls
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls':
 | 
				
			||||||
 | 
					# pulls:
 | 
				
			||||||
 | 
					#   daysUntilStale: 30
 | 
				
			||||||
 | 
					#   markComment: >
 | 
				
			||||||
 | 
					#     This pull request has been automatically marked as stale because it has not had
 | 
				
			||||||
 | 
					#     recent activity. It will be closed if no further activity occurs. Thank you
 | 
				
			||||||
 | 
					#     for your contributions.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# issues:
 | 
				
			||||||
 | 
					#   exemptLabels:
 | 
				
			||||||
 | 
					#     - confirmed
 | 
				
			||||||
							
								
								
									
										55
									
								
								.github/workflows/ci-docker.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								.github/workflows/ci-docker.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
				
			|||||||
 | 
					name: CI for docker images
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Only run when docker paths change
 | 
				
			||||||
 | 
					on:
 | 
				
			||||||
 | 
					  push:
 | 
				
			||||||
 | 
					    branches: [dev, beta, master]
 | 
				
			||||||
 | 
					    paths:
 | 
				
			||||||
 | 
					      - 'docker/**'
 | 
				
			||||||
 | 
					      - '.github/workflows/**'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  pull_request:
 | 
				
			||||||
 | 
					    paths:
 | 
				
			||||||
 | 
					      - 'docker/**'
 | 
				
			||||||
 | 
					      - '.github/workflows/**'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jobs:
 | 
				
			||||||
 | 
					  check-docker:
 | 
				
			||||||
 | 
					    name: Build docker containers
 | 
				
			||||||
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					    strategy:
 | 
				
			||||||
 | 
					      fail-fast: false
 | 
				
			||||||
 | 
					      matrix:
 | 
				
			||||||
 | 
					        arch: [amd64, armv7, aarch64]
 | 
				
			||||||
 | 
					        build_type: ["hassio", "docker"]
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					      - uses: actions/checkout@v2
 | 
				
			||||||
 | 
					      - name: Set up env variables
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          base_version="3.4.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          if [[ "${{ matrix.build_type }}" == "hassio" ]]; then
 | 
				
			||||||
 | 
					            build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}"
 | 
				
			||||||
 | 
					            build_to="esphome/esphome-hassio-${{ matrix.arch }}"
 | 
				
			||||||
 | 
					            dockerfile="docker/Dockerfile.hassio"
 | 
				
			||||||
 | 
					          else
 | 
				
			||||||
 | 
					            build_from="esphome/esphome-base-${{ matrix.arch }}:${base_version}"
 | 
				
			||||||
 | 
					            build_to="esphome/esphome-${{ matrix.arch }}"
 | 
				
			||||||
 | 
					            dockerfile="docker/Dockerfile"
 | 
				
			||||||
 | 
					          fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          echo "BUILD_FROM=${build_from}" >> $GITHUB_ENV
 | 
				
			||||||
 | 
					          echo "BUILD_TO=${build_to}" >> $GITHUB_ENV
 | 
				
			||||||
 | 
					          echo "DOCKERFILE=${dockerfile}" >> $GITHUB_ENV
 | 
				
			||||||
 | 
					      - name: Pull for cache
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          docker pull "${BUILD_TO}:dev" || true
 | 
				
			||||||
 | 
					      - name: Register QEMU binfmt
 | 
				
			||||||
 | 
					        run: docker run --rm --privileged multiarch/qemu-user-static:5.2.0-2 --reset -p yes
 | 
				
			||||||
 | 
					      - run: |
 | 
				
			||||||
 | 
					          docker build \
 | 
				
			||||||
 | 
					            --build-arg "BUILD_FROM=${BUILD_FROM}" \
 | 
				
			||||||
 | 
					            --build-arg "BUILD_VERSION=ci" \
 | 
				
			||||||
 | 
					            --cache-from "${BUILD_TO}:dev" \
 | 
				
			||||||
 | 
					            --file "${DOCKERFILE}" \
 | 
				
			||||||
 | 
					            .
 | 
				
			||||||
							
								
								
									
										161
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										161
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,161 @@
 | 
				
			|||||||
 | 
					# THESE JOBS ARE COPIED IN release.yml and release-dev.yml
 | 
				
			||||||
 | 
					# PLEASE ALSO UPDATE THOSE FILES WHEN CHANGING LINES HERE
 | 
				
			||||||
 | 
					name: CI
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					on:
 | 
				
			||||||
 | 
					  push:
 | 
				
			||||||
 | 
					    # On dev branch release-dev already performs CI checks
 | 
				
			||||||
 | 
					    # On other branches the `pull_request` trigger will be used
 | 
				
			||||||
 | 
					    branches: [beta, master]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  pull_request:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jobs:
 | 
				
			||||||
 | 
					  lint-clang-format:
 | 
				
			||||||
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					    # cpp lint job runs with esphome-lint docker image so that clang-format-*
 | 
				
			||||||
 | 
					    # doesn't have to be installed
 | 
				
			||||||
 | 
					    container: esphome/esphome-lint:1.1
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					      - uses: actions/checkout@v2
 | 
				
			||||||
 | 
					      # Set up the pio project so that the cpp checks know how files are compiled
 | 
				
			||||||
 | 
					      # (build flags, libraries etc)
 | 
				
			||||||
 | 
					      - name: Set up platformio environment
 | 
				
			||||||
 | 
					        run: pio init --ide atom
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - name: Run clang-format
 | 
				
			||||||
 | 
					        run: script/clang-format -i
 | 
				
			||||||
 | 
					      - name: Suggest changes
 | 
				
			||||||
 | 
					        run: script/ci-suggest-changes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  lint-clang-tidy:
 | 
				
			||||||
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					    # cpp lint job runs with esphome-lint docker image so that clang-format-*
 | 
				
			||||||
 | 
					    # doesn't have to be installed
 | 
				
			||||||
 | 
					    container: esphome/esphome-lint:1.1
 | 
				
			||||||
 | 
					    # Split clang-tidy check into 4 jobs. Each one will check 1/4th of the .cpp files
 | 
				
			||||||
 | 
					    strategy:
 | 
				
			||||||
 | 
					      fail-fast: false
 | 
				
			||||||
 | 
					      matrix:
 | 
				
			||||||
 | 
					        split: [1, 2, 3, 4]
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					      - uses: actions/checkout@v2
 | 
				
			||||||
 | 
					      # Set up the pio project so that the cpp checks know how files are compiled
 | 
				
			||||||
 | 
					      # (build flags, libraries etc)
 | 
				
			||||||
 | 
					      - name: Set up platformio environment
 | 
				
			||||||
 | 
					        run: pio init --ide atom
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - name: Register problem matchers
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          echo "::add-matcher::.github/workflows/matchers/clang-tidy.json"
 | 
				
			||||||
 | 
					          echo "::add-matcher::.github/workflows/matchers/gcc.json"
 | 
				
			||||||
 | 
					      - name: Run clang-tidy
 | 
				
			||||||
 | 
					        run: script/clang-tidy --all-headers --fix --split-num 4 --split-at ${{ matrix.split }}
 | 
				
			||||||
 | 
					      - name: Suggest changes
 | 
				
			||||||
 | 
					        run: script/ci-suggest-changes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  lint-python:
 | 
				
			||||||
 | 
					    # Don't use the esphome-lint docker image because it may contain outdated requirements.
 | 
				
			||||||
 | 
					    # This way, all dependencies are cached via the cache action.
 | 
				
			||||||
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					      - uses: actions/checkout@v2
 | 
				
			||||||
 | 
					      - name: Set up Python
 | 
				
			||||||
 | 
					        uses: actions/setup-python@v2
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          python-version: '3.7'
 | 
				
			||||||
 | 
					      - name: Cache pip modules
 | 
				
			||||||
 | 
					        uses: actions/cache@v1
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          path: ~/.cache/pip
 | 
				
			||||||
 | 
					          key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
 | 
				
			||||||
 | 
					          restore-keys: |
 | 
				
			||||||
 | 
					            esphome-pip-3.7-
 | 
				
			||||||
 | 
					      - name: Set up python environment
 | 
				
			||||||
 | 
					        run: script/setup
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - name: Register problem matchers
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          echo "::add-matcher::.github/workflows/matchers/ci-custom.json"
 | 
				
			||||||
 | 
					          echo "::add-matcher::.github/workflows/matchers/lint-python.json"
 | 
				
			||||||
 | 
					          echo "::add-matcher::.github/workflows/matchers/python.json"
 | 
				
			||||||
 | 
					      - name: Lint Custom
 | 
				
			||||||
 | 
					        run: script/ci-custom.py
 | 
				
			||||||
 | 
					      - name: Lint Python
 | 
				
			||||||
 | 
					        run: script/lint-python
 | 
				
			||||||
 | 
					      - name: Lint CODEOWNERS
 | 
				
			||||||
 | 
					        run: script/build_codeowners.py --check
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test:
 | 
				
			||||||
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					    strategy:
 | 
				
			||||||
 | 
					      fail-fast: false
 | 
				
			||||||
 | 
					      matrix:
 | 
				
			||||||
 | 
					          test:
 | 
				
			||||||
 | 
					          - test1
 | 
				
			||||||
 | 
					          - test2
 | 
				
			||||||
 | 
					          - test3
 | 
				
			||||||
 | 
					          - test4
 | 
				
			||||||
 | 
					          - test5
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					      - uses: actions/checkout@v2
 | 
				
			||||||
 | 
					      - name: Set up Python
 | 
				
			||||||
 | 
					        uses: actions/setup-python@v2
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          python-version: '3.7'
 | 
				
			||||||
 | 
					      - name: Cache pip modules
 | 
				
			||||||
 | 
					        uses: actions/cache@v1
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          path: ~/.cache/pip
 | 
				
			||||||
 | 
					          key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
 | 
				
			||||||
 | 
					          restore-keys: |
 | 
				
			||||||
 | 
					            esphome-pip-3.7-
 | 
				
			||||||
 | 
					      # Use per test platformio cache because tests have different platform versions
 | 
				
			||||||
 | 
					      - name: Cache ~/.platformio
 | 
				
			||||||
 | 
					        uses: actions/cache@v1
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          path: ~/.platformio
 | 
				
			||||||
 | 
					          key: test-home-platformio-${{ matrix.test }}-${{ hashFiles('esphome/core/config.py') }}
 | 
				
			||||||
 | 
					          restore-keys: |
 | 
				
			||||||
 | 
					            test-home-platformio-${{ matrix.test }}-
 | 
				
			||||||
 | 
					      - name: Set up environment
 | 
				
			||||||
 | 
					        run: script/setup
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - name: Register problem matchers
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          echo "::add-matcher::.github/workflows/matchers/gcc.json"
 | 
				
			||||||
 | 
					          echo "::add-matcher::.github/workflows/matchers/python.json"
 | 
				
			||||||
 | 
					      - run: esphome compile tests/${{ matrix.test }}.yaml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  pytest:
 | 
				
			||||||
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					      - uses: actions/checkout@v2
 | 
				
			||||||
 | 
					      - name: Set up Python
 | 
				
			||||||
 | 
					        uses: actions/setup-python@v2
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          python-version: '3.7'
 | 
				
			||||||
 | 
					      - name: Cache pip modules
 | 
				
			||||||
 | 
					        uses: actions/cache@v1
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          path: ~/.cache/pip
 | 
				
			||||||
 | 
					          key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
 | 
				
			||||||
 | 
					          restore-keys: |
 | 
				
			||||||
 | 
					            esphome-pip-3.7-
 | 
				
			||||||
 | 
					      - name: Set up environment
 | 
				
			||||||
 | 
					        run: script/setup
 | 
				
			||||||
 | 
					      - name: Install Github Actions annotator
 | 
				
			||||||
 | 
					        run: pip install pytest-github-actions-annotate-failures
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - name: Register problem matchers
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          echo "::add-matcher::.github/workflows/matchers/python.json"
 | 
				
			||||||
 | 
					      - name: Run pytest
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          pytest \
 | 
				
			||||||
 | 
					            -qq \
 | 
				
			||||||
 | 
					            --durations=10 \
 | 
				
			||||||
 | 
					            -o console_output_style=count \
 | 
				
			||||||
 | 
					            tests
 | 
				
			||||||
							
								
								
									
										42
									
								
								.github/workflows/docker-lint-build.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								.github/workflows/docker-lint-build.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
				
			|||||||
 | 
					name: Build and publish lint docker image
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Only run when docker paths change
 | 
				
			||||||
 | 
					on:
 | 
				
			||||||
 | 
					  push:
 | 
				
			||||||
 | 
					    branches: [dev]
 | 
				
			||||||
 | 
					    paths:
 | 
				
			||||||
 | 
					      - 'docker/Dockerfile.lint'
 | 
				
			||||||
 | 
					      - 'requirements.txt'
 | 
				
			||||||
 | 
					      - 'requirements_optional.txt'
 | 
				
			||||||
 | 
					      - 'requirements_test.txt'
 | 
				
			||||||
 | 
					      - 'platformio.ini'
 | 
				
			||||||
 | 
					      - '.github/workflows/docker-lint-build.yml'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jobs:
 | 
				
			||||||
 | 
					  publish-docker-lint-iage:
 | 
				
			||||||
 | 
					    name: Build docker containers
 | 
				
			||||||
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					      - uses: actions/checkout@v2
 | 
				
			||||||
 | 
					      - name: Set TAG
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          echo "TAG=1.1" >> $GITHUB_ENV
 | 
				
			||||||
 | 
					      - name: Pull for cache
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          docker pull "esphome/esphome-lint:latest" || true
 | 
				
			||||||
 | 
					      - name: Build
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          docker build \
 | 
				
			||||||
 | 
					            --cache-from "esphome/esphome-lint:latest" \
 | 
				
			||||||
 | 
					            --file "docker/Dockerfile.lint" \
 | 
				
			||||||
 | 
					            --tag "esphome/esphome-lint:latest" \
 | 
				
			||||||
 | 
					            --tag "esphome/esphome-lint:${TAG}" \
 | 
				
			||||||
 | 
					            .
 | 
				
			||||||
 | 
					      - name: Log in to docker hub
 | 
				
			||||||
 | 
					        env:
 | 
				
			||||||
 | 
					          DOCKER_USER: ${{ secrets.DOCKER_USER }}
 | 
				
			||||||
 | 
					          DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
 | 
				
			||||||
 | 
					        run: docker login -u "${DOCKER_USER}" -p "${DOCKER_PASSWORD}"
 | 
				
			||||||
 | 
					      - run: |
 | 
				
			||||||
 | 
					          docker push "esphome/esphome-lint:${TAG}"
 | 
				
			||||||
 | 
					          docker push "esphome/esphome-lint:latest"
 | 
				
			||||||
							
								
								
									
										16
									
								
								.github/workflows/matchers/ci-custom.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								.github/workflows/matchers/ci-custom.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					    "problemMatcher": [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "owner": "ci-custom",
 | 
				
			||||||
 | 
					            "pattern": [
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    "regexp": "^ERROR (.*):(\\d+):(\\d+) - (.*)$",
 | 
				
			||||||
 | 
					                    "file": 1,
 | 
				
			||||||
 | 
					                    "line": 2,
 | 
				
			||||||
 | 
					                    "column": 3,
 | 
				
			||||||
 | 
					                    "message": 4
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            ]
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										17
									
								
								.github/workflows/matchers/clang-tidy.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								.github/workflows/matchers/clang-tidy.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					    "problemMatcher": [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "owner": "clang-tidy",
 | 
				
			||||||
 | 
					            "pattern": [
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    "regexp": "^(.*):(\\d+):(\\d+):\\s+(error):\\s+(.*) \\[([a-z0-9,\\-]+)\\]\\s*$",
 | 
				
			||||||
 | 
					                    "file": 1,
 | 
				
			||||||
 | 
					                    "line": 2,
 | 
				
			||||||
 | 
					                    "column": 3,
 | 
				
			||||||
 | 
					                    "severity": 4,
 | 
				
			||||||
 | 
					                    "message": 5
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            ]
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										18
									
								
								.github/workflows/matchers/gcc.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								.github/workflows/matchers/gcc.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "problemMatcher": [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "owner": "gcc",
 | 
				
			||||||
 | 
					      "severity": "error",
 | 
				
			||||||
 | 
					      "pattern": [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          "regexp": "^(.*):(\\d+):(\\d+):\\s+(?:fatal\\s+)?(warning|error):\\s+(.*)$",
 | 
				
			||||||
 | 
					          "file": 1,
 | 
				
			||||||
 | 
					          "line": 2,
 | 
				
			||||||
 | 
					          "column": 3,
 | 
				
			||||||
 | 
					          "severity": 4,
 | 
				
			||||||
 | 
					          "message": 5
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      ]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  ]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										28
									
								
								.github/workflows/matchers/lint-python.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								.github/workflows/matchers/lint-python.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "problemMatcher": [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "owner": "flake8",
 | 
				
			||||||
 | 
					      "severity": "error",
 | 
				
			||||||
 | 
					      "pattern": [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					          "regexp": "^(.*):(\\d+) - ([EFCDNW]\\d{3}.*)$",
 | 
				
			||||||
 | 
					          "file": 1,
 | 
				
			||||||
 | 
					          "line": 2,
 | 
				
			||||||
 | 
					          "message": 3
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					      ]
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "owner": "pylint",
 | 
				
			||||||
 | 
					      "severity": "error",
 | 
				
			||||||
 | 
					      "pattern": [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          "regexp": "^(.*):(\\d+) - (\\[[EFCRW]\\d{4}\\(.*\\),.*\\].*)$",
 | 
				
			||||||
 | 
					          "file": 1,
 | 
				
			||||||
 | 
					          "line": 2,
 | 
				
			||||||
 | 
					          "message": 3
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      ]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  ]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										18
									
								
								.github/workflows/matchers/python.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								.github/workflows/matchers/python.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					    "problemMatcher": [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "owner": "python",
 | 
				
			||||||
 | 
					            "pattern": [
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    "regexp": "^\\s*File\\s\\\"(.*)\\\",\\sline\\s(\\d+),\\sin\\s(.*)$",
 | 
				
			||||||
 | 
					                    "file": 1,
 | 
				
			||||||
 | 
					                    "line": 2
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    "regexp": "^\\s*raise\\s(.*)\\(\\'(.*)\\'\\)$",
 | 
				
			||||||
 | 
					                    "message": 2
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            ]
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										247
									
								
								.github/workflows/release-dev.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										247
									
								
								.github/workflows/release-dev.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,247 @@
 | 
				
			|||||||
 | 
					name: Publish dev releases to docker hub
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					on:
 | 
				
			||||||
 | 
					  push:
 | 
				
			||||||
 | 
					    branches:
 | 
				
			||||||
 | 
					    - dev
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jobs:
 | 
				
			||||||
 | 
					  # THE LINT/TEST JOBS ARE COPIED FROM ci.yaml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  lint-clang-format:
 | 
				
			||||||
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					    # cpp lint job runs with esphome-lint docker image so that clang-format-*
 | 
				
			||||||
 | 
					    # doesn't have to be installed
 | 
				
			||||||
 | 
					    container: esphome/esphome-lint:1.1
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					      - uses: actions/checkout@v2
 | 
				
			||||||
 | 
					      # Set up the pio project so that the cpp checks know how files are compiled
 | 
				
			||||||
 | 
					      # (build flags, libraries etc)
 | 
				
			||||||
 | 
					      - name: Set up platformio environment
 | 
				
			||||||
 | 
					        run: pio init --ide atom
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - name: Run clang-format
 | 
				
			||||||
 | 
					        run: script/clang-format -i
 | 
				
			||||||
 | 
					      - name: Suggest changes
 | 
				
			||||||
 | 
					        run: script/ci-suggest-changes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  lint-clang-tidy:
 | 
				
			||||||
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					    # cpp lint job runs with esphome-lint docker image so that clang-format-*
 | 
				
			||||||
 | 
					    # doesn't have to be installed
 | 
				
			||||||
 | 
					    container: esphome/esphome-lint:1.1
 | 
				
			||||||
 | 
					    # Split clang-tidy check into 4 jobs. Each one will check 1/4th of the .cpp files
 | 
				
			||||||
 | 
					    strategy:
 | 
				
			||||||
 | 
					      fail-fast: false
 | 
				
			||||||
 | 
					      matrix:
 | 
				
			||||||
 | 
					        split: [1, 2, 3, 4]
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					      - uses: actions/checkout@v2
 | 
				
			||||||
 | 
					      # Set up the pio project so that the cpp checks know how files are compiled
 | 
				
			||||||
 | 
					      # (build flags, libraries etc)
 | 
				
			||||||
 | 
					      - name: Set up platformio environment
 | 
				
			||||||
 | 
					        run: pio init --ide atom
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - name: Register problem matchers
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          echo "::add-matcher::.github/workflows/matchers/clang-tidy.json"
 | 
				
			||||||
 | 
					          echo "::add-matcher::.github/workflows/matchers/gcc.json"
 | 
				
			||||||
 | 
					      - name: Run clang-tidy
 | 
				
			||||||
 | 
					        run: script/clang-tidy --all-headers --fix --split-num 4 --split-at ${{ matrix.split }}
 | 
				
			||||||
 | 
					      - name: Suggest changes
 | 
				
			||||||
 | 
					        run: script/ci-suggest-changes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  lint-python:
 | 
				
			||||||
 | 
					    # Don't use the esphome-lint docker image because it may contain outdated requirements.
 | 
				
			||||||
 | 
					    # This way, all dependencies are cached via the cache action.
 | 
				
			||||||
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					      - uses: actions/checkout@v2
 | 
				
			||||||
 | 
					      - name: Set up Python
 | 
				
			||||||
 | 
					        uses: actions/setup-python@v2
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          python-version: '3.7'
 | 
				
			||||||
 | 
					      - name: Cache pip modules
 | 
				
			||||||
 | 
					        uses: actions/cache@v1
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          path: ~/.cache/pip
 | 
				
			||||||
 | 
					          key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
 | 
				
			||||||
 | 
					          restore-keys: |
 | 
				
			||||||
 | 
					            esphome-pip-3.7-
 | 
				
			||||||
 | 
					      - name: Set up python environment
 | 
				
			||||||
 | 
					        run: script/setup
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - name: Register problem matchers
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          echo "::add-matcher::.github/workflows/matchers/ci-custom.json"
 | 
				
			||||||
 | 
					          echo "::add-matcher::.github/workflows/matchers/lint-python.json"
 | 
				
			||||||
 | 
					          echo "::add-matcher::.github/workflows/matchers/python.json"
 | 
				
			||||||
 | 
					      - name: Lint Custom
 | 
				
			||||||
 | 
					        run: script/ci-custom.py
 | 
				
			||||||
 | 
					      - name: Lint Python
 | 
				
			||||||
 | 
					        run: script/lint-python
 | 
				
			||||||
 | 
					      - name: Lint CODEOWNERS
 | 
				
			||||||
 | 
					        run: script/build_codeowners.py --check
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test:
 | 
				
			||||||
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					    strategy:
 | 
				
			||||||
 | 
					      fail-fast: false
 | 
				
			||||||
 | 
					      matrix:
 | 
				
			||||||
 | 
					          test:
 | 
				
			||||||
 | 
					          - test1
 | 
				
			||||||
 | 
					          - test2
 | 
				
			||||||
 | 
					          - test3
 | 
				
			||||||
 | 
					          - test4
 | 
				
			||||||
 | 
					          - test5
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					      - uses: actions/checkout@v2
 | 
				
			||||||
 | 
					      - name: Set up Python
 | 
				
			||||||
 | 
					        uses: actions/setup-python@v2
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          python-version: '3.7'
 | 
				
			||||||
 | 
					      - name: Cache pip modules
 | 
				
			||||||
 | 
					        uses: actions/cache@v1
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          path: ~/.cache/pip
 | 
				
			||||||
 | 
					          key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
 | 
				
			||||||
 | 
					          restore-keys: |
 | 
				
			||||||
 | 
					            esphome-pip-3.7-
 | 
				
			||||||
 | 
					      # Use per test platformio cache because tests have different platform versions
 | 
				
			||||||
 | 
					      - name: Cache ~/.platformio
 | 
				
			||||||
 | 
					        uses: actions/cache@v1
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          path: ~/.platformio
 | 
				
			||||||
 | 
					          key: test-home-platformio-${{ matrix.test }}-${{ hashFiles('esphome/core/config.py') }}
 | 
				
			||||||
 | 
					          restore-keys: |
 | 
				
			||||||
 | 
					            test-home-platformio-${{ matrix.test }}-
 | 
				
			||||||
 | 
					      - name: Set up environment
 | 
				
			||||||
 | 
					        run: script/setup
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - name: Register problem matchers
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          echo "::add-matcher::.github/workflows/matchers/gcc.json"
 | 
				
			||||||
 | 
					          echo "::add-matcher::.github/workflows/matchers/python.json"
 | 
				
			||||||
 | 
					      - run: esphome compile tests/${{ matrix.test }}.yaml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  pytest:
 | 
				
			||||||
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					      - uses: actions/checkout@v2
 | 
				
			||||||
 | 
					      - name: Set up Python
 | 
				
			||||||
 | 
					        uses: actions/setup-python@v2
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          python-version: '3.7'
 | 
				
			||||||
 | 
					      - name: Cache pip modules
 | 
				
			||||||
 | 
					        uses: actions/cache@v1
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          path: ~/.cache/pip
 | 
				
			||||||
 | 
					          key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
 | 
				
			||||||
 | 
					          restore-keys: |
 | 
				
			||||||
 | 
					            esphome-pip-3.7-
 | 
				
			||||||
 | 
					      - name: Set up environment
 | 
				
			||||||
 | 
					        run: script/setup
 | 
				
			||||||
 | 
					      - name: Install Github Actions annotator
 | 
				
			||||||
 | 
					        run: pip install pytest-github-actions-annotate-failures
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - name: Register problem matchers
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          echo "::add-matcher::.github/workflows/matchers/python.json"
 | 
				
			||||||
 | 
					      - name: Run pytest
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          pytest \
 | 
				
			||||||
 | 
					            -qq \
 | 
				
			||||||
 | 
					            --durations=10 \
 | 
				
			||||||
 | 
					            -o console_output_style=count \
 | 
				
			||||||
 | 
					            tests
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  deploy-docker:
 | 
				
			||||||
 | 
					    name: Build and publish docker containers
 | 
				
			||||||
 | 
					    if: github.repository == 'esphome/esphome'
 | 
				
			||||||
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					    needs: [lint-clang-format, lint-clang-tidy, lint-python, test, pytest]
 | 
				
			||||||
 | 
					    strategy:
 | 
				
			||||||
 | 
					      matrix:
 | 
				
			||||||
 | 
					        arch: [amd64, armv7, aarch64]
 | 
				
			||||||
 | 
					        # Hassio dev image doesn't use esphome/esphome-hassio-$arch and uses base directly
 | 
				
			||||||
 | 
					        build_type: ["docker"]
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					      - uses: actions/checkout@v2
 | 
				
			||||||
 | 
					      - name: Set TAG
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          TAG="${GITHUB_SHA:0:7}"
 | 
				
			||||||
 | 
					          echo "TAG=${TAG}" >> $GITHUB_ENV
 | 
				
			||||||
 | 
					      - name: Set up env variables
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          base_version="3.4.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          if [[ "${{ matrix.build_type }}" == "hassio" ]]; then
 | 
				
			||||||
 | 
					            build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}"
 | 
				
			||||||
 | 
					            build_to="esphome/esphome-hassio-${{ matrix.arch }}"
 | 
				
			||||||
 | 
					            dockerfile="docker/Dockerfile.hassio"
 | 
				
			||||||
 | 
					          else
 | 
				
			||||||
 | 
					            build_from="esphome/esphome-base-${{ matrix.arch }}:${base_version}"
 | 
				
			||||||
 | 
					            build_to="esphome/esphome-${{ matrix.arch }}"
 | 
				
			||||||
 | 
					            dockerfile="docker/Dockerfile"
 | 
				
			||||||
 | 
					          fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          echo "BUILD_FROM=${build_from}" >> $GITHUB_ENV
 | 
				
			||||||
 | 
					          echo "BUILD_TO=${build_to}" >> $GITHUB_ENV
 | 
				
			||||||
 | 
					          echo "DOCKERFILE=${dockerfile}" >> $GITHUB_ENV
 | 
				
			||||||
 | 
					      - name: Pull for cache
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          docker pull "${BUILD_TO}:dev" || true
 | 
				
			||||||
 | 
					      - name: Register QEMU binfmt
 | 
				
			||||||
 | 
					        run: docker run --rm --privileged multiarch/qemu-user-static:5.2.0-2 --reset -p yes
 | 
				
			||||||
 | 
					      - run: |
 | 
				
			||||||
 | 
					          docker build \
 | 
				
			||||||
 | 
					            --build-arg "BUILD_FROM=${BUILD_FROM}" \
 | 
				
			||||||
 | 
					            --build-arg "BUILD_VERSION=${TAG}" \
 | 
				
			||||||
 | 
					            --tag "${BUILD_TO}:${TAG}" \
 | 
				
			||||||
 | 
					            --tag "${BUILD_TO}:dev" \
 | 
				
			||||||
 | 
					            --cache-from "${BUILD_TO}:dev" \
 | 
				
			||||||
 | 
					            --file "${DOCKERFILE}" \
 | 
				
			||||||
 | 
					            .
 | 
				
			||||||
 | 
					      - name: Log in to docker hub
 | 
				
			||||||
 | 
					        env:
 | 
				
			||||||
 | 
					          DOCKER_USER: ${{ secrets.DOCKER_USER }}
 | 
				
			||||||
 | 
					          DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
 | 
				
			||||||
 | 
					        run: docker login -u "${DOCKER_USER}" -p "${DOCKER_PASSWORD}"
 | 
				
			||||||
 | 
					      - run: |
 | 
				
			||||||
 | 
					          docker push "${BUILD_TO}:${TAG}"
 | 
				
			||||||
 | 
					          docker push "${BUILD_TO}:dev"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  deploy-docker-manifest:
 | 
				
			||||||
 | 
					    if: github.repository == 'esphome/esphome'
 | 
				
			||||||
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					    needs: [deploy-docker]
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					    - name: Enable experimental manifest support
 | 
				
			||||||
 | 
					      run: |
 | 
				
			||||||
 | 
					        mkdir -p ~/.docker
 | 
				
			||||||
 | 
					        echo "{\"experimental\": \"enabled\"}" > ~/.docker/config.json
 | 
				
			||||||
 | 
					    - name: Set TAG
 | 
				
			||||||
 | 
					      run: |
 | 
				
			||||||
 | 
					        TAG="${GITHUB_SHA:0:7}"
 | 
				
			||||||
 | 
					        echo "TAG=${TAG}" >> $GITHUB_ENV
 | 
				
			||||||
 | 
					    - name: Log in to docker hub
 | 
				
			||||||
 | 
					      env:
 | 
				
			||||||
 | 
					        DOCKER_USER: ${{ secrets.DOCKER_USER }}
 | 
				
			||||||
 | 
					        DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
 | 
				
			||||||
 | 
					      run: docker login -u "${DOCKER_USER}" -p "${DOCKER_PASSWORD}"
 | 
				
			||||||
 | 
					    - name: "Create the manifest"
 | 
				
			||||||
 | 
					      run: |
 | 
				
			||||||
 | 
					        docker manifest create esphome/esphome:${TAG} \
 | 
				
			||||||
 | 
					          esphome/esphome-aarch64:${TAG} \
 | 
				
			||||||
 | 
					          esphome/esphome-amd64:${TAG} \
 | 
				
			||||||
 | 
					          esphome/esphome-armv7:${TAG}
 | 
				
			||||||
 | 
					        docker manifest push esphome/esphome:${TAG}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        docker manifest create esphome/esphome:dev \
 | 
				
			||||||
 | 
					          esphome/esphome-aarch64:${TAG} \
 | 
				
			||||||
 | 
					          esphome/esphome-amd64:${TAG} \
 | 
				
			||||||
 | 
					          esphome/esphome-armv7:${TAG}
 | 
				
			||||||
 | 
					        docker manifest push esphome/esphome:dev
 | 
				
			||||||
							
								
								
									
										310
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										310
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,310 @@
 | 
				
			|||||||
 | 
					name: Publish Release
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					on:
 | 
				
			||||||
 | 
					  release:
 | 
				
			||||||
 | 
					    types: [published]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jobs:
 | 
				
			||||||
 | 
					  # THE LINT/TEST JOBS ARE COPIED FROM ci.yaml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  lint-clang-format:
 | 
				
			||||||
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					    # cpp lint job runs with esphome-lint docker image so that clang-format-*
 | 
				
			||||||
 | 
					    # doesn't have to be installed
 | 
				
			||||||
 | 
					    container: esphome/esphome-lint:1.1
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					      - uses: actions/checkout@v2
 | 
				
			||||||
 | 
					      # Set up the pio project so that the cpp checks know how files are compiled
 | 
				
			||||||
 | 
					      # (build flags, libraries etc)
 | 
				
			||||||
 | 
					      - name: Set up platformio environment
 | 
				
			||||||
 | 
					        run: pio init --ide atom
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - name: Run clang-format
 | 
				
			||||||
 | 
					        run: script/clang-format -i
 | 
				
			||||||
 | 
					      - name: Suggest changes
 | 
				
			||||||
 | 
					        run: script/ci-suggest-changes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  lint-clang-tidy:
 | 
				
			||||||
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					    # cpp lint job runs with esphome-lint docker image so that clang-format-*
 | 
				
			||||||
 | 
					    # doesn't have to be installed
 | 
				
			||||||
 | 
					    container: esphome/esphome-lint:1.1
 | 
				
			||||||
 | 
					    # Split clang-tidy check into 4 jobs. Each one will check 1/4th of the .cpp files
 | 
				
			||||||
 | 
					    strategy:
 | 
				
			||||||
 | 
					      fail-fast: false
 | 
				
			||||||
 | 
					      matrix:
 | 
				
			||||||
 | 
					        split: [1, 2, 3, 4]
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					      - uses: actions/checkout@v2
 | 
				
			||||||
 | 
					      # Set up the pio project so that the cpp checks know how files are compiled
 | 
				
			||||||
 | 
					      # (build flags, libraries etc)
 | 
				
			||||||
 | 
					      - name: Set up platformio environment
 | 
				
			||||||
 | 
					        run: pio init --ide atom
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - name: Register problem matchers
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          echo "::add-matcher::.github/workflows/matchers/clang-tidy.json"
 | 
				
			||||||
 | 
					          echo "::add-matcher::.github/workflows/matchers/gcc.json"
 | 
				
			||||||
 | 
					      - name: Run clang-tidy
 | 
				
			||||||
 | 
					        run: script/clang-tidy --all-headers --fix --split-num 4 --split-at ${{ matrix.split }}
 | 
				
			||||||
 | 
					      - name: Suggest changes
 | 
				
			||||||
 | 
					        run: script/ci-suggest-changes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  lint-python:
 | 
				
			||||||
 | 
					    # Don't use the esphome-lint docker image because it may contain outdated requirements.
 | 
				
			||||||
 | 
					    # This way, all dependencies are cached via the cache action.
 | 
				
			||||||
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					      - uses: actions/checkout@v2
 | 
				
			||||||
 | 
					      - name: Set up Python
 | 
				
			||||||
 | 
					        uses: actions/setup-python@v2
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          python-version: '3.7'
 | 
				
			||||||
 | 
					      - name: Cache pip modules
 | 
				
			||||||
 | 
					        uses: actions/cache@v1
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          path: ~/.cache/pip
 | 
				
			||||||
 | 
					          key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
 | 
				
			||||||
 | 
					          restore-keys: |
 | 
				
			||||||
 | 
					            esphome-pip-3.7-
 | 
				
			||||||
 | 
					      - name: Set up python environment
 | 
				
			||||||
 | 
					        run: script/setup
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - name: Register problem matchers
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          echo "::add-matcher::.github/workflows/matchers/ci-custom.json"
 | 
				
			||||||
 | 
					          echo "::add-matcher::.github/workflows/matchers/lint-python.json"
 | 
				
			||||||
 | 
					          echo "::add-matcher::.github/workflows/matchers/python.json"
 | 
				
			||||||
 | 
					      - name: Lint Custom
 | 
				
			||||||
 | 
					        run: script/ci-custom.py
 | 
				
			||||||
 | 
					      - name: Lint Python
 | 
				
			||||||
 | 
					        run: script/lint-python
 | 
				
			||||||
 | 
					      - name: Lint CODEOWNERS
 | 
				
			||||||
 | 
					        run: script/build_codeowners.py --check
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test:
 | 
				
			||||||
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					    strategy:
 | 
				
			||||||
 | 
					      fail-fast: false
 | 
				
			||||||
 | 
					      matrix:
 | 
				
			||||||
 | 
					          test:
 | 
				
			||||||
 | 
					          - test1
 | 
				
			||||||
 | 
					          - test2
 | 
				
			||||||
 | 
					          - test3
 | 
				
			||||||
 | 
					          - test4
 | 
				
			||||||
 | 
					          - test5
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					      - uses: actions/checkout@v2
 | 
				
			||||||
 | 
					      - name: Set up Python
 | 
				
			||||||
 | 
					        uses: actions/setup-python@v2
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          python-version: '3.7'
 | 
				
			||||||
 | 
					      - name: Cache pip modules
 | 
				
			||||||
 | 
					        uses: actions/cache@v1
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          path: ~/.cache/pip
 | 
				
			||||||
 | 
					          key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
 | 
				
			||||||
 | 
					          restore-keys: |
 | 
				
			||||||
 | 
					            esphome-pip-3.7-
 | 
				
			||||||
 | 
					      # Use per test platformio cache because tests have different platform versions
 | 
				
			||||||
 | 
					      - name: Cache ~/.platformio
 | 
				
			||||||
 | 
					        uses: actions/cache@v1
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          path: ~/.platformio
 | 
				
			||||||
 | 
					          key: test-home-platformio-${{ matrix.test }}-${{ hashFiles('esphome/core/config.py') }}
 | 
				
			||||||
 | 
					          restore-keys: |
 | 
				
			||||||
 | 
					            test-home-platformio-${{ matrix.test }}-
 | 
				
			||||||
 | 
					      - name: Set up environment
 | 
				
			||||||
 | 
					        run: script/setup
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - name: Register problem matchers
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          echo "::add-matcher::.github/workflows/matchers/gcc.json"
 | 
				
			||||||
 | 
					          echo "::add-matcher::.github/workflows/matchers/python.json"
 | 
				
			||||||
 | 
					      - run: esphome compile tests/${{ matrix.test }}.yaml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  pytest:
 | 
				
			||||||
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					      - uses: actions/checkout@v2
 | 
				
			||||||
 | 
					      - name: Set up Python
 | 
				
			||||||
 | 
					        uses: actions/setup-python@v2
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          python-version: '3.7'
 | 
				
			||||||
 | 
					      - name: Cache pip modules
 | 
				
			||||||
 | 
					        uses: actions/cache@v1
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          path: ~/.cache/pip
 | 
				
			||||||
 | 
					          key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
 | 
				
			||||||
 | 
					          restore-keys: |
 | 
				
			||||||
 | 
					            esphome-pip-3.7-
 | 
				
			||||||
 | 
					      - name: Set up environment
 | 
				
			||||||
 | 
					        run: script/setup
 | 
				
			||||||
 | 
					      - name: Install Github Actions annotator
 | 
				
			||||||
 | 
					        run: pip install pytest-github-actions-annotate-failures
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - name: Register problem matchers
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          echo "::add-matcher::.github/workflows/matchers/python.json"
 | 
				
			||||||
 | 
					      - name: Run pytest
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          pytest \
 | 
				
			||||||
 | 
					            -qq \
 | 
				
			||||||
 | 
					            --durations=10 \
 | 
				
			||||||
 | 
					            -o console_output_style=count \
 | 
				
			||||||
 | 
					            tests
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  deploy-pypi:
 | 
				
			||||||
 | 
					    name: Build and publish to PyPi
 | 
				
			||||||
 | 
					    if: github.repository == 'esphome/esphome'
 | 
				
			||||||
 | 
					    needs: [lint-clang-format, lint-clang-tidy, lint-python, test, pytest]
 | 
				
			||||||
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					      - uses: actions/checkout@v2
 | 
				
			||||||
 | 
					      - name: Set up Python
 | 
				
			||||||
 | 
					        uses: actions/setup-python@v1
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          python-version: '3.x'
 | 
				
			||||||
 | 
					      - name: Set up python environment
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          script/setup
 | 
				
			||||||
 | 
					          pip install setuptools wheel twine
 | 
				
			||||||
 | 
					      - name: Build
 | 
				
			||||||
 | 
					        run: python setup.py sdist bdist_wheel
 | 
				
			||||||
 | 
					      - name: Upload
 | 
				
			||||||
 | 
					        env:
 | 
				
			||||||
 | 
					          TWINE_USERNAME: __token__
 | 
				
			||||||
 | 
					          TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
 | 
				
			||||||
 | 
					        run: twine upload dist/*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  deploy-docker:
 | 
				
			||||||
 | 
					    name: Build and publish docker containers
 | 
				
			||||||
 | 
					    if: github.repository == 'esphome/esphome'
 | 
				
			||||||
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					    needs: [lint-clang-format, lint-clang-tidy, lint-python, test, pytest]
 | 
				
			||||||
 | 
					    strategy:
 | 
				
			||||||
 | 
					      matrix:
 | 
				
			||||||
 | 
					        arch: [amd64, armv7, aarch64]
 | 
				
			||||||
 | 
					        build_type: ["hassio", "docker"]
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					      - uses: actions/checkout@v2
 | 
				
			||||||
 | 
					      - name: Set TAG
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          TAG="${GITHUB_REF#refs/tags/v}"
 | 
				
			||||||
 | 
					          echo "TAG=${TAG}" >> $GITHUB_ENV
 | 
				
			||||||
 | 
					      - name: Set up env variables
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          base_version="3.4.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          if [[ "${{ matrix.build_type }}" == "hassio" ]]; then
 | 
				
			||||||
 | 
					            build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}"
 | 
				
			||||||
 | 
					            build_to="esphome/esphome-hassio-${{ matrix.arch }}"
 | 
				
			||||||
 | 
					            dockerfile="docker/Dockerfile.hassio"
 | 
				
			||||||
 | 
					          else
 | 
				
			||||||
 | 
					            build_from="esphome/esphome-base-${{ matrix.arch }}:${base_version}"
 | 
				
			||||||
 | 
					            build_to="esphome/esphome-${{ matrix.arch }}"
 | 
				
			||||||
 | 
					            dockerfile="docker/Dockerfile"
 | 
				
			||||||
 | 
					          fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          if [[ "${{ github.event.release.prerelease }}" == "true" ]]; then
 | 
				
			||||||
 | 
					            cache_tag="beta"
 | 
				
			||||||
 | 
					          else
 | 
				
			||||||
 | 
					            cache_tag="latest"
 | 
				
			||||||
 | 
					          fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          # Set env variables so these values don't need to be calculated again
 | 
				
			||||||
 | 
					          echo "BUILD_FROM=${build_from}" >> $GITHUB_ENV
 | 
				
			||||||
 | 
					          echo "BUILD_TO=${build_to}" >> $GITHUB_ENV
 | 
				
			||||||
 | 
					          echo "DOCKERFILE=${dockerfile}" >> $GITHUB_ENV
 | 
				
			||||||
 | 
					          echo "CACHE_TAG=${cache_tag}" >> $GITHUB_ENV
 | 
				
			||||||
 | 
					      - name: Pull for cache
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          docker pull "${BUILD_TO}:${CACHE_TAG}" || true
 | 
				
			||||||
 | 
					      - name: Register QEMU binfmt
 | 
				
			||||||
 | 
					        run: docker run --rm --privileged multiarch/qemu-user-static:5.2.0-2 --reset -p yes
 | 
				
			||||||
 | 
					      - run: |
 | 
				
			||||||
 | 
					          docker build \
 | 
				
			||||||
 | 
					            --build-arg "BUILD_FROM=${BUILD_FROM}" \
 | 
				
			||||||
 | 
					            --build-arg "BUILD_VERSION=${TAG}" \
 | 
				
			||||||
 | 
					            --tag "${BUILD_TO}:${TAG}" \
 | 
				
			||||||
 | 
					            --cache-from "${BUILD_TO}:${CACHE_TAG}" \
 | 
				
			||||||
 | 
					            --file "${DOCKERFILE}" \
 | 
				
			||||||
 | 
					            .
 | 
				
			||||||
 | 
					      - name: Log in to docker hub
 | 
				
			||||||
 | 
					        env:
 | 
				
			||||||
 | 
					          DOCKER_USER: ${{ secrets.DOCKER_USER }}
 | 
				
			||||||
 | 
					          DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
 | 
				
			||||||
 | 
					        run: docker login -u "${DOCKER_USER}" -p "${DOCKER_PASSWORD}"
 | 
				
			||||||
 | 
					      - run: docker push "${BUILD_TO}:${TAG}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      # Always publish to beta tag (also full releases)
 | 
				
			||||||
 | 
					      - name: Publish docker beta tag
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          docker tag "${BUILD_TO}:${TAG}" "${BUILD_TO}:beta"
 | 
				
			||||||
 | 
					          docker push "${BUILD_TO}:beta"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - if: ${{ !github.event.release.prerelease }}
 | 
				
			||||||
 | 
					        name: Publish docker latest tag
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          docker tag "${BUILD_TO}:${TAG}" "${BUILD_TO}:latest"
 | 
				
			||||||
 | 
					          docker push "${BUILD_TO}:latest"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  deploy-docker-manifest:
 | 
				
			||||||
 | 
					    if: github.repository == 'esphome/esphome'
 | 
				
			||||||
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					    needs: [deploy-docker]
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					    - name: Enable experimental manifest support
 | 
				
			||||||
 | 
					      run: |
 | 
				
			||||||
 | 
					        mkdir -p ~/.docker
 | 
				
			||||||
 | 
					        echo "{\"experimental\": \"enabled\"}" > ~/.docker/config.json
 | 
				
			||||||
 | 
					    - name: Set TAG
 | 
				
			||||||
 | 
					      run: |
 | 
				
			||||||
 | 
					        TAG="${GITHUB_REF#refs/tags/v}"
 | 
				
			||||||
 | 
					        echo "TAG=${TAG}" >> $GITHUB_ENV
 | 
				
			||||||
 | 
					    - name: Log in to docker hub
 | 
				
			||||||
 | 
					      env:
 | 
				
			||||||
 | 
					        DOCKER_USER: ${{ secrets.DOCKER_USER }}
 | 
				
			||||||
 | 
					        DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
 | 
				
			||||||
 | 
					      run: docker login -u "${DOCKER_USER}" -p "${DOCKER_PASSWORD}"
 | 
				
			||||||
 | 
					    - name: "Create the manifest"
 | 
				
			||||||
 | 
					      run: |
 | 
				
			||||||
 | 
					        docker manifest create esphome/esphome:${TAG} \
 | 
				
			||||||
 | 
					          esphome/esphome-aarch64:${TAG} \
 | 
				
			||||||
 | 
					          esphome/esphome-amd64:${TAG} \
 | 
				
			||||||
 | 
					          esphome/esphome-armv7:${TAG}
 | 
				
			||||||
 | 
					        docker manifest push esphome/esphome:${TAG}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - name: Publish docker beta tag
 | 
				
			||||||
 | 
					      run: |
 | 
				
			||||||
 | 
					        docker manifest create esphome/esphome:beta \
 | 
				
			||||||
 | 
					          esphome/esphome-aarch64:${TAG} \
 | 
				
			||||||
 | 
					          esphome/esphome-amd64:${TAG} \
 | 
				
			||||||
 | 
					          esphome/esphome-armv7:${TAG}
 | 
				
			||||||
 | 
					        docker manifest push esphome/esphome:beta
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - name: Publish docker latest tag
 | 
				
			||||||
 | 
					      if: ${{ !github.event.release.prerelease }}
 | 
				
			||||||
 | 
					      run: |
 | 
				
			||||||
 | 
					        docker manifest create esphome/esphome:latest \
 | 
				
			||||||
 | 
					          esphome/esphome-aarch64:${TAG} \
 | 
				
			||||||
 | 
					          esphome/esphome-amd64:${TAG} \
 | 
				
			||||||
 | 
					          esphome/esphome-armv7:${TAG}
 | 
				
			||||||
 | 
					        docker manifest push esphome/esphome:latest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  deploy-hassio-repo:
 | 
				
			||||||
 | 
					    if: github.repository == 'esphome/esphome'
 | 
				
			||||||
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					    needs: [deploy-docker]
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					      - env:
 | 
				
			||||||
 | 
					          TOKEN: ${{ secrets.DEPLOY_HASSIO_TOKEN }}
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          TAG="${GITHUB_REF#refs/tags/v}"
 | 
				
			||||||
 | 
					          curl \
 | 
				
			||||||
 | 
					            -u ":$TOKEN" \
 | 
				
			||||||
 | 
					            -X POST \
 | 
				
			||||||
 | 
					            -H "Accept: application/vnd.github.v3+json" \
 | 
				
			||||||
 | 
					            https://api.github.com/repos/esphome/hassio/actions/workflows/bump-version.yml/dispatches \
 | 
				
			||||||
 | 
					            -d "{\"ref\":\"master\",\"inputs\":{\"version\":\"$TAG\"}}"
 | 
				
			||||||
							
								
								
									
										11
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -10,6 +10,9 @@ __pycache__/
 | 
				
			|||||||
*.sublime-project
 | 
					*.sublime-project
 | 
				
			||||||
*.sublime-workspace
 | 
					*.sublime-workspace
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Intellij Idea
 | 
				
			||||||
 | 
					.idea
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Hide some OS X stuff
 | 
					# Hide some OS X stuff
 | 
				
			||||||
.DS_Store
 | 
					.DS_Store
 | 
				
			||||||
.AppleDouble
 | 
					.AppleDouble
 | 
				
			||||||
@@ -48,8 +51,10 @@ htmlcov/
 | 
				
			|||||||
.coverage
 | 
					.coverage
 | 
				
			||||||
.coverage.*
 | 
					.coverage.*
 | 
				
			||||||
.cache
 | 
					.cache
 | 
				
			||||||
 | 
					.esphome
 | 
				
			||||||
nosetests.xml
 | 
					nosetests.xml
 | 
				
			||||||
coverage.xml
 | 
					coverage.xml
 | 
				
			||||||
 | 
					cov.xml
 | 
				
			||||||
*.cover
 | 
					*.cover
 | 
				
			||||||
.hypothesis/
 | 
					.hypothesis/
 | 
				
			||||||
.pytest_cache/
 | 
					.pytest_cache/
 | 
				
			||||||
@@ -76,7 +81,8 @@ venv.bak/
 | 
				
			|||||||
.pioenvs
 | 
					.pioenvs
 | 
				
			||||||
.piolibdeps
 | 
					.piolibdeps
 | 
				
			||||||
.pio
 | 
					.pio
 | 
				
			||||||
.vscode
 | 
					.vscode/
 | 
				
			||||||
 | 
					!.vscode/tasks.json
 | 
				
			||||||
CMakeListsPrivate.txt
 | 
					CMakeListsPrivate.txt
 | 
				
			||||||
CMakeLists.txt
 | 
					CMakeLists.txt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -94,6 +100,8 @@ CMakeLists.txt
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# CMake
 | 
					# CMake
 | 
				
			||||||
cmake-build-debug/
 | 
					cmake-build-debug/
 | 
				
			||||||
 | 
					cmake-build-livingroom8266/
 | 
				
			||||||
 | 
					cmake-build-livingroom32/
 | 
				
			||||||
cmake-build-release/
 | 
					cmake-build-release/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CMakeCache.txt
 | 
					CMakeCache.txt
 | 
				
			||||||
@@ -114,3 +122,4 @@ config/
 | 
				
			|||||||
tests/build/
 | 
					tests/build/
 | 
				
			||||||
tests/.esphome/
 | 
					tests/.esphome/
 | 
				
			||||||
/.temp-clang-tidy.cpp
 | 
					/.temp-clang-tidy.cpp
 | 
				
			||||||
 | 
					.pio/
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										342
									
								
								.gitlab-ci.yml
									
									
									
									
									
								
							
							
						
						
									
										342
									
								
								.gitlab-ci.yml
									
									
									
									
									
								
							@@ -1,342 +0,0 @@
 | 
				
			|||||||
---
 | 
					 | 
				
			||||||
# Based on https://gitlab.com/hassio-addons/addon-node-red/blob/master/.gitlab-ci.yml
 | 
					 | 
				
			||||||
variables:
 | 
					 | 
				
			||||||
  DOCKER_DRIVER: overlay2
 | 
					 | 
				
			||||||
  DOCKER_HOST: tcp://docker:2375/
 | 
					 | 
				
			||||||
  BASE_VERSION: '2.0.1'
 | 
					 | 
				
			||||||
  TZ: UTC
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
stages:
 | 
					 | 
				
			||||||
  - lint
 | 
					 | 
				
			||||||
  - test
 | 
					 | 
				
			||||||
  - deploy
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.lint: &lint
 | 
					 | 
				
			||||||
  image: esphome/esphome-lint:latest
 | 
					 | 
				
			||||||
  stage: lint
 | 
					 | 
				
			||||||
  before_script:
 | 
					 | 
				
			||||||
    - script/setup
 | 
					 | 
				
			||||||
  tags:
 | 
					 | 
				
			||||||
    - docker
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.test: &test
 | 
					 | 
				
			||||||
  image: esphome/esphome-lint:latest
 | 
					 | 
				
			||||||
  stage: test
 | 
					 | 
				
			||||||
  before_script:
 | 
					 | 
				
			||||||
    - script/setup
 | 
					 | 
				
			||||||
  tags:
 | 
					 | 
				
			||||||
    - docker
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.docker-base: &docker-base
 | 
					 | 
				
			||||||
  image: esphome/esphome-base-builder
 | 
					 | 
				
			||||||
  before_script:
 | 
					 | 
				
			||||||
    - docker info
 | 
					 | 
				
			||||||
    - docker login -u "$DOCKER_USER" -p "$DOCKER_PASSWORD"
 | 
					 | 
				
			||||||
  script:
 | 
					 | 
				
			||||||
    - docker run --rm --privileged multiarch/qemu-user-static:4.1.0-1 --reset -p yes
 | 
					 | 
				
			||||||
    - TAG="${CI_COMMIT_TAG#v}"
 | 
					 | 
				
			||||||
    - TAG="${TAG:-${CI_COMMIT_SHA:0:7}}"
 | 
					 | 
				
			||||||
    - echo "Tag ${TAG}"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    - |
 | 
					 | 
				
			||||||
      if [[ "${IS_HASSIO}" == "YES" ]]; then
 | 
					 | 
				
			||||||
        BUILD_FROM=esphome/esphome-hassio-base-${BUILD_ARCH}:${BASE_VERSION}
 | 
					 | 
				
			||||||
        BUILD_TO=esphome/esphome-hassio-${BUILD_ARCH}
 | 
					 | 
				
			||||||
        DOCKERFILE=docker/Dockerfile.hassio
 | 
					 | 
				
			||||||
      else
 | 
					 | 
				
			||||||
        BUILD_FROM=esphome/esphome-base-${BUILD_ARCH}:${BASE_VERSION}
 | 
					 | 
				
			||||||
        if [[ "${BUILD_ARCH}" == "amd64" ]]; then
 | 
					 | 
				
			||||||
          BUILD_TO=esphome/esphome
 | 
					 | 
				
			||||||
        else
 | 
					 | 
				
			||||||
          BUILD_TO=esphome/esphome-${BUILD_ARCH}
 | 
					 | 
				
			||||||
        fi
 | 
					 | 
				
			||||||
        DOCKERFILE=docker/Dockerfile
 | 
					 | 
				
			||||||
      fi
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    - |
 | 
					 | 
				
			||||||
      docker build \
 | 
					 | 
				
			||||||
        --build-arg "BUILD_FROM=${BUILD_FROM}" \
 | 
					 | 
				
			||||||
        --build-arg "BUILD_VERSION=${TAG}" \
 | 
					 | 
				
			||||||
        --tag "${BUILD_TO}:${TAG}" \
 | 
					 | 
				
			||||||
        --file "${DOCKERFILE}" \
 | 
					 | 
				
			||||||
        .
 | 
					 | 
				
			||||||
    - |
 | 
					 | 
				
			||||||
      if [[ "${RELEASE}" = "YES" ]]; then
 | 
					 | 
				
			||||||
        echo "Pushing to ${BUILD_TO}:${TAG}"
 | 
					 | 
				
			||||||
        docker push "${BUILD_TO}:${TAG}"
 | 
					 | 
				
			||||||
      fi
 | 
					 | 
				
			||||||
    - |
 | 
					 | 
				
			||||||
      if [[ "${LATEST}" = "YES" ]]; then
 | 
					 | 
				
			||||||
        echo "Pushing to :latest"
 | 
					 | 
				
			||||||
        docker tag ${BUILD_TO}:${TAG} ${BUILD_TO}:latest
 | 
					 | 
				
			||||||
        docker push ${BUILD_TO}:latest
 | 
					 | 
				
			||||||
      fi
 | 
					 | 
				
			||||||
    - |
 | 
					 | 
				
			||||||
      if [[ "${BETA}" = "YES" ]]; then
 | 
					 | 
				
			||||||
        echo "Pushing to :beta"
 | 
					 | 
				
			||||||
        docker tag \
 | 
					 | 
				
			||||||
          ${BUILD_TO}:${TAG} \
 | 
					 | 
				
			||||||
          ${BUILD_TO}:beta
 | 
					 | 
				
			||||||
        docker push ${BUILD_TO}:beta
 | 
					 | 
				
			||||||
      fi
 | 
					 | 
				
			||||||
    - |
 | 
					 | 
				
			||||||
      if [[ "${DEV}" = "YES" ]]; then
 | 
					 | 
				
			||||||
        echo "Pushing to :dev"
 | 
					 | 
				
			||||||
        docker tag \
 | 
					 | 
				
			||||||
          ${BUILD_TO}:${TAG} \
 | 
					 | 
				
			||||||
          ${BUILD_TO}:dev
 | 
					 | 
				
			||||||
        docker push ${BUILD_TO}:dev
 | 
					 | 
				
			||||||
      fi
 | 
					 | 
				
			||||||
  services:
 | 
					 | 
				
			||||||
    - docker:dind
 | 
					 | 
				
			||||||
  tags:
 | 
					 | 
				
			||||||
    - docker
 | 
					 | 
				
			||||||
  stage: deploy
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
lint-custom:
 | 
					 | 
				
			||||||
  <<: *lint
 | 
					 | 
				
			||||||
  script:
 | 
					 | 
				
			||||||
    - script/ci-custom.py
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
lint-python:
 | 
					 | 
				
			||||||
  <<: *lint
 | 
					 | 
				
			||||||
  script:
 | 
					 | 
				
			||||||
    - script/lint-python
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
lint-tidy:
 | 
					 | 
				
			||||||
  <<: *lint
 | 
					 | 
				
			||||||
  script:
 | 
					 | 
				
			||||||
    - pio init --ide atom
 | 
					 | 
				
			||||||
    - script/clang-tidy --all-headers --fix
 | 
					 | 
				
			||||||
    - script/ci-suggest-changes
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
lint-format:
 | 
					 | 
				
			||||||
  <<: *lint
 | 
					 | 
				
			||||||
  script:
 | 
					 | 
				
			||||||
    - script/clang-format -i
 | 
					 | 
				
			||||||
    - script/ci-suggest-changes
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
test1:
 | 
					 | 
				
			||||||
  <<: *test
 | 
					 | 
				
			||||||
  script:
 | 
					 | 
				
			||||||
    - esphome tests/test1.yaml compile
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
test2:
 | 
					 | 
				
			||||||
  <<: *test
 | 
					 | 
				
			||||||
  script:
 | 
					 | 
				
			||||||
    - esphome tests/test2.yaml compile
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
test3:
 | 
					 | 
				
			||||||
  <<: *test
 | 
					 | 
				
			||||||
  script:
 | 
					 | 
				
			||||||
    - esphome tests/test3.yaml compile
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.deploy-pypi: &deploy-pypi
 | 
					 | 
				
			||||||
  <<: *lint
 | 
					 | 
				
			||||||
  stage: deploy
 | 
					 | 
				
			||||||
  script:
 | 
					 | 
				
			||||||
    - pip install twine wheel
 | 
					 | 
				
			||||||
    - python setup.py sdist bdist_wheel
 | 
					 | 
				
			||||||
    - twine upload dist/*
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
deploy-release:pypi:
 | 
					 | 
				
			||||||
  <<: *deploy-pypi
 | 
					 | 
				
			||||||
  only:
 | 
					 | 
				
			||||||
    - /^v\d+\.\d+\.\d+$/
 | 
					 | 
				
			||||||
  except:
 | 
					 | 
				
			||||||
    - /^(?!master).+@/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
deploy-beta:pypi:
 | 
					 | 
				
			||||||
  <<: *deploy-pypi
 | 
					 | 
				
			||||||
  only:
 | 
					 | 
				
			||||||
    - /^v\d+\.\d+\.\d+b\d+$/
 | 
					 | 
				
			||||||
  except:
 | 
					 | 
				
			||||||
    - /^(?!rc).+@/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.latest: &latest
 | 
					 | 
				
			||||||
  <<: *docker-base
 | 
					 | 
				
			||||||
  only:
 | 
					 | 
				
			||||||
    - /^v([0-9\.]+)$/
 | 
					 | 
				
			||||||
  except:
 | 
					 | 
				
			||||||
    - branches
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.beta: &beta
 | 
					 | 
				
			||||||
  <<: *docker-base
 | 
					 | 
				
			||||||
  only:
 | 
					 | 
				
			||||||
    - /^v([0-9\.]+b\d+)$/
 | 
					 | 
				
			||||||
  except:
 | 
					 | 
				
			||||||
    - branches
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.dev: &dev
 | 
					 | 
				
			||||||
  <<: *docker-base
 | 
					 | 
				
			||||||
  only:
 | 
					 | 
				
			||||||
    - dev
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
aarch64-beta-docker:
 | 
					 | 
				
			||||||
  <<: *beta
 | 
					 | 
				
			||||||
  variables:
 | 
					 | 
				
			||||||
    BETA: "YES"
 | 
					 | 
				
			||||||
    BUILD_ARCH: aarch64
 | 
					 | 
				
			||||||
    IS_HASSIO: "NO"
 | 
					 | 
				
			||||||
    RELEASE: "YES"
 | 
					 | 
				
			||||||
aarch64-beta-hassio:
 | 
					 | 
				
			||||||
  <<: *beta
 | 
					 | 
				
			||||||
  variables:
 | 
					 | 
				
			||||||
    BETA: "YES"
 | 
					 | 
				
			||||||
    BUILD_ARCH: aarch64
 | 
					 | 
				
			||||||
    IS_HASSIO: "YES"
 | 
					 | 
				
			||||||
    RELEASE: "YES"
 | 
					 | 
				
			||||||
aarch64-dev-docker:
 | 
					 | 
				
			||||||
  <<: *dev
 | 
					 | 
				
			||||||
  variables:
 | 
					 | 
				
			||||||
    BUILD_ARCH: aarch64
 | 
					 | 
				
			||||||
    DEV: "YES"
 | 
					 | 
				
			||||||
    IS_HASSIO: "NO"
 | 
					 | 
				
			||||||
aarch64-dev-hassio:
 | 
					 | 
				
			||||||
  <<: *dev
 | 
					 | 
				
			||||||
  variables:
 | 
					 | 
				
			||||||
    BUILD_ARCH: aarch64
 | 
					 | 
				
			||||||
    DEV: "YES"
 | 
					 | 
				
			||||||
    IS_HASSIO: "YES"
 | 
					 | 
				
			||||||
aarch64-latest-docker:
 | 
					 | 
				
			||||||
  <<: *latest
 | 
					 | 
				
			||||||
  variables:
 | 
					 | 
				
			||||||
    BETA: "YES"
 | 
					 | 
				
			||||||
    BUILD_ARCH: aarch64
 | 
					 | 
				
			||||||
    IS_HASSIO: "NO"
 | 
					 | 
				
			||||||
    LATEST: "YES"
 | 
					 | 
				
			||||||
    RELEASE: "YES"
 | 
					 | 
				
			||||||
aarch64-latest-hassio:
 | 
					 | 
				
			||||||
  <<: *latest
 | 
					 | 
				
			||||||
  variables:
 | 
					 | 
				
			||||||
    BETA: "YES"
 | 
					 | 
				
			||||||
    BUILD_ARCH: aarch64
 | 
					 | 
				
			||||||
    IS_HASSIO: "YES"
 | 
					 | 
				
			||||||
    LATEST: "YES"
 | 
					 | 
				
			||||||
    RELEASE: "YES"
 | 
					 | 
				
			||||||
amd64-beta-docker:
 | 
					 | 
				
			||||||
  <<: *beta
 | 
					 | 
				
			||||||
  variables:
 | 
					 | 
				
			||||||
    BETA: "YES"
 | 
					 | 
				
			||||||
    BUILD_ARCH: amd64
 | 
					 | 
				
			||||||
    IS_HASSIO: "NO"
 | 
					 | 
				
			||||||
    RELEASE: "YES"
 | 
					 | 
				
			||||||
amd64-beta-hassio:
 | 
					 | 
				
			||||||
  <<: *beta
 | 
					 | 
				
			||||||
  variables:
 | 
					 | 
				
			||||||
    BETA: "YES"
 | 
					 | 
				
			||||||
    BUILD_ARCH: amd64
 | 
					 | 
				
			||||||
    IS_HASSIO: "YES"
 | 
					 | 
				
			||||||
    RELEASE: "YES"
 | 
					 | 
				
			||||||
amd64-dev-docker:
 | 
					 | 
				
			||||||
  <<: *dev
 | 
					 | 
				
			||||||
  variables:
 | 
					 | 
				
			||||||
    BUILD_ARCH: amd64
 | 
					 | 
				
			||||||
    DEV: "YES"
 | 
					 | 
				
			||||||
    IS_HASSIO: "NO"
 | 
					 | 
				
			||||||
amd64-dev-hassio:
 | 
					 | 
				
			||||||
  <<: *dev
 | 
					 | 
				
			||||||
  variables:
 | 
					 | 
				
			||||||
    BUILD_ARCH: amd64
 | 
					 | 
				
			||||||
    DEV: "YES"
 | 
					 | 
				
			||||||
    IS_HASSIO: "YES"
 | 
					 | 
				
			||||||
amd64-latest-docker:
 | 
					 | 
				
			||||||
  <<: *latest
 | 
					 | 
				
			||||||
  variables:
 | 
					 | 
				
			||||||
    BETA: "YES"
 | 
					 | 
				
			||||||
    BUILD_ARCH: amd64
 | 
					 | 
				
			||||||
    IS_HASSIO: "NO"
 | 
					 | 
				
			||||||
    LATEST: "YES"
 | 
					 | 
				
			||||||
    RELEASE: "YES"
 | 
					 | 
				
			||||||
amd64-latest-hassio:
 | 
					 | 
				
			||||||
  <<: *latest
 | 
					 | 
				
			||||||
  variables:
 | 
					 | 
				
			||||||
    BETA: "YES"
 | 
					 | 
				
			||||||
    BUILD_ARCH: amd64
 | 
					 | 
				
			||||||
    IS_HASSIO: "YES"
 | 
					 | 
				
			||||||
    LATEST: "YES"
 | 
					 | 
				
			||||||
    RELEASE: "YES"
 | 
					 | 
				
			||||||
armv7-beta-docker:
 | 
					 | 
				
			||||||
  <<: *beta
 | 
					 | 
				
			||||||
  variables:
 | 
					 | 
				
			||||||
    BETA: "YES"
 | 
					 | 
				
			||||||
    BUILD_ARCH: armv7
 | 
					 | 
				
			||||||
    IS_HASSIO: "NO"
 | 
					 | 
				
			||||||
    RELEASE: "YES"
 | 
					 | 
				
			||||||
armv7-beta-hassio:
 | 
					 | 
				
			||||||
  <<: *beta
 | 
					 | 
				
			||||||
  variables:
 | 
					 | 
				
			||||||
    BETA: "YES"
 | 
					 | 
				
			||||||
    BUILD_ARCH: armv7
 | 
					 | 
				
			||||||
    IS_HASSIO: "YES"
 | 
					 | 
				
			||||||
    RELEASE: "YES"
 | 
					 | 
				
			||||||
armv7-dev-docker:
 | 
					 | 
				
			||||||
  <<: *dev
 | 
					 | 
				
			||||||
  variables:
 | 
					 | 
				
			||||||
    BUILD_ARCH: armv7
 | 
					 | 
				
			||||||
    DEV: "YES"
 | 
					 | 
				
			||||||
    IS_HASSIO: "NO"
 | 
					 | 
				
			||||||
armv7-dev-hassio:
 | 
					 | 
				
			||||||
  <<: *dev
 | 
					 | 
				
			||||||
  variables:
 | 
					 | 
				
			||||||
    BUILD_ARCH: armv7
 | 
					 | 
				
			||||||
    DEV: "YES"
 | 
					 | 
				
			||||||
    IS_HASSIO: "YES"
 | 
					 | 
				
			||||||
armv7-latest-docker:
 | 
					 | 
				
			||||||
  <<: *latest
 | 
					 | 
				
			||||||
  variables:
 | 
					 | 
				
			||||||
    BETA: "YES"
 | 
					 | 
				
			||||||
    BUILD_ARCH: armv7
 | 
					 | 
				
			||||||
    IS_HASSIO: "NO"
 | 
					 | 
				
			||||||
    LATEST: "YES"
 | 
					 | 
				
			||||||
    RELEASE: "YES"
 | 
					 | 
				
			||||||
armv7-latest-hassio:
 | 
					 | 
				
			||||||
  <<: *latest
 | 
					 | 
				
			||||||
  variables:
 | 
					 | 
				
			||||||
    BETA: "YES"
 | 
					 | 
				
			||||||
    BUILD_ARCH: armv7
 | 
					 | 
				
			||||||
    IS_HASSIO: "YES"
 | 
					 | 
				
			||||||
    LATEST: "YES"
 | 
					 | 
				
			||||||
    RELEASE: "YES"
 | 
					 | 
				
			||||||
i386-beta-docker:
 | 
					 | 
				
			||||||
  <<: *beta
 | 
					 | 
				
			||||||
  variables:
 | 
					 | 
				
			||||||
    BETA: "YES"
 | 
					 | 
				
			||||||
    BUILD_ARCH: i386
 | 
					 | 
				
			||||||
    IS_HASSIO: "NO"
 | 
					 | 
				
			||||||
    RELEASE: "YES"
 | 
					 | 
				
			||||||
i386-beta-hassio:
 | 
					 | 
				
			||||||
  <<: *beta
 | 
					 | 
				
			||||||
  variables:
 | 
					 | 
				
			||||||
    BETA: "YES"
 | 
					 | 
				
			||||||
    BUILD_ARCH: i386
 | 
					 | 
				
			||||||
    IS_HASSIO: "YES"
 | 
					 | 
				
			||||||
    RELEASE: "YES"
 | 
					 | 
				
			||||||
i386-dev-docker:
 | 
					 | 
				
			||||||
  <<: *dev
 | 
					 | 
				
			||||||
  variables:
 | 
					 | 
				
			||||||
    BUILD_ARCH: i386
 | 
					 | 
				
			||||||
    DEV: "YES"
 | 
					 | 
				
			||||||
    IS_HASSIO: "NO"
 | 
					 | 
				
			||||||
i386-dev-hassio:
 | 
					 | 
				
			||||||
  <<: *dev
 | 
					 | 
				
			||||||
  variables:
 | 
					 | 
				
			||||||
    BUILD_ARCH: i386
 | 
					 | 
				
			||||||
    DEV: "YES"
 | 
					 | 
				
			||||||
    IS_HASSIO: "YES"
 | 
					 | 
				
			||||||
i386-latest-docker:
 | 
					 | 
				
			||||||
  <<: *latest
 | 
					 | 
				
			||||||
  variables:
 | 
					 | 
				
			||||||
    BETA: "YES"
 | 
					 | 
				
			||||||
    BUILD_ARCH: i386
 | 
					 | 
				
			||||||
    IS_HASSIO: "NO"
 | 
					 | 
				
			||||||
    LATEST: "YES"
 | 
					 | 
				
			||||||
    RELEASE: "YES"
 | 
					 | 
				
			||||||
i386-latest-hassio:
 | 
					 | 
				
			||||||
  <<: *latest
 | 
					 | 
				
			||||||
  variables:
 | 
					 | 
				
			||||||
    BETA: "YES"
 | 
					 | 
				
			||||||
    BUILD_ARCH: i386
 | 
					 | 
				
			||||||
    IS_HASSIO: "YES"
 | 
					 | 
				
			||||||
    LATEST: "YES"
 | 
					 | 
				
			||||||
    RELEASE: "YES"
 | 
					 | 
				
			||||||
@@ -2,5 +2,5 @@ ports:
 | 
				
			|||||||
- port: 6052
 | 
					- port: 6052
 | 
				
			||||||
  onOpen: open-preview
 | 
					  onOpen: open-preview
 | 
				
			||||||
tasks:
 | 
					tasks:
 | 
				
			||||||
- before: script/setup
 | 
					- before: pyenv local $(pyenv version | grep '^3\.' | cut -d ' ' -f 1) && script/setup
 | 
				
			||||||
  command: python -m esphome config dashboard
 | 
					  command: python -m esphome config dashboard
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										27
									
								
								.pre-commit-config.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								.pre-commit-config.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					# See https://pre-commit.com for more information
 | 
				
			||||||
 | 
					# See https://pre-commit.com/hooks.html for more hooks
 | 
				
			||||||
 | 
					repos:
 | 
				
			||||||
 | 
					  - repo: https://github.com/ambv/black
 | 
				
			||||||
 | 
					    rev: 20.8b1
 | 
				
			||||||
 | 
					    hooks:
 | 
				
			||||||
 | 
					    - id: black
 | 
				
			||||||
 | 
					      args:
 | 
				
			||||||
 | 
					        - --safe
 | 
				
			||||||
 | 
					        - --quiet
 | 
				
			||||||
 | 
					      files: ^((esphome|script|tests)/.+)?[^/]+\.py$
 | 
				
			||||||
 | 
					  - repo: https://gitlab.com/pycqa/flake8
 | 
				
			||||||
 | 
					    rev: 3.8.4
 | 
				
			||||||
 | 
					    hooks:
 | 
				
			||||||
 | 
					      - id: flake8
 | 
				
			||||||
 | 
					        additional_dependencies:
 | 
				
			||||||
 | 
					          - flake8-docstrings==1.5.0
 | 
				
			||||||
 | 
					          - pydocstyle==5.1.1
 | 
				
			||||||
 | 
					        files: ^(esphome|tests)/.+\.py$
 | 
				
			||||||
 | 
					  - repo: https://github.com/pre-commit/pre-commit-hooks
 | 
				
			||||||
 | 
					    rev: v3.4.0
 | 
				
			||||||
 | 
					    hooks:
 | 
				
			||||||
 | 
					      - id: no-commit-to-branch
 | 
				
			||||||
 | 
					        args:
 | 
				
			||||||
 | 
					          - --branch=dev
 | 
				
			||||||
 | 
					          - --branch=master
 | 
				
			||||||
 | 
					          - --branch=beta
 | 
				
			||||||
							
								
								
									
										49
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										49
									
								
								.travis.yml
									
									
									
									
									
								
							@@ -1,49 +0,0 @@
 | 
				
			|||||||
sudo: false
 | 
					 | 
				
			||||||
language: python
 | 
					 | 
				
			||||||
python: '3.6'
 | 
					 | 
				
			||||||
install: script/setup
 | 
					 | 
				
			||||||
cache:
 | 
					 | 
				
			||||||
  directories:
 | 
					 | 
				
			||||||
    - "~/.platformio"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
matrix:
 | 
					 | 
				
			||||||
  fast_finish: true
 | 
					 | 
				
			||||||
  include:
 | 
					 | 
				
			||||||
    - python: "3.7"
 | 
					 | 
				
			||||||
      env: TARGET=Lint3.7
 | 
					 | 
				
			||||||
      script:
 | 
					 | 
				
			||||||
        - script/ci-custom.py
 | 
					 | 
				
			||||||
        - flake8 esphome
 | 
					 | 
				
			||||||
        - pylint esphome
 | 
					 | 
				
			||||||
    - python: "3.6"
 | 
					 | 
				
			||||||
      env: TARGET=Test3.6
 | 
					 | 
				
			||||||
      script:
 | 
					 | 
				
			||||||
        - esphome tests/test1.yaml compile
 | 
					 | 
				
			||||||
        - esphome tests/test2.yaml compile
 | 
					 | 
				
			||||||
        - esphome tests/test3.yaml compile
 | 
					 | 
				
			||||||
    - python: "2.7"
 | 
					 | 
				
			||||||
      env: TARGET=Test2.7
 | 
					 | 
				
			||||||
      script:
 | 
					 | 
				
			||||||
        - esphome tests/test1.yaml compile
 | 
					 | 
				
			||||||
        - esphome tests/test2.yaml compile
 | 
					 | 
				
			||||||
        - esphome tests/test3.yaml compile
 | 
					 | 
				
			||||||
    - env: TARGET=Cpp-Lint
 | 
					 | 
				
			||||||
      dist: trusty
 | 
					 | 
				
			||||||
      sudo: required
 | 
					 | 
				
			||||||
      addons:
 | 
					 | 
				
			||||||
        apt:
 | 
					 | 
				
			||||||
          sources:
 | 
					 | 
				
			||||||
            - ubuntu-toolchain-r-test
 | 
					 | 
				
			||||||
            - llvm-toolchain-trusty-7
 | 
					 | 
				
			||||||
          packages:
 | 
					 | 
				
			||||||
            - clang-tidy-7
 | 
					 | 
				
			||||||
            - clang-format-7
 | 
					 | 
				
			||||||
      before_script:
 | 
					 | 
				
			||||||
        - pio init --ide atom
 | 
					 | 
				
			||||||
        - clang-tidy-7 -version
 | 
					 | 
				
			||||||
        - clang-format-7 -version
 | 
					 | 
				
			||||||
        - clang-apply-replacements-7 -version
 | 
					 | 
				
			||||||
      script:
 | 
					 | 
				
			||||||
        - script/clang-tidy --all-headers -j 2 --fix
 | 
					 | 
				
			||||||
        - script/clang-format -i -j 2
 | 
					 | 
				
			||||||
        - script/ci-suggest-changes
 | 
					 | 
				
			||||||
							
								
								
									
										11
									
								
								.vscode/tasks.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								.vscode/tasks.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					    "version": "2.0.0",
 | 
				
			||||||
 | 
					    "tasks": [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "label": "run",
 | 
				
			||||||
 | 
					            "type": "shell",
 | 
				
			||||||
 | 
					            "command": "python3 -m esphome dashboard config",
 | 
				
			||||||
 | 
					            "problemMatcher": []
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										132
									
								
								CODEOWNERS
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								CODEOWNERS
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,132 @@
 | 
				
			|||||||
 | 
					# This file is generated by script/build_codeowners.py
 | 
				
			||||||
 | 
					# People marked here will be automatically requested for a review
 | 
				
			||||||
 | 
					# when the code that they own is touched.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Every time an issue is created with a label corresponding to an integration,
 | 
				
			||||||
 | 
					# the integration's code owner is automatically notified.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Core Code
 | 
				
			||||||
 | 
					setup.py @esphome/core
 | 
				
			||||||
 | 
					esphome/*.py @esphome/core
 | 
				
			||||||
 | 
					esphome/core/* @esphome/core
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Integrations
 | 
				
			||||||
 | 
					esphome/components/ac_dimmer/* @glmnet
 | 
				
			||||||
 | 
					esphome/components/adc/* @esphome/core
 | 
				
			||||||
 | 
					esphome/components/addressable_light/* @justfalter
 | 
				
			||||||
 | 
					esphome/components/animation/* @syndlex
 | 
				
			||||||
 | 
					esphome/components/api/* @OttoWinter
 | 
				
			||||||
 | 
					esphome/components/async_tcp/* @OttoWinter
 | 
				
			||||||
 | 
					esphome/components/atc_mithermometer/* @ahpohl
 | 
				
			||||||
 | 
					esphome/components/b_parasite/* @rbaron
 | 
				
			||||||
 | 
					esphome/components/bang_bang/* @OttoWinter
 | 
				
			||||||
 | 
					esphome/components/binary_sensor/* @esphome/core
 | 
				
			||||||
 | 
					esphome/components/ble_client/* @buxtronix
 | 
				
			||||||
 | 
					esphome/components/bme680_bsec/* @trvrnrth
 | 
				
			||||||
 | 
					esphome/components/canbus/* @danielschramm @mvturnho
 | 
				
			||||||
 | 
					esphome/components/captive_portal/* @OttoWinter
 | 
				
			||||||
 | 
					esphome/components/climate/* @esphome/core
 | 
				
			||||||
 | 
					esphome/components/climate_ir/* @glmnet
 | 
				
			||||||
 | 
					esphome/components/coolix/* @glmnet
 | 
				
			||||||
 | 
					esphome/components/cover/* @esphome/core
 | 
				
			||||||
 | 
					esphome/components/cs5460a/* @balrog-kun
 | 
				
			||||||
 | 
					esphome/components/ct_clamp/* @jesserockz
 | 
				
			||||||
 | 
					esphome/components/debug/* @OttoWinter
 | 
				
			||||||
 | 
					esphome/components/dfplayer/* @glmnet
 | 
				
			||||||
 | 
					esphome/components/dht/* @OttoWinter
 | 
				
			||||||
 | 
					esphome/components/ds1307/* @badbadc0ffee
 | 
				
			||||||
 | 
					esphome/components/esp32_ble/* @jesserockz
 | 
				
			||||||
 | 
					esphome/components/esp32_ble_server/* @jesserockz
 | 
				
			||||||
 | 
					esphome/components/esp32_improv/* @jesserockz
 | 
				
			||||||
 | 
					esphome/components/exposure_notifications/* @OttoWinter
 | 
				
			||||||
 | 
					esphome/components/ezo/* @ssieb
 | 
				
			||||||
 | 
					esphome/components/fastled_base/* @OttoWinter
 | 
				
			||||||
 | 
					esphome/components/fingerprint_grow/* @OnFreund @loongyh
 | 
				
			||||||
 | 
					esphome/components/globals/* @esphome/core
 | 
				
			||||||
 | 
					esphome/components/gpio/* @esphome/core
 | 
				
			||||||
 | 
					esphome/components/gps/* @coogle
 | 
				
			||||||
 | 
					esphome/components/homeassistant/* @OttoWinter
 | 
				
			||||||
 | 
					esphome/components/i2c/* @esphome/core
 | 
				
			||||||
 | 
					esphome/components/improv/* @jesserockz
 | 
				
			||||||
 | 
					esphome/components/inkbird_ibsth1_mini/* @fkirill
 | 
				
			||||||
 | 
					esphome/components/inkplate6/* @jesserockz
 | 
				
			||||||
 | 
					esphome/components/integration/* @OttoWinter
 | 
				
			||||||
 | 
					esphome/components/interval/* @esphome/core
 | 
				
			||||||
 | 
					esphome/components/json/* @OttoWinter
 | 
				
			||||||
 | 
					esphome/components/ledc/* @OttoWinter
 | 
				
			||||||
 | 
					esphome/components/light/* @esphome/core
 | 
				
			||||||
 | 
					esphome/components/logger/* @esphome/core
 | 
				
			||||||
 | 
					esphome/components/max7219digit/* @rspaargaren
 | 
				
			||||||
 | 
					esphome/components/mcp23008/* @jesserockz
 | 
				
			||||||
 | 
					esphome/components/mcp23017/* @jesserockz
 | 
				
			||||||
 | 
					esphome/components/mcp23s08/* @SenexCrenshaw @jesserockz
 | 
				
			||||||
 | 
					esphome/components/mcp23s17/* @SenexCrenshaw @jesserockz
 | 
				
			||||||
 | 
					esphome/components/mcp23x08_base/* @jesserockz
 | 
				
			||||||
 | 
					esphome/components/mcp23x17_base/* @jesserockz
 | 
				
			||||||
 | 
					esphome/components/mcp23xxx_base/* @jesserockz
 | 
				
			||||||
 | 
					esphome/components/mcp2515/* @danielschramm @mvturnho
 | 
				
			||||||
 | 
					esphome/components/mcp9808/* @k7hpn
 | 
				
			||||||
 | 
					esphome/components/midea_ac/* @dudanov
 | 
				
			||||||
 | 
					esphome/components/midea_dongle/* @dudanov
 | 
				
			||||||
 | 
					esphome/components/mitsubishi/* @RubyBailey
 | 
				
			||||||
 | 
					esphome/components/network/* @esphome/core
 | 
				
			||||||
 | 
					esphome/components/nfc/* @jesserockz
 | 
				
			||||||
 | 
					esphome/components/ota/* @esphome/core
 | 
				
			||||||
 | 
					esphome/components/output/* @esphome/core
 | 
				
			||||||
 | 
					esphome/components/pid/* @OttoWinter
 | 
				
			||||||
 | 
					esphome/components/pn532/* @OttoWinter @jesserockz
 | 
				
			||||||
 | 
					esphome/components/pn532_i2c/* @OttoWinter @jesserockz
 | 
				
			||||||
 | 
					esphome/components/pn532_spi/* @OttoWinter @jesserockz
 | 
				
			||||||
 | 
					esphome/components/power_supply/* @esphome/core
 | 
				
			||||||
 | 
					esphome/components/pulse_meter/* @stevebaxter
 | 
				
			||||||
 | 
					esphome/components/rc522/* @glmnet
 | 
				
			||||||
 | 
					esphome/components/rc522_i2c/* @glmnet
 | 
				
			||||||
 | 
					esphome/components/rc522_spi/* @glmnet
 | 
				
			||||||
 | 
					esphome/components/restart/* @esphome/core
 | 
				
			||||||
 | 
					esphome/components/rf_bridge/* @jesserockz
 | 
				
			||||||
 | 
					esphome/components/rtttl/* @glmnet
 | 
				
			||||||
 | 
					esphome/components/script/* @esphome/core
 | 
				
			||||||
 | 
					esphome/components/sdm_meter/* @jesserockz @polyfaces
 | 
				
			||||||
 | 
					esphome/components/sensor/* @esphome/core
 | 
				
			||||||
 | 
					esphome/components/sgp40/* @SenexCrenshaw
 | 
				
			||||||
 | 
					esphome/components/sht4x/* @sjtrny
 | 
				
			||||||
 | 
					esphome/components/shutdown/* @esphome/core
 | 
				
			||||||
 | 
					esphome/components/sim800l/* @glmnet
 | 
				
			||||||
 | 
					esphome/components/sm2135/* @BoukeHaarsma23
 | 
				
			||||||
 | 
					esphome/components/spi/* @esphome/core
 | 
				
			||||||
 | 
					esphome/components/ssd1322_base/* @kbx81
 | 
				
			||||||
 | 
					esphome/components/ssd1322_spi/* @kbx81
 | 
				
			||||||
 | 
					esphome/components/ssd1325_base/* @kbx81
 | 
				
			||||||
 | 
					esphome/components/ssd1325_spi/* @kbx81
 | 
				
			||||||
 | 
					esphome/components/ssd1327_base/* @kbx81
 | 
				
			||||||
 | 
					esphome/components/ssd1327_i2c/* @kbx81
 | 
				
			||||||
 | 
					esphome/components/ssd1327_spi/* @kbx81
 | 
				
			||||||
 | 
					esphome/components/ssd1331_base/* @kbx81
 | 
				
			||||||
 | 
					esphome/components/ssd1331_spi/* @kbx81
 | 
				
			||||||
 | 
					esphome/components/ssd1351_base/* @kbx81
 | 
				
			||||||
 | 
					esphome/components/ssd1351_spi/* @kbx81
 | 
				
			||||||
 | 
					esphome/components/st7735/* @SenexCrenshaw
 | 
				
			||||||
 | 
					esphome/components/st7789v/* @kbx81
 | 
				
			||||||
 | 
					esphome/components/substitutions/* @esphome/core
 | 
				
			||||||
 | 
					esphome/components/sun/* @OttoWinter
 | 
				
			||||||
 | 
					esphome/components/switch/* @esphome/core
 | 
				
			||||||
 | 
					esphome/components/tca9548a/* @andreashergert1984
 | 
				
			||||||
 | 
					esphome/components/tcl112/* @glmnet
 | 
				
			||||||
 | 
					esphome/components/teleinfo/* @0hax
 | 
				
			||||||
 | 
					esphome/components/thermostat/* @kbx81
 | 
				
			||||||
 | 
					esphome/components/time/* @OttoWinter
 | 
				
			||||||
 | 
					esphome/components/tm1637/* @glmnet
 | 
				
			||||||
 | 
					esphome/components/tmp102/* @timsavage
 | 
				
			||||||
 | 
					esphome/components/tof10120/* @wstrzalka
 | 
				
			||||||
 | 
					esphome/components/tuya/binary_sensor/* @jesserockz
 | 
				
			||||||
 | 
					esphome/components/tuya/climate/* @jesserockz
 | 
				
			||||||
 | 
					esphome/components/tuya/sensor/* @jesserockz
 | 
				
			||||||
 | 
					esphome/components/tuya/switch/* @jesserockz
 | 
				
			||||||
 | 
					esphome/components/uart/* @esphome/core
 | 
				
			||||||
 | 
					esphome/components/ultrasonic/* @OttoWinter
 | 
				
			||||||
 | 
					esphome/components/version/* @esphome/core
 | 
				
			||||||
 | 
					esphome/components/web_server_base/* @OttoWinter
 | 
				
			||||||
 | 
					esphome/components/whirlpool/* @glmnet
 | 
				
			||||||
 | 
					esphome/components/xiaomi_lywsd03mmc/* @ahpohl
 | 
				
			||||||
 | 
					esphome/components/xiaomi_mhoc401/* @vevsvevs
 | 
				
			||||||
 | 
					esphome/components/xpt2046/* @numo68
 | 
				
			||||||
@@ -8,19 +8,19 @@ In the interest of fostering an open and welcoming environment, we as contributo
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
Examples of behavior that contributes to creating a positive environment include:
 | 
					Examples of behavior that contributes to creating a positive environment include:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* Using welcoming and inclusive language
 | 
					- Using welcoming and inclusive language
 | 
				
			||||||
* Being respectful of differing viewpoints and experiences
 | 
					- Being respectful of differing viewpoints and experiences
 | 
				
			||||||
* Gracefully accepting constructive criticism
 | 
					- Gracefully accepting constructive criticism
 | 
				
			||||||
* Focusing on what is best for the community
 | 
					- Focusing on what is best for the community
 | 
				
			||||||
* Showing empathy towards other community members
 | 
					- Showing empathy towards other community members
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Examples of unacceptable behavior by participants include:
 | 
					Examples of unacceptable behavior by participants include:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* The use of sexualized language or imagery and unwelcome sexual attention or advances
 | 
					- The use of sexualized language or imagery and unwelcome sexual attention or advances
 | 
				
			||||||
* Trolling, insulting/derogatory comments, and personal or political attacks
 | 
					- Trolling, insulting/derogatory comments, and personal or political attacks
 | 
				
			||||||
* Public or private harassment
 | 
					- Public or private harassment
 | 
				
			||||||
* Publishing others' private information, such as a physical or electronic address, without explicit permission
 | 
					- Publishing others' private information, such as a physical or electronic address, without explicit permission
 | 
				
			||||||
* Other conduct which could reasonably be considered inappropriate in a professional setting
 | 
					- Other conduct which could reasonably be considered inappropriate in a professional setting
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Our Responsibilities
 | 
					## Our Responsibilities
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -34,7 +34,7 @@ This Code of Conduct applies both within project spaces and in public spaces whe
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
## Enforcement
 | 
					## Enforcement
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at contact@otto-winter.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
 | 
					Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at esphome@nabucasa.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
 | 
					Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,6 @@
 | 
				
			|||||||
include LICENSE
 | 
					include LICENSE
 | 
				
			||||||
include README.md
 | 
					include README.md
 | 
				
			||||||
 | 
					include requirements.txt
 | 
				
			||||||
include esphome/dashboard/templates/*.html
 | 
					include esphome/dashboard/templates/*.html
 | 
				
			||||||
recursive-include esphome/dashboard/static *.ico *.js *.css *.woff* LICENSE
 | 
					recursive-include esphome/dashboard/static *.ico *.js *.css *.woff* LICENSE
 | 
				
			||||||
recursive-include esphome *.cpp *.h *.tcc
 | 
					recursive-include esphome *.cpp *.h *.tcc
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,12 +1,30 @@
 | 
				
			|||||||
ARG BUILD_FROM=esphome/esphome-base-amd64:2.0.1
 | 
					ARG BUILD_FROM=esphome/esphome-base-amd64:3.4.0
 | 
				
			||||||
FROM ${BUILD_FROM}
 | 
					FROM ${BUILD_FROM}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# First install requirements to leverage caching when requirements don't change
 | 
				
			||||||
 | 
					COPY requirements.txt requirements_optional.txt docker/platformio_install_deps.py platformio.ini /
 | 
				
			||||||
 | 
					RUN \
 | 
				
			||||||
 | 
					    pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt \
 | 
				
			||||||
 | 
					    && /platformio_install_deps.py /platformio.ini
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Then copy esphome and install
 | 
				
			||||||
COPY . .
 | 
					COPY . .
 | 
				
			||||||
RUN pip3 install --no-cache-dir -e .
 | 
					RUN pip3 install --no-cache-dir -e .
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ENV USERNAME=""
 | 
					# Settings for dashboard
 | 
				
			||||||
ENV PASSWORD=""
 | 
					ENV USERNAME="" PASSWORD=""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Expose the dashboard to Docker
 | 
				
			||||||
 | 
					EXPOSE 6052
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Run healthcheck (heartbeat)
 | 
				
			||||||
 | 
					HEALTHCHECK --interval=30s --timeout=30s \
 | 
				
			||||||
 | 
					  CMD curl --fail http://localhost:6052 || exit 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# The directory the user should mount their configuration files to
 | 
				
			||||||
WORKDIR /config
 | 
					WORKDIR /config
 | 
				
			||||||
 | 
					# Set entrypoint to esphome so that the user doesn't have to type 'esphome'
 | 
				
			||||||
 | 
					# in every docker command twice
 | 
				
			||||||
ENTRYPOINT ["esphome"]
 | 
					ENTRYPOINT ["esphome"]
 | 
				
			||||||
CMD ["/config", "dashboard"]
 | 
					# When no arguments given, start the dashboard in the workdir
 | 
				
			||||||
 | 
					CMD ["dashboard", "/config"]
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										13
									
								
								docker/Dockerfile.dev
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								docker/Dockerfile.dev
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
				
			|||||||
 | 
					FROM esphome/esphome-base-amd64:3.4.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					COPY . .
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RUN apt-get update \
 | 
				
			||||||
 | 
					    && apt-get install -y --no-install-recommends \
 | 
				
			||||||
 | 
					        python3-wheel \
 | 
				
			||||||
 | 
					        net-tools \
 | 
				
			||||||
 | 
					    && apt-get clean \
 | 
				
			||||||
 | 
					    && rm -rf /var/lib/apt/lists/*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					WORKDIR /workspaces
 | 
				
			||||||
 | 
					ENV SHELL /bin/bash
 | 
				
			||||||
@@ -1,11 +1,17 @@
 | 
				
			|||||||
ARG BUILD_FROM
 | 
					ARG BUILD_FROM
 | 
				
			||||||
FROM ${BUILD_FROM}
 | 
					FROM ${BUILD_FROM}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# First install requirements to leverage caching when requirements don't change
 | 
				
			||||||
 | 
					COPY requirements.txt requirements_optional.txt docker/platformio_install_deps.py platformio.ini /
 | 
				
			||||||
 | 
					RUN \
 | 
				
			||||||
 | 
					    pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt \
 | 
				
			||||||
 | 
					    && /platformio_install_deps.py /platformio.ini
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Copy root filesystem
 | 
					# Copy root filesystem
 | 
				
			||||||
COPY docker/rootfs/ /
 | 
					COPY docker/rootfs/ /
 | 
				
			||||||
COPY setup.py setup.cfg MANIFEST.in /opt/esphome/
 | 
					 | 
				
			||||||
COPY esphome /opt/esphome/esphome
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Then copy esphome and install
 | 
				
			||||||
 | 
					COPY . /opt/esphome/
 | 
				
			||||||
RUN pip3 install --no-cache-dir -e /opt/esphome
 | 
					RUN pip3 install --no-cache-dir -e /opt/esphome
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Build arguments
 | 
					# Build arguments
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,20 +1,9 @@
 | 
				
			|||||||
FROM esphome/esphome-base-amd64:2.0.1
 | 
					FROM esphome/esphome-lint-base:3.4.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					COPY requirements.txt requirements_optional.txt requirements_test.txt docker/platformio_install_deps.py  platformio.ini /
 | 
				
			||||||
RUN \
 | 
					RUN \
 | 
				
			||||||
    apt-get update \
 | 
					    pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt -r /requirements_test.txt \
 | 
				
			||||||
    && apt-get install -y --no-install-recommends \
 | 
					    && /platformio_install_deps.py /platformio.ini
 | 
				
			||||||
        clang-format-7 \
 | 
					 | 
				
			||||||
        clang-tidy-7 \
 | 
					 | 
				
			||||||
        patch \
 | 
					 | 
				
			||||||
    && rm -rf \
 | 
					 | 
				
			||||||
        /tmp/* \
 | 
					 | 
				
			||||||
        /var/{cache,log}/* \
 | 
					 | 
				
			||||||
        /var/lib/apt/lists/*
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
COPY requirements_test.txt /requirements_test.txt
 | 
					 | 
				
			||||||
RUN pip3 install --no-cache-dir wheel && pip3 install --no-cache-dir -r /requirements_test.txt
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
RUN ln -s /usr/bin/pip3 /usr/bin/pip && ln -f -s /usr/bin/python3 /usr/bin/python
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
VOLUME ["/esphome"]
 | 
					VOLUME ["/esphome"]
 | 
				
			||||||
WORKDIR /esphome
 | 
					WORKDIR /esphome
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										20
									
								
								docker/platformio_install_deps.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										20
									
								
								docker/platformio_install_deps.py
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					#!/usr/bin/env python3
 | 
				
			||||||
 | 
					# This script is used in the docker containers to preinstall
 | 
				
			||||||
 | 
					# all platformio libraries in the global storage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import configparser
 | 
				
			||||||
 | 
					import re
 | 
				
			||||||
 | 
					import subprocess
 | 
				
			||||||
 | 
					import sys
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					config = configparser.ConfigParser()
 | 
				
			||||||
 | 
					config.read(sys.argv[1])
 | 
				
			||||||
 | 
					libs = []
 | 
				
			||||||
 | 
					for line in config['common']['lib_deps'].splitlines():
 | 
				
			||||||
 | 
					    # Format: '1655@1.0.2  ; TinyGPSPlus (has name conflict)' (includes comment)
 | 
				
			||||||
 | 
					    m = re.search(r'([a-zA-Z0-9-_/]+@[0-9\.]+)', line)
 | 
				
			||||||
 | 
					    if m is None:
 | 
				
			||||||
 | 
					        continue
 | 
				
			||||||
 | 
					    libs.append(m.group(1))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					subprocess.check_call(['platformio', 'lib', '-g', 'install', *libs])
 | 
				
			||||||
							
								
								
									
										10
									
								
								docker/rootfs/etc/cont-init.d/30-esphome.sh
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										10
									
								
								docker/rootfs/etc/cont-init.d/30-esphome.sh
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							@@ -8,7 +8,15 @@ declare esphome_version
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
if bashio::config.has_value 'esphome_version'; then
 | 
					if bashio::config.has_value 'esphome_version'; then
 | 
				
			||||||
    esphome_version=$(bashio::config 'esphome_version')
 | 
					    esphome_version=$(bashio::config 'esphome_version')
 | 
				
			||||||
    full_url="https://github.com/esphome/esphome/archive/${esphome_version}.zip"
 | 
					    if [[ $esphome_version == *":"* ]]; then
 | 
				
			||||||
 | 
					      IFS=':' read -r -a array <<< "$esphome_version"
 | 
				
			||||||
 | 
					      username=${array[0]}
 | 
				
			||||||
 | 
					      ref=${array[1]}
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					      username="esphome"
 | 
				
			||||||
 | 
					      ref=$esphome_version
 | 
				
			||||||
 | 
					    fi
 | 
				
			||||||
 | 
					    full_url="https://github.com/${username}/esphome/archive/${ref}.zip"
 | 
				
			||||||
    bashio::log.info "Installing esphome version '${esphome_version}' (${full_url})..."
 | 
					    bashio::log.info "Installing esphome version '${esphome_version}' (${full_url})..."
 | 
				
			||||||
    pip3 install -U --no-cache-dir "${full_url}" \
 | 
					    pip3 install -U --no-cache-dir "${full_url}" \
 | 
				
			||||||
      || bashio::exit.nok "Failed installing esphome pinned version."
 | 
					      || bashio::exit.nok "Failed installing esphome pinned version."
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										0
									
								
								docker/rootfs/etc/cont-init.d/40-migrate.sh
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										0
									
								
								docker/rootfs/etc/cont-init.d/40-migrate.sh
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
								
								
									
										0
									
								
								docker/rootfs/etc/nginx/nginx.conf
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										0
									
								
								docker/rootfs/etc/nginx/nginx.conf
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							@@ -23,4 +23,4 @@ if bashio::config.has_value 'relative_url'; then
 | 
				
			|||||||
fi
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bashio::log.info "Starting ESPHome dashboard..."
 | 
					bashio::log.info "Starting ESPHome dashboard..."
 | 
				
			||||||
exec esphome /config/esphome dashboard --socket /var/run/esphome.sock --hassio
 | 
					exec esphome dashboard /config/esphome --socket /var/run/esphome.sock --hassio
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,3 @@
 | 
				
			|||||||
from __future__ import print_function
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import argparse
 | 
					import argparse
 | 
				
			||||||
import functools
 | 
					import functools
 | 
				
			||||||
import logging
 | 
					import logging
 | 
				
			||||||
@@ -10,44 +8,47 @@ from datetime import datetime
 | 
				
			|||||||
from esphome import const, writer, yaml_util
 | 
					from esphome import const, writer, yaml_util
 | 
				
			||||||
import esphome.codegen as cg
 | 
					import esphome.codegen as cg
 | 
				
			||||||
from esphome.config import iter_components, read_config, strip_default_ids
 | 
					from esphome.config import iter_components, read_config, strip_default_ids
 | 
				
			||||||
from esphome.const import CONF_BAUD_RATE, CONF_BROKER, CONF_LOGGER, CONF_OTA, \
 | 
					from esphome.const import (
 | 
				
			||||||
    CONF_PASSWORD, CONF_PORT, CONF_ESPHOME, CONF_PLATFORMIO_OPTIONS
 | 
					    CONF_BAUD_RATE,
 | 
				
			||||||
from esphome.core import CORE, EsphomeError, coroutine, coroutine_with_priority
 | 
					    CONF_BROKER,
 | 
				
			||||||
from esphome.helpers import color, indent
 | 
					    CONF_LOGGER,
 | 
				
			||||||
from esphome.py_compat import IS_PY2, safe_input, IS_PY3
 | 
					    CONF_OTA,
 | 
				
			||||||
from esphome.util import run_external_command, run_external_process, safe_print, list_yaml_files
 | 
					    CONF_PASSWORD,
 | 
				
			||||||
 | 
					    CONF_PORT,
 | 
				
			||||||
 | 
					    CONF_ESPHOME,
 | 
				
			||||||
 | 
					    CONF_PLATFORMIO_OPTIONS,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					from esphome.core import CORE, EsphomeError, coroutine
 | 
				
			||||||
 | 
					from esphome.helpers import indent
 | 
				
			||||||
 | 
					from esphome.util import (
 | 
				
			||||||
 | 
					    run_external_command,
 | 
				
			||||||
 | 
					    run_external_process,
 | 
				
			||||||
 | 
					    safe_print,
 | 
				
			||||||
 | 
					    list_yaml_files,
 | 
				
			||||||
 | 
					    get_serial_ports,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					from esphome.log import color, setup_log, Fore
 | 
				
			||||||
 | 
					
 | 
				
			||||||
_LOGGER = logging.getLogger(__name__)
 | 
					_LOGGER = logging.getLogger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def get_serial_ports():
 | 
					 | 
				
			||||||
    # from https://github.com/pyserial/pyserial/blob/master/serial/tools/list_ports.py
 | 
					 | 
				
			||||||
    from serial.tools.list_ports import comports
 | 
					 | 
				
			||||||
    result = []
 | 
					 | 
				
			||||||
    for port, desc, info in comports(include_links=True):
 | 
					 | 
				
			||||||
        if not port:
 | 
					 | 
				
			||||||
            continue
 | 
					 | 
				
			||||||
        if "VID:PID" in info:
 | 
					 | 
				
			||||||
            result.append((port, desc))
 | 
					 | 
				
			||||||
    result.sort(key=lambda x: x[0])
 | 
					 | 
				
			||||||
    return result
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def choose_prompt(options):
 | 
					def choose_prompt(options):
 | 
				
			||||||
    if not options:
 | 
					    if not options:
 | 
				
			||||||
        raise EsphomeError("Found no valid options for upload/logging, please make sure relevant "
 | 
					        raise EsphomeError(
 | 
				
			||||||
                           "sections (ota, mqtt, ...) are in your configuration and/or the device "
 | 
					            "Found no valid options for upload/logging, please make sure relevant "
 | 
				
			||||||
                           "is plugged in.")
 | 
					            "sections (ota, api, mqtt, ...) are in your configuration and/or the "
 | 
				
			||||||
 | 
					            "device is plugged in."
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if len(options) == 1:
 | 
					    if len(options) == 1:
 | 
				
			||||||
        return options[0][1]
 | 
					        return options[0][1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    safe_print(u"Found multiple options, please choose one:")
 | 
					    safe_print("Found multiple options, please choose one:")
 | 
				
			||||||
    for i, (desc, _) in enumerate(options):
 | 
					    for i, (desc, _) in enumerate(options):
 | 
				
			||||||
        safe_print(u"  [{}] {}".format(i + 1, desc))
 | 
					        safe_print(f"  [{i+1}] {desc}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    while True:
 | 
					    while True:
 | 
				
			||||||
        opt = safe_input('(number): ')
 | 
					        opt = input("(number): ")
 | 
				
			||||||
        if opt in options:
 | 
					        if opt in options:
 | 
				
			||||||
            opt = options.index(opt)
 | 
					            opt = options.index(opt)
 | 
				
			||||||
            break
 | 
					            break
 | 
				
			||||||
@@ -57,22 +58,22 @@ def choose_prompt(options):
 | 
				
			|||||||
                raise ValueError
 | 
					                raise ValueError
 | 
				
			||||||
            break
 | 
					            break
 | 
				
			||||||
        except ValueError:
 | 
					        except ValueError:
 | 
				
			||||||
            safe_print(color('red', u"Invalid option: '{}'".format(opt)))
 | 
					            safe_print(color(Fore.RED, f"Invalid option: '{opt}'"))
 | 
				
			||||||
    return options[opt - 1][1]
 | 
					    return options[opt - 1][1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def choose_upload_log_host(default, check_default, show_ota, show_mqtt, show_api):
 | 
					def choose_upload_log_host(default, check_default, show_ota, show_mqtt, show_api):
 | 
				
			||||||
    options = []
 | 
					    options = []
 | 
				
			||||||
    for res, desc in get_serial_ports():
 | 
					    for port in get_serial_ports():
 | 
				
			||||||
        options.append((u"{} ({})".format(res, desc), res))
 | 
					        options.append((f"{port.path} ({port.description})", port.path))
 | 
				
			||||||
    if (show_ota and 'ota' in CORE.config) or (show_api and 'api' in CORE.config):
 | 
					    if (show_ota and "ota" in CORE.config) or (show_api and "api" in CORE.config):
 | 
				
			||||||
        options.append((u"Over The Air ({})".format(CORE.address), CORE.address))
 | 
					        options.append((f"Over The Air ({CORE.address})", CORE.address))
 | 
				
			||||||
        if default == 'OTA':
 | 
					        if default == "OTA":
 | 
				
			||||||
            return CORE.address
 | 
					            return CORE.address
 | 
				
			||||||
    if show_mqtt and 'mqtt' in CORE.config:
 | 
					    if show_mqtt and "mqtt" in CORE.config:
 | 
				
			||||||
        options.append((u"MQTT ({})".format(CORE.config['mqtt'][CONF_BROKER]), 'MQTT'))
 | 
					        options.append(("MQTT ({})".format(CORE.config["mqtt"][CONF_BROKER]), "MQTT"))
 | 
				
			||||||
        if default == 'OTA':
 | 
					        if default == "OTA":
 | 
				
			||||||
            return 'MQTT'
 | 
					            return "MQTT"
 | 
				
			||||||
    if default is not None:
 | 
					    if default is not None:
 | 
				
			||||||
        return default
 | 
					        return default
 | 
				
			||||||
    if check_default is not None and check_default in [opt[1] for opt in options]:
 | 
					    if check_default is not None and check_default in [opt[1] for opt in options]:
 | 
				
			||||||
@@ -81,11 +82,11 @@ def choose_upload_log_host(default, check_default, show_ota, show_mqtt, show_api
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def get_port_type(port):
 | 
					def get_port_type(port):
 | 
				
			||||||
    if port.startswith('/') or port.startswith('COM'):
 | 
					    if port.startswith("/") or port.startswith("COM"):
 | 
				
			||||||
        return 'SERIAL'
 | 
					        return "SERIAL"
 | 
				
			||||||
    if port == 'MQTT':
 | 
					    if port == "MQTT":
 | 
				
			||||||
        return 'MQTT'
 | 
					        return "MQTT"
 | 
				
			||||||
    return 'NETWORK'
 | 
					    return "NETWORK"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def run_miniterm(config, port):
 | 
					def run_miniterm(config, port):
 | 
				
			||||||
@@ -95,7 +96,7 @@ def run_miniterm(config, port):
 | 
				
			|||||||
    if CONF_LOGGER not in config:
 | 
					    if CONF_LOGGER not in config:
 | 
				
			||||||
        _LOGGER.info("Logger is not enabled. Not starting UART logs.")
 | 
					        _LOGGER.info("Logger is not enabled. Not starting UART logs.")
 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
    baud_rate = config['logger'][CONF_BAUD_RATE]
 | 
					    baud_rate = config["logger"][CONF_BAUD_RATE]
 | 
				
			||||||
    if baud_rate == 0:
 | 
					    if baud_rate == 0:
 | 
				
			||||||
        _LOGGER.info("UART logging is disabled (baud_rate=0). Not starting UART logs.")
 | 
					        _LOGGER.info("UART logging is disabled (baud_rate=0). Not starting UART logs.")
 | 
				
			||||||
    _LOGGER.info("Starting log output from %s with baud rate %s", port, baud_rate)
 | 
					    _LOGGER.info("Starting log output from %s with baud rate %s", port, baud_rate)
 | 
				
			||||||
@@ -108,38 +109,43 @@ def run_miniterm(config, port):
 | 
				
			|||||||
            except serial.SerialException:
 | 
					            except serial.SerialException:
 | 
				
			||||||
                _LOGGER.error("Serial port closed!")
 | 
					                _LOGGER.error("Serial port closed!")
 | 
				
			||||||
                return
 | 
					                return
 | 
				
			||||||
            if IS_PY2:
 | 
					            line = (
 | 
				
			||||||
                line = raw.replace('\r', '').replace('\n', '')
 | 
					                raw.replace(b"\r", b"")
 | 
				
			||||||
            else:
 | 
					                .replace(b"\n", b"")
 | 
				
			||||||
                line = raw.replace(b'\r', b'').replace(b'\n', b'').decode('utf8',
 | 
					                .decode("utf8", "backslashreplace")
 | 
				
			||||||
                                                                          'backslashreplace')
 | 
					            )
 | 
				
			||||||
            time = datetime.now().time().strftime('[%H:%M:%S]')
 | 
					            time = datetime.now().time().strftime("[%H:%M:%S]")
 | 
				
			||||||
            message = time + line
 | 
					            message = time + line
 | 
				
			||||||
            safe_print(message)
 | 
					            safe_print(message)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            backtrace_state = platformio_api.process_stacktrace(
 | 
					            backtrace_state = platformio_api.process_stacktrace(
 | 
				
			||||||
                config, line, backtrace_state=backtrace_state)
 | 
					                config, line, backtrace_state=backtrace_state
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def wrap_to_code(name, comp):
 | 
					def wrap_to_code(name, comp):
 | 
				
			||||||
    coro = coroutine(comp.to_code)
 | 
					    coro = coroutine(comp.to_code)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @functools.wraps(comp.to_code)
 | 
					    @functools.wraps(comp.to_code)
 | 
				
			||||||
    @coroutine_with_priority(coro.priority)
 | 
					    async def wrapped(conf):
 | 
				
			||||||
    def wrapped(conf):
 | 
					        cg.add(cg.LineComment(f"{name}:"))
 | 
				
			||||||
        cg.add(cg.LineComment(u"{}:".format(name)))
 | 
					 | 
				
			||||||
        if comp.config_schema is not None:
 | 
					        if comp.config_schema is not None:
 | 
				
			||||||
            conf_str = yaml_util.dump(conf)
 | 
					            conf_str = yaml_util.dump(conf)
 | 
				
			||||||
            if IS_PY2:
 | 
					            conf_str = conf_str.replace("//", "")
 | 
				
			||||||
                conf_str = conf_str.decode('utf-8')
 | 
					 | 
				
			||||||
            conf_str = conf_str.replace('//', '')
 | 
					 | 
				
			||||||
            cg.add(cg.LineComment(indent(conf_str)))
 | 
					            cg.add(cg.LineComment(indent(conf_str)))
 | 
				
			||||||
        yield coro(conf)
 | 
					        await coro(conf)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if hasattr(coro, "priority"):
 | 
				
			||||||
 | 
					        wrapped.priority = coro.priority
 | 
				
			||||||
    return wrapped
 | 
					    return wrapped
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def write_cpp(config):
 | 
					def write_cpp(config):
 | 
				
			||||||
 | 
					    generate_cpp_contents(config)
 | 
				
			||||||
 | 
					    return write_cpp_file()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def generate_cpp_contents(config):
 | 
				
			||||||
    _LOGGER.info("Generating C++ source...")
 | 
					    _LOGGER.info("Generating C++ source...")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for name, component, conf in iter_components(CORE.config):
 | 
					    for name, component, conf in iter_components(CORE.config):
 | 
				
			||||||
@@ -149,6 +155,8 @@ def write_cpp(config):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    CORE.flush_tasks()
 | 
					    CORE.flush_tasks()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def write_cpp_file():
 | 
				
			||||||
    writer.write_platformio_project()
 | 
					    writer.write_platformio_project()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    code_s = indent(CORE.cpp_main_section)
 | 
					    code_s = indent(CORE.cpp_main_section)
 | 
				
			||||||
@@ -165,15 +173,31 @@ def compile_program(args, config):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
def upload_using_esptool(config, port):
 | 
					def upload_using_esptool(config, port):
 | 
				
			||||||
    path = CORE.firmware_bin
 | 
					    path = CORE.firmware_bin
 | 
				
			||||||
    first_baudrate = config[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS].get('upload_speed', 460800)
 | 
					    first_baudrate = config[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS].get(
 | 
				
			||||||
 | 
					        "upload_speed", 460800
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def run_esptool(baud_rate):
 | 
					    def run_esptool(baud_rate):
 | 
				
			||||||
        cmd = ['esptool.py', '--before', 'default_reset', '--after', 'hard_reset',
 | 
					        cmd = [
 | 
				
			||||||
               '--baud', str(baud_rate),
 | 
					            "esptool.py",
 | 
				
			||||||
               '--chip', 'esp8266', '--port', port, 'write_flash', '0x0', path]
 | 
					            "--before",
 | 
				
			||||||
 | 
					            "default_reset",
 | 
				
			||||||
 | 
					            "--after",
 | 
				
			||||||
 | 
					            "hard_reset",
 | 
				
			||||||
 | 
					            "--baud",
 | 
				
			||||||
 | 
					            str(baud_rate),
 | 
				
			||||||
 | 
					            "--chip",
 | 
				
			||||||
 | 
					            "esp8266",
 | 
				
			||||||
 | 
					            "--port",
 | 
				
			||||||
 | 
					            port,
 | 
				
			||||||
 | 
					            "write_flash",
 | 
				
			||||||
 | 
					            "0x0",
 | 
				
			||||||
 | 
					            path,
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if os.environ.get('ESPHOME_USE_SUBPROCESS') is None:
 | 
					        if os.environ.get("ESPHOME_USE_SUBPROCESS") is None:
 | 
				
			||||||
            import esptool
 | 
					            import esptool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # pylint: disable=protected-access
 | 
					            # pylint: disable=protected-access
 | 
				
			||||||
            return run_external_command(esptool._main, *cmd)
 | 
					            return run_external_command(esptool._main, *cmd)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -183,14 +207,16 @@ def upload_using_esptool(config, port):
 | 
				
			|||||||
    if rc == 0 or first_baudrate == 115200:
 | 
					    if rc == 0 or first_baudrate == 115200:
 | 
				
			||||||
        return rc
 | 
					        return rc
 | 
				
			||||||
    # Try with 115200 baud rate, with some serial chips the faster baud rates do not work well
 | 
					    # Try with 115200 baud rate, with some serial chips the faster baud rates do not work well
 | 
				
			||||||
    _LOGGER.info("Upload with baud rate %s failed. Trying again with baud rate 115200.",
 | 
					    _LOGGER.info(
 | 
				
			||||||
                 first_baudrate)
 | 
					        "Upload with baud rate %s failed. Trying again with baud rate 115200.",
 | 
				
			||||||
 | 
					        first_baudrate,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
    return run_esptool(115200)
 | 
					    return run_esptool(115200)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def upload_program(config, args, host):
 | 
					def upload_program(config, args, host):
 | 
				
			||||||
    # if upload is to a serial port use platformio, otherwise assume ota
 | 
					    # if upload is to a serial port use platformio, otherwise assume ota
 | 
				
			||||||
    if get_port_type(host) == 'SERIAL':
 | 
					    if get_port_type(host) == "SERIAL":
 | 
				
			||||||
        from esphome import platformio_api
 | 
					        from esphome import platformio_api
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if CORE.is_esp8266:
 | 
					        if CORE.is_esp8266:
 | 
				
			||||||
@@ -199,6 +225,12 @@ def upload_program(config, args, host):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    from esphome import espota2
 | 
					    from esphome import espota2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if CONF_OTA not in config:
 | 
				
			||||||
 | 
					        raise EsphomeError(
 | 
				
			||||||
 | 
					            "Cannot upload Over the Air as the config does not include the ota: "
 | 
				
			||||||
 | 
					            "component"
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ota_conf = config[CONF_OTA]
 | 
					    ota_conf = config[CONF_OTA]
 | 
				
			||||||
    remote_port = ota_conf[CONF_PORT]
 | 
					    remote_port = ota_conf[CONF_PORT]
 | 
				
			||||||
    password = ota_conf[CONF_PASSWORD]
 | 
					    password = ota_conf[CONF_PASSWORD]
 | 
				
			||||||
@@ -206,19 +238,21 @@ def upload_program(config, args, host):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def show_logs(config, args, port):
 | 
					def show_logs(config, args, port):
 | 
				
			||||||
    if 'logger' not in config:
 | 
					    if "logger" not in config:
 | 
				
			||||||
        raise EsphomeError("Logger is not configured!")
 | 
					        raise EsphomeError("Logger is not configured!")
 | 
				
			||||||
    if get_port_type(port) == 'SERIAL':
 | 
					    if get_port_type(port) == "SERIAL":
 | 
				
			||||||
        run_miniterm(config, port)
 | 
					        run_miniterm(config, port)
 | 
				
			||||||
        return 0
 | 
					        return 0
 | 
				
			||||||
    if get_port_type(port) == 'NETWORK' and 'api' in config:
 | 
					    if get_port_type(port) == "NETWORK" and "api" in config:
 | 
				
			||||||
        from esphome.api.client import run_logs
 | 
					        from esphome.api.client import run_logs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return run_logs(config, port)
 | 
					        return run_logs(config, port)
 | 
				
			||||||
    if get_port_type(port) == 'MQTT' and 'mqtt' in config:
 | 
					    if get_port_type(port) == "MQTT" and "mqtt" in config:
 | 
				
			||||||
        from esphome import mqtt
 | 
					        from esphome import mqtt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return mqtt.show_logs(config, args.topic, args.username, args.password, args.client_id)
 | 
					        return mqtt.show_logs(
 | 
				
			||||||
 | 
					            config, args.topic, args.username, args.password, args.client_id
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    raise EsphomeError("No remote or local logging method configured (api/mqtt/logger)")
 | 
					    raise EsphomeError("No remote or local logging method configured (api/mqtt/logger)")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -226,46 +260,15 @@ def show_logs(config, args, port):
 | 
				
			|||||||
def clean_mqtt(config, args):
 | 
					def clean_mqtt(config, args):
 | 
				
			||||||
    from esphome import mqtt
 | 
					    from esphome import mqtt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return mqtt.clear_topic(config, args.topic, args.username, args.password, args.client_id)
 | 
					    return mqtt.clear_topic(
 | 
				
			||||||
 | 
					        config, args.topic, args.username, args.password, args.client_id
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
def setup_log(debug=False, quiet=False):
 | 
					 | 
				
			||||||
    if debug:
 | 
					 | 
				
			||||||
        log_level = logging.DEBUG
 | 
					 | 
				
			||||||
        CORE.verbose = True
 | 
					 | 
				
			||||||
    elif quiet:
 | 
					 | 
				
			||||||
        log_level = logging.CRITICAL
 | 
					 | 
				
			||||||
    else:
 | 
					 | 
				
			||||||
        log_level = logging.INFO
 | 
					 | 
				
			||||||
    logging.basicConfig(level=log_level)
 | 
					 | 
				
			||||||
    fmt = "%(levelname)s %(message)s"
 | 
					 | 
				
			||||||
    colorfmt = "%(log_color)s{}%(reset)s".format(fmt)
 | 
					 | 
				
			||||||
    datefmt = '%H:%M:%S'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    logging.getLogger('urllib3').setLevel(logging.WARNING)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    try:
 | 
					 | 
				
			||||||
        from colorlog import ColoredFormatter
 | 
					 | 
				
			||||||
        logging.getLogger().handlers[0].setFormatter(ColoredFormatter(
 | 
					 | 
				
			||||||
            colorfmt,
 | 
					 | 
				
			||||||
            datefmt=datefmt,
 | 
					 | 
				
			||||||
            reset=True,
 | 
					 | 
				
			||||||
            log_colors={
 | 
					 | 
				
			||||||
                'DEBUG': 'cyan',
 | 
					 | 
				
			||||||
                'INFO': 'green',
 | 
					 | 
				
			||||||
                'WARNING': 'yellow',
 | 
					 | 
				
			||||||
                'ERROR': 'red',
 | 
					 | 
				
			||||||
                'CRITICAL': 'red',
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        ))
 | 
					 | 
				
			||||||
    except ImportError:
 | 
					 | 
				
			||||||
        pass
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def command_wizard(args):
 | 
					def command_wizard(args):
 | 
				
			||||||
    from esphome import wizard
 | 
					    from esphome import wizard
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return wizard.wizard(args.configuration[0])
 | 
					    return wizard.wizard(args.configuration)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def command_config(args, config):
 | 
					def command_config(args, config):
 | 
				
			||||||
@@ -279,7 +282,9 @@ def command_config(args, config):
 | 
				
			|||||||
def command_vscode(args):
 | 
					def command_vscode(args):
 | 
				
			||||||
    from esphome import vscode
 | 
					    from esphome import vscode
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    CORE.config_path = args.configuration[0]
 | 
					    logging.disable(logging.INFO)
 | 
				
			||||||
 | 
					    logging.disable(logging.WARNING)
 | 
				
			||||||
 | 
					    CORE.config_path = args.configuration
 | 
				
			||||||
    vscode.read_config(args)
 | 
					    vscode.read_config(args)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -288,28 +293,38 @@ def command_compile(args, config):
 | 
				
			|||||||
    if exit_code != 0:
 | 
					    if exit_code != 0:
 | 
				
			||||||
        return exit_code
 | 
					        return exit_code
 | 
				
			||||||
    if args.only_generate:
 | 
					    if args.only_generate:
 | 
				
			||||||
        _LOGGER.info(u"Successfully generated source code.")
 | 
					        _LOGGER.info("Successfully generated source code.")
 | 
				
			||||||
        return 0
 | 
					        return 0
 | 
				
			||||||
    exit_code = compile_program(args, config)
 | 
					    exit_code = compile_program(args, config)
 | 
				
			||||||
    if exit_code != 0:
 | 
					    if exit_code != 0:
 | 
				
			||||||
        return exit_code
 | 
					        return exit_code
 | 
				
			||||||
    _LOGGER.info(u"Successfully compiled program.")
 | 
					    _LOGGER.info("Successfully compiled program.")
 | 
				
			||||||
    return 0
 | 
					    return 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def command_upload(args, config):
 | 
					def command_upload(args, config):
 | 
				
			||||||
    port = choose_upload_log_host(default=args.upload_port, check_default=None,
 | 
					    port = choose_upload_log_host(
 | 
				
			||||||
                                  show_ota=True, show_mqtt=False, show_api=False)
 | 
					        default=args.device,
 | 
				
			||||||
 | 
					        check_default=None,
 | 
				
			||||||
 | 
					        show_ota=True,
 | 
				
			||||||
 | 
					        show_mqtt=False,
 | 
				
			||||||
 | 
					        show_api=False,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
    exit_code = upload_program(config, args, port)
 | 
					    exit_code = upload_program(config, args, port)
 | 
				
			||||||
    if exit_code != 0:
 | 
					    if exit_code != 0:
 | 
				
			||||||
        return exit_code
 | 
					        return exit_code
 | 
				
			||||||
    _LOGGER.info(u"Successfully uploaded program.")
 | 
					    _LOGGER.info("Successfully uploaded program.")
 | 
				
			||||||
    return 0
 | 
					    return 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def command_logs(args, config):
 | 
					def command_logs(args, config):
 | 
				
			||||||
    port = choose_upload_log_host(default=args.serial_port, check_default=None,
 | 
					    port = choose_upload_log_host(
 | 
				
			||||||
                                  show_ota=False, show_mqtt=True, show_api=True)
 | 
					        default=args.device,
 | 
				
			||||||
 | 
					        check_default=None,
 | 
				
			||||||
 | 
					        show_ota=False,
 | 
				
			||||||
 | 
					        show_mqtt=True,
 | 
				
			||||||
 | 
					        show_api=True,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
    return show_logs(config, args, port)
 | 
					    return show_logs(config, args, port)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -320,17 +335,27 @@ def command_run(args, config):
 | 
				
			|||||||
    exit_code = compile_program(args, config)
 | 
					    exit_code = compile_program(args, config)
 | 
				
			||||||
    if exit_code != 0:
 | 
					    if exit_code != 0:
 | 
				
			||||||
        return exit_code
 | 
					        return exit_code
 | 
				
			||||||
    _LOGGER.info(u"Successfully compiled program.")
 | 
					    _LOGGER.info("Successfully compiled program.")
 | 
				
			||||||
    port = choose_upload_log_host(default=args.upload_port, check_default=None,
 | 
					    port = choose_upload_log_host(
 | 
				
			||||||
                                  show_ota=True, show_mqtt=False, show_api=True)
 | 
					        default=args.device,
 | 
				
			||||||
 | 
					        check_default=None,
 | 
				
			||||||
 | 
					        show_ota=True,
 | 
				
			||||||
 | 
					        show_mqtt=False,
 | 
				
			||||||
 | 
					        show_api=True,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
    exit_code = upload_program(config, args, port)
 | 
					    exit_code = upload_program(config, args, port)
 | 
				
			||||||
    if exit_code != 0:
 | 
					    if exit_code != 0:
 | 
				
			||||||
        return exit_code
 | 
					        return exit_code
 | 
				
			||||||
    _LOGGER.info(u"Successfully uploaded program.")
 | 
					    _LOGGER.info("Successfully uploaded program.")
 | 
				
			||||||
    if args.no_logs:
 | 
					    if args.no_logs:
 | 
				
			||||||
        return 0
 | 
					        return 0
 | 
				
			||||||
    port = choose_upload_log_host(default=args.upload_port, check_default=port,
 | 
					    port = choose_upload_log_host(
 | 
				
			||||||
                                  show_ota=False, show_mqtt=True, show_api=True)
 | 
					        default=args.device,
 | 
				
			||||||
 | 
					        check_default=port,
 | 
				
			||||||
 | 
					        show_ota=False,
 | 
				
			||||||
 | 
					        show_mqtt=True,
 | 
				
			||||||
 | 
					        show_api=True,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
    return show_logs(config, args, port)
 | 
					    return show_logs(config, args, port)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -345,7 +370,7 @@ def command_mqtt_fingerprint(args, config):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def command_version(args):
 | 
					def command_version(args):
 | 
				
			||||||
    safe_print(u"Version: {}".format(const.__version__))
 | 
					    safe_print(f"Version: {const.__version__}")
 | 
				
			||||||
    return 0
 | 
					    return 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -369,145 +394,289 @@ def command_update_all(args):
 | 
				
			|||||||
    import click
 | 
					    import click
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    success = {}
 | 
					    success = {}
 | 
				
			||||||
    files = list_yaml_files(args.configuration[0])
 | 
					    files = list_yaml_files(args.configuration)
 | 
				
			||||||
    twidth = 60
 | 
					    twidth = 60
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def print_bar(middle_text):
 | 
					    def print_bar(middle_text):
 | 
				
			||||||
        middle_text = " {} ".format(middle_text)
 | 
					        middle_text = f" {middle_text} "
 | 
				
			||||||
        width = len(click.unstyle(middle_text))
 | 
					        width = len(click.unstyle(middle_text))
 | 
				
			||||||
        half_line = "=" * ((twidth - width) // 2)
 | 
					        half_line = "=" * ((twidth - width) // 2)
 | 
				
			||||||
        click.echo("%s%s%s" % (half_line, middle_text, half_line))
 | 
					        click.echo(f"{half_line}{middle_text}{half_line}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for f in files:
 | 
					    for f in files:
 | 
				
			||||||
        print("Updating {}".format(color('cyan', f)))
 | 
					        print("Updating {}".format(color(Fore.CYAN, f)))
 | 
				
			||||||
        print('-' * twidth)
 | 
					        print("-" * twidth)
 | 
				
			||||||
        print()
 | 
					        print()
 | 
				
			||||||
        rc = run_external_process('esphome', '--dashboard', f, 'run', '--no-logs', '--upload-port',
 | 
					        rc = run_external_process(
 | 
				
			||||||
                                  'OTA')
 | 
					            "esphome", "--dashboard", "run", f, "--no-logs", "--device", "OTA"
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
        if rc == 0:
 | 
					        if rc == 0:
 | 
				
			||||||
            print_bar("[{}] {}".format(color('bold_green', 'SUCCESS'), f))
 | 
					            print_bar("[{}] {}".format(color(Fore.BOLD_GREEN, "SUCCESS"), f))
 | 
				
			||||||
            success[f] = True
 | 
					            success[f] = True
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            print_bar("[{}] {}".format(color('bold_red', 'ERROR'), f))
 | 
					            print_bar("[{}] {}".format(color(Fore.BOLD_RED, "ERROR"), f))
 | 
				
			||||||
            success[f] = False
 | 
					            success[f] = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        print()
 | 
					        print()
 | 
				
			||||||
        print()
 | 
					        print()
 | 
				
			||||||
        print()
 | 
					        print()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    print_bar('[{}]'.format(color('bold_white', 'SUMMARY')))
 | 
					    print_bar("[{}]".format(color(Fore.BOLD_WHITE, "SUMMARY")))
 | 
				
			||||||
    failed = 0
 | 
					    failed = 0
 | 
				
			||||||
    for f in files:
 | 
					    for f in files:
 | 
				
			||||||
        if success[f]:
 | 
					        if success[f]:
 | 
				
			||||||
            print("  - {}: {}".format(f, color('green', 'SUCCESS')))
 | 
					            print("  - {}: {}".format(f, color(Fore.GREEN, "SUCCESS")))
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            print("  - {}: {}".format(f, color('bold_red', 'FAILED')))
 | 
					            print("  - {}: {}".format(f, color(Fore.BOLD_RED, "FAILED")))
 | 
				
			||||||
            failed += 1
 | 
					            failed += 1
 | 
				
			||||||
    return failed
 | 
					    return failed
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
PRE_CONFIG_ACTIONS = {
 | 
					PRE_CONFIG_ACTIONS = {
 | 
				
			||||||
    'wizard': command_wizard,
 | 
					    "wizard": command_wizard,
 | 
				
			||||||
    'version': command_version,
 | 
					    "version": command_version,
 | 
				
			||||||
    'dashboard': command_dashboard,
 | 
					    "dashboard": command_dashboard,
 | 
				
			||||||
    'vscode': command_vscode,
 | 
					    "vscode": command_vscode,
 | 
				
			||||||
    'update-all': command_update_all,
 | 
					    "update-all": command_update_all,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
POST_CONFIG_ACTIONS = {
 | 
					POST_CONFIG_ACTIONS = {
 | 
				
			||||||
    'config': command_config,
 | 
					    "config": command_config,
 | 
				
			||||||
    'compile': command_compile,
 | 
					    "compile": command_compile,
 | 
				
			||||||
    'upload': command_upload,
 | 
					    "upload": command_upload,
 | 
				
			||||||
    'logs': command_logs,
 | 
					    "logs": command_logs,
 | 
				
			||||||
    'run': command_run,
 | 
					    "run": command_run,
 | 
				
			||||||
    'clean-mqtt': command_clean_mqtt,
 | 
					    "clean-mqtt": command_clean_mqtt,
 | 
				
			||||||
    'mqtt-fingerprint': command_mqtt_fingerprint,
 | 
					    "mqtt-fingerprint": command_mqtt_fingerprint,
 | 
				
			||||||
    'clean': command_clean,
 | 
					    "clean": command_clean,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def parse_args(argv):
 | 
					def parse_args(argv):
 | 
				
			||||||
    parser = argparse.ArgumentParser(description='ESPHome v{}'.format(const.__version__))
 | 
					    options_parser = argparse.ArgumentParser(add_help=False)
 | 
				
			||||||
    parser.add_argument('-v', '--verbose', help="Enable verbose esphome logs.",
 | 
					    options_parser.add_argument(
 | 
				
			||||||
                        action='store_true')
 | 
					        "-v", "--verbose", help="Enable verbose ESPHome logs.", action="store_true"
 | 
				
			||||||
    parser.add_argument('-q', '--quiet', help="Disable all esphome logs.",
 | 
					    )
 | 
				
			||||||
                        action='store_true')
 | 
					    options_parser.add_argument(
 | 
				
			||||||
    parser.add_argument('--dashboard', help=argparse.SUPPRESS, action='store_true')
 | 
					        "-q", "--quiet", help="Disable all ESPHome logs.", action="store_true"
 | 
				
			||||||
    parser.add_argument('configuration', help='Your YAML configuration file.', nargs='*')
 | 
					    )
 | 
				
			||||||
 | 
					    options_parser.add_argument(
 | 
				
			||||||
 | 
					        "--dashboard", help=argparse.SUPPRESS, action="store_true"
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    options_parser.add_argument(
 | 
				
			||||||
 | 
					        "-s",
 | 
				
			||||||
 | 
					        "--substitution",
 | 
				
			||||||
 | 
					        nargs=2,
 | 
				
			||||||
 | 
					        action="append",
 | 
				
			||||||
 | 
					        help="Add a substitution",
 | 
				
			||||||
 | 
					        metavar=("key", "value"),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    subparsers = parser.add_subparsers(help='Commands', dest='command')
 | 
					    # Keep backward compatibility with the old command line format of
 | 
				
			||||||
 | 
					    # esphome <config> <command>.
 | 
				
			||||||
 | 
					    #
 | 
				
			||||||
 | 
					    # Unfortunately this can't be done by adding another configuration argument to the
 | 
				
			||||||
 | 
					    # main config parser, as argparse is greedy when parsing arguments, so in regular
 | 
				
			||||||
 | 
					    # usage it'll eat the command as the configuration argument and error out out
 | 
				
			||||||
 | 
					    # because it can't parse the configuration as a command.
 | 
				
			||||||
 | 
					    #
 | 
				
			||||||
 | 
					    # Instead, construct an ad-hoc parser for the old format that doesn't actually
 | 
				
			||||||
 | 
					    # process the arguments, but parses them enough to let us figure out if the old
 | 
				
			||||||
 | 
					    # format is used. In that case, swap the command and configuration in the arguments
 | 
				
			||||||
 | 
					    # and continue on with the normal parser (after raising a deprecation warning).
 | 
				
			||||||
 | 
					    #
 | 
				
			||||||
 | 
					    # 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")
 | 
				
			||||||
 | 
					    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",
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # on Python 3.9+ we can simply set exit_on_error=False in the constructor
 | 
				
			||||||
 | 
					    def _raise(x):
 | 
				
			||||||
 | 
					        raise argparse.ArgumentError(None, x)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    compat_parser.error = _raise
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        result, unparsed = compat_parser.parse_known_args(argv[1:])
 | 
				
			||||||
 | 
					        last_option = len(argv) - len(unparsed) - 1 - len(result.configuration)
 | 
				
			||||||
 | 
					        argv = argv[0:last_option] + [result.command] + result.configuration + unparsed
 | 
				
			||||||
 | 
					        deprecated_argv_suggestion = argv
 | 
				
			||||||
 | 
					    except argparse.ArgumentError:
 | 
				
			||||||
 | 
					        # This is not an old-style command line, so we don't have to do anything.
 | 
				
			||||||
 | 
					        deprecated_argv_suggestion = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # And continue on with regular parsing
 | 
				
			||||||
 | 
					    parser = argparse.ArgumentParser(
 | 
				
			||||||
 | 
					        description=f"ESPHome v{const.__version__}", parents=[options_parser]
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    parser.set_defaults(deprecated_argv_suggestion=deprecated_argv_suggestion)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mqtt_options = argparse.ArgumentParser(add_help=False)
 | 
				
			||||||
 | 
					    mqtt_options.add_argument("--topic", help="Manually set the MQTT topic.")
 | 
				
			||||||
 | 
					    mqtt_options.add_argument("--username", help="Manually set the MQTT username.")
 | 
				
			||||||
 | 
					    mqtt_options.add_argument("--password", help="Manually set the MQTT password.")
 | 
				
			||||||
 | 
					    mqtt_options.add_argument("--client-id", help="Manually set the MQTT client id.")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    subparsers = parser.add_subparsers(
 | 
				
			||||||
 | 
					        help="Command to run:", dest="command", metavar="command"
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
    subparsers.required = True
 | 
					    subparsers.required = True
 | 
				
			||||||
    subparsers.add_parser('config', help='Validate the configuration and spit it out.')
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    parser_compile = subparsers.add_parser('compile',
 | 
					    parser_config = subparsers.add_parser(
 | 
				
			||||||
                                           help='Read the configuration and compile a program.')
 | 
					        "config", help="Validate the configuration and spit it out."
 | 
				
			||||||
    parser_compile.add_argument('--only-generate',
 | 
					    )
 | 
				
			||||||
                                help="Only generate source code, do not compile.",
 | 
					    parser_config.add_argument(
 | 
				
			||||||
                                action='store_true')
 | 
					        "configuration", help="Your YAML configuration file(s).", nargs="+"
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    parser_upload = subparsers.add_parser('upload', help='Validate the configuration '
 | 
					    parser_compile = subparsers.add_parser(
 | 
				
			||||||
                                                         'and upload the latest binary.')
 | 
					        "compile", help="Read the configuration and compile a program."
 | 
				
			||||||
    parser_upload.add_argument('--upload-port', help="Manually specify the upload port to use. "
 | 
					    )
 | 
				
			||||||
                                                     "For example /dev/cu.SLAB_USBtoUART.")
 | 
					    parser_compile.add_argument(
 | 
				
			||||||
 | 
					        "configuration", help="Your YAML configuration file(s).", nargs="+"
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    parser_compile.add_argument(
 | 
				
			||||||
 | 
					        "--only-generate",
 | 
				
			||||||
 | 
					        help="Only generate source code, do not compile.",
 | 
				
			||||||
 | 
					        action="store_true",
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    parser_logs = subparsers.add_parser('logs', help='Validate the configuration '
 | 
					    parser_upload = subparsers.add_parser(
 | 
				
			||||||
                                                     'and show all MQTT logs.')
 | 
					        "upload", help="Validate the configuration and upload the latest binary."
 | 
				
			||||||
    parser_logs.add_argument('--topic', help='Manually set the topic to subscribe to.')
 | 
					    )
 | 
				
			||||||
    parser_logs.add_argument('--username', help='Manually set the username.')
 | 
					    parser_upload.add_argument(
 | 
				
			||||||
    parser_logs.add_argument('--password', help='Manually set the password.')
 | 
					        "configuration", help="Your YAML configuration file(s).", nargs="+"
 | 
				
			||||||
    parser_logs.add_argument('--client-id', help='Manually set the client id.')
 | 
					    )
 | 
				
			||||||
    parser_logs.add_argument('--serial-port', help="Manually specify a serial port to use"
 | 
					    parser_upload.add_argument(
 | 
				
			||||||
                                                   "For example /dev/cu.SLAB_USBtoUART.")
 | 
					        "--device",
 | 
				
			||||||
 | 
					        help="Manually specify the serial port/address to use, for example /dev/ttyUSB0.",
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    parser_run = subparsers.add_parser('run', help='Validate the configuration, create a binary, '
 | 
					    parser_logs = subparsers.add_parser(
 | 
				
			||||||
                                                   'upload it, and start MQTT logs.')
 | 
					        "logs",
 | 
				
			||||||
    parser_run.add_argument('--upload-port', help="Manually specify the upload port/ip to use. "
 | 
					        help="Validate the configuration and show all logs.",
 | 
				
			||||||
                                                  "For example /dev/cu.SLAB_USBtoUART.")
 | 
					        parents=[mqtt_options],
 | 
				
			||||||
    parser_run.add_argument('--no-logs', help='Disable starting MQTT logs.',
 | 
					    )
 | 
				
			||||||
                            action='store_true')
 | 
					    parser_logs.add_argument(
 | 
				
			||||||
    parser_run.add_argument('--topic', help='Manually set the topic to subscribe to for logs.')
 | 
					        "configuration", help="Your YAML configuration file.", nargs=1
 | 
				
			||||||
    parser_run.add_argument('--username', help='Manually set the MQTT username for logs.')
 | 
					    )
 | 
				
			||||||
    parser_run.add_argument('--password', help='Manually set the MQTT password for logs.')
 | 
					    parser_logs.add_argument(
 | 
				
			||||||
    parser_run.add_argument('--client-id', help='Manually set the client id for logs.')
 | 
					        "--device",
 | 
				
			||||||
 | 
					        help="Manually specify the serial port/address to use, for example /dev/ttyUSB0.",
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    parser_clean = subparsers.add_parser('clean-mqtt', help="Helper to clear an MQTT topic from "
 | 
					    parser_run = subparsers.add_parser(
 | 
				
			||||||
                                                            "retain messages.")
 | 
					        "run",
 | 
				
			||||||
    parser_clean.add_argument('--topic', help='Manually set the topic to subscribe to.')
 | 
					        help="Validate the configuration, create a binary, upload it, and start logs.",
 | 
				
			||||||
    parser_clean.add_argument('--username', help='Manually set the username.')
 | 
					        parents=[mqtt_options],
 | 
				
			||||||
    parser_clean.add_argument('--password', help='Manually set the password.')
 | 
					    )
 | 
				
			||||||
    parser_clean.add_argument('--client-id', help='Manually set the client id.')
 | 
					    parser_run.add_argument(
 | 
				
			||||||
 | 
					        "configuration", help="Your YAML configuration file(s).", nargs="+"
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    parser_run.add_argument(
 | 
				
			||||||
 | 
					        "--device",
 | 
				
			||||||
 | 
					        help="Manually specify the serial port/address to use, for example /dev/ttyUSB0.",
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    parser_run.add_argument(
 | 
				
			||||||
 | 
					        "--no-logs", help="Disable starting logs.", action="store_true"
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    subparsers.add_parser('wizard', help="A helpful setup wizard that will guide "
 | 
					    parser_clean = subparsers.add_parser(
 | 
				
			||||||
                                         "you through setting up esphome.")
 | 
					        "clean-mqtt",
 | 
				
			||||||
 | 
					        help="Helper to clear retained messages from an MQTT topic.",
 | 
				
			||||||
 | 
					        parents=[mqtt_options],
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    parser_clean.add_argument(
 | 
				
			||||||
 | 
					        "configuration", help="Your YAML configuration file(s).", nargs="+"
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    subparsers.add_parser('mqtt-fingerprint', help="Get the SSL fingerprint from a MQTT broker.")
 | 
					    parser_wizard = subparsers.add_parser(
 | 
				
			||||||
 | 
					        "wizard",
 | 
				
			||||||
 | 
					        help="A helpful setup wizard that will guide you through setting up ESPHome.",
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    parser_wizard.add_argument(
 | 
				
			||||||
 | 
					        "configuration",
 | 
				
			||||||
 | 
					        help="Your YAML configuration file.",
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    subparsers.add_parser('version', help="Print the esphome version and exit.")
 | 
					    parser_fingerprint = subparsers.add_parser(
 | 
				
			||||||
 | 
					        "mqtt-fingerprint", help="Get the SSL fingerprint from a MQTT broker."
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    parser_fingerprint.add_argument(
 | 
				
			||||||
 | 
					        "configuration", help="Your YAML configuration file(s).", nargs="+"
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    subparsers.add_parser('clean', help="Delete all temporary build files.")
 | 
					    subparsers.add_parser("version", help="Print the ESPHome version and exit.")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    dashboard = subparsers.add_parser('dashboard',
 | 
					    parser_clean = subparsers.add_parser(
 | 
				
			||||||
                                      help="Create a simple web server for a dashboard.")
 | 
					        "clean", help="Delete all temporary build files."
 | 
				
			||||||
    dashboard.add_argument("--port", help="The HTTP port to open connections on. Defaults to 6052.",
 | 
					    )
 | 
				
			||||||
                           type=int, default=6052)
 | 
					    parser_clean.add_argument(
 | 
				
			||||||
    dashboard.add_argument("--username", help="The optional username to require "
 | 
					        "configuration", help="Your YAML configuration file(s).", nargs="+"
 | 
				
			||||||
                                              "for authentication.",
 | 
					    )
 | 
				
			||||||
                           type=str, default='')
 | 
					 | 
				
			||||||
    dashboard.add_argument("--password", help="The optional password to require "
 | 
					 | 
				
			||||||
                                              "for authentication.",
 | 
					 | 
				
			||||||
                           type=str, default='')
 | 
					 | 
				
			||||||
    dashboard.add_argument("--open-ui", help="Open the dashboard UI in a browser.",
 | 
					 | 
				
			||||||
                           action='store_true')
 | 
					 | 
				
			||||||
    dashboard.add_argument("--hassio",
 | 
					 | 
				
			||||||
                           help=argparse.SUPPRESS,
 | 
					 | 
				
			||||||
                           action="store_true")
 | 
					 | 
				
			||||||
    dashboard.add_argument("--socket",
 | 
					 | 
				
			||||||
                           help="Make the dashboard serve under a unix socket", type=str)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    vscode = subparsers.add_parser('vscode', help=argparse.SUPPRESS)
 | 
					    parser_dashboard = subparsers.add_parser(
 | 
				
			||||||
    vscode.add_argument('--ace', action='store_true')
 | 
					        "dashboard", help="Create a simple web server for a dashboard."
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    parser_dashboard.add_argument(
 | 
				
			||||||
 | 
					        "configuration",
 | 
				
			||||||
 | 
					        help="Your YAML configuration file directory.",
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    parser_dashboard.add_argument(
 | 
				
			||||||
 | 
					        "--port",
 | 
				
			||||||
 | 
					        help="The HTTP port to open connections on. Defaults to 6052.",
 | 
				
			||||||
 | 
					        type=int,
 | 
				
			||||||
 | 
					        default=6052,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    parser_dashboard.add_argument(
 | 
				
			||||||
 | 
					        "--username",
 | 
				
			||||||
 | 
					        help="The optional username to require for authentication.",
 | 
				
			||||||
 | 
					        type=str,
 | 
				
			||||||
 | 
					        default="",
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    parser_dashboard.add_argument(
 | 
				
			||||||
 | 
					        "--password",
 | 
				
			||||||
 | 
					        help="The optional password to require for authentication.",
 | 
				
			||||||
 | 
					        type=str,
 | 
				
			||||||
 | 
					        default="",
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    parser_dashboard.add_argument(
 | 
				
			||||||
 | 
					        "--open-ui", help="Open the dashboard UI in a browser.", action="store_true"
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    parser_dashboard.add_argument(
 | 
				
			||||||
 | 
					        "--hassio", help=argparse.SUPPRESS, action="store_true"
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    parser_dashboard.add_argument(
 | 
				
			||||||
 | 
					        "--socket", help="Make the dashboard serve under a unix socket", type=str
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    subparsers.add_parser('update-all', help=argparse.SUPPRESS)
 | 
					    parser_vscode = subparsers.add_parser("vscode")
 | 
				
			||||||
 | 
					    parser_vscode.add_argument(
 | 
				
			||||||
 | 
					        "configuration", help="Your YAML configuration file.", nargs=1
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    parser_vscode.add_argument("--ace", action="store_true")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    parser_update = subparsers.add_parser("update-all")
 | 
				
			||||||
 | 
					    parser_update.add_argument(
 | 
				
			||||||
 | 
					        "configuration", help="Your YAML configuration file directory.", nargs=1
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return parser.parse_args(argv[1:])
 | 
					    return parser.parse_args(argv[1:])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -517,42 +686,44 @@ def run_esphome(argv):
 | 
				
			|||||||
    CORE.dashboard = args.dashboard
 | 
					    CORE.dashboard = args.dashboard
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    setup_log(args.verbose, args.quiet)
 | 
					    setup_log(args.verbose, args.quiet)
 | 
				
			||||||
    if args.command != 'version' and not args.configuration:
 | 
					    if args.deprecated_argv_suggestion is not None:
 | 
				
			||||||
        _LOGGER.error("Missing configuration parameter, see esphome --help.")
 | 
					        _LOGGER.warning(
 | 
				
			||||||
        return 1
 | 
					            "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[1:]))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if IS_PY2:
 | 
					    if sys.version_info < (3, 7, 0):
 | 
				
			||||||
        _LOGGER.warning("You're using ESPHome with python 2. Support for python 2 is deprecated "
 | 
					        _LOGGER.error(
 | 
				
			||||||
                        "and will be removed in 1.15.0. Please reinstall ESPHome with python 3.6 "
 | 
					            "You're running ESPHome with Python <3.7. ESPHome is no longer compatible "
 | 
				
			||||||
                        "or higher.")
 | 
					            "with this Python version. Please reinstall ESPHome with Python 3.7+"
 | 
				
			||||||
    elif IS_PY3 and sys.version_info < (3, 6, 0):
 | 
					        )
 | 
				
			||||||
        _LOGGER.warning("You're using ESPHome with python 3.5. Support for python 3.5 is "
 | 
					        return 1
 | 
				
			||||||
                        "deprecated and will be removed in 1.15.0. Please reinstall ESPHome with "
 | 
					 | 
				
			||||||
                        "python 3.6 or higher.")
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if args.command in PRE_CONFIG_ACTIONS:
 | 
					    if args.command in PRE_CONFIG_ACTIONS:
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            return PRE_CONFIG_ACTIONS[args.command](args)
 | 
					            return PRE_CONFIG_ACTIONS[args.command](args)
 | 
				
			||||||
        except EsphomeError as e:
 | 
					        except EsphomeError as e:
 | 
				
			||||||
            _LOGGER.error(e)
 | 
					            _LOGGER.error(e, exc_info=args.verbose)
 | 
				
			||||||
            return 1
 | 
					            return 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for conf_path in args.configuration:
 | 
					    for conf_path in args.configuration:
 | 
				
			||||||
        CORE.config_path = conf_path
 | 
					        CORE.config_path = conf_path
 | 
				
			||||||
        CORE.dashboard = args.dashboard
 | 
					        CORE.dashboard = args.dashboard
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        config = read_config()
 | 
					        config = read_config(dict(args.substitution) if args.substitution else {})
 | 
				
			||||||
        if config is None:
 | 
					        if config is None:
 | 
				
			||||||
            return 1
 | 
					            return 1
 | 
				
			||||||
        CORE.config = config
 | 
					        CORE.config = config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if args.command not in POST_CONFIG_ACTIONS:
 | 
					        if args.command not in POST_CONFIG_ACTIONS:
 | 
				
			||||||
            safe_print(u"Unknown command {}".format(args.command))
 | 
					            safe_print(f"Unknown command {args.command}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            rc = POST_CONFIG_ACTIONS[args.command](args, config)
 | 
					            rc = POST_CONFIG_ACTIONS[args.command](args, config)
 | 
				
			||||||
        except EsphomeError as e:
 | 
					        except EsphomeError as e:
 | 
				
			||||||
            _LOGGER.error(e)
 | 
					            _LOGGER.error(e, exc_info=args.verbose)
 | 
				
			||||||
            return 1
 | 
					            return 1
 | 
				
			||||||
        if rc != 0:
 | 
					        if rc != 0:
 | 
				
			||||||
            return rc
 | 
					            return rc
 | 
				
			||||||
 
 | 
				
			|||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@@ -13,8 +13,8 @@ from esphome import const
 | 
				
			|||||||
import esphome.api.api_pb2 as pb
 | 
					import esphome.api.api_pb2 as pb
 | 
				
			||||||
from esphome.const import CONF_PASSWORD, CONF_PORT
 | 
					from esphome.const import CONF_PASSWORD, CONF_PORT
 | 
				
			||||||
from esphome.core import EsphomeError
 | 
					from esphome.core import EsphomeError
 | 
				
			||||||
from esphome.helpers import resolve_ip_address, indent, color
 | 
					from esphome.helpers import resolve_ip_address, indent
 | 
				
			||||||
from esphome.py_compat import text_type, IS_PY2, byte_to_bytes, char_to_byte
 | 
					from esphome.log import color, Fore
 | 
				
			||||||
from esphome.util import safe_print
 | 
					from esphome.util import safe_print
 | 
				
			||||||
 | 
					
 | 
				
			||||||
_LOGGER = logging.getLogger(__name__)
 | 
					_LOGGER = logging.getLogger(__name__)
 | 
				
			||||||
@@ -67,16 +67,16 @@ MESSAGE_TYPE_TO_PROTO = {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
def _varuint_to_bytes(value):
 | 
					def _varuint_to_bytes(value):
 | 
				
			||||||
    if value <= 0x7F:
 | 
					    if value <= 0x7F:
 | 
				
			||||||
        return byte_to_bytes(value)
 | 
					        return bytes([value])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ret = bytes()
 | 
					    ret = bytes()
 | 
				
			||||||
    while value:
 | 
					    while value:
 | 
				
			||||||
        temp = value & 0x7F
 | 
					        temp = value & 0x7F
 | 
				
			||||||
        value >>= 7
 | 
					        value >>= 7
 | 
				
			||||||
        if value:
 | 
					        if value:
 | 
				
			||||||
            ret += byte_to_bytes(temp | 0x80)
 | 
					            ret += bytes([temp | 0x80])
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            ret += byte_to_bytes(temp)
 | 
					            ret += bytes([temp])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return ret
 | 
					    return ret
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -84,8 +84,7 @@ def _varuint_to_bytes(value):
 | 
				
			|||||||
def _bytes_to_varuint(value):
 | 
					def _bytes_to_varuint(value):
 | 
				
			||||||
    result = 0
 | 
					    result = 0
 | 
				
			||||||
    bitpos = 0
 | 
					    bitpos = 0
 | 
				
			||||||
    for c in value:
 | 
					    for val in value:
 | 
				
			||||||
        val = char_to_byte(c)
 | 
					 | 
				
			||||||
        result |= (val & 0x7F) << bitpos
 | 
					        result |= (val & 0x7F) << bitpos
 | 
				
			||||||
        bitpos += 7
 | 
					        bitpos += 7
 | 
				
			||||||
        if (val & 0x80) == 0:
 | 
					        if (val & 0x80) == 0:
 | 
				
			||||||
@@ -179,11 +178,15 @@ class APIClient(threading.Thread):
 | 
				
			|||||||
        try:
 | 
					        try:
 | 
				
			||||||
            ip = resolve_ip_address(self._address)
 | 
					            ip = resolve_ip_address(self._address)
 | 
				
			||||||
        except EsphomeError as err:
 | 
					        except EsphomeError as err:
 | 
				
			||||||
            _LOGGER.warning("Error resolving IP address of %s. Is it connected to WiFi?",
 | 
					            _LOGGER.warning(
 | 
				
			||||||
                            self._address)
 | 
					                "Error resolving IP address of %s. Is it connected to WiFi?",
 | 
				
			||||||
            _LOGGER.warning("(If this error persists, please set a static IP address: "
 | 
					                self._address,
 | 
				
			||||||
                            "https://esphome.io/components/wifi.html#manual-ips)")
 | 
					            )
 | 
				
			||||||
            raise APIConnectionError(err)
 | 
					            _LOGGER.warning(
 | 
				
			||||||
 | 
					                "(If this error persists, please set a static IP address: "
 | 
				
			||||||
 | 
					                "https://esphome.io/components/wifi.html#manual-ips)"
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            raise APIConnectionError(err) from err
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        _LOGGER.info("Connecting to %s:%s (%s)", self._address, self._port, ip)
 | 
					        _LOGGER.info("Connecting to %s:%s (%s)", self._address, self._port, ip)
 | 
				
			||||||
        self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 | 
					        self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 | 
				
			||||||
@@ -191,8 +194,8 @@ class APIClient(threading.Thread):
 | 
				
			|||||||
        self._socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
 | 
					        self._socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            self._socket.connect((ip, self._port))
 | 
					            self._socket.connect((ip, self._port))
 | 
				
			||||||
        except socket.error as err:
 | 
					        except OSError as err:
 | 
				
			||||||
            err = APIConnectionError("Error connecting to {}: {}".format(ip, err))
 | 
					            err = APIConnectionError(f"Error connecting to {ip}: {err}")
 | 
				
			||||||
            self._fatal_error(err)
 | 
					            self._fatal_error(err)
 | 
				
			||||||
            raise err
 | 
					            raise err
 | 
				
			||||||
        self._socket.settimeout(0.1)
 | 
					        self._socket.settimeout(0.1)
 | 
				
			||||||
@@ -200,14 +203,19 @@ class APIClient(threading.Thread):
 | 
				
			|||||||
        self._socket_open_event.set()
 | 
					        self._socket_open_event.set()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        hello = pb.HelloRequest()
 | 
					        hello = pb.HelloRequest()
 | 
				
			||||||
        hello.client_info = 'ESPHome v{}'.format(const.__version__)
 | 
					        hello.client_info = f"ESPHome v{const.__version__}"
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            resp = self._send_message_await_response(hello, pb.HelloResponse)
 | 
					            resp = self._send_message_await_response(hello, pb.HelloResponse)
 | 
				
			||||||
        except APIConnectionError as err:
 | 
					        except APIConnectionError as err:
 | 
				
			||||||
            self._fatal_error(err)
 | 
					            self._fatal_error(err)
 | 
				
			||||||
            raise err
 | 
					            raise err
 | 
				
			||||||
        _LOGGER.debug("Successfully connected to %s ('%s' API=%s.%s)", self._address,
 | 
					        _LOGGER.debug(
 | 
				
			||||||
                      resp.server_info, resp.api_version_major, resp.api_version_minor)
 | 
					            "Successfully connected to %s ('%s' API=%s.%s)",
 | 
				
			||||||
 | 
					            self._address,
 | 
				
			||||||
 | 
					            resp.server_info,
 | 
				
			||||||
 | 
					            resp.api_version_major,
 | 
				
			||||||
 | 
					            resp.api_version_minor,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
        self._connected = True
 | 
					        self._connected = True
 | 
				
			||||||
        self._refresh_ping()
 | 
					        self._refresh_ping()
 | 
				
			||||||
        if self.on_connect is not None:
 | 
					        if self.on_connect is not None:
 | 
				
			||||||
@@ -251,8 +259,8 @@ class APIClient(threading.Thread):
 | 
				
			|||||||
        with self._socket_write_lock:
 | 
					        with self._socket_write_lock:
 | 
				
			||||||
            try:
 | 
					            try:
 | 
				
			||||||
                self._socket.sendall(data)
 | 
					                self._socket.sendall(data)
 | 
				
			||||||
            except socket.error as err:
 | 
					            except OSError as err:
 | 
				
			||||||
                err = APIConnectionError("Error while writing data: {}".format(err))
 | 
					                err = APIConnectionError(f"Error while writing data: {err}")
 | 
				
			||||||
                self._fatal_error(err)
 | 
					                self._fatal_error(err)
 | 
				
			||||||
                raise err
 | 
					                raise err
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -265,17 +273,16 @@ class APIClient(threading.Thread):
 | 
				
			|||||||
            raise ValueError
 | 
					            raise ValueError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        encoded = msg.SerializeToString()
 | 
					        encoded = msg.SerializeToString()
 | 
				
			||||||
        _LOGGER.debug("Sending %s:\n%s", type(msg), indent(text_type(msg)))
 | 
					        _LOGGER.debug("Sending %s:\n%s", type(msg), indent(str(msg)))
 | 
				
			||||||
        if IS_PY2:
 | 
					        req = bytes([0])
 | 
				
			||||||
            req = chr(0x00)
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            req = bytes([0])
 | 
					 | 
				
			||||||
        req += _varuint_to_bytes(len(encoded))
 | 
					        req += _varuint_to_bytes(len(encoded))
 | 
				
			||||||
        req += _varuint_to_bytes(message_type)
 | 
					        req += _varuint_to_bytes(message_type)
 | 
				
			||||||
        req += encoded
 | 
					        req += encoded
 | 
				
			||||||
        self._write(req)
 | 
					        self._write(req)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _send_message_await_response_complex(self, send_msg, do_append, do_stop, timeout=5):
 | 
					    def _send_message_await_response_complex(
 | 
				
			||||||
 | 
					        self, send_msg, do_append, do_stop, timeout=5
 | 
				
			||||||
 | 
					    ):
 | 
				
			||||||
        event = threading.Event()
 | 
					        event = threading.Event()
 | 
				
			||||||
        responses = []
 | 
					        responses = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -300,12 +307,15 @@ class APIClient(threading.Thread):
 | 
				
			|||||||
        def is_response(msg):
 | 
					        def is_response(msg):
 | 
				
			||||||
            return isinstance(msg, response_type)
 | 
					            return isinstance(msg, response_type)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return self._send_message_await_response_complex(send_msg, is_response, is_response,
 | 
					        return self._send_message_await_response_complex(
 | 
				
			||||||
                                                         timeout)[0]
 | 
					            send_msg, is_response, is_response, timeout
 | 
				
			||||||
 | 
					        )[0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def device_info(self):
 | 
					    def device_info(self):
 | 
				
			||||||
        self._check_connected()
 | 
					        self._check_connected()
 | 
				
			||||||
        return self._send_message_await_response(pb.DeviceInfoRequest(), pb.DeviceInfoResponse)
 | 
					        return self._send_message_await_response(
 | 
				
			||||||
 | 
					            pb.DeviceInfoRequest(), pb.DeviceInfoResponse
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def ping(self):
 | 
					    def ping(self):
 | 
				
			||||||
        self._check_connected()
 | 
					        self._check_connected()
 | 
				
			||||||
@@ -315,7 +325,9 @@ class APIClient(threading.Thread):
 | 
				
			|||||||
        self._check_connected()
 | 
					        self._check_connected()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            self._send_message_await_response(pb.DisconnectRequest(), pb.DisconnectResponse)
 | 
					            self._send_message_await_response(
 | 
				
			||||||
 | 
					                pb.DisconnectRequest(), pb.DisconnectResponse
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
        except APIConnectionError:
 | 
					        except APIConnectionError:
 | 
				
			||||||
            pass
 | 
					            pass
 | 
				
			||||||
        self._close_socket()
 | 
					        self._close_socket()
 | 
				
			||||||
@@ -351,18 +363,18 @@ class APIClient(threading.Thread):
 | 
				
			|||||||
                raise APIConnectionError("No socket!")
 | 
					                raise APIConnectionError("No socket!")
 | 
				
			||||||
            try:
 | 
					            try:
 | 
				
			||||||
                val = self._socket.recv(amount - len(ret))
 | 
					                val = self._socket.recv(amount - len(ret))
 | 
				
			||||||
            except AttributeError:
 | 
					            except AttributeError as err:
 | 
				
			||||||
                raise APIConnectionError("Socket was closed")
 | 
					                raise APIConnectionError("Socket was closed") from err
 | 
				
			||||||
            except socket.timeout:
 | 
					            except socket.timeout:
 | 
				
			||||||
                continue
 | 
					                continue
 | 
				
			||||||
            except socket.error as err:
 | 
					            except OSError as err:
 | 
				
			||||||
                raise APIConnectionError("Error while receiving data: {}".format(err))
 | 
					                raise APIConnectionError(f"Error while receiving data: {err}") from err
 | 
				
			||||||
            ret += val
 | 
					            ret += val
 | 
				
			||||||
        return ret
 | 
					        return ret
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _recv_varint(self):
 | 
					    def _recv_varint(self):
 | 
				
			||||||
        raw = bytes()
 | 
					        raw = bytes()
 | 
				
			||||||
        while not raw or char_to_byte(raw[-1]) & 0x80:
 | 
					        while not raw or raw[-1] & 0x80:
 | 
				
			||||||
            raw += self._recv(1)
 | 
					            raw += self._recv(1)
 | 
				
			||||||
        return _bytes_to_varuint(raw)
 | 
					        return _bytes_to_varuint(raw)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -371,7 +383,7 @@ class APIClient(threading.Thread):
 | 
				
			|||||||
            return
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Preamble
 | 
					        # Preamble
 | 
				
			||||||
        if char_to_byte(self._recv(1)[0]) != 0x00:
 | 
					        if self._recv(1)[0] != 0x00:
 | 
				
			||||||
            raise APIConnectionError("Invalid preamble")
 | 
					            raise APIConnectionError("Invalid preamble")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        length = self._recv_varint()
 | 
					        length = self._recv_varint()
 | 
				
			||||||
@@ -420,7 +432,7 @@ class APIClient(threading.Thread):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def run_logs(config, address):
 | 
					def run_logs(config, address):
 | 
				
			||||||
    conf = config['api']
 | 
					    conf = config["api"]
 | 
				
			||||||
    port = conf[CONF_PORT]
 | 
					    port = conf[CONF_PORT]
 | 
				
			||||||
    password = conf[CONF_PASSWORD]
 | 
					    password = conf[CONF_PASSWORD]
 | 
				
			||||||
    _LOGGER.info("Starting log output from %s using esphome API", address)
 | 
					    _LOGGER.info("Starting log output from %s using esphome API", address)
 | 
				
			||||||
@@ -436,7 +448,7 @@ def run_logs(config, address):
 | 
				
			|||||||
            return
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if err:
 | 
					        if err:
 | 
				
			||||||
            _LOGGER.warning(u"Disconnected from API: %s", err)
 | 
					            _LOGGER.warning("Disconnected from API: %s", err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        while retry_timer:
 | 
					        while retry_timer:
 | 
				
			||||||
            retry_timer.pop(0).cancel()
 | 
					            retry_timer.pop(0).cancel()
 | 
				
			||||||
@@ -452,24 +464,35 @@ def run_logs(config, address):
 | 
				
			|||||||
            _LOGGER.info("Successfully connected to %s", address)
 | 
					            _LOGGER.info("Successfully connected to %s", address)
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        wait_time = int(min(1.5**min(tries, 100), 30))
 | 
					        wait_time = int(min(1.5 ** min(tries, 100), 30))
 | 
				
			||||||
        if not has_connects:
 | 
					        if not has_connects:
 | 
				
			||||||
            _LOGGER.warning(u"Initial connection failed. The ESP might not be connected "
 | 
					            _LOGGER.warning(
 | 
				
			||||||
                            u"to WiFi yet (%s). Re-Trying in %s seconds",
 | 
					                "Initial connection failed. The ESP might not be connected "
 | 
				
			||||||
                            error, wait_time)
 | 
					                "to WiFi yet (%s). Re-Trying in %s seconds",
 | 
				
			||||||
 | 
					                error,
 | 
				
			||||||
 | 
					                wait_time,
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            _LOGGER.warning(u"Couldn't connect to API (%s). Trying to reconnect in %s seconds",
 | 
					            _LOGGER.warning(
 | 
				
			||||||
                            error, wait_time)
 | 
					                "Couldn't connect to API (%s). Trying to reconnect in %s seconds",
 | 
				
			||||||
        timer = threading.Timer(wait_time, functools.partial(try_connect, None, tries + 1))
 | 
					                error,
 | 
				
			||||||
 | 
					                wait_time,
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        timer = threading.Timer(
 | 
				
			||||||
 | 
					            wait_time, functools.partial(try_connect, None, tries + 1)
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
        timer.start()
 | 
					        timer.start()
 | 
				
			||||||
        retry_timer.append(timer)
 | 
					        retry_timer.append(timer)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def on_log(msg):
 | 
					    def on_log(msg):
 | 
				
			||||||
        time_ = datetime.now().time().strftime(u'[%H:%M:%S]')
 | 
					        time_ = datetime.now().time().strftime("[%H:%M:%S]")
 | 
				
			||||||
        text = msg.message
 | 
					        text = msg.message
 | 
				
			||||||
        if msg.send_failed:
 | 
					        if msg.send_failed:
 | 
				
			||||||
            text = color('white', '(Message skipped because it was too big to fit in '
 | 
					            text = color(
 | 
				
			||||||
                                  'TCP buffer - This is only cosmetic)')
 | 
					                Fore.WHITE,
 | 
				
			||||||
 | 
					                "(Message skipped because it was too big to fit in "
 | 
				
			||||||
 | 
					                "TCP buffer - This is only cosmetic)",
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
        safe_print(time_ + text)
 | 
					        safe_print(time_ + text)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def on_login():
 | 
					    def on_login():
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,19 +1,36 @@
 | 
				
			|||||||
import esphome.codegen as cg
 | 
					import esphome.codegen as cg
 | 
				
			||||||
import esphome.config_validation as cv
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
from esphome.const import CONF_AUTOMATION_ID, CONF_CONDITION, CONF_ELSE, CONF_ID, CONF_THEN, \
 | 
					from esphome.const import (
 | 
				
			||||||
    CONF_TRIGGER_ID, CONF_TYPE_ID, CONF_TIME
 | 
					    CONF_AUTOMATION_ID,
 | 
				
			||||||
from esphome.core import coroutine
 | 
					    CONF_CONDITION,
 | 
				
			||||||
 | 
					    CONF_ELSE,
 | 
				
			||||||
 | 
					    CONF_ID,
 | 
				
			||||||
 | 
					    CONF_THEN,
 | 
				
			||||||
 | 
					    CONF_TRIGGER_ID,
 | 
				
			||||||
 | 
					    CONF_TYPE_ID,
 | 
				
			||||||
 | 
					    CONF_TIME,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					from esphome.jsonschema import jschema_extractor
 | 
				
			||||||
from esphome.util import Registry
 | 
					from esphome.util import Registry
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def maybe_simple_id(*validators):
 | 
					def maybe_simple_id(*validators):
 | 
				
			||||||
 | 
					    return maybe_conf(CONF_ID, *validators)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def maybe_conf(conf, *validators):
 | 
				
			||||||
    validator = cv.All(*validators)
 | 
					    validator = cv.All(*validators)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @jschema_extractor("maybe")
 | 
				
			||||||
    def validate(value):
 | 
					    def validate(value):
 | 
				
			||||||
 | 
					        # pylint: disable=comparison-with-callable
 | 
				
			||||||
 | 
					        if value == jschema_extractor:
 | 
				
			||||||
 | 
					            return validator
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if isinstance(value, dict):
 | 
					        if isinstance(value, dict):
 | 
				
			||||||
            return validator(value)
 | 
					            return validator(value)
 | 
				
			||||||
        with cv.remove_prepend_path([CONF_ID]):
 | 
					        with cv.remove_prepend_path([conf]):
 | 
				
			||||||
            return validator({CONF_ID: value})
 | 
					            return validator({conf: value})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return validate
 | 
					    return validate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -26,36 +43,34 @@ def register_condition(name, condition_type, schema):
 | 
				
			|||||||
    return CONDITION_REGISTRY.register(name, condition_type, schema)
 | 
					    return CONDITION_REGISTRY.register(name, condition_type, schema)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Action = cg.esphome_ns.class_('Action')
 | 
					Action = cg.esphome_ns.class_("Action")
 | 
				
			||||||
Trigger = cg.esphome_ns.class_('Trigger')
 | 
					Trigger = cg.esphome_ns.class_("Trigger")
 | 
				
			||||||
ACTION_REGISTRY = Registry()
 | 
					ACTION_REGISTRY = Registry()
 | 
				
			||||||
Condition = cg.esphome_ns.class_('Condition')
 | 
					Condition = cg.esphome_ns.class_("Condition")
 | 
				
			||||||
CONDITION_REGISTRY = Registry()
 | 
					CONDITION_REGISTRY = Registry()
 | 
				
			||||||
validate_action = cv.validate_registry_entry('action', ACTION_REGISTRY)
 | 
					validate_action = cv.validate_registry_entry("action", ACTION_REGISTRY)
 | 
				
			||||||
validate_action_list = cv.validate_registry('action', ACTION_REGISTRY)
 | 
					validate_action_list = cv.validate_registry("action", ACTION_REGISTRY)
 | 
				
			||||||
validate_condition = cv.validate_registry_entry('condition', CONDITION_REGISTRY)
 | 
					validate_condition = cv.validate_registry_entry("condition", CONDITION_REGISTRY)
 | 
				
			||||||
validate_condition_list = cv.validate_registry('condition', CONDITION_REGISTRY)
 | 
					validate_condition_list = cv.validate_registry("condition", CONDITION_REGISTRY)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def validate_potentially_and_condition(value):
 | 
					def validate_potentially_and_condition(value):
 | 
				
			||||||
    if isinstance(value, list):
 | 
					    if isinstance(value, list):
 | 
				
			||||||
        with cv.remove_prepend_path(['and']):
 | 
					        with cv.remove_prepend_path(["and"]):
 | 
				
			||||||
            return validate_condition({
 | 
					            return validate_condition({"and": value})
 | 
				
			||||||
                'and': value
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
    return validate_condition(value)
 | 
					    return validate_condition(value)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
DelayAction = cg.esphome_ns.class_('DelayAction', Action, cg.Component)
 | 
					DelayAction = cg.esphome_ns.class_("DelayAction", Action, cg.Component)
 | 
				
			||||||
LambdaAction = cg.esphome_ns.class_('LambdaAction', Action)
 | 
					LambdaAction = cg.esphome_ns.class_("LambdaAction", Action)
 | 
				
			||||||
IfAction = cg.esphome_ns.class_('IfAction', Action)
 | 
					IfAction = cg.esphome_ns.class_("IfAction", Action)
 | 
				
			||||||
WhileAction = cg.esphome_ns.class_('WhileAction', Action)
 | 
					WhileAction = cg.esphome_ns.class_("WhileAction", Action)
 | 
				
			||||||
WaitUntilAction = cg.esphome_ns.class_('WaitUntilAction', Action, cg.Component)
 | 
					WaitUntilAction = cg.esphome_ns.class_("WaitUntilAction", Action, cg.Component)
 | 
				
			||||||
UpdateComponentAction = cg.esphome_ns.class_('UpdateComponentAction', Action)
 | 
					UpdateComponentAction = cg.esphome_ns.class_("UpdateComponentAction", Action)
 | 
				
			||||||
Automation = cg.esphome_ns.class_('Automation')
 | 
					Automation = cg.esphome_ns.class_("Automation")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LambdaCondition = cg.esphome_ns.class_('LambdaCondition', Condition)
 | 
					LambdaCondition = cg.esphome_ns.class_("LambdaCondition", Condition)
 | 
				
			||||||
ForCondition = cg.esphome_ns.class_('ForCondition', Condition, cg.Component)
 | 
					ForCondition = cg.esphome_ns.class_("ForCondition", Condition, cg.Component)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def validate_automation(extra_schema=None, extra_validators=None, single=False):
 | 
					def validate_automation(extra_schema=None, extra_validators=None, single=False):
 | 
				
			||||||
@@ -79,9 +94,10 @@ def validate_automation(extra_schema=None, extra_validators=None, single=False):
 | 
				
			|||||||
                try:
 | 
					                try:
 | 
				
			||||||
                    return cv.Schema([schema])(value)
 | 
					                    return cv.Schema([schema])(value)
 | 
				
			||||||
                except cv.Invalid as err2:
 | 
					                except cv.Invalid as err2:
 | 
				
			||||||
                    if u'extra keys not allowed' in str(err2) and len(err2.path) == 2:
 | 
					                    if "extra keys not allowed" in str(err2) and len(err2.path) == 2:
 | 
				
			||||||
 | 
					                        # pylint: disable=raise-missing-from
 | 
				
			||||||
                        raise err
 | 
					                        raise err
 | 
				
			||||||
                    if u'Unable to find action' in str(err):
 | 
					                    if "Unable to find action" in str(err):
 | 
				
			||||||
                        raise err2
 | 
					                        raise err2
 | 
				
			||||||
                    raise cv.MultipleInvalid([err, err2])
 | 
					                    raise cv.MultipleInvalid([err, err2])
 | 
				
			||||||
        elif isinstance(value, dict):
 | 
					        elif isinstance(value, dict):
 | 
				
			||||||
@@ -92,7 +108,13 @@ def validate_automation(extra_schema=None, extra_validators=None, single=False):
 | 
				
			|||||||
        # This should only happen with invalid configs, but let's have a nice error message.
 | 
					        # This should only happen with invalid configs, but let's have a nice error message.
 | 
				
			||||||
        return [schema(value)]
 | 
					        return [schema(value)]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @jschema_extractor("automation")
 | 
				
			||||||
    def validator(value):
 | 
					    def validator(value):
 | 
				
			||||||
 | 
					        # hack to get the schema
 | 
				
			||||||
 | 
					        # pylint: disable=comparison-with-callable
 | 
				
			||||||
 | 
					        if value == jschema_extractor:
 | 
				
			||||||
 | 
					            return schema
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        value = validator_(value)
 | 
					        value = validator_(value)
 | 
				
			||||||
        if extra_validators is not None:
 | 
					        if extra_validators is not None:
 | 
				
			||||||
            value = cv.Schema([extra_validators])(value)
 | 
					            value = cv.Schema([extra_validators])(value)
 | 
				
			||||||
@@ -105,162 +127,198 @@ def validate_automation(extra_schema=None, extra_validators=None, single=False):
 | 
				
			|||||||
    return validator
 | 
					    return validator
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
AUTOMATION_SCHEMA = cv.Schema({
 | 
					AUTOMATION_SCHEMA = cv.Schema(
 | 
				
			||||||
    cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(Trigger),
 | 
					    {
 | 
				
			||||||
    cv.GenerateID(CONF_AUTOMATION_ID): cv.declare_id(Automation),
 | 
					        cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(Trigger),
 | 
				
			||||||
    cv.Required(CONF_THEN): validate_action_list,
 | 
					        cv.GenerateID(CONF_AUTOMATION_ID): cv.declare_id(Automation),
 | 
				
			||||||
})
 | 
					        cv.Required(CONF_THEN): validate_action_list,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
AndCondition = cg.esphome_ns.class_('AndCondition', Condition)
 | 
					AndCondition = cg.esphome_ns.class_("AndCondition", Condition)
 | 
				
			||||||
OrCondition = cg.esphome_ns.class_('OrCondition', Condition)
 | 
					OrCondition = cg.esphome_ns.class_("OrCondition", Condition)
 | 
				
			||||||
NotCondition = cg.esphome_ns.class_('NotCondition', Condition)
 | 
					NotCondition = cg.esphome_ns.class_("NotCondition", Condition)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@register_condition('and', AndCondition, validate_condition_list)
 | 
					@register_condition("and", AndCondition, validate_condition_list)
 | 
				
			||||||
def and_condition_to_code(config, condition_id, template_arg, args):
 | 
					async def and_condition_to_code(config, condition_id, template_arg, args):
 | 
				
			||||||
    conditions = yield build_condition_list(config, template_arg, args)
 | 
					    conditions = await build_condition_list(config, template_arg, args)
 | 
				
			||||||
    yield cg.new_Pvariable(condition_id, template_arg, conditions)
 | 
					    return cg.new_Pvariable(condition_id, template_arg, conditions)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@register_condition('or', OrCondition, validate_condition_list)
 | 
					@register_condition("or", OrCondition, validate_condition_list)
 | 
				
			||||||
def or_condition_to_code(config, condition_id, template_arg, args):
 | 
					async def or_condition_to_code(config, condition_id, template_arg, args):
 | 
				
			||||||
    conditions = yield build_condition_list(config, template_arg, args)
 | 
					    conditions = await build_condition_list(config, template_arg, args)
 | 
				
			||||||
    yield cg.new_Pvariable(condition_id, template_arg, conditions)
 | 
					    return cg.new_Pvariable(condition_id, template_arg, conditions)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@register_condition('not', NotCondition, validate_potentially_and_condition)
 | 
					@register_condition("not", NotCondition, validate_potentially_and_condition)
 | 
				
			||||||
def not_condition_to_code(config, condition_id, template_arg, args):
 | 
					async def not_condition_to_code(config, condition_id, template_arg, args):
 | 
				
			||||||
    condition = yield build_condition(config, template_arg, args)
 | 
					    condition = await build_condition(config, template_arg, args)
 | 
				
			||||||
    yield cg.new_Pvariable(condition_id, template_arg, condition)
 | 
					    return cg.new_Pvariable(condition_id, template_arg, condition)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@register_condition('lambda', LambdaCondition, cv.lambda_)
 | 
					@register_condition("lambda", LambdaCondition, cv.returning_lambda)
 | 
				
			||||||
def lambda_condition_to_code(config, condition_id, template_arg, args):
 | 
					async def lambda_condition_to_code(config, condition_id, template_arg, args):
 | 
				
			||||||
    lambda_ = yield cg.process_lambda(config, args, return_type=bool)
 | 
					    lambda_ = await cg.process_lambda(config, args, return_type=bool)
 | 
				
			||||||
    yield cg.new_Pvariable(condition_id, template_arg, lambda_)
 | 
					    return cg.new_Pvariable(condition_id, template_arg, lambda_)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@register_condition('for', ForCondition, cv.Schema({
 | 
					@register_condition(
 | 
				
			||||||
    cv.Required(CONF_TIME): cv.templatable(cv.positive_time_period_milliseconds),
 | 
					    "for",
 | 
				
			||||||
    cv.Required(CONF_CONDITION): validate_potentially_and_condition,
 | 
					    ForCondition,
 | 
				
			||||||
}).extend(cv.COMPONENT_SCHEMA))
 | 
					    cv.Schema(
 | 
				
			||||||
def for_condition_to_code(config, condition_id, template_arg, args):
 | 
					        {
 | 
				
			||||||
    condition = yield build_condition(config[CONF_CONDITION], cg.TemplateArguments(), [])
 | 
					            cv.Required(CONF_TIME): cv.templatable(
 | 
				
			||||||
 | 
					                cv.positive_time_period_milliseconds
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Required(CONF_CONDITION): validate_potentially_and_condition,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    ).extend(cv.COMPONENT_SCHEMA),
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					async def for_condition_to_code(config, condition_id, template_arg, args):
 | 
				
			||||||
 | 
					    condition = await build_condition(
 | 
				
			||||||
 | 
					        config[CONF_CONDITION], cg.TemplateArguments(), []
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
    var = cg.new_Pvariable(condition_id, template_arg, condition)
 | 
					    var = cg.new_Pvariable(condition_id, template_arg, condition)
 | 
				
			||||||
    yield cg.register_component(var, config)
 | 
					    await cg.register_component(var, config)
 | 
				
			||||||
    templ = yield cg.templatable(config[CONF_TIME], args, cg.uint32)
 | 
					    templ = await cg.templatable(config[CONF_TIME], args, cg.uint32)
 | 
				
			||||||
    cg.add(var.set_time(templ))
 | 
					    cg.add(var.set_time(templ))
 | 
				
			||||||
    yield var
 | 
					    return var
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@register_action('delay', DelayAction, cv.templatable(cv.positive_time_period_milliseconds))
 | 
					@register_action(
 | 
				
			||||||
def delay_action_to_code(config, action_id, template_arg, args):
 | 
					    "delay", DelayAction, cv.templatable(cv.positive_time_period_milliseconds)
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					async def delay_action_to_code(config, action_id, template_arg, args):
 | 
				
			||||||
    var = cg.new_Pvariable(action_id, template_arg)
 | 
					    var = cg.new_Pvariable(action_id, template_arg)
 | 
				
			||||||
    yield cg.register_component(var, {})
 | 
					    await cg.register_component(var, {})
 | 
				
			||||||
    template_ = yield cg.templatable(config, args, cg.uint32)
 | 
					    template_ = await cg.templatable(config, args, cg.uint32)
 | 
				
			||||||
    cg.add(var.set_delay(template_))
 | 
					    cg.add(var.set_delay(template_))
 | 
				
			||||||
    yield var
 | 
					    return var
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@register_action('if', IfAction, cv.All({
 | 
					@register_action(
 | 
				
			||||||
    cv.Required(CONF_CONDITION): validate_potentially_and_condition,
 | 
					    "if",
 | 
				
			||||||
    cv.Optional(CONF_THEN): validate_action_list,
 | 
					    IfAction,
 | 
				
			||||||
    cv.Optional(CONF_ELSE): validate_action_list,
 | 
					    cv.All(
 | 
				
			||||||
}, cv.has_at_least_one_key(CONF_THEN, CONF_ELSE)))
 | 
					        {
 | 
				
			||||||
def if_action_to_code(config, action_id, template_arg, args):
 | 
					            cv.Required(CONF_CONDITION): validate_potentially_and_condition,
 | 
				
			||||||
    conditions = yield build_condition(config[CONF_CONDITION], template_arg, args)
 | 
					            cv.Optional(CONF_THEN): validate_action_list,
 | 
				
			||||||
 | 
					            cv.Optional(CONF_ELSE): validate_action_list,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        cv.has_at_least_one_key(CONF_THEN, CONF_ELSE),
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					async def if_action_to_code(config, action_id, template_arg, args):
 | 
				
			||||||
 | 
					    conditions = await build_condition(config[CONF_CONDITION], template_arg, args)
 | 
				
			||||||
    var = cg.new_Pvariable(action_id, template_arg, conditions)
 | 
					    var = cg.new_Pvariable(action_id, template_arg, conditions)
 | 
				
			||||||
    if CONF_THEN in config:
 | 
					    if CONF_THEN in config:
 | 
				
			||||||
        actions = yield build_action_list(config[CONF_THEN], template_arg, args)
 | 
					        actions = await build_action_list(config[CONF_THEN], template_arg, args)
 | 
				
			||||||
        cg.add(var.add_then(actions))
 | 
					        cg.add(var.add_then(actions))
 | 
				
			||||||
    if CONF_ELSE in config:
 | 
					    if CONF_ELSE in config:
 | 
				
			||||||
        actions = yield build_action_list(config[CONF_ELSE], template_arg, args)
 | 
					        actions = await build_action_list(config[CONF_ELSE], template_arg, args)
 | 
				
			||||||
        cg.add(var.add_else(actions))
 | 
					        cg.add(var.add_else(actions))
 | 
				
			||||||
    yield var
 | 
					    return var
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@register_action('while', WhileAction, cv.Schema({
 | 
					@register_action(
 | 
				
			||||||
    cv.Required(CONF_CONDITION): validate_potentially_and_condition,
 | 
					    "while",
 | 
				
			||||||
    cv.Required(CONF_THEN): validate_action_list,
 | 
					    WhileAction,
 | 
				
			||||||
}))
 | 
					    cv.Schema(
 | 
				
			||||||
def while_action_to_code(config, action_id, template_arg, args):
 | 
					        {
 | 
				
			||||||
    conditions = yield build_condition(config[CONF_CONDITION], template_arg, args)
 | 
					            cv.Required(CONF_CONDITION): validate_potentially_and_condition,
 | 
				
			||||||
 | 
					            cv.Required(CONF_THEN): validate_action_list,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					async def while_action_to_code(config, action_id, template_arg, args):
 | 
				
			||||||
 | 
					    conditions = await build_condition(config[CONF_CONDITION], template_arg, args)
 | 
				
			||||||
    var = cg.new_Pvariable(action_id, template_arg, conditions)
 | 
					    var = cg.new_Pvariable(action_id, template_arg, conditions)
 | 
				
			||||||
    actions = yield build_action_list(config[CONF_THEN], template_arg, args)
 | 
					    actions = await build_action_list(config[CONF_THEN], template_arg, args)
 | 
				
			||||||
    cg.add(var.add_then(actions))
 | 
					    cg.add(var.add_then(actions))
 | 
				
			||||||
    yield var
 | 
					    return var
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def validate_wait_until(value):
 | 
					def validate_wait_until(value):
 | 
				
			||||||
    schema = cv.Schema({
 | 
					    schema = cv.Schema(
 | 
				
			||||||
        cv.Required(CONF_CONDITION): validate_potentially_and_condition,
 | 
					        {
 | 
				
			||||||
    })
 | 
					            cv.Required(CONF_CONDITION): validate_potentially_and_condition,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
    if isinstance(value, dict) and CONF_CONDITION in value:
 | 
					    if isinstance(value, dict) and CONF_CONDITION in value:
 | 
				
			||||||
        return schema(value)
 | 
					        return schema(value)
 | 
				
			||||||
    return validate_wait_until({CONF_CONDITION: value})
 | 
					    return validate_wait_until({CONF_CONDITION: value})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@register_action('wait_until', WaitUntilAction, validate_wait_until)
 | 
					@register_action("wait_until", WaitUntilAction, validate_wait_until)
 | 
				
			||||||
def wait_until_action_to_code(config, action_id, template_arg, args):
 | 
					async def wait_until_action_to_code(config, action_id, template_arg, args):
 | 
				
			||||||
    conditions = yield build_condition(config[CONF_CONDITION], template_arg, args)
 | 
					    conditions = await build_condition(config[CONF_CONDITION], template_arg, args)
 | 
				
			||||||
    var = cg.new_Pvariable(action_id, template_arg, conditions)
 | 
					    var = cg.new_Pvariable(action_id, template_arg, conditions)
 | 
				
			||||||
    yield cg.register_component(var, {})
 | 
					    await cg.register_component(var, {})
 | 
				
			||||||
    yield var
 | 
					    return var
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@register_action('lambda', LambdaAction, cv.lambda_)
 | 
					@register_action("lambda", LambdaAction, cv.lambda_)
 | 
				
			||||||
def lambda_action_to_code(config, action_id, template_arg, args):
 | 
					async def lambda_action_to_code(config, action_id, template_arg, args):
 | 
				
			||||||
    lambda_ = yield cg.process_lambda(config, args, return_type=cg.void)
 | 
					    lambda_ = await cg.process_lambda(config, args, return_type=cg.void)
 | 
				
			||||||
    yield cg.new_Pvariable(action_id, template_arg, lambda_)
 | 
					    return cg.new_Pvariable(action_id, template_arg, lambda_)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@register_action('component.update', UpdateComponentAction, maybe_simple_id({
 | 
					@register_action(
 | 
				
			||||||
    cv.Required(CONF_ID): cv.use_id(cg.PollingComponent),
 | 
					    "component.update",
 | 
				
			||||||
}))
 | 
					    UpdateComponentAction,
 | 
				
			||||||
def component_update_action_to_code(config, action_id, template_arg, args):
 | 
					    maybe_simple_id(
 | 
				
			||||||
    comp = yield cg.get_variable(config[CONF_ID])
 | 
					        {
 | 
				
			||||||
    yield cg.new_Pvariable(action_id, template_arg, comp)
 | 
					            cv.Required(CONF_ID): cv.use_id(cg.PollingComponent),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					async def component_update_action_to_code(config, action_id, template_arg, args):
 | 
				
			||||||
 | 
					    comp = await cg.get_variable(config[CONF_ID])
 | 
				
			||||||
 | 
					    return cg.new_Pvariable(action_id, template_arg, comp)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@coroutine
 | 
					async def build_action(full_config, template_arg, args):
 | 
				
			||||||
def build_action(full_config, template_arg, args):
 | 
					    registry_entry, config = cg.extract_registry_entry_config(
 | 
				
			||||||
    registry_entry, config = cg.extract_registry_entry_config(ACTION_REGISTRY, full_config)
 | 
					        ACTION_REGISTRY, full_config
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
    action_id = full_config[CONF_TYPE_ID]
 | 
					    action_id = full_config[CONF_TYPE_ID]
 | 
				
			||||||
    builder = registry_entry.coroutine_fun
 | 
					    builder = registry_entry.coroutine_fun
 | 
				
			||||||
    yield builder(config, action_id, template_arg, args)
 | 
					    ret = await builder(config, action_id, template_arg, args)
 | 
				
			||||||
 | 
					    return ret
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@coroutine
 | 
					async def build_action_list(config, templ, arg_type):
 | 
				
			||||||
def build_action_list(config, templ, arg_type):
 | 
					 | 
				
			||||||
    actions = []
 | 
					    actions = []
 | 
				
			||||||
    for conf in config:
 | 
					    for conf in config:
 | 
				
			||||||
        action = yield build_action(conf, templ, arg_type)
 | 
					        action = await build_action(conf, templ, arg_type)
 | 
				
			||||||
        actions.append(action)
 | 
					        actions.append(action)
 | 
				
			||||||
    yield actions
 | 
					    return actions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@coroutine
 | 
					async def build_condition(full_config, template_arg, args):
 | 
				
			||||||
def build_condition(full_config, template_arg, args):
 | 
					    registry_entry, config = cg.extract_registry_entry_config(
 | 
				
			||||||
    registry_entry, config = cg.extract_registry_entry_config(CONDITION_REGISTRY, full_config)
 | 
					        CONDITION_REGISTRY, full_config
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
    action_id = full_config[CONF_TYPE_ID]
 | 
					    action_id = full_config[CONF_TYPE_ID]
 | 
				
			||||||
    builder = registry_entry.coroutine_fun
 | 
					    builder = registry_entry.coroutine_fun
 | 
				
			||||||
    yield builder(config, action_id, template_arg, args)
 | 
					    ret = await builder(config, action_id, template_arg, args)
 | 
				
			||||||
 | 
					    return ret
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@coroutine
 | 
					async def build_condition_list(config, templ, args):
 | 
				
			||||||
def build_condition_list(config, templ, args):
 | 
					 | 
				
			||||||
    conditions = []
 | 
					    conditions = []
 | 
				
			||||||
    for conf in config:
 | 
					    for conf in config:
 | 
				
			||||||
        condition = yield build_condition(conf, templ, args)
 | 
					        condition = await build_condition(conf, templ, args)
 | 
				
			||||||
        conditions.append(condition)
 | 
					        conditions.append(condition)
 | 
				
			||||||
    yield conditions
 | 
					    return conditions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@coroutine
 | 
					async def build_automation(trigger, args, config):
 | 
				
			||||||
def build_automation(trigger, args, config):
 | 
					 | 
				
			||||||
    arg_types = [arg[0] for arg in args]
 | 
					    arg_types = [arg[0] for arg in args]
 | 
				
			||||||
    templ = cg.TemplateArguments(*arg_types)
 | 
					    templ = cg.TemplateArguments(*arg_types)
 | 
				
			||||||
    obj = cg.new_Pvariable(config[CONF_AUTOMATION_ID], templ, trigger)
 | 
					    obj = cg.new_Pvariable(config[CONF_AUTOMATION_ID], templ, trigger)
 | 
				
			||||||
    actions = yield build_action_list(config[CONF_THEN], templ, args)
 | 
					    actions = await build_action_list(config[CONF_THEN], templ, args)
 | 
				
			||||||
    cg.add(obj.add_actions(actions))
 | 
					    cg.add(obj.add_actions(actions))
 | 
				
			||||||
    yield obj
 | 
					    return obj
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,18 +9,72 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# pylint: disable=unused-import
 | 
					# pylint: disable=unused-import
 | 
				
			||||||
from esphome.cpp_generator import (  # noqa
 | 
					from esphome.cpp_generator import (  # noqa
 | 
				
			||||||
    Expression, RawExpression, RawStatement, TemplateArguments,
 | 
					    Expression,
 | 
				
			||||||
    StructInitializer, ArrayInitializer, safe_exp, Statement, LineComment,
 | 
					    RawExpression,
 | 
				
			||||||
    progmem_array, statement, variable, Pvariable, new_Pvariable,
 | 
					    RawStatement,
 | 
				
			||||||
    add, add_global, add_library, add_build_flag, add_define,
 | 
					    TemplateArguments,
 | 
				
			||||||
    get_variable, get_variable_with_full_id, process_lambda, is_template, templatable, MockObj,
 | 
					    StructInitializer,
 | 
				
			||||||
    MockObjClass)
 | 
					    ArrayInitializer,
 | 
				
			||||||
 | 
					    safe_exp,
 | 
				
			||||||
 | 
					    Statement,
 | 
				
			||||||
 | 
					    LineComment,
 | 
				
			||||||
 | 
					    progmem_array,
 | 
				
			||||||
 | 
					    static_const_array,
 | 
				
			||||||
 | 
					    statement,
 | 
				
			||||||
 | 
					    variable,
 | 
				
			||||||
 | 
					    new_variable,
 | 
				
			||||||
 | 
					    Pvariable,
 | 
				
			||||||
 | 
					    new_Pvariable,
 | 
				
			||||||
 | 
					    add,
 | 
				
			||||||
 | 
					    add_global,
 | 
				
			||||||
 | 
					    add_library,
 | 
				
			||||||
 | 
					    add_build_flag,
 | 
				
			||||||
 | 
					    add_define,
 | 
				
			||||||
 | 
					    get_variable,
 | 
				
			||||||
 | 
					    get_variable_with_full_id,
 | 
				
			||||||
 | 
					    process_lambda,
 | 
				
			||||||
 | 
					    is_template,
 | 
				
			||||||
 | 
					    templatable,
 | 
				
			||||||
 | 
					    MockObj,
 | 
				
			||||||
 | 
					    MockObjClass,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
from esphome.cpp_helpers import (  # noqa
 | 
					from esphome.cpp_helpers import (  # noqa
 | 
				
			||||||
    gpio_pin_expression, register_component, build_registry_entry,
 | 
					    gpio_pin_expression,
 | 
				
			||||||
    build_registry_list, extract_registry_entry_config, register_parented)
 | 
					    register_component,
 | 
				
			||||||
 | 
					    build_registry_entry,
 | 
				
			||||||
 | 
					    build_registry_list,
 | 
				
			||||||
 | 
					    extract_registry_entry_config,
 | 
				
			||||||
 | 
					    register_parented,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
from esphome.cpp_types import (  # noqa
 | 
					from esphome.cpp_types import (  # noqa
 | 
				
			||||||
    global_ns, void, nullptr, float_, double, bool_, std_ns, std_string,
 | 
					    global_ns,
 | 
				
			||||||
    std_vector, uint8, uint16, uint32, int32, const_char_ptr, NAN,
 | 
					    void,
 | 
				
			||||||
    esphome_ns, App, Nameable, Component, ComponentPtr,
 | 
					    nullptr,
 | 
				
			||||||
    PollingComponent, Application, optional, arduino_json_ns, JsonObject,
 | 
					    float_,
 | 
				
			||||||
    JsonObjectRef, JsonObjectConstRef, Controller, GPIOPin)
 | 
					    double,
 | 
				
			||||||
 | 
					    bool_,
 | 
				
			||||||
 | 
					    int_,
 | 
				
			||||||
 | 
					    std_ns,
 | 
				
			||||||
 | 
					    std_string,
 | 
				
			||||||
 | 
					    std_vector,
 | 
				
			||||||
 | 
					    uint8,
 | 
				
			||||||
 | 
					    uint16,
 | 
				
			||||||
 | 
					    uint32,
 | 
				
			||||||
 | 
					    int32,
 | 
				
			||||||
 | 
					    const_char_ptr,
 | 
				
			||||||
 | 
					    NAN,
 | 
				
			||||||
 | 
					    esphome_ns,
 | 
				
			||||||
 | 
					    App,
 | 
				
			||||||
 | 
					    Nameable,
 | 
				
			||||||
 | 
					    Component,
 | 
				
			||||||
 | 
					    ComponentPtr,
 | 
				
			||||||
 | 
					    PollingComponent,
 | 
				
			||||||
 | 
					    Application,
 | 
				
			||||||
 | 
					    optional,
 | 
				
			||||||
 | 
					    arduino_json_ns,
 | 
				
			||||||
 | 
					    JsonObject,
 | 
				
			||||||
 | 
					    JsonObjectRef,
 | 
				
			||||||
 | 
					    JsonObjectConstRef,
 | 
				
			||||||
 | 
					    Controller,
 | 
				
			||||||
 | 
					    GPIOPin,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,6 +11,7 @@ void A4988::setup() {
 | 
				
			|||||||
  if (this->sleep_pin_ != nullptr) {
 | 
					  if (this->sleep_pin_ != nullptr) {
 | 
				
			||||||
    this->sleep_pin_->setup();
 | 
					    this->sleep_pin_->setup();
 | 
				
			||||||
    this->sleep_pin_->digital_write(false);
 | 
					    this->sleep_pin_->digital_write(false);
 | 
				
			||||||
 | 
					    this->sleep_pin_state_ = false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  this->step_pin_->setup();
 | 
					  this->step_pin_->setup();
 | 
				
			||||||
  this->step_pin_->digital_write(false);
 | 
					  this->step_pin_->digital_write(false);
 | 
				
			||||||
@@ -27,7 +28,12 @@ void A4988::dump_config() {
 | 
				
			|||||||
void A4988::loop() {
 | 
					void A4988::loop() {
 | 
				
			||||||
  bool at_target = this->has_reached_target();
 | 
					  bool at_target = this->has_reached_target();
 | 
				
			||||||
  if (this->sleep_pin_ != nullptr) {
 | 
					  if (this->sleep_pin_ != nullptr) {
 | 
				
			||||||
 | 
					    bool sleep_rising_edge = !sleep_pin_state_ & !at_target;
 | 
				
			||||||
    this->sleep_pin_->digital_write(!at_target);
 | 
					    this->sleep_pin_->digital_write(!at_target);
 | 
				
			||||||
 | 
					    this->sleep_pin_state_ = !at_target;
 | 
				
			||||||
 | 
					    if (sleep_rising_edge) {
 | 
				
			||||||
 | 
					      delayMicroseconds(1000);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  if (at_target) {
 | 
					  if (at_target) {
 | 
				
			||||||
    this->high_freq_.stop();
 | 
					    this->high_freq_.stop();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,6 +21,7 @@ class A4988 : public stepper::Stepper, public Component {
 | 
				
			|||||||
  GPIOPin *step_pin_;
 | 
					  GPIOPin *step_pin_;
 | 
				
			||||||
  GPIOPin *dir_pin_;
 | 
					  GPIOPin *dir_pin_;
 | 
				
			||||||
  GPIOPin *sleep_pin_{nullptr};
 | 
					  GPIOPin *sleep_pin_{nullptr};
 | 
				
			||||||
 | 
					  bool sleep_pin_state_;
 | 
				
			||||||
  HighFrequencyLoopRequester high_freq_;
 | 
					  HighFrequencyLoopRequester high_freq_;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,27 +5,29 @@ import esphome.codegen as cg
 | 
				
			|||||||
from esphome.const import CONF_DIR_PIN, CONF_ID, CONF_SLEEP_PIN, CONF_STEP_PIN
 | 
					from esphome.const import CONF_DIR_PIN, CONF_ID, CONF_SLEEP_PIN, CONF_STEP_PIN
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
a4988_ns = cg.esphome_ns.namespace('a4988')
 | 
					a4988_ns = cg.esphome_ns.namespace("a4988")
 | 
				
			||||||
A4988 = a4988_ns.class_('A4988', stepper.Stepper, cg.Component)
 | 
					A4988 = a4988_ns.class_("A4988", stepper.Stepper, cg.Component)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CONFIG_SCHEMA = stepper.STEPPER_SCHEMA.extend({
 | 
					CONFIG_SCHEMA = stepper.STEPPER_SCHEMA.extend(
 | 
				
			||||||
    cv.Required(CONF_ID): cv.declare_id(A4988),
 | 
					    {
 | 
				
			||||||
    cv.Required(CONF_STEP_PIN): pins.gpio_output_pin_schema,
 | 
					        cv.Required(CONF_ID): cv.declare_id(A4988),
 | 
				
			||||||
    cv.Required(CONF_DIR_PIN): pins.gpio_output_pin_schema,
 | 
					        cv.Required(CONF_STEP_PIN): pins.gpio_output_pin_schema,
 | 
				
			||||||
    cv.Optional(CONF_SLEEP_PIN): pins.gpio_output_pin_schema,
 | 
					        cv.Required(CONF_DIR_PIN): pins.gpio_output_pin_schema,
 | 
				
			||||||
}).extend(cv.COMPONENT_SCHEMA)
 | 
					        cv.Optional(CONF_SLEEP_PIN): pins.gpio_output_pin_schema,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					).extend(cv.COMPONENT_SCHEMA)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def to_code(config):
 | 
					async def to_code(config):
 | 
				
			||||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
					    var = cg.new_Pvariable(config[CONF_ID])
 | 
				
			||||||
    yield cg.register_component(var, config)
 | 
					    await cg.register_component(var, config)
 | 
				
			||||||
    yield stepper.register_stepper(var, config)
 | 
					    await stepper.register_stepper(var, config)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    step_pin = yield cg.gpio_pin_expression(config[CONF_STEP_PIN])
 | 
					    step_pin = await cg.gpio_pin_expression(config[CONF_STEP_PIN])
 | 
				
			||||||
    cg.add(var.set_step_pin(step_pin))
 | 
					    cg.add(var.set_step_pin(step_pin))
 | 
				
			||||||
    dir_pin = yield cg.gpio_pin_expression(config[CONF_DIR_PIN])
 | 
					    dir_pin = await cg.gpio_pin_expression(config[CONF_DIR_PIN])
 | 
				
			||||||
    cg.add(var.set_dir_pin(dir_pin))
 | 
					    cg.add(var.set_dir_pin(dir_pin))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if CONF_SLEEP_PIN in config:
 | 
					    if CONF_SLEEP_PIN in config:
 | 
				
			||||||
        sleep_pin = yield cg.gpio_pin_expression(config[CONF_SLEEP_PIN])
 | 
					        sleep_pin = await cg.gpio_pin_expression(config[CONF_SLEEP_PIN])
 | 
				
			||||||
        cg.add(var.set_sleep_pin(sleep_pin))
 | 
					        cg.add(var.set_sleep_pin(sleep_pin))
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										0
									
								
								esphome/components/ac_dimmer/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								esphome/components/ac_dimmer/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										217
									
								
								esphome/components/ac_dimmer/ac_dimmer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										217
									
								
								esphome/components/ac_dimmer/ac_dimmer.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,217 @@
 | 
				
			|||||||
 | 
					#include "ac_dimmer.h"
 | 
				
			||||||
 | 
					#include "esphome/core/helpers.h"
 | 
				
			||||||
 | 
					#include "esphome/core/log.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef ARDUINO_ARCH_ESP8266
 | 
				
			||||||
 | 
					#include <core_esp8266_waveform.h>
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace esphome {
 | 
				
			||||||
 | 
					namespace ac_dimmer {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char *TAG = "ac_dimmer";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Global array to store dimmer objects
 | 
				
			||||||
 | 
					static AcDimmerDataStore *all_dimmers[32];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Time in microseconds the gate should be held high
 | 
				
			||||||
 | 
					/// 10µs should be long enough for most triacs
 | 
				
			||||||
 | 
					/// For reference: BT136 datasheet says 2µs nominal (page 7)
 | 
				
			||||||
 | 
					static uint32_t GATE_ENABLE_TIME = 10;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Function called from timer interrupt
 | 
				
			||||||
 | 
					/// Input is current time in microseconds (micros())
 | 
				
			||||||
 | 
					/// Returns when next "event" is expected in µs, or 0 if no such event known.
 | 
				
			||||||
 | 
					uint32_t ICACHE_RAM_ATTR HOT AcDimmerDataStore::timer_intr(uint32_t now) {
 | 
				
			||||||
 | 
					  // If no ZC signal received yet.
 | 
				
			||||||
 | 
					  if (this->crossed_zero_at == 0)
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  uint32_t time_since_zc = now - this->crossed_zero_at;
 | 
				
			||||||
 | 
					  if (this->value == 65535 || this->value == 0) {
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (this->enable_time_us != 0 && time_since_zc >= this->enable_time_us) {
 | 
				
			||||||
 | 
					    this->enable_time_us = 0;
 | 
				
			||||||
 | 
					    this->gate_pin->digital_write(true);
 | 
				
			||||||
 | 
					    // Prevent too short pulses
 | 
				
			||||||
 | 
					    this->disable_time_us = max(this->disable_time_us, time_since_zc + GATE_ENABLE_TIME);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (this->disable_time_us != 0 && time_since_zc >= this->disable_time_us) {
 | 
				
			||||||
 | 
					    this->disable_time_us = 0;
 | 
				
			||||||
 | 
					    this->gate_pin->digital_write(false);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (time_since_zc < this->enable_time_us)
 | 
				
			||||||
 | 
					    // Next event is enable, return time until that event
 | 
				
			||||||
 | 
					    return this->enable_time_us - time_since_zc;
 | 
				
			||||||
 | 
					  else if (time_since_zc < disable_time_us) {
 | 
				
			||||||
 | 
					    // Next event is disable, return time until that event
 | 
				
			||||||
 | 
					    return this->disable_time_us - time_since_zc;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (time_since_zc >= this->cycle_time_us) {
 | 
				
			||||||
 | 
					    // Already past last cycle time, schedule next call shortly
 | 
				
			||||||
 | 
					    return 100;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return this->cycle_time_us - time_since_zc;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Run timer interrupt code and return in how many µs the next event is expected
 | 
				
			||||||
 | 
					uint32_t ICACHE_RAM_ATTR HOT timer_interrupt() {
 | 
				
			||||||
 | 
					  // run at least with 1kHz
 | 
				
			||||||
 | 
					  uint32_t min_dt_us = 1000;
 | 
				
			||||||
 | 
					  uint32_t now = micros();
 | 
				
			||||||
 | 
					  for (auto *dimmer : all_dimmers) {
 | 
				
			||||||
 | 
					    if (dimmer == nullptr)
 | 
				
			||||||
 | 
					      // no more dimmers
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    uint32_t res = dimmer->timer_intr(now);
 | 
				
			||||||
 | 
					    if (res != 0 && res < min_dt_us)
 | 
				
			||||||
 | 
					      min_dt_us = res;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  // return time until next timer1 interrupt in µs
 | 
				
			||||||
 | 
					  return min_dt_us;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// GPIO interrupt routine, called when ZC pin triggers
 | 
				
			||||||
 | 
					void ICACHE_RAM_ATTR HOT AcDimmerDataStore::gpio_intr() {
 | 
				
			||||||
 | 
					  uint32_t prev_crossed = this->crossed_zero_at;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 50Hz mains frequency should give a half cycle of 10ms a 60Hz will give 8.33ms
 | 
				
			||||||
 | 
					  // in any case the cycle last at least 5ms
 | 
				
			||||||
 | 
					  this->crossed_zero_at = micros();
 | 
				
			||||||
 | 
					  uint32_t cycle_time = this->crossed_zero_at - prev_crossed;
 | 
				
			||||||
 | 
					  if (cycle_time > 5000) {
 | 
				
			||||||
 | 
					    this->cycle_time_us = cycle_time;
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    // Otherwise this is noise and this is 2nd (or 3rd...) fall in the same pulse
 | 
				
			||||||
 | 
					    // Consider this is the right fall edge and accumulate the cycle time instead
 | 
				
			||||||
 | 
					    this->cycle_time_us += cycle_time;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (this->value == 65535) {
 | 
				
			||||||
 | 
					    // fully on, enable output immediately
 | 
				
			||||||
 | 
					    this->gate_pin->digital_write(true);
 | 
				
			||||||
 | 
					  } else if (this->init_cycle) {
 | 
				
			||||||
 | 
					    // send a full cycle
 | 
				
			||||||
 | 
					    this->init_cycle = false;
 | 
				
			||||||
 | 
					    this->enable_time_us = 0;
 | 
				
			||||||
 | 
					    this->disable_time_us = cycle_time_us;
 | 
				
			||||||
 | 
					  } else if (this->value == 0) {
 | 
				
			||||||
 | 
					    // fully off, disable output immediately
 | 
				
			||||||
 | 
					    this->gate_pin->digital_write(false);
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    if (this->method == DIM_METHOD_TRAILING) {
 | 
				
			||||||
 | 
					      this->enable_time_us = 1;  // cannot be 0
 | 
				
			||||||
 | 
					      this->disable_time_us = max((uint32_t) 10, this->value * this->cycle_time_us / 65535);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      // calculate time until enable in µs: (1.0-value)*cycle_time, but with integer arithmetic
 | 
				
			||||||
 | 
					      // also take into account min_power
 | 
				
			||||||
 | 
					      auto min_us = this->cycle_time_us * this->min_power / 1000;
 | 
				
			||||||
 | 
					      this->enable_time_us = max((uint32_t) 1, ((65535 - this->value) * (this->cycle_time_us - min_us)) / 65535);
 | 
				
			||||||
 | 
					      if (this->method == DIM_METHOD_LEADING_PULSE) {
 | 
				
			||||||
 | 
					        // Minimum pulse time should be enough for the triac to trigger when it is close to the ZC zone
 | 
				
			||||||
 | 
					        // this is for brightness near 99%
 | 
				
			||||||
 | 
					        this->disable_time_us = max(this->enable_time_us + GATE_ENABLE_TIME, (uint32_t) cycle_time_us / 10);
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        this->gate_pin->digital_write(false);
 | 
				
			||||||
 | 
					        this->disable_time_us = this->cycle_time_us;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ICACHE_RAM_ATTR HOT AcDimmerDataStore::s_gpio_intr(AcDimmerDataStore *store) {
 | 
				
			||||||
 | 
					  // Attaching pin interrupts on the same pin will override the previous interupt
 | 
				
			||||||
 | 
					  // However, the user expects that multiple dimmers sharing the same ZC pin will work.
 | 
				
			||||||
 | 
					  // We solve this in a bit of a hacky way: On each pin interrupt, we check all dimmers
 | 
				
			||||||
 | 
					  // if any of them are using the same ZC pin, and also trigger the interrupt for *them*.
 | 
				
			||||||
 | 
					  for (auto *dimmer : all_dimmers) {
 | 
				
			||||||
 | 
					    if (dimmer == nullptr)
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    if (dimmer->zero_cross_pin_number == store->zero_cross_pin_number) {
 | 
				
			||||||
 | 
					      dimmer->gpio_intr();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef ARDUINO_ARCH_ESP32
 | 
				
			||||||
 | 
					// ESP32 implementation, uses basically the same code but needs to wrap
 | 
				
			||||||
 | 
					// timer_interrupt() function to auto-reschedule
 | 
				
			||||||
 | 
					static hw_timer_t *dimmer_timer = nullptr;
 | 
				
			||||||
 | 
					void ICACHE_RAM_ATTR HOT AcDimmerDataStore::s_timer_intr() { timer_interrupt(); }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void AcDimmer::setup() {
 | 
				
			||||||
 | 
					  // extend all_dimmers array with our dimmer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Need to be sure the zero cross pin is setup only once, ESP8266 fails and ESP32 seems to fail silently
 | 
				
			||||||
 | 
					  auto setup_zero_cross_pin = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (auto &all_dimmer : all_dimmers) {
 | 
				
			||||||
 | 
					    if (all_dimmer == nullptr) {
 | 
				
			||||||
 | 
					      all_dimmer = &this->store_;
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (all_dimmer->zero_cross_pin_number == this->zero_cross_pin_->get_pin()) {
 | 
				
			||||||
 | 
					      setup_zero_cross_pin = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  this->gate_pin_->setup();
 | 
				
			||||||
 | 
					  this->store_.gate_pin = this->gate_pin_->to_isr();
 | 
				
			||||||
 | 
					  this->store_.zero_cross_pin_number = this->zero_cross_pin_->get_pin();
 | 
				
			||||||
 | 
					  this->store_.min_power = static_cast<uint16_t>(this->min_power_ * 1000);
 | 
				
			||||||
 | 
					  this->min_power_ = 0;
 | 
				
			||||||
 | 
					  this->store_.method = this->method_;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (setup_zero_cross_pin) {
 | 
				
			||||||
 | 
					    this->zero_cross_pin_->setup();
 | 
				
			||||||
 | 
					    this->store_.zero_cross_pin = this->zero_cross_pin_->to_isr();
 | 
				
			||||||
 | 
					    this->zero_cross_pin_->attach_interrupt(&AcDimmerDataStore::s_gpio_intr, &this->store_, FALLING);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef ARDUINO_ARCH_ESP8266
 | 
				
			||||||
 | 
					  // Uses ESP8266 waveform (soft PWM) class
 | 
				
			||||||
 | 
					  // PWM and AcDimmer can even run at the same time this way
 | 
				
			||||||
 | 
					  setTimer1Callback(&timer_interrupt);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#ifdef ARDUINO_ARCH_ESP32
 | 
				
			||||||
 | 
					  // 80 Divider -> 1 count=1µs
 | 
				
			||||||
 | 
					  dimmer_timer = timerBegin(0, 80, true);
 | 
				
			||||||
 | 
					  timerAttachInterrupt(dimmer_timer, &AcDimmerDataStore::s_timer_intr, true);
 | 
				
			||||||
 | 
					  // For ESP32, we can't use dynamic interval calculation because the timerX functions
 | 
				
			||||||
 | 
					  // are not callable from ISR (placed in flash storage).
 | 
				
			||||||
 | 
					  // Here we just use an interrupt firing every 50 µs.
 | 
				
			||||||
 | 
					  timerAlarmWrite(dimmer_timer, 50, true);
 | 
				
			||||||
 | 
					  timerAlarmEnable(dimmer_timer);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					void AcDimmer::write_state(float state) {
 | 
				
			||||||
 | 
					  auto new_value = static_cast<uint16_t>(roundf(state * 65535));
 | 
				
			||||||
 | 
					  if (new_value != 0 && this->store_.value == 0)
 | 
				
			||||||
 | 
					    this->store_.init_cycle = this->init_with_half_cycle_;
 | 
				
			||||||
 | 
					  this->store_.value = new_value;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					void AcDimmer::dump_config() {
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "AcDimmer:");
 | 
				
			||||||
 | 
					  LOG_PIN("  Output Pin: ", this->gate_pin_);
 | 
				
			||||||
 | 
					  LOG_PIN("  Zero-Cross Pin: ", this->zero_cross_pin_);
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "   Min Power: %.1f%%", this->store_.min_power / 10.0f);
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "   Init with half cycle: %s", YESNO(this->init_with_half_cycle_));
 | 
				
			||||||
 | 
					  if (method_ == DIM_METHOD_LEADING_PULSE)
 | 
				
			||||||
 | 
					    ESP_LOGCONFIG(TAG, "   Method: leading pulse");
 | 
				
			||||||
 | 
					  else if (method_ == DIM_METHOD_LEADING)
 | 
				
			||||||
 | 
					    ESP_LOGCONFIG(TAG, "   Method: leading");
 | 
				
			||||||
 | 
					  else
 | 
				
			||||||
 | 
					    ESP_LOGCONFIG(TAG, "   Method: trailing");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  LOG_FLOAT_OUTPUT(this);
 | 
				
			||||||
 | 
					  ESP_LOGV(TAG, "  Estimated Frequency: %.3fHz", 1e6f / this->store_.cycle_time_us / 2);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace ac_dimmer
 | 
				
			||||||
 | 
					}  // namespace esphome
 | 
				
			||||||
							
								
								
									
										66
									
								
								esphome/components/ac_dimmer/ac_dimmer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								esphome/components/ac_dimmer/ac_dimmer.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,66 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "esphome/core/component.h"
 | 
				
			||||||
 | 
					#include "esphome/core/esphal.h"
 | 
				
			||||||
 | 
					#include "esphome/components/output/float_output.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace esphome {
 | 
				
			||||||
 | 
					namespace ac_dimmer {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum DimMethod { DIM_METHOD_LEADING_PULSE = 0, DIM_METHOD_LEADING, DIM_METHOD_TRAILING };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct AcDimmerDataStore {
 | 
				
			||||||
 | 
					  /// Zero-cross pin
 | 
				
			||||||
 | 
					  ISRInternalGPIOPin *zero_cross_pin;
 | 
				
			||||||
 | 
					  /// Zero-cross pin number - used to share ZC pin across multiple dimmers
 | 
				
			||||||
 | 
					  uint8_t zero_cross_pin_number;
 | 
				
			||||||
 | 
					  /// Output pin to write to
 | 
				
			||||||
 | 
					  ISRInternalGPIOPin *gate_pin;
 | 
				
			||||||
 | 
					  /// Value of the dimmer - 0 to 65535.
 | 
				
			||||||
 | 
					  uint16_t value;
 | 
				
			||||||
 | 
					  /// Minimum power for activation
 | 
				
			||||||
 | 
					  uint16_t min_power;
 | 
				
			||||||
 | 
					  /// Time between the last two ZC pulses
 | 
				
			||||||
 | 
					  uint32_t cycle_time_us;
 | 
				
			||||||
 | 
					  /// Time (in micros()) of last ZC signal
 | 
				
			||||||
 | 
					  uint32_t crossed_zero_at;
 | 
				
			||||||
 | 
					  /// Time since last ZC pulse to enable gate pin. 0 means not set.
 | 
				
			||||||
 | 
					  uint32_t enable_time_us;
 | 
				
			||||||
 | 
					  /// Time since last ZC pulse to disable gate pin. 0 means no disable.
 | 
				
			||||||
 | 
					  uint32_t disable_time_us;
 | 
				
			||||||
 | 
					  /// Set to send the first half ac cycle complete
 | 
				
			||||||
 | 
					  bool init_cycle;
 | 
				
			||||||
 | 
					  /// Dimmer method
 | 
				
			||||||
 | 
					  DimMethod method;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  uint32_t timer_intr(uint32_t now);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void gpio_intr();
 | 
				
			||||||
 | 
					  static void s_gpio_intr(AcDimmerDataStore *store);
 | 
				
			||||||
 | 
					#ifdef ARDUINO_ARCH_ESP32
 | 
				
			||||||
 | 
					  static void s_timer_intr();
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AcDimmer : public output::FloatOutput, public Component {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  void setup() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void dump_config() override;
 | 
				
			||||||
 | 
					  void set_gate_pin(GPIOPin *gate_pin) { gate_pin_ = gate_pin; }
 | 
				
			||||||
 | 
					  void set_zero_cross_pin(GPIOPin *zero_cross_pin) { zero_cross_pin_ = zero_cross_pin; }
 | 
				
			||||||
 | 
					  void set_init_with_half_cycle(bool init_with_half_cycle) { init_with_half_cycle_ = init_with_half_cycle; }
 | 
				
			||||||
 | 
					  void set_method(DimMethod method) { method_ = method; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 protected:
 | 
				
			||||||
 | 
					  void write_state(float state) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  GPIOPin *gate_pin_;
 | 
				
			||||||
 | 
					  GPIOPin *zero_cross_pin_;
 | 
				
			||||||
 | 
					  AcDimmerDataStore store_;
 | 
				
			||||||
 | 
					  bool init_with_half_cycle_;
 | 
				
			||||||
 | 
					  DimMethod method_;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace ac_dimmer
 | 
				
			||||||
 | 
					}  // namespace esphome
 | 
				
			||||||
							
								
								
									
										49
									
								
								esphome/components/ac_dimmer/output.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								esphome/components/ac_dimmer/output.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,49 @@
 | 
				
			|||||||
 | 
					import esphome.codegen as cg
 | 
				
			||||||
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
 | 
					from esphome import pins
 | 
				
			||||||
 | 
					from esphome.components import output
 | 
				
			||||||
 | 
					from esphome.const import CONF_ID, CONF_MIN_POWER, CONF_METHOD
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CODEOWNERS = ["@glmnet"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ac_dimmer_ns = cg.esphome_ns.namespace("ac_dimmer")
 | 
				
			||||||
 | 
					AcDimmer = ac_dimmer_ns.class_("AcDimmer", output.FloatOutput, cg.Component)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DimMethod = ac_dimmer_ns.enum("DimMethod")
 | 
				
			||||||
 | 
					DIM_METHODS = {
 | 
				
			||||||
 | 
					    "LEADING_PULSE": DimMethod.DIM_METHOD_LEADING_PULSE,
 | 
				
			||||||
 | 
					    "LEADING": DimMethod.DIM_METHOD_LEADING,
 | 
				
			||||||
 | 
					    "TRAILING": DimMethod.DIM_METHOD_TRAILING,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CONF_GATE_PIN = "gate_pin"
 | 
				
			||||||
 | 
					CONF_ZERO_CROSS_PIN = "zero_cross_pin"
 | 
				
			||||||
 | 
					CONF_INIT_WITH_HALF_CYCLE = "init_with_half_cycle"
 | 
				
			||||||
 | 
					CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        cv.Required(CONF_ID): cv.declare_id(AcDimmer),
 | 
				
			||||||
 | 
					        cv.Required(CONF_GATE_PIN): pins.internal_gpio_output_pin_schema,
 | 
				
			||||||
 | 
					        cv.Required(CONF_ZERO_CROSS_PIN): pins.internal_gpio_input_pin_schema,
 | 
				
			||||||
 | 
					        cv.Optional(CONF_INIT_WITH_HALF_CYCLE, default=True): cv.boolean,
 | 
				
			||||||
 | 
					        cv.Optional(CONF_METHOD, default="leading pulse"): cv.enum(
 | 
				
			||||||
 | 
					            DIM_METHODS, upper=True, space="_"
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					).extend(cv.COMPONENT_SCHEMA)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async def to_code(config):
 | 
				
			||||||
 | 
					    var = cg.new_Pvariable(config[CONF_ID])
 | 
				
			||||||
 | 
					    await cg.register_component(var, config)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # override default min power to 10%
 | 
				
			||||||
 | 
					    if CONF_MIN_POWER not in config:
 | 
				
			||||||
 | 
					        config[CONF_MIN_POWER] = 0.1
 | 
				
			||||||
 | 
					    await output.register_output(var, config)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pin = await cg.gpio_pin_expression(config[CONF_GATE_PIN])
 | 
				
			||||||
 | 
					    cg.add(var.set_gate_pin(pin))
 | 
				
			||||||
 | 
					    pin = await cg.gpio_pin_expression(config[CONF_ZERO_CROSS_PIN])
 | 
				
			||||||
 | 
					    cg.add(var.set_zero_cross_pin(pin))
 | 
				
			||||||
 | 
					    cg.add(var.set_init_with_half_cycle(config[CONF_INIT_WITH_HALF_CYCLE]))
 | 
				
			||||||
 | 
					    cg.add(var.set_method(config[CONF_METHOD]))
 | 
				
			||||||
							
								
								
									
										27
									
								
								esphome/components/adalight/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								esphome/components/adalight/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					import esphome.codegen as cg
 | 
				
			||||||
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
 | 
					from esphome.components import uart
 | 
				
			||||||
 | 
					from esphome.components.light.types import AddressableLightEffect
 | 
				
			||||||
 | 
					from esphome.components.light.effects import register_addressable_effect
 | 
				
			||||||
 | 
					from esphome.const import CONF_NAME, CONF_UART_ID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEPENDENCIES = ["uart"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					adalight_ns = cg.esphome_ns.namespace("adalight")
 | 
				
			||||||
 | 
					AdalightLightEffect = adalight_ns.class_(
 | 
				
			||||||
 | 
					    "AdalightLightEffect", uart.UARTDevice, AddressableLightEffect
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CONFIG_SCHEMA = cv.Schema({})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@register_addressable_effect(
 | 
				
			||||||
 | 
					    "adalight",
 | 
				
			||||||
 | 
					    AdalightLightEffect,
 | 
				
			||||||
 | 
					    "Adalight",
 | 
				
			||||||
 | 
					    {cv.GenerateID(CONF_UART_ID): cv.use_id(uart.UARTComponent)},
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					async def adalight_light_effect_to_code(config, effect_id):
 | 
				
			||||||
 | 
					    effect = cg.new_Pvariable(effect_id, config[CONF_NAME])
 | 
				
			||||||
 | 
					    await uart.register_uart_device(effect, config)
 | 
				
			||||||
 | 
					    return effect
 | 
				
			||||||
							
								
								
									
										140
									
								
								esphome/components/adalight/adalight_light_effect.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								esphome/components/adalight/adalight_light_effect.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,140 @@
 | 
				
			|||||||
 | 
					#include "adalight_light_effect.h"
 | 
				
			||||||
 | 
					#include "esphome/core/log.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace esphome {
 | 
				
			||||||
 | 
					namespace adalight {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char *TAG = "adalight_light_effect";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const uint32_t ADALIGHT_ACK_INTERVAL = 1000;
 | 
				
			||||||
 | 
					static const uint32_t ADALIGHT_RECEIVE_TIMEOUT = 1000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					AdalightLightEffect::AdalightLightEffect(const std::string &name) : AddressableLightEffect(name) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void AdalightLightEffect::start() {
 | 
				
			||||||
 | 
					  AddressableLightEffect::start();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  last_ack_ = 0;
 | 
				
			||||||
 | 
					  last_byte_ = 0;
 | 
				
			||||||
 | 
					  last_reset_ = 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void AdalightLightEffect::stop() {
 | 
				
			||||||
 | 
					  frame_.resize(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  AddressableLightEffect::stop();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int AdalightLightEffect::get_frame_size_(int led_count) const {
 | 
				
			||||||
 | 
					  // 3 bytes: Ada
 | 
				
			||||||
 | 
					  // 2 bytes: LED count
 | 
				
			||||||
 | 
					  // 1 byte: checksum
 | 
				
			||||||
 | 
					  // 3 bytes per LED
 | 
				
			||||||
 | 
					  return 3 + 2 + 1 + led_count * 3;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void AdalightLightEffect::reset_frame_(light::AddressableLight &it) {
 | 
				
			||||||
 | 
					  int buffer_capacity = get_frame_size_(it.size());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  frame_.clear();
 | 
				
			||||||
 | 
					  frame_.reserve(buffer_capacity);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void AdalightLightEffect::blank_all_leds_(light::AddressableLight &it) {
 | 
				
			||||||
 | 
					  for (int led = it.size(); led-- > 0;) {
 | 
				
			||||||
 | 
					    it[led].set(COLOR_BLACK);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void AdalightLightEffect::apply(light::AddressableLight &it, const Color ¤t_color) {
 | 
				
			||||||
 | 
					  const uint32_t now = millis();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (now - this->last_ack_ >= ADALIGHT_ACK_INTERVAL) {
 | 
				
			||||||
 | 
					    ESP_LOGV(TAG, "Sending ACK");
 | 
				
			||||||
 | 
					    this->write_str("Ada\n");
 | 
				
			||||||
 | 
					    this->last_ack_ = now;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!this->last_reset_) {
 | 
				
			||||||
 | 
					    ESP_LOGW(TAG, "Frame: Reset.");
 | 
				
			||||||
 | 
					    reset_frame_(it);
 | 
				
			||||||
 | 
					    blank_all_leds_(it);
 | 
				
			||||||
 | 
					    this->last_reset_ = now;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!this->frame_.empty() && now - this->last_byte_ >= ADALIGHT_RECEIVE_TIMEOUT) {
 | 
				
			||||||
 | 
					    ESP_LOGW(TAG, "Frame: Receive timeout (size=%zu).", this->frame_.size());
 | 
				
			||||||
 | 
					    reset_frame_(it);
 | 
				
			||||||
 | 
					    blank_all_leds_(it);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (this->available() > 0) {
 | 
				
			||||||
 | 
					    ESP_LOGV(TAG, "Frame: Available (size=%d).", this->available());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  while (this->available() != 0) {
 | 
				
			||||||
 | 
					    uint8_t data;
 | 
				
			||||||
 | 
					    if (!this->read_byte(&data))
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    this->frame_.push_back(data);
 | 
				
			||||||
 | 
					    this->last_byte_ = now;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    switch (this->parse_frame_(it)) {
 | 
				
			||||||
 | 
					      case INVALID:
 | 
				
			||||||
 | 
					        ESP_LOGD(TAG, "Frame: Invalid (size=%zu, first=%d).", this->frame_.size(), this->frame_[0]);
 | 
				
			||||||
 | 
					        reset_frame_(it);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      case PARTIAL:
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      case CONSUMED:
 | 
				
			||||||
 | 
					        ESP_LOGV(TAG, "Frame: Consumed (size=%zu).", this->frame_.size());
 | 
				
			||||||
 | 
					        reset_frame_(it);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					AdalightLightEffect::Frame AdalightLightEffect::parse_frame_(light::AddressableLight &it) {
 | 
				
			||||||
 | 
					  if (frame_.empty())
 | 
				
			||||||
 | 
					    return INVALID;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Check header: `Ada`
 | 
				
			||||||
 | 
					  if (frame_[0] != 'A')
 | 
				
			||||||
 | 
					    return INVALID;
 | 
				
			||||||
 | 
					  if (frame_.size() > 1 && frame_[1] != 'd')
 | 
				
			||||||
 | 
					    return INVALID;
 | 
				
			||||||
 | 
					  if (frame_.size() > 2 && frame_[2] != 'a')
 | 
				
			||||||
 | 
					    return INVALID;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 3 bytes: Count Hi, Count Lo, Checksum
 | 
				
			||||||
 | 
					  if (frame_.size() < 6)
 | 
				
			||||||
 | 
					    return PARTIAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Check checksum
 | 
				
			||||||
 | 
					  uint16_t checksum = frame_[3] ^ frame_[4] ^ 0x55;
 | 
				
			||||||
 | 
					  if (checksum != frame_[5])
 | 
				
			||||||
 | 
					    return INVALID;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Check if we received the full frame
 | 
				
			||||||
 | 
					  uint16_t led_count = (frame_[3] << 8) + frame_[4] + 1;
 | 
				
			||||||
 | 
					  auto buffer_size = get_frame_size_(led_count);
 | 
				
			||||||
 | 
					  if (frame_.size() < buffer_size)
 | 
				
			||||||
 | 
					    return PARTIAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Apply lights
 | 
				
			||||||
 | 
					  auto accepted_led_count = std::min<int>(led_count, it.size());
 | 
				
			||||||
 | 
					  uint8_t *led_data = &frame_[6];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (int led = 0; led < accepted_led_count; led++, led_data += 3) {
 | 
				
			||||||
 | 
					    auto white = std::min(std::min(led_data[0], led_data[1]), led_data[2]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it[led].set(Color(led_data[0], led_data[1], led_data[2], white));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return CONSUMED;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace adalight
 | 
				
			||||||
 | 
					}  // namespace esphome
 | 
				
			||||||
							
								
								
									
										41
									
								
								esphome/components/adalight/adalight_light_effect.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								esphome/components/adalight/adalight_light_effect.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "esphome/core/component.h"
 | 
				
			||||||
 | 
					#include "esphome/components/light/addressable_light_effect.h"
 | 
				
			||||||
 | 
					#include "esphome/components/uart/uart.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <vector>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace esphome {
 | 
				
			||||||
 | 
					namespace adalight {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AdalightLightEffect : public light::AddressableLightEffect, public uart::UARTDevice {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  AdalightLightEffect(const std::string &name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  void start() override;
 | 
				
			||||||
 | 
					  void stop() override;
 | 
				
			||||||
 | 
					  void apply(light::AddressableLight &it, const Color ¤t_color) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 protected:
 | 
				
			||||||
 | 
					  enum Frame {
 | 
				
			||||||
 | 
					    INVALID,
 | 
				
			||||||
 | 
					    PARTIAL,
 | 
				
			||||||
 | 
					    CONSUMED,
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  int get_frame_size_(int led_count) const;
 | 
				
			||||||
 | 
					  void reset_frame_(light::AddressableLight &it);
 | 
				
			||||||
 | 
					  void blank_all_leds_(light::AddressableLight &it);
 | 
				
			||||||
 | 
					  Frame parse_frame_(light::AddressableLight &it);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 protected:
 | 
				
			||||||
 | 
					  uint32_t last_ack_{0};
 | 
				
			||||||
 | 
					  uint32_t last_byte_{0};
 | 
				
			||||||
 | 
					  uint32_t last_reset_{0};
 | 
				
			||||||
 | 
					  std::vector<uint8_t> frame_;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace adalight
 | 
				
			||||||
 | 
					}  // namespace esphome
 | 
				
			||||||
@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					CODEOWNERS = ["@esphome/core"]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,7 +16,9 @@ void ADCSensor::set_attenuation(adc_attenuation_t attenuation) { this->attenuati
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void ADCSensor::setup() {
 | 
					void ADCSensor::setup() {
 | 
				
			||||||
  ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
 | 
					  ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
 | 
				
			||||||
 | 
					#ifndef USE_ADC_SENSOR_VCC
 | 
				
			||||||
  GPIOPin(this->pin_, INPUT).setup();
 | 
					  GPIOPin(this->pin_, INPUT).setup();
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef ARDUINO_ARCH_ESP32
 | 
					#ifdef ARDUINO_ARCH_ESP32
 | 
				
			||||||
  analogSetPinAttenuation(this->pin_, this->attenuation_);
 | 
					  analogSetPinAttenuation(this->pin_, this->attenuation_);
 | 
				
			||||||
@@ -58,7 +60,7 @@ void ADCSensor::update() {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
float ADCSensor::sample() {
 | 
					float ADCSensor::sample() {
 | 
				
			||||||
#ifdef ARDUINO_ARCH_ESP32
 | 
					#ifdef ARDUINO_ARCH_ESP32
 | 
				
			||||||
  float value_v = analogRead(this->pin_) / 4095.0f;
 | 
					  float value_v = analogRead(this->pin_) / 4095.0f;  // NOLINT
 | 
				
			||||||
  switch (this->attenuation_) {
 | 
					  switch (this->attenuation_) {
 | 
				
			||||||
    case ADC_0db:
 | 
					    case ADC_0db:
 | 
				
			||||||
      value_v *= 1.1;
 | 
					      value_v *= 1.1;
 | 
				
			||||||
@@ -80,7 +82,7 @@ float ADCSensor::sample() {
 | 
				
			|||||||
#ifdef USE_ADC_SENSOR_VCC
 | 
					#ifdef USE_ADC_SENSOR_VCC
 | 
				
			||||||
  return ESP.getVcc() / 1024.0f;
 | 
					  return ESP.getVcc() / 1024.0f;
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
  return analogRead(this->pin_) / 1024.0f;
 | 
					  return analogRead(this->pin_) / 1024.0f;  // NOLINT
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,45 +2,63 @@ import esphome.codegen as cg
 | 
				
			|||||||
import esphome.config_validation as cv
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
from esphome import pins
 | 
					from esphome import pins
 | 
				
			||||||
from esphome.components import sensor, voltage_sampler
 | 
					from esphome.components import sensor, voltage_sampler
 | 
				
			||||||
from esphome.const import CONF_ATTENUATION, CONF_ID, CONF_PIN, ICON_FLASH, UNIT_VOLT
 | 
					from esphome.const import (
 | 
				
			||||||
 | 
					    CONF_ATTENUATION,
 | 
				
			||||||
 | 
					    CONF_ID,
 | 
				
			||||||
 | 
					    CONF_PIN,
 | 
				
			||||||
 | 
					    DEVICE_CLASS_VOLTAGE,
 | 
				
			||||||
 | 
					    ICON_EMPTY,
 | 
				
			||||||
 | 
					    STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					    UNIT_VOLT,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
AUTO_LOAD = ['voltage_sampler']
 | 
					AUTO_LOAD = ["voltage_sampler"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ATTENUATION_MODES = {
 | 
					ATTENUATION_MODES = {
 | 
				
			||||||
    '0db': cg.global_ns.ADC_0db,
 | 
					    "0db": cg.global_ns.ADC_0db,
 | 
				
			||||||
    '2.5db': cg.global_ns.ADC_2_5db,
 | 
					    "2.5db": cg.global_ns.ADC_2_5db,
 | 
				
			||||||
    '6db': cg.global_ns.ADC_6db,
 | 
					    "6db": cg.global_ns.ADC_6db,
 | 
				
			||||||
    '11db': cg.global_ns.ADC_11db,
 | 
					    "11db": cg.global_ns.ADC_11db,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def validate_adc_pin(value):
 | 
					def validate_adc_pin(value):
 | 
				
			||||||
    vcc = str(value).upper()
 | 
					    vcc = str(value).upper()
 | 
				
			||||||
    if vcc == 'VCC':
 | 
					    if vcc == "VCC":
 | 
				
			||||||
        return cv.only_on_esp8266(vcc)
 | 
					        return cv.only_on_esp8266(vcc)
 | 
				
			||||||
    return pins.analog_pin(value)
 | 
					    return pins.analog_pin(value)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
adc_ns = cg.esphome_ns.namespace('adc')
 | 
					adc_ns = cg.esphome_ns.namespace("adc")
 | 
				
			||||||
ADCSensor = adc_ns.class_('ADCSensor', sensor.Sensor, cg.PollingComponent,
 | 
					ADCSensor = adc_ns.class_(
 | 
				
			||||||
                          voltage_sampler.VoltageSampler)
 | 
					    "ADCSensor", sensor.Sensor, cg.PollingComponent, voltage_sampler.VoltageSampler
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CONFIG_SCHEMA = sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 2).extend({
 | 
					CONFIG_SCHEMA = (
 | 
				
			||||||
    cv.GenerateID(): cv.declare_id(ADCSensor),
 | 
					    sensor.sensor_schema(
 | 
				
			||||||
    cv.Required(CONF_PIN): validate_adc_pin,
 | 
					        UNIT_VOLT, ICON_EMPTY, 2, DEVICE_CLASS_VOLTAGE, STATE_CLASS_MEASUREMENT
 | 
				
			||||||
    cv.SplitDefault(CONF_ATTENUATION, esp32='0db'):
 | 
					    )
 | 
				
			||||||
        cv.All(cv.only_on_esp32, cv.enum(ATTENUATION_MODES, lower=True)),
 | 
					    .extend(
 | 
				
			||||||
}).extend(cv.polling_component_schema('60s'))
 | 
					        {
 | 
				
			||||||
 | 
					            cv.GenerateID(): cv.declare_id(ADCSensor),
 | 
				
			||||||
 | 
					            cv.Required(CONF_PIN): validate_adc_pin,
 | 
				
			||||||
 | 
					            cv.SplitDefault(CONF_ATTENUATION, esp32="0db"): cv.All(
 | 
				
			||||||
 | 
					                cv.only_on_esp32, cv.enum(ATTENUATION_MODES, lower=True)
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    .extend(cv.polling_component_schema("60s"))
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def to_code(config):
 | 
					async def to_code(config):
 | 
				
			||||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
					    var = cg.new_Pvariable(config[CONF_ID])
 | 
				
			||||||
    yield cg.register_component(var, config)
 | 
					    await cg.register_component(var, config)
 | 
				
			||||||
    yield sensor.register_sensor(var, config)
 | 
					    await sensor.register_sensor(var, config)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if config[CONF_PIN] == 'VCC':
 | 
					    if config[CONF_PIN] == "VCC":
 | 
				
			||||||
        cg.add_define('USE_ADC_SENSOR_VCC')
 | 
					        cg.add_define("USE_ADC_SENSOR_VCC")
 | 
				
			||||||
    else:
 | 
					    else:
 | 
				
			||||||
        cg.add(var.set_pin(config[CONF_PIN]))
 | 
					        cg.add(var.set_pin(config[CONF_PIN]))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										0
									
								
								esphome/components/addressable_light/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								esphome/components/addressable_light/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,67 @@
 | 
				
			|||||||
 | 
					#include "addressable_light_display.h"
 | 
				
			||||||
 | 
					#include "esphome/core/log.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace esphome {
 | 
				
			||||||
 | 
					namespace addressable_light {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char* TAG = "addressable_light.display";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int AddressableLightDisplay::get_width_internal() { return this->width_; }
 | 
				
			||||||
 | 
					int AddressableLightDisplay::get_height_internal() { return this->height_; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void AddressableLightDisplay::setup() {
 | 
				
			||||||
 | 
					  this->addressable_light_buffer_.resize(this->width_ * this->height_, {0, 0, 0, 0});
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void AddressableLightDisplay::update() {
 | 
				
			||||||
 | 
					  if (!this->enabled_)
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  this->do_update_();
 | 
				
			||||||
 | 
					  this->display();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void AddressableLightDisplay::display() {
 | 
				
			||||||
 | 
					  bool dirty = false;
 | 
				
			||||||
 | 
					  uint8_t old_r, old_g, old_b, old_w;
 | 
				
			||||||
 | 
					  Color* c;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (uint32_t offset = 0; offset < this->addressable_light_buffer_.size(); offset++) {
 | 
				
			||||||
 | 
					    c = &(this->addressable_light_buffer_[offset]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    light::ESPColorView pixel = (*this->light_)[offset];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Track the original values for the pixel view. If it has changed updating, then
 | 
				
			||||||
 | 
					    // we trigger a redraw. Avoiding redraws == avoiding flicker!
 | 
				
			||||||
 | 
					    old_r = pixel.get_red();
 | 
				
			||||||
 | 
					    old_g = pixel.get_green();
 | 
				
			||||||
 | 
					    old_b = pixel.get_blue();
 | 
				
			||||||
 | 
					    old_w = pixel.get_white();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pixel.set_rgbw(c->r, c->g, c->b, c->w);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // If the actual value of the pixel changed, then schedule a redraw.
 | 
				
			||||||
 | 
					    if (pixel.get_red() != old_r || pixel.get_green() != old_g || pixel.get_blue() != old_b ||
 | 
				
			||||||
 | 
					        pixel.get_white() != old_w) {
 | 
				
			||||||
 | 
					      dirty = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (dirty) {
 | 
				
			||||||
 | 
					    this->light_->schedule_show();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void HOT AddressableLightDisplay::draw_absolute_pixel_internal(int x, int y, Color color) {
 | 
				
			||||||
 | 
					  if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0)
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (this->pixel_mapper_f_.has_value()) {
 | 
				
			||||||
 | 
					    // Params are passed by reference, so they may be modified in call.
 | 
				
			||||||
 | 
					    this->addressable_light_buffer_[(*this->pixel_mapper_f_)(x, y)] = color;
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    this->addressable_light_buffer_[y * this->get_width_internal() + x] = color;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					}  // namespace addressable_light
 | 
				
			||||||
 | 
					}  // namespace esphome
 | 
				
			||||||
@@ -0,0 +1,59 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "esphome/core/component.h"
 | 
				
			||||||
 | 
					#include "esphome/core/color.h"
 | 
				
			||||||
 | 
					#include "esphome/components/display/display_buffer.h"
 | 
				
			||||||
 | 
					#include "esphome/components/light/addressable_light.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace esphome {
 | 
				
			||||||
 | 
					namespace addressable_light {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AddressableLightDisplay : public display::DisplayBuffer, public PollingComponent {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  light::AddressableLight *get_light() const { return this->light_; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void set_width(int32_t width) { width_ = width; }
 | 
				
			||||||
 | 
					  void set_height(int32_t height) { height_ = height; }
 | 
				
			||||||
 | 
					  void set_light(light::LightState *state) {
 | 
				
			||||||
 | 
					    light_state_ = state;
 | 
				
			||||||
 | 
					    light_ = static_cast<light::AddressableLight *>(state->get_output());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  void set_enabled(bool enabled) {
 | 
				
			||||||
 | 
					    if (light_state_) {
 | 
				
			||||||
 | 
					      if (enabled_ && !enabled) {  // enabled -> disabled
 | 
				
			||||||
 | 
					        // - Tell the parent light to refresh, effectively wiping the display. Also
 | 
				
			||||||
 | 
					        //   restores the previous effect (if any).
 | 
				
			||||||
 | 
					        light_state_->make_call().set_effect(this->last_effect_).perform();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      } else if (!enabled_ && enabled) {  // disabled -> enabled
 | 
				
			||||||
 | 
					        // - Save the current effect.
 | 
				
			||||||
 | 
					        this->last_effect_ = light_state_->get_effect_name();
 | 
				
			||||||
 | 
					        // - Disable any current effect.
 | 
				
			||||||
 | 
					        light_state_->make_call().set_effect(0).perform();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    enabled_ = enabled;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  bool get_enabled() { return enabled_; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void set_pixel_mapper(std::function<int(int, int)> &&pixel_mapper_f) { this->pixel_mapper_f_ = pixel_mapper_f; }
 | 
				
			||||||
 | 
					  void setup() override;
 | 
				
			||||||
 | 
					  void display();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 protected:
 | 
				
			||||||
 | 
					  int get_width_internal() override;
 | 
				
			||||||
 | 
					  int get_height_internal() override;
 | 
				
			||||||
 | 
					  void draw_absolute_pixel_internal(int x, int y, Color color) override;
 | 
				
			||||||
 | 
					  void update() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  light::LightState *light_state_;
 | 
				
			||||||
 | 
					  light::AddressableLight *light_;
 | 
				
			||||||
 | 
					  bool enabled_{true};
 | 
				
			||||||
 | 
					  int32_t width_;
 | 
				
			||||||
 | 
					  int32_t height_;
 | 
				
			||||||
 | 
					  std::vector<Color> addressable_light_buffer_;
 | 
				
			||||||
 | 
					  optional<std::string> last_effect_;
 | 
				
			||||||
 | 
					  optional<std::function<int(int, int)>> pixel_mapper_f_;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					}  // namespace addressable_light
 | 
				
			||||||
 | 
					}  // namespace esphome
 | 
				
			||||||
							
								
								
									
										63
									
								
								esphome/components/addressable_light/display.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								esphome/components/addressable_light/display.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,63 @@
 | 
				
			|||||||
 | 
					import esphome.codegen as cg
 | 
				
			||||||
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
 | 
					from esphome.components import display, light
 | 
				
			||||||
 | 
					from esphome.const import (
 | 
				
			||||||
 | 
					    CONF_ID,
 | 
				
			||||||
 | 
					    CONF_LAMBDA,
 | 
				
			||||||
 | 
					    CONF_PAGES,
 | 
				
			||||||
 | 
					    CONF_ADDRESSABLE_LIGHT_ID,
 | 
				
			||||||
 | 
					    CONF_HEIGHT,
 | 
				
			||||||
 | 
					    CONF_WIDTH,
 | 
				
			||||||
 | 
					    CONF_UPDATE_INTERVAL,
 | 
				
			||||||
 | 
					    CONF_PIXEL_MAPPER,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CODEOWNERS = ["@justfalter"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					addressable_light_ns = cg.esphome_ns.namespace("addressable_light")
 | 
				
			||||||
 | 
					AddressableLightDisplay = addressable_light_ns.class_(
 | 
				
			||||||
 | 
					    "AddressableLightDisplay", display.DisplayBuffer, cg.PollingComponent
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CONFIG_SCHEMA = cv.All(
 | 
				
			||||||
 | 
					    display.FULL_DISPLAY_SCHEMA.extend(
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            cv.GenerateID(): cv.declare_id(AddressableLightDisplay),
 | 
				
			||||||
 | 
					            cv.Required(CONF_ADDRESSABLE_LIGHT_ID): cv.use_id(
 | 
				
			||||||
 | 
					                light.AddressableLightState
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Required(CONF_WIDTH): cv.positive_int,
 | 
				
			||||||
 | 
					            cv.Required(CONF_HEIGHT): cv.positive_int,
 | 
				
			||||||
 | 
					            cv.Optional(
 | 
				
			||||||
 | 
					                CONF_UPDATE_INTERVAL, default="16ms"
 | 
				
			||||||
 | 
					            ): cv.positive_time_period_milliseconds,
 | 
				
			||||||
 | 
					            cv.Optional(CONF_PIXEL_MAPPER): cv.returning_lambda,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA),
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async def to_code(config):
 | 
				
			||||||
 | 
					    var = cg.new_Pvariable(config[CONF_ID])
 | 
				
			||||||
 | 
					    wrapped_light = await cg.get_variable(config[CONF_ADDRESSABLE_LIGHT_ID])
 | 
				
			||||||
 | 
					    cg.add(var.set_width(config[CONF_WIDTH]))
 | 
				
			||||||
 | 
					    cg.add(var.set_height(config[CONF_HEIGHT]))
 | 
				
			||||||
 | 
					    cg.add(var.set_light(wrapped_light))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await cg.register_component(var, config)
 | 
				
			||||||
 | 
					    await display.register_display(var, config)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if CONF_PIXEL_MAPPER in config:
 | 
				
			||||||
 | 
					        pixel_mapper_template_ = await cg.process_lambda(
 | 
				
			||||||
 | 
					            config[CONF_PIXEL_MAPPER],
 | 
				
			||||||
 | 
					            [(int, "x"), (int, "y")],
 | 
				
			||||||
 | 
					            return_type=cg.int_,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        cg.add(var.set_pixel_mapper(pixel_mapper_template_))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if CONF_LAMBDA in config:
 | 
				
			||||||
 | 
					        lambda_ = await cg.process_lambda(
 | 
				
			||||||
 | 
					            config[CONF_LAMBDA], [(display.DisplayBufferRef, "it")], return_type=cg.void
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        cg.add(var.set_writer(lambda_))
 | 
				
			||||||
@@ -8,6 +8,9 @@ static const char *TAG = "ade7953";
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void ADE7953::dump_config() {
 | 
					void ADE7953::dump_config() {
 | 
				
			||||||
  ESP_LOGCONFIG(TAG, "ADE7953:");
 | 
					  ESP_LOGCONFIG(TAG, "ADE7953:");
 | 
				
			||||||
 | 
					  if (this->has_irq_) {
 | 
				
			||||||
 | 
					    ESP_LOGCONFIG(TAG, "  IRQ Pin: GPIO%u", this->irq_pin_number_);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  LOG_I2C_DEVICE(this);
 | 
					  LOG_I2C_DEVICE(this);
 | 
				
			||||||
  LOG_UPDATE_INTERVAL(this);
 | 
					  LOG_UPDATE_INTERVAL(this);
 | 
				
			||||||
  LOG_SENSOR("  ", "Voltage Sensor", this->voltage_sensor_);
 | 
					  LOG_SENSOR("  ", "Voltage Sensor", this->voltage_sensor_);
 | 
				
			||||||
@@ -18,7 +21,7 @@ void ADE7953::dump_config() {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define ADE_PUBLISH_(name, factor) \
 | 
					#define ADE_PUBLISH_(name, factor) \
 | 
				
			||||||
  if (name) { \
 | 
					  if (name && this->name##_sensor_) { \
 | 
				
			||||||
    float value = *name / factor; \
 | 
					    float value = *name / factor; \
 | 
				
			||||||
    this->name##_sensor_->publish_state(value); \
 | 
					    this->name##_sensor_->publish_state(value); \
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,6 +9,10 @@ namespace ade7953 {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class ADE7953 : public i2c::I2CDevice, public PollingComponent {
 | 
					class ADE7953 : public i2c::I2CDevice, public PollingComponent {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
 | 
					  void set_irq_pin(uint8_t irq_pin) {
 | 
				
			||||||
 | 
					    has_irq_ = true;
 | 
				
			||||||
 | 
					    irq_pin_number_ = irq_pin;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; }
 | 
					  void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; }
 | 
				
			||||||
  void set_current_a_sensor(sensor::Sensor *current_a_sensor) { current_a_sensor_ = current_a_sensor; }
 | 
					  void set_current_a_sensor(sensor::Sensor *current_a_sensor) { current_a_sensor_ = current_a_sensor; }
 | 
				
			||||||
  void set_current_b_sensor(sensor::Sensor *current_b_sensor) { current_b_sensor_ = current_b_sensor; }
 | 
					  void set_current_b_sensor(sensor::Sensor *current_b_sensor) { current_b_sensor_ = current_b_sensor; }
 | 
				
			||||||
@@ -20,6 +24,11 @@ class ADE7953 : public i2c::I2CDevice, public PollingComponent {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void setup() override {
 | 
					  void setup() override {
 | 
				
			||||||
 | 
					    if (this->has_irq_) {
 | 
				
			||||||
 | 
					      auto pin = GPIOPin(this->irq_pin_number_, INPUT);
 | 
				
			||||||
 | 
					      this->irq_pin_ = &pin;
 | 
				
			||||||
 | 
					      this->irq_pin_->setup();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    this->set_timeout(100, [this]() {
 | 
					    this->set_timeout(100, [this]() {
 | 
				
			||||||
      this->ade_write_<uint8_t>(0x0010, 0x04);
 | 
					      this->ade_write_<uint8_t>(0x0010, 0x04);
 | 
				
			||||||
      this->ade_write_<uint8_t>(0x00FE, 0xAD);
 | 
					      this->ade_write_<uint8_t>(0x00FE, 0xAD);
 | 
				
			||||||
@@ -55,6 +64,9 @@ class ADE7953 : public i2c::I2CDevice, public PollingComponent {
 | 
				
			|||||||
    return result;
 | 
					    return result;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  bool has_irq_ = false;
 | 
				
			||||||
 | 
					  uint8_t irq_pin_number_;
 | 
				
			||||||
 | 
					  GPIOPin *irq_pin_{nullptr};
 | 
				
			||||||
  bool is_setup_{false};
 | 
					  bool is_setup_{false};
 | 
				
			||||||
  sensor::Sensor *voltage_sensor_{nullptr};
 | 
					  sensor::Sensor *voltage_sensor_{nullptr};
 | 
				
			||||||
  sensor::Sensor *current_a_sensor_{nullptr};
 | 
					  sensor::Sensor *current_a_sensor_{nullptr};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,39 +1,83 @@
 | 
				
			|||||||
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 sensor, i2c
 | 
					from esphome.components import sensor, i2c
 | 
				
			||||||
from esphome.const import CONF_ID, CONF_VOLTAGE, \
 | 
					from esphome import pins
 | 
				
			||||||
    UNIT_VOLT, ICON_FLASH, UNIT_AMPERE, UNIT_WATT
 | 
					from esphome.const import (
 | 
				
			||||||
 | 
					    CONF_ID,
 | 
				
			||||||
 | 
					    CONF_VOLTAGE,
 | 
				
			||||||
 | 
					    DEVICE_CLASS_CURRENT,
 | 
				
			||||||
 | 
					    DEVICE_CLASS_POWER,
 | 
				
			||||||
 | 
					    DEVICE_CLASS_VOLTAGE,
 | 
				
			||||||
 | 
					    ICON_EMPTY,
 | 
				
			||||||
 | 
					    STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					    UNIT_VOLT,
 | 
				
			||||||
 | 
					    UNIT_AMPERE,
 | 
				
			||||||
 | 
					    UNIT_WATT,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
DEPENDENCIES = ['i2c']
 | 
					DEPENDENCIES = ["i2c"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ace7953_ns = cg.esphome_ns.namespace('ade7953')
 | 
					ade7953_ns = cg.esphome_ns.namespace("ade7953")
 | 
				
			||||||
ADE7953 = ace7953_ns.class_('ADE7953', cg.PollingComponent, i2c.I2CDevice)
 | 
					ADE7953 = ade7953_ns.class_("ADE7953", cg.PollingComponent, i2c.I2CDevice)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CONF_CURRENT_A = 'current_a'
 | 
					CONF_IRQ_PIN = "irq_pin"
 | 
				
			||||||
CONF_CURRENT_B = 'current_b'
 | 
					CONF_CURRENT_A = "current_a"
 | 
				
			||||||
CONF_ACTIVE_POWER_A = 'active_power_a'
 | 
					CONF_CURRENT_B = "current_b"
 | 
				
			||||||
CONF_ACTIVE_POWER_B = 'active_power_b'
 | 
					CONF_ACTIVE_POWER_A = "active_power_a"
 | 
				
			||||||
 | 
					CONF_ACTIVE_POWER_B = "active_power_b"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CONFIG_SCHEMA = cv.Schema({
 | 
					CONFIG_SCHEMA = (
 | 
				
			||||||
    cv.GenerateID(): cv.declare_id(ADE7953),
 | 
					    cv.Schema(
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
    cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 1),
 | 
					            cv.GenerateID(): cv.declare_id(ADE7953),
 | 
				
			||||||
    cv.Optional(CONF_CURRENT_A): sensor.sensor_schema(UNIT_AMPERE, ICON_FLASH, 2),
 | 
					            cv.Optional(CONF_IRQ_PIN): pins.input_pin,
 | 
				
			||||||
    cv.Optional(CONF_CURRENT_B): sensor.sensor_schema(UNIT_AMPERE, ICON_FLASH, 2),
 | 
					            cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
 | 
				
			||||||
    cv.Optional(CONF_ACTIVE_POWER_A): sensor.sensor_schema(UNIT_WATT, ICON_FLASH, 1),
 | 
					                UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE, STATE_CLASS_MEASUREMENT
 | 
				
			||||||
    cv.Optional(CONF_ACTIVE_POWER_B): sensor.sensor_schema(UNIT_WATT, ICON_FLASH, 1),
 | 
					            ),
 | 
				
			||||||
}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x38))
 | 
					            cv.Optional(CONF_CURRENT_A): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                UNIT_AMPERE,
 | 
				
			||||||
 | 
					                ICON_EMPTY,
 | 
				
			||||||
 | 
					                2,
 | 
				
			||||||
 | 
					                DEVICE_CLASS_CURRENT,
 | 
				
			||||||
 | 
					                STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_CURRENT_B): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                UNIT_AMPERE,
 | 
				
			||||||
 | 
					                ICON_EMPTY,
 | 
				
			||||||
 | 
					                2,
 | 
				
			||||||
 | 
					                DEVICE_CLASS_CURRENT,
 | 
				
			||||||
 | 
					                STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_ACTIVE_POWER_A): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                UNIT_WATT, ICON_EMPTY, 1, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_ACTIVE_POWER_B): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                UNIT_WATT, ICON_EMPTY, 1, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    .extend(cv.polling_component_schema("60s"))
 | 
				
			||||||
 | 
					    .extend(i2c.i2c_device_schema(0x38))
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def to_code(config):
 | 
					async def to_code(config):
 | 
				
			||||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
					    var = cg.new_Pvariable(config[CONF_ID])
 | 
				
			||||||
    yield cg.register_component(var, config)
 | 
					    await cg.register_component(var, config)
 | 
				
			||||||
    yield i2c.register_i2c_device(var, config)
 | 
					    await i2c.register_i2c_device(var, config)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for key in [CONF_VOLTAGE, CONF_CURRENT_A, CONF_CURRENT_B, CONF_ACTIVE_POWER_A,
 | 
					    if CONF_IRQ_PIN in config:
 | 
				
			||||||
                CONF_ACTIVE_POWER_B]:
 | 
					        cg.add(var.set_irq_pin(config[CONF_IRQ_PIN]))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for key in [
 | 
				
			||||||
 | 
					        CONF_VOLTAGE,
 | 
				
			||||||
 | 
					        CONF_CURRENT_A,
 | 
				
			||||||
 | 
					        CONF_CURRENT_B,
 | 
				
			||||||
 | 
					        CONF_ACTIVE_POWER_A,
 | 
				
			||||||
 | 
					        CONF_ACTIVE_POWER_B,
 | 
				
			||||||
 | 
					    ]:
 | 
				
			||||||
        if key not in config:
 | 
					        if key not in config:
 | 
				
			||||||
            continue
 | 
					            continue
 | 
				
			||||||
        conf = config[key]
 | 
					        conf = config[key]
 | 
				
			||||||
        sens = yield sensor.new_sensor(conf)
 | 
					        sens = await sensor.new_sensor(conf)
 | 
				
			||||||
        cg.add(getattr(var, 'set_{}_sensor'.format(key))(sens))
 | 
					        cg.add(getattr(var, f"set_{key}_sensor")(sens))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,23 +3,29 @@ import esphome.config_validation as cv
 | 
				
			|||||||
from esphome.components import i2c
 | 
					from esphome.components import i2c
 | 
				
			||||||
from esphome.const import CONF_ID
 | 
					from esphome.const import CONF_ID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
DEPENDENCIES = ['i2c']
 | 
					DEPENDENCIES = ["i2c"]
 | 
				
			||||||
AUTO_LOAD = ['sensor', 'voltage_sampler']
 | 
					AUTO_LOAD = ["sensor", "voltage_sampler"]
 | 
				
			||||||
MULTI_CONF = True
 | 
					MULTI_CONF = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ads1115_ns = cg.esphome_ns.namespace('ads1115')
 | 
					ads1115_ns = cg.esphome_ns.namespace("ads1115")
 | 
				
			||||||
ADS1115Component = ads1115_ns.class_('ADS1115Component', cg.Component, i2c.I2CDevice)
 | 
					ADS1115Component = ads1115_ns.class_("ADS1115Component", cg.Component, i2c.I2CDevice)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CONF_CONTINUOUS_MODE = 'continuous_mode'
 | 
					CONF_CONTINUOUS_MODE = "continuous_mode"
 | 
				
			||||||
CONFIG_SCHEMA = cv.Schema({
 | 
					CONFIG_SCHEMA = (
 | 
				
			||||||
    cv.GenerateID(): cv.declare_id(ADS1115Component),
 | 
					    cv.Schema(
 | 
				
			||||||
    cv.Optional(CONF_CONTINUOUS_MODE, default=False): cv.boolean,
 | 
					        {
 | 
				
			||||||
}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(None))
 | 
					            cv.GenerateID(): cv.declare_id(ADS1115Component),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_CONTINUOUS_MODE, default=False): cv.boolean,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    .extend(cv.COMPONENT_SCHEMA)
 | 
				
			||||||
 | 
					    .extend(i2c.i2c_device_schema(None))
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def to_code(config):
 | 
					async def to_code(config):
 | 
				
			||||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
					    var = cg.new_Pvariable(config[CONF_ID])
 | 
				
			||||||
    yield cg.register_component(var, config)
 | 
					    await cg.register_component(var, config)
 | 
				
			||||||
    yield i2c.register_i2c_device(var, config)
 | 
					    await i2c.register_i2c_device(var, config)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    cg.add(var.set_continuous_mode(config[CONF_CONTINUOUS_MODE]))
 | 
					    cg.add(var.set_continuous_mode(config[CONF_CONTINUOUS_MODE]))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,61 +1,77 @@
 | 
				
			|||||||
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 sensor, voltage_sampler
 | 
					from esphome.components import sensor, voltage_sampler
 | 
				
			||||||
from esphome.const import CONF_GAIN, CONF_MULTIPLEXER, ICON_FLASH, UNIT_VOLT, CONF_ID
 | 
					from esphome.const import (
 | 
				
			||||||
from esphome.py_compat import string_types
 | 
					    CONF_GAIN,
 | 
				
			||||||
 | 
					    CONF_MULTIPLEXER,
 | 
				
			||||||
 | 
					    DEVICE_CLASS_VOLTAGE,
 | 
				
			||||||
 | 
					    ICON_EMPTY,
 | 
				
			||||||
 | 
					    STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					    UNIT_VOLT,
 | 
				
			||||||
 | 
					    CONF_ID,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
from . import ads1115_ns, ADS1115Component
 | 
					from . import ads1115_ns, ADS1115Component
 | 
				
			||||||
 | 
					
 | 
				
			||||||
DEPENDENCIES = ['ads1115']
 | 
					DEPENDENCIES = ["ads1115"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ADS1115Multiplexer = ads1115_ns.enum('ADS1115Multiplexer')
 | 
					ADS1115Multiplexer = ads1115_ns.enum("ADS1115Multiplexer")
 | 
				
			||||||
MUX = {
 | 
					MUX = {
 | 
				
			||||||
    'A0_A1': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P0_N1,
 | 
					    "A0_A1": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P0_N1,
 | 
				
			||||||
    'A0_A3': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P0_N3,
 | 
					    "A0_A3": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P0_N3,
 | 
				
			||||||
    'A1_A3': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P1_N3,
 | 
					    "A1_A3": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P1_N3,
 | 
				
			||||||
    'A2_A3': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P2_N3,
 | 
					    "A2_A3": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P2_N3,
 | 
				
			||||||
    'A0_GND': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P0_NG,
 | 
					    "A0_GND": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P0_NG,
 | 
				
			||||||
    'A1_GND': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P1_NG,
 | 
					    "A1_GND": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P1_NG,
 | 
				
			||||||
    'A2_GND': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P2_NG,
 | 
					    "A2_GND": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P2_NG,
 | 
				
			||||||
    'A3_GND': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P3_NG,
 | 
					    "A3_GND": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P3_NG,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ADS1115Gain = ads1115_ns.enum('ADS1115Gain')
 | 
					ADS1115Gain = ads1115_ns.enum("ADS1115Gain")
 | 
				
			||||||
GAIN = {
 | 
					GAIN = {
 | 
				
			||||||
    '6.144': ADS1115Gain.ADS1115_GAIN_6P144,
 | 
					    "6.144": ADS1115Gain.ADS1115_GAIN_6P144,
 | 
				
			||||||
    '4.096': ADS1115Gain.ADS1115_GAIN_4P096,
 | 
					    "4.096": ADS1115Gain.ADS1115_GAIN_4P096,
 | 
				
			||||||
    '2.048': ADS1115Gain.ADS1115_GAIN_2P048,
 | 
					    "2.048": ADS1115Gain.ADS1115_GAIN_2P048,
 | 
				
			||||||
    '1.024': ADS1115Gain.ADS1115_GAIN_1P024,
 | 
					    "1.024": ADS1115Gain.ADS1115_GAIN_1P024,
 | 
				
			||||||
    '0.512': ADS1115Gain.ADS1115_GAIN_0P512,
 | 
					    "0.512": ADS1115Gain.ADS1115_GAIN_0P512,
 | 
				
			||||||
    '0.256': ADS1115Gain.ADS1115_GAIN_0P256,
 | 
					    "0.256": ADS1115Gain.ADS1115_GAIN_0P256,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def validate_gain(value):
 | 
					def validate_gain(value):
 | 
				
			||||||
    if isinstance(value, float):
 | 
					    if isinstance(value, float):
 | 
				
			||||||
        value = u'{:0.03f}'.format(value)
 | 
					        value = f"{value:0.03f}"
 | 
				
			||||||
    elif not isinstance(value, string_types):
 | 
					    elif not isinstance(value, str):
 | 
				
			||||||
        raise cv.Invalid('invalid gain "{}"'.format(value))
 | 
					        raise cv.Invalid(f'invalid gain "{value}"')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return cv.enum(GAIN)(value)
 | 
					    return cv.enum(GAIN)(value)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ADS1115Sensor = ads1115_ns.class_('ADS1115Sensor', sensor.Sensor, cg.PollingComponent,
 | 
					ADS1115Sensor = ads1115_ns.class_(
 | 
				
			||||||
                                  voltage_sampler.VoltageSampler)
 | 
					    "ADS1115Sensor", sensor.Sensor, cg.PollingComponent, voltage_sampler.VoltageSampler
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CONF_ADS1115_ID = 'ads1115_id'
 | 
					CONF_ADS1115_ID = "ads1115_id"
 | 
				
			||||||
CONFIG_SCHEMA = sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 3).extend({
 | 
					CONFIG_SCHEMA = (
 | 
				
			||||||
    cv.GenerateID(): cv.declare_id(ADS1115Sensor),
 | 
					    sensor.sensor_schema(
 | 
				
			||||||
    cv.GenerateID(CONF_ADS1115_ID): cv.use_id(ADS1115Component),
 | 
					        UNIT_VOLT, ICON_EMPTY, 3, DEVICE_CLASS_VOLTAGE, STATE_CLASS_MEASUREMENT
 | 
				
			||||||
    cv.Required(CONF_MULTIPLEXER): cv.enum(MUX, upper=True, space='_'),
 | 
					    )
 | 
				
			||||||
    cv.Required(CONF_GAIN): validate_gain,
 | 
					    .extend(
 | 
				
			||||||
}).extend(cv.polling_component_schema('60s'))
 | 
					        {
 | 
				
			||||||
 | 
					            cv.GenerateID(): cv.declare_id(ADS1115Sensor),
 | 
				
			||||||
 | 
					            cv.GenerateID(CONF_ADS1115_ID): cv.use_id(ADS1115Component),
 | 
				
			||||||
 | 
					            cv.Required(CONF_MULTIPLEXER): cv.enum(MUX, upper=True, space="_"),
 | 
				
			||||||
 | 
					            cv.Required(CONF_GAIN): validate_gain,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    .extend(cv.polling_component_schema("60s"))
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def to_code(config):
 | 
					async def to_code(config):
 | 
				
			||||||
    paren = yield cg.get_variable(config[CONF_ADS1115_ID])
 | 
					    paren = await cg.get_variable(config[CONF_ADS1115_ID])
 | 
				
			||||||
    var = cg.new_Pvariable(config[CONF_ID], paren)
 | 
					    var = cg.new_Pvariable(config[CONF_ID], paren)
 | 
				
			||||||
    yield sensor.register_sensor(var, config)
 | 
					    await sensor.register_sensor(var, config)
 | 
				
			||||||
    yield cg.register_component(var, config)
 | 
					    await cg.register_component(var, config)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    cg.add(var.set_multiplexer(config[CONF_MULTIPLEXER]))
 | 
					    cg.add(var.set_multiplexer(config[CONF_MULTIPLEXER]))
 | 
				
			||||||
    cg.add(var.set_gain(config[CONF_GAIN]))
 | 
					    cg.add(var.set_gain(config[CONF_GAIN]))
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										0
									
								
								esphome/components/aht10/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								esphome/components/aht10/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										128
									
								
								esphome/components/aht10/aht10.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								esphome/components/aht10/aht10.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,128 @@
 | 
				
			|||||||
 | 
					// Implementation based on:
 | 
				
			||||||
 | 
					//  - AHT10: https://github.com/Thinary/AHT10
 | 
				
			||||||
 | 
					//  - Official Datasheet (cn):
 | 
				
			||||||
 | 
					//  http://www.aosong.com/userfiles/files/media/aht10%E8%A7%84%E6%A0%BC%E4%B9%A6v1_1%EF%BC%8820191015%EF%BC%89.pdf
 | 
				
			||||||
 | 
					//  - Unofficial Translated Datasheet (en):
 | 
				
			||||||
 | 
					//  https://wiki.liutyi.info/download/attachments/30507639/Aosong_AHT10_en_draft_0c.pdf
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// When configured for humidity, the log 'Components should block for at most 20-30ms in loop().' will be generated in
 | 
				
			||||||
 | 
					// verbose mode. This is due to technical specs of the sensor and can not be avoided.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// According to the datasheet, the component is supposed to respond in more than 75ms. In fact, it can answer almost
 | 
				
			||||||
 | 
					// immediately for temperature. But for humidity, it takes >90ms to get a valid data. From experience, we have best
 | 
				
			||||||
 | 
					// results making successive requests; the current implementation make 3 attemps with a delay of 30ms each time.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "aht10.h"
 | 
				
			||||||
 | 
					#include "esphome/core/log.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace esphome {
 | 
				
			||||||
 | 
					namespace aht10 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char *TAG = "aht10";
 | 
				
			||||||
 | 
					static const uint8_t AHT10_CALIBRATE_CMD[] = {0xE1};
 | 
				
			||||||
 | 
					static const uint8_t AHT10_MEASURE_CMD[] = {0xAC, 0x33, 0x00};
 | 
				
			||||||
 | 
					static const uint8_t AHT10_DEFAULT_DELAY = 5;    // ms, for calibration and temperature measurement
 | 
				
			||||||
 | 
					static const uint8_t AHT10_HUMIDITY_DELAY = 30;  // ms
 | 
				
			||||||
 | 
					static const uint8_t AHT10_ATTEMPS = 3;          // safety margin, normally 3 attemps are enough: 3*30=90ms
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void AHT10Component::setup() {
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "Setting up AHT10...");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!this->write_bytes(0, AHT10_CALIBRATE_CMD, sizeof(AHT10_CALIBRATE_CMD))) {
 | 
				
			||||||
 | 
					    ESP_LOGE(TAG, "Communication with AHT10 failed!");
 | 
				
			||||||
 | 
					    this->mark_failed();
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  uint8_t data;
 | 
				
			||||||
 | 
					  if (!this->read_byte(0, &data, AHT10_DEFAULT_DELAY)) {
 | 
				
			||||||
 | 
					    ESP_LOGD(TAG, "Communication with AHT10 failed!");
 | 
				
			||||||
 | 
					    this->mark_failed();
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if ((data & 0x68) != 0x08) {  // Bit[6:5] = 0b00, NORMAL mode and Bit[3] = 0b1, CALIBRATED
 | 
				
			||||||
 | 
					    ESP_LOGE(TAG, "AHT10 calibration failed!");
 | 
				
			||||||
 | 
					    this->mark_failed();
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ESP_LOGV(TAG, "AHT10 calibrated");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void AHT10Component::update() {
 | 
				
			||||||
 | 
					  if (!this->write_bytes(0, AHT10_MEASURE_CMD, sizeof(AHT10_MEASURE_CMD))) {
 | 
				
			||||||
 | 
					    ESP_LOGE(TAG, "Communication with AHT10 failed!");
 | 
				
			||||||
 | 
					    this->status_set_warning();
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  uint8_t data[6];
 | 
				
			||||||
 | 
					  uint8_t delay = AHT10_DEFAULT_DELAY;
 | 
				
			||||||
 | 
					  if (this->humidity_sensor_ != nullptr)
 | 
				
			||||||
 | 
					    delay = AHT10_HUMIDITY_DELAY;
 | 
				
			||||||
 | 
					  for (int i = 0; i < AHT10_ATTEMPS; ++i) {
 | 
				
			||||||
 | 
					    ESP_LOGVV(TAG, "Attemps %u at %6ld", i, millis());
 | 
				
			||||||
 | 
					    delay_microseconds_accurate(4);
 | 
				
			||||||
 | 
					    if (!this->read_bytes(0, data, 6, delay)) {
 | 
				
			||||||
 | 
					      ESP_LOGD(TAG, "Communication with AHT10 failed, waiting...");
 | 
				
			||||||
 | 
					    } else if ((data[0] & 0x80) == 0x80) {  // Bit[7] = 0b1, device is busy
 | 
				
			||||||
 | 
					      ESP_LOGD(TAG, "AHT10 is busy, waiting...");
 | 
				
			||||||
 | 
					    } else if (data[1] == 0x0 && data[2] == 0x0 && (data[3] >> 4) == 0x0) {
 | 
				
			||||||
 | 
					      // Unrealistic humidity (0x0)
 | 
				
			||||||
 | 
					      if (this->humidity_sensor_ == nullptr) {
 | 
				
			||||||
 | 
					        ESP_LOGVV(TAG, "ATH10 Unrealistic humidity (0x0), but humidity is not required");
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        ESP_LOGD(TAG, "ATH10 Unrealistic humidity (0x0), retrying...");
 | 
				
			||||||
 | 
					        if (!this->write_bytes(0, AHT10_MEASURE_CMD, sizeof(AHT10_MEASURE_CMD))) {
 | 
				
			||||||
 | 
					          ESP_LOGE(TAG, "Communication with AHT10 failed!");
 | 
				
			||||||
 | 
					          this->status_set_warning();
 | 
				
			||||||
 | 
					          return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      // data is valid, we can break the loop
 | 
				
			||||||
 | 
					      ESP_LOGVV(TAG, "Answer at %6ld", millis());
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if ((data[0] & 0x80) == 0x80) {
 | 
				
			||||||
 | 
					    ESP_LOGE(TAG, "Measurements reading timed-out!");
 | 
				
			||||||
 | 
					    this->status_set_warning();
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  uint32_t raw_temperature = ((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5];
 | 
				
			||||||
 | 
					  uint32_t raw_humidity = ((data[1] << 16) | (data[2] << 8) | data[3]) >> 4;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  float temperature = ((200.0 * (float) raw_temperature) / 1048576.0) - 50.0;
 | 
				
			||||||
 | 
					  float humidity;
 | 
				
			||||||
 | 
					  if (raw_humidity == 0) {  // unrealistic value
 | 
				
			||||||
 | 
					    humidity = NAN;
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    humidity = (float) raw_humidity * 100.0 / 1048576.0;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (this->temperature_sensor_ != nullptr) {
 | 
				
			||||||
 | 
					    this->temperature_sensor_->publish_state(temperature);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (this->humidity_sensor_ != nullptr) {
 | 
				
			||||||
 | 
					    if (isnan(humidity))
 | 
				
			||||||
 | 
					      ESP_LOGW(TAG, "Invalid humidity! Sensor reported 0%% Hum");
 | 
				
			||||||
 | 
					    this->humidity_sensor_->publish_state(humidity);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  this->status_clear_warning();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					float AHT10Component::get_setup_priority() const { return setup_priority::DATA; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void AHT10Component::dump_config() {
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "AHT10:");
 | 
				
			||||||
 | 
					  LOG_I2C_DEVICE(this);
 | 
				
			||||||
 | 
					  if (this->is_failed()) {
 | 
				
			||||||
 | 
					    ESP_LOGE(TAG, "Communication with AHT10 failed!");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Temperature", this->temperature_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Humidity", this->humidity_sensor_);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace aht10
 | 
				
			||||||
 | 
					}  // namespace esphome
 | 
				
			||||||
							
								
								
									
										26
									
								
								esphome/components/aht10/aht10.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								esphome/components/aht10/aht10.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "esphome/core/component.h"
 | 
				
			||||||
 | 
					#include "esphome/components/sensor/sensor.h"
 | 
				
			||||||
 | 
					#include "esphome/components/i2c/i2c.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace esphome {
 | 
				
			||||||
 | 
					namespace aht10 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AHT10Component : public PollingComponent, public i2c::I2CDevice {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  void setup() override;
 | 
				
			||||||
 | 
					  void update() override;
 | 
				
			||||||
 | 
					  void dump_config() override;
 | 
				
			||||||
 | 
					  float get_setup_priority() const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; }
 | 
				
			||||||
 | 
					  void set_humidity_sensor(sensor::Sensor *humidity_sensor) { humidity_sensor_ = humidity_sensor; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 protected:
 | 
				
			||||||
 | 
					  sensor::Sensor *temperature_sensor_;
 | 
				
			||||||
 | 
					  sensor::Sensor *humidity_sensor_;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace aht10
 | 
				
			||||||
 | 
					}  // namespace esphome
 | 
				
			||||||
							
								
								
									
										57
									
								
								esphome/components/aht10/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								esphome/components/aht10/sensor.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
				
			|||||||
 | 
					import esphome.codegen as cg
 | 
				
			||||||
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
 | 
					from esphome.components import i2c, sensor
 | 
				
			||||||
 | 
					from esphome.const import (
 | 
				
			||||||
 | 
					    CONF_HUMIDITY,
 | 
				
			||||||
 | 
					    CONF_ID,
 | 
				
			||||||
 | 
					    CONF_TEMPERATURE,
 | 
				
			||||||
 | 
					    DEVICE_CLASS_HUMIDITY,
 | 
				
			||||||
 | 
					    DEVICE_CLASS_TEMPERATURE,
 | 
				
			||||||
 | 
					    ICON_EMPTY,
 | 
				
			||||||
 | 
					    STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					    UNIT_CELSIUS,
 | 
				
			||||||
 | 
					    UNIT_PERCENT,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEPENDENCIES = ["i2c"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					aht10_ns = cg.esphome_ns.namespace("aht10")
 | 
				
			||||||
 | 
					AHT10Component = aht10_ns.class_("AHT10Component", cg.PollingComponent, i2c.I2CDevice)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CONFIG_SCHEMA = (
 | 
				
			||||||
 | 
					    cv.Schema(
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            cv.GenerateID(): cv.declare_id(AHT10Component),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                UNIT_CELSIUS,
 | 
				
			||||||
 | 
					                ICON_EMPTY,
 | 
				
			||||||
 | 
					                2,
 | 
				
			||||||
 | 
					                DEVICE_CLASS_TEMPERATURE,
 | 
				
			||||||
 | 
					                STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                UNIT_PERCENT,
 | 
				
			||||||
 | 
					                ICON_EMPTY,
 | 
				
			||||||
 | 
					                2,
 | 
				
			||||||
 | 
					                DEVICE_CLASS_HUMIDITY,
 | 
				
			||||||
 | 
					                STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    .extend(cv.polling_component_schema("60s"))
 | 
				
			||||||
 | 
					    .extend(i2c.i2c_device_schema(0x38))
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async def to_code(config):
 | 
				
			||||||
 | 
					    var = cg.new_Pvariable(config[CONF_ID])
 | 
				
			||||||
 | 
					    await cg.register_component(var, config)
 | 
				
			||||||
 | 
					    await i2c.register_i2c_device(var, config)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if CONF_TEMPERATURE in config:
 | 
				
			||||||
 | 
					        sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
 | 
				
			||||||
 | 
					        cg.add(var.set_temperature_sensor(sens))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if CONF_HUMIDITY in config:
 | 
				
			||||||
 | 
					        sens = await sensor.new_sensor(config[CONF_HUMIDITY])
 | 
				
			||||||
 | 
					        cg.add(var.set_humidity_sensor(sens))
 | 
				
			||||||
@@ -1,30 +1,59 @@
 | 
				
			|||||||
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, sensor
 | 
					from esphome.components import i2c, sensor
 | 
				
			||||||
from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_TEMPERATURE, \
 | 
					from esphome.const import (
 | 
				
			||||||
    UNIT_CELSIUS, ICON_THERMOMETER, ICON_WATER_PERCENT, UNIT_PERCENT
 | 
					    CONF_HUMIDITY,
 | 
				
			||||||
 | 
					    CONF_ID,
 | 
				
			||||||
 | 
					    CONF_TEMPERATURE,
 | 
				
			||||||
 | 
					    DEVICE_CLASS_HUMIDITY,
 | 
				
			||||||
 | 
					    DEVICE_CLASS_TEMPERATURE,
 | 
				
			||||||
 | 
					    STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					    UNIT_CELSIUS,
 | 
				
			||||||
 | 
					    ICON_EMPTY,
 | 
				
			||||||
 | 
					    UNIT_PERCENT,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
DEPENDENCIES = ['i2c']
 | 
					DEPENDENCIES = ["i2c"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
am2320_ns = cg.esphome_ns.namespace('am2320')
 | 
					am2320_ns = cg.esphome_ns.namespace("am2320")
 | 
				
			||||||
AM2320Component = am2320_ns.class_('AM2320Component', cg.PollingComponent, i2c.I2CDevice)
 | 
					AM2320Component = am2320_ns.class_(
 | 
				
			||||||
 | 
					    "AM2320Component", cg.PollingComponent, i2c.I2CDevice
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CONFIG_SCHEMA = cv.Schema({
 | 
					CONFIG_SCHEMA = (
 | 
				
			||||||
    cv.GenerateID(): cv.declare_id(AM2320Component),
 | 
					    cv.Schema(
 | 
				
			||||||
    cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1),
 | 
					        {
 | 
				
			||||||
    cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1),
 | 
					            cv.GenerateID(): cv.declare_id(AM2320Component),
 | 
				
			||||||
}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x5C))
 | 
					            cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                UNIT_CELSIUS,
 | 
				
			||||||
 | 
					                ICON_EMPTY,
 | 
				
			||||||
 | 
					                1,
 | 
				
			||||||
 | 
					                DEVICE_CLASS_TEMPERATURE,
 | 
				
			||||||
 | 
					                STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                UNIT_PERCENT,
 | 
				
			||||||
 | 
					                ICON_EMPTY,
 | 
				
			||||||
 | 
					                1,
 | 
				
			||||||
 | 
					                DEVICE_CLASS_HUMIDITY,
 | 
				
			||||||
 | 
					                STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    .extend(cv.polling_component_schema("60s"))
 | 
				
			||||||
 | 
					    .extend(i2c.i2c_device_schema(0x5C))
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def to_code(config):
 | 
					async def to_code(config):
 | 
				
			||||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
					    var = cg.new_Pvariable(config[CONF_ID])
 | 
				
			||||||
    yield cg.register_component(var, config)
 | 
					    await cg.register_component(var, config)
 | 
				
			||||||
    yield i2c.register_i2c_device(var, config)
 | 
					    await i2c.register_i2c_device(var, config)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if CONF_TEMPERATURE in config:
 | 
					    if CONF_TEMPERATURE in config:
 | 
				
			||||||
        sens = yield sensor.new_sensor(config[CONF_TEMPERATURE])
 | 
					        sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
 | 
				
			||||||
        cg.add(var.set_temperature_sensor(sens))
 | 
					        cg.add(var.set_temperature_sensor(sens))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if CONF_HUMIDITY in config:
 | 
					    if CONF_HUMIDITY in config:
 | 
				
			||||||
        sens = yield sensor.new_sensor(config[CONF_HUMIDITY])
 | 
					        sens = await sensor.new_sensor(config[CONF_HUMIDITY])
 | 
				
			||||||
        cg.add(var.set_humidity_sensor(sens))
 | 
					        cg.add(var.set_humidity_sensor(sens))
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										106
									
								
								esphome/components/animation/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								esphome/components/animation/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,106 @@
 | 
				
			|||||||
 | 
					import logging
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from esphome import core
 | 
				
			||||||
 | 
					from esphome.components import display, font
 | 
				
			||||||
 | 
					import esphome.components.image as espImage
 | 
				
			||||||
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
 | 
					import esphome.codegen as cg
 | 
				
			||||||
 | 
					from esphome.const import CONF_FILE, CONF_ID, CONF_TYPE, CONF_RESIZE
 | 
				
			||||||
 | 
					from esphome.core import CORE, HexInt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_LOGGER = logging.getLogger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEPENDENCIES = ["display"]
 | 
				
			||||||
 | 
					MULTI_CONF = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Animation_ = display.display_ns.class_("Animation")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CONF_RAW_DATA_ID = "raw_data_id"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ANIMATION_SCHEMA = cv.Schema(
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        cv.Required(CONF_ID): cv.declare_id(Animation_),
 | 
				
			||||||
 | 
					        cv.Required(CONF_FILE): cv.file_,
 | 
				
			||||||
 | 
					        cv.Optional(CONF_RESIZE): cv.dimensions,
 | 
				
			||||||
 | 
					        cv.Optional(CONF_TYPE, default="BINARY"): cv.enum(
 | 
				
			||||||
 | 
					            espImage.IMAGE_TYPE, upper=True
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CONFIG_SCHEMA = cv.All(font.validate_pillow_installed, ANIMATION_SCHEMA)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CODEOWNERS = ["@syndlex"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async def to_code(config):
 | 
				
			||||||
 | 
					    from PIL import Image
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    path = CORE.relative_config_path(config[CONF_FILE])
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        image = Image.open(path)
 | 
				
			||||||
 | 
					    except Exception as e:
 | 
				
			||||||
 | 
					        raise core.EsphomeError(f"Could not load image file {path}: {e}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    width, height = image.size
 | 
				
			||||||
 | 
					    frames = image.n_frames
 | 
				
			||||||
 | 
					    if CONF_RESIZE in config:
 | 
				
			||||||
 | 
					        image.thumbnail(config[CONF_RESIZE])
 | 
				
			||||||
 | 
					        width, height = image.size
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        if width > 500 or height > 500:
 | 
				
			||||||
 | 
					            _LOGGER.warning(
 | 
				
			||||||
 | 
					                "The image you requested is very big. Please consider using"
 | 
				
			||||||
 | 
					                " the resize parameter."
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if config[CONF_TYPE] == "GRAYSCALE":
 | 
				
			||||||
 | 
					        data = [0 for _ in range(height * width * frames)]
 | 
				
			||||||
 | 
					        pos = 0
 | 
				
			||||||
 | 
					        for frameIndex in range(frames):
 | 
				
			||||||
 | 
					            image.seek(frameIndex)
 | 
				
			||||||
 | 
					            frame = image.convert("L", dither=Image.NONE)
 | 
				
			||||||
 | 
					            pixels = list(frame.getdata())
 | 
				
			||||||
 | 
					            for pix in pixels:
 | 
				
			||||||
 | 
					                data[pos] = pix
 | 
				
			||||||
 | 
					                pos += 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    elif config[CONF_TYPE] == "RGB24":
 | 
				
			||||||
 | 
					        data = [0 for _ in range(height * width * 3 * frames)]
 | 
				
			||||||
 | 
					        pos = 0
 | 
				
			||||||
 | 
					        for frameIndex in range(frames):
 | 
				
			||||||
 | 
					            image.seek(frameIndex)
 | 
				
			||||||
 | 
					            frame = image.convert("RGB")
 | 
				
			||||||
 | 
					            pixels = list(frame.getdata())
 | 
				
			||||||
 | 
					            for pix in pixels:
 | 
				
			||||||
 | 
					                data[pos] = pix[0]
 | 
				
			||||||
 | 
					                pos += 1
 | 
				
			||||||
 | 
					                data[pos] = pix[1]
 | 
				
			||||||
 | 
					                pos += 1
 | 
				
			||||||
 | 
					                data[pos] = pix[2]
 | 
				
			||||||
 | 
					                pos += 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    elif config[CONF_TYPE] == "BINARY":
 | 
				
			||||||
 | 
					        width8 = ((width + 7) // 8) * 8
 | 
				
			||||||
 | 
					        data = [0 for _ in range((height * width8 // 8) * frames)]
 | 
				
			||||||
 | 
					        for frameIndex in range(frames):
 | 
				
			||||||
 | 
					            image.seek(frameIndex)
 | 
				
			||||||
 | 
					            frame = image.convert("1", dither=Image.NONE)
 | 
				
			||||||
 | 
					            for y in range(height):
 | 
				
			||||||
 | 
					                for x in range(width):
 | 
				
			||||||
 | 
					                    if frame.getpixel((x, y)):
 | 
				
			||||||
 | 
					                        continue
 | 
				
			||||||
 | 
					                    pos = x + y * width8 + (height * width8 * frameIndex)
 | 
				
			||||||
 | 
					                    data[pos // 8] |= 0x80 >> (pos % 8)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    rhs = [HexInt(x) for x in data]
 | 
				
			||||||
 | 
					    prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs)
 | 
				
			||||||
 | 
					    cg.new_Pvariable(
 | 
				
			||||||
 | 
					        config[CONF_ID],
 | 
				
			||||||
 | 
					        prog_arr,
 | 
				
			||||||
 | 
					        width,
 | 
				
			||||||
 | 
					        height,
 | 
				
			||||||
 | 
					        frames,
 | 
				
			||||||
 | 
					        espImage.IMAGE_TYPE[config[CONF_TYPE]],
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
@@ -3,21 +3,27 @@ import esphome.config_validation as cv
 | 
				
			|||||||
from esphome.components import i2c
 | 
					from esphome.components import i2c
 | 
				
			||||||
from esphome.const import CONF_ID
 | 
					from esphome.const import CONF_ID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
DEPENDENCIES = ['i2c']
 | 
					DEPENDENCIES = ["i2c"]
 | 
				
			||||||
AUTO_LOAD = ['sensor', 'binary_sensor']
 | 
					AUTO_LOAD = ["sensor", "binary_sensor"]
 | 
				
			||||||
MULTI_CONF = True
 | 
					MULTI_CONF = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CONF_APDS9960_ID = 'apds9960_id'
 | 
					CONF_APDS9960_ID = "apds9960_id"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
apds9960_nds = cg.esphome_ns.namespace('apds9960')
 | 
					apds9960_nds = cg.esphome_ns.namespace("apds9960")
 | 
				
			||||||
APDS9960 = apds9960_nds.class_('APDS9960', cg.PollingComponent, i2c.I2CDevice)
 | 
					APDS9960 = apds9960_nds.class_("APDS9960", cg.PollingComponent, i2c.I2CDevice)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CONFIG_SCHEMA = cv.Schema({
 | 
					CONFIG_SCHEMA = (
 | 
				
			||||||
    cv.GenerateID(): cv.declare_id(APDS9960),
 | 
					    cv.Schema(
 | 
				
			||||||
}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x39))
 | 
					        {
 | 
				
			||||||
 | 
					            cv.GenerateID(): cv.declare_id(APDS9960),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    .extend(cv.polling_component_schema("60s"))
 | 
				
			||||||
 | 
					    .extend(i2c.i2c_device_schema(0x39))
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def to_code(config):
 | 
					async def to_code(config):
 | 
				
			||||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
					    var = cg.new_Pvariable(config[CONF_ID])
 | 
				
			||||||
    yield cg.register_component(var, config)
 | 
					    await cg.register_component(var, config)
 | 
				
			||||||
    yield i2c.register_i2c_device(var, config)
 | 
					    await i2c.register_i2c_device(var, config)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,24 +4,28 @@ from esphome.components import binary_sensor
 | 
				
			|||||||
from esphome.const import CONF_DIRECTION, CONF_DEVICE_CLASS, DEVICE_CLASS_MOVING
 | 
					from esphome.const import CONF_DIRECTION, CONF_DEVICE_CLASS, DEVICE_CLASS_MOVING
 | 
				
			||||||
from . import APDS9960, CONF_APDS9960_ID
 | 
					from . import APDS9960, CONF_APDS9960_ID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
DEPENDENCIES = ['apds9960']
 | 
					DEPENDENCIES = ["apds9960"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
DIRECTIONS = {
 | 
					DIRECTIONS = {
 | 
				
			||||||
    'UP': 'set_up_direction',
 | 
					    "UP": "set_up_direction",
 | 
				
			||||||
    'DOWN': 'set_down_direction',
 | 
					    "DOWN": "set_down_direction",
 | 
				
			||||||
    'LEFT': 'set_left_direction',
 | 
					    "LEFT": "set_left_direction",
 | 
				
			||||||
    'RIGHT': 'set_right_direction',
 | 
					    "RIGHT": "set_right_direction",
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({
 | 
					CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend(
 | 
				
			||||||
    cv.Required(CONF_DIRECTION): cv.one_of(*DIRECTIONS, upper=True),
 | 
					    {
 | 
				
			||||||
    cv.GenerateID(CONF_APDS9960_ID): cv.use_id(APDS9960),
 | 
					        cv.Required(CONF_DIRECTION): cv.one_of(*DIRECTIONS, upper=True),
 | 
				
			||||||
    cv.Optional(CONF_DEVICE_CLASS, default=DEVICE_CLASS_MOVING): binary_sensor.device_class,
 | 
					        cv.GenerateID(CONF_APDS9960_ID): cv.use_id(APDS9960),
 | 
				
			||||||
})
 | 
					        cv.Optional(
 | 
				
			||||||
 | 
					            CONF_DEVICE_CLASS, default=DEVICE_CLASS_MOVING
 | 
				
			||||||
 | 
					        ): binary_sensor.device_class,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def to_code(config):
 | 
					async def to_code(config):
 | 
				
			||||||
    hub = yield cg.get_variable(config[CONF_APDS9960_ID])
 | 
					    hub = await cg.get_variable(config[CONF_APDS9960_ID])
 | 
				
			||||||
    var = yield binary_sensor.new_binary_sensor(config)
 | 
					    var = await binary_sensor.new_binary_sensor(config)
 | 
				
			||||||
    func = getattr(hub, DIRECTIONS[config[CONF_DIRECTION]])
 | 
					    func = getattr(hub, DIRECTIONS[config[CONF_DIRECTION]])
 | 
				
			||||||
    cg.add(func(var))
 | 
					    cg.add(func(var))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,27 +1,37 @@
 | 
				
			|||||||
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 sensor
 | 
					from esphome.components import sensor
 | 
				
			||||||
from esphome.const import CONF_TYPE, UNIT_PERCENT, ICON_LIGHTBULB
 | 
					from esphome.const import (
 | 
				
			||||||
 | 
					    CONF_TYPE,
 | 
				
			||||||
 | 
					    DEVICE_CLASS_EMPTY,
 | 
				
			||||||
 | 
					    STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					    UNIT_PERCENT,
 | 
				
			||||||
 | 
					    ICON_LIGHTBULB,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
from . import APDS9960, CONF_APDS9960_ID
 | 
					from . import APDS9960, CONF_APDS9960_ID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
DEPENDENCIES = ['apds9960']
 | 
					DEPENDENCIES = ["apds9960"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TYPES = {
 | 
					TYPES = {
 | 
				
			||||||
    'CLEAR': 'set_clear_channel',
 | 
					    "CLEAR": "set_clear_channel",
 | 
				
			||||||
    'RED': 'set_red_channel',
 | 
					    "RED": "set_red_channel",
 | 
				
			||||||
    'GREEN': 'set_green_channel',
 | 
					    "GREEN": "set_green_channel",
 | 
				
			||||||
    'BLUE': 'set_blue_channel',
 | 
					    "BLUE": "set_blue_channel",
 | 
				
			||||||
    'PROXIMITY': 'set_proximity',
 | 
					    "PROXIMITY": "set_proximity",
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CONFIG_SCHEMA = sensor.sensor_schema(UNIT_PERCENT, ICON_LIGHTBULB, 1).extend({
 | 
					CONFIG_SCHEMA = sensor.sensor_schema(
 | 
				
			||||||
    cv.Required(CONF_TYPE): cv.one_of(*TYPES, upper=True),
 | 
					    UNIT_PERCENT, ICON_LIGHTBULB, 1, DEVICE_CLASS_EMPTY, STATE_CLASS_MEASUREMENT
 | 
				
			||||||
    cv.GenerateID(CONF_APDS9960_ID): cv.use_id(APDS9960),
 | 
					).extend(
 | 
				
			||||||
})
 | 
					    {
 | 
				
			||||||
 | 
					        cv.Required(CONF_TYPE): cv.one_of(*TYPES, upper=True),
 | 
				
			||||||
 | 
					        cv.GenerateID(CONF_APDS9960_ID): cv.use_id(APDS9960),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def to_code(config):
 | 
					async def to_code(config):
 | 
				
			||||||
    hub = yield cg.get_variable(config[CONF_APDS9960_ID])
 | 
					    hub = await cg.get_variable(config[CONF_APDS9960_ID])
 | 
				
			||||||
    var = yield sensor.new_sensor(config)
 | 
					    var = await sensor.new_sensor(config)
 | 
				
			||||||
    func = getattr(hub, TYPES[config[CONF_TYPE]])
 | 
					    func = getattr(hub, TYPES[config[CONF_TYPE]])
 | 
				
			||||||
    cg.add(func(var))
 | 
					    cg.add(func(var))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,50 +2,75 @@ import esphome.codegen as cg
 | 
				
			|||||||
import esphome.config_validation as cv
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
from esphome import automation
 | 
					from esphome import automation
 | 
				
			||||||
from esphome.automation import Condition
 | 
					from esphome.automation import Condition
 | 
				
			||||||
from esphome.const import CONF_DATA, CONF_DATA_TEMPLATE, CONF_ID, CONF_PASSWORD, CONF_PORT, \
 | 
					from esphome.const import (
 | 
				
			||||||
    CONF_REBOOT_TIMEOUT, CONF_SERVICE, CONF_VARIABLES, CONF_SERVICES, CONF_TRIGGER_ID, CONF_EVENT
 | 
					    CONF_DATA,
 | 
				
			||||||
 | 
					    CONF_DATA_TEMPLATE,
 | 
				
			||||||
 | 
					    CONF_ID,
 | 
				
			||||||
 | 
					    CONF_PASSWORD,
 | 
				
			||||||
 | 
					    CONF_PORT,
 | 
				
			||||||
 | 
					    CONF_REBOOT_TIMEOUT,
 | 
				
			||||||
 | 
					    CONF_SERVICE,
 | 
				
			||||||
 | 
					    CONF_VARIABLES,
 | 
				
			||||||
 | 
					    CONF_SERVICES,
 | 
				
			||||||
 | 
					    CONF_TRIGGER_ID,
 | 
				
			||||||
 | 
					    CONF_EVENT,
 | 
				
			||||||
 | 
					    CONF_TAG,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
from esphome.core import coroutine_with_priority
 | 
					from esphome.core import coroutine_with_priority
 | 
				
			||||||
 | 
					
 | 
				
			||||||
DEPENDENCIES = ['network']
 | 
					DEPENDENCIES = ["network"]
 | 
				
			||||||
AUTO_LOAD = ['async_tcp']
 | 
					AUTO_LOAD = ["async_tcp"]
 | 
				
			||||||
 | 
					CODEOWNERS = ["@OttoWinter"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
api_ns = cg.esphome_ns.namespace('api')
 | 
					api_ns = cg.esphome_ns.namespace("api")
 | 
				
			||||||
APIServer = api_ns.class_('APIServer', cg.Component, cg.Controller)
 | 
					APIServer = api_ns.class_("APIServer", cg.Component, cg.Controller)
 | 
				
			||||||
HomeAssistantServiceCallAction = api_ns.class_('HomeAssistantServiceCallAction', automation.Action)
 | 
					HomeAssistantServiceCallAction = api_ns.class_(
 | 
				
			||||||
APIConnectedCondition = api_ns.class_('APIConnectedCondition', Condition)
 | 
					    "HomeAssistantServiceCallAction", automation.Action
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					APIConnectedCondition = api_ns.class_("APIConnectedCondition", Condition)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
UserServiceTrigger = api_ns.class_('UserServiceTrigger', automation.Trigger)
 | 
					UserServiceTrigger = api_ns.class_("UserServiceTrigger", automation.Trigger)
 | 
				
			||||||
ListEntitiesServicesArgument = api_ns.class_('ListEntitiesServicesArgument')
 | 
					ListEntitiesServicesArgument = api_ns.class_("ListEntitiesServicesArgument")
 | 
				
			||||||
SERVICE_ARG_NATIVE_TYPES = {
 | 
					SERVICE_ARG_NATIVE_TYPES = {
 | 
				
			||||||
    'bool': bool,
 | 
					    "bool": bool,
 | 
				
			||||||
    'int': cg.int32,
 | 
					    "int": cg.int32,
 | 
				
			||||||
    'float': float,
 | 
					    "float": float,
 | 
				
			||||||
    'string': cg.std_string,
 | 
					    "string": cg.std_string,
 | 
				
			||||||
    'bool[]': cg.std_vector.template(bool),
 | 
					    "bool[]": cg.std_vector.template(bool),
 | 
				
			||||||
    'int[]': cg.std_vector.template(cg.int32),
 | 
					    "int[]": cg.std_vector.template(cg.int32),
 | 
				
			||||||
    'float[]': cg.std_vector.template(float),
 | 
					    "float[]": cg.std_vector.template(float),
 | 
				
			||||||
    'string[]': cg.std_vector.template(cg.std_string),
 | 
					    "string[]": cg.std_vector.template(cg.std_string),
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CONFIG_SCHEMA = cv.Schema({
 | 
					CONFIG_SCHEMA = cv.Schema(
 | 
				
			||||||
    cv.GenerateID(): cv.declare_id(APIServer),
 | 
					    {
 | 
				
			||||||
    cv.Optional(CONF_PORT, default=6053): cv.port,
 | 
					        cv.GenerateID(): cv.declare_id(APIServer),
 | 
				
			||||||
    cv.Optional(CONF_PASSWORD, default=''): cv.string_strict,
 | 
					        cv.Optional(CONF_PORT, default=6053): cv.port,
 | 
				
			||||||
    cv.Optional(CONF_REBOOT_TIMEOUT, default='15min'): cv.positive_time_period_milliseconds,
 | 
					        cv.Optional(CONF_PASSWORD, default=""): cv.string_strict,
 | 
				
			||||||
    cv.Optional(CONF_SERVICES): automation.validate_automation({
 | 
					        cv.Optional(
 | 
				
			||||||
        cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(UserServiceTrigger),
 | 
					            CONF_REBOOT_TIMEOUT, default="15min"
 | 
				
			||||||
        cv.Required(CONF_SERVICE): cv.valid_name,
 | 
					        ): cv.positive_time_period_milliseconds,
 | 
				
			||||||
        cv.Optional(CONF_VARIABLES, default={}): cv.Schema({
 | 
					        cv.Optional(CONF_SERVICES): automation.validate_automation(
 | 
				
			||||||
            cv.validate_id_name: cv.one_of(*SERVICE_ARG_NATIVE_TYPES, lower=True),
 | 
					            {
 | 
				
			||||||
        }),
 | 
					                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(UserServiceTrigger),
 | 
				
			||||||
    }),
 | 
					                cv.Required(CONF_SERVICE): cv.valid_name,
 | 
				
			||||||
}).extend(cv.COMPONENT_SCHEMA)
 | 
					                cv.Optional(CONF_VARIABLES, default={}): cv.Schema(
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        cv.validate_id_name: cv.one_of(
 | 
				
			||||||
 | 
					                            *SERVICE_ARG_NATIVE_TYPES, lower=True
 | 
				
			||||||
 | 
					                        ),
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					).extend(cv.COMPONENT_SCHEMA)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@coroutine_with_priority(40.0)
 | 
					@coroutine_with_priority(40.0)
 | 
				
			||||||
def to_code(config):
 | 
					async def to_code(config):
 | 
				
			||||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
					    var = cg.new_Pvariable(config[CONF_ID])
 | 
				
			||||||
    yield cg.register_component(var, config)
 | 
					    await cg.register_component(var, config)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    cg.add(var.set_port(config[CONF_PORT]))
 | 
					    cg.add(var.set_port(config[CONF_PORT]))
 | 
				
			||||||
    cg.add(var.set_password(config[CONF_PASSWORD]))
 | 
					    cg.add(var.set_password(config[CONF_PASSWORD]))
 | 
				
			||||||
@@ -61,81 +86,119 @@ def to_code(config):
 | 
				
			|||||||
            func_args.append((native, name))
 | 
					            func_args.append((native, name))
 | 
				
			||||||
            service_arg_names.append(name)
 | 
					            service_arg_names.append(name)
 | 
				
			||||||
        templ = cg.TemplateArguments(*template_args)
 | 
					        templ = cg.TemplateArguments(*template_args)
 | 
				
			||||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], templ,
 | 
					        trigger = cg.new_Pvariable(
 | 
				
			||||||
                                   conf[CONF_SERVICE], service_arg_names)
 | 
					            conf[CONF_TRIGGER_ID], templ, conf[CONF_SERVICE], service_arg_names
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
        cg.add(var.register_user_service(trigger))
 | 
					        cg.add(var.register_user_service(trigger))
 | 
				
			||||||
        yield automation.build_automation(trigger, func_args, conf)
 | 
					        await automation.build_automation(trigger, func_args, conf)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    cg.add_define('USE_API')
 | 
					    cg.add_define("USE_API")
 | 
				
			||||||
    cg.add_global(api_ns.using)
 | 
					    cg.add_global(api_ns.using)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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_SERVICE_ACTION_SCHEMA = cv.Schema(
 | 
				
			||||||
    cv.GenerateID(): cv.use_id(APIServer),
 | 
					    {
 | 
				
			||||||
    cv.Required(CONF_SERVICE): cv.templatable(cv.string),
 | 
					        cv.GenerateID(): cv.use_id(APIServer),
 | 
				
			||||||
    cv.Optional(CONF_DATA, default={}): KEY_VALUE_SCHEMA,
 | 
					        cv.Required(CONF_SERVICE): cv.templatable(cv.string),
 | 
				
			||||||
    cv.Optional(CONF_DATA_TEMPLATE, default={}): KEY_VALUE_SCHEMA,
 | 
					        cv.Optional(CONF_DATA, default={}): KEY_VALUE_SCHEMA,
 | 
				
			||||||
    cv.Optional(CONF_VARIABLES, default={}): cv.Schema({cv.string: cv.returning_lambda}),
 | 
					        cv.Optional(CONF_DATA_TEMPLATE, default={}): KEY_VALUE_SCHEMA,
 | 
				
			||||||
})
 | 
					        cv.Optional(CONF_VARIABLES, default={}): cv.Schema(
 | 
				
			||||||
 | 
					            {cv.string: cv.returning_lambda}
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@automation.register_action('homeassistant.service', HomeAssistantServiceCallAction,
 | 
					@automation.register_action(
 | 
				
			||||||
                            HOMEASSISTANT_SERVICE_ACTION_SCHEMA)
 | 
					    "homeassistant.service",
 | 
				
			||||||
def homeassistant_service_to_code(config, action_id, template_arg, args):
 | 
					    HomeAssistantServiceCallAction,
 | 
				
			||||||
    serv = yield cg.get_variable(config[CONF_ID])
 | 
					    HOMEASSISTANT_SERVICE_ACTION_SCHEMA,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					async def homeassistant_service_to_code(config, action_id, template_arg, args):
 | 
				
			||||||
 | 
					    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 = yield cg.templatable(config[CONF_SERVICE], args, None)
 | 
					    templ = await cg.templatable(config[CONF_SERVICE], 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 = yield cg.templatable(value, args, None)
 | 
					        templ = await cg.templatable(value, args, None)
 | 
				
			||||||
        cg.add(var.add_data(key, templ))
 | 
					        cg.add(var.add_data(key, templ))
 | 
				
			||||||
    for key, value in config[CONF_DATA_TEMPLATE].items():
 | 
					    for key, value in config[CONF_DATA_TEMPLATE].items():
 | 
				
			||||||
        templ = yield cg.templatable(value, args, None)
 | 
					        templ = await cg.templatable(value, args, None)
 | 
				
			||||||
        cg.add(var.add_data_template(key, templ))
 | 
					        cg.add(var.add_data_template(key, templ))
 | 
				
			||||||
    for key, value in config[CONF_VARIABLES].items():
 | 
					    for key, value in config[CONF_VARIABLES].items():
 | 
				
			||||||
        templ = yield cg.templatable(value, args, None)
 | 
					        templ = await cg.templatable(value, args, None)
 | 
				
			||||||
        cg.add(var.add_variable(key, templ))
 | 
					        cg.add(var.add_variable(key, templ))
 | 
				
			||||||
    yield var
 | 
					    return var
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def validate_homeassistant_event(value):
 | 
					def validate_homeassistant_event(value):
 | 
				
			||||||
    value = cv.string(value)
 | 
					    value = cv.string(value)
 | 
				
			||||||
    if not value.startswith(u'esphome.'):
 | 
					    if not value.startswith("esphome."):
 | 
				
			||||||
        raise cv.Invalid("ESPHome can only generate Home Assistant events that begin with "
 | 
					        raise cv.Invalid(
 | 
				
			||||||
                         "esphome. For example 'esphome.xyz'")
 | 
					            "ESPHome can only generate Home Assistant events that begin with "
 | 
				
			||||||
 | 
					            "esphome. For example 'esphome.xyz'"
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
    return value
 | 
					    return value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
HOMEASSISTANT_EVENT_ACTION_SCHEMA = cv.Schema({
 | 
					HOMEASSISTANT_EVENT_ACTION_SCHEMA = cv.Schema(
 | 
				
			||||||
    cv.GenerateID(): cv.use_id(APIServer),
 | 
					    {
 | 
				
			||||||
    cv.Required(CONF_EVENT): validate_homeassistant_event,
 | 
					        cv.GenerateID(): cv.use_id(APIServer),
 | 
				
			||||||
    cv.Optional(CONF_DATA, default={}): KEY_VALUE_SCHEMA,
 | 
					        cv.Required(CONF_EVENT): validate_homeassistant_event,
 | 
				
			||||||
    cv.Optional(CONF_DATA_TEMPLATE, default={}): KEY_VALUE_SCHEMA,
 | 
					        cv.Optional(CONF_DATA, default={}): KEY_VALUE_SCHEMA,
 | 
				
			||||||
    cv.Optional(CONF_VARIABLES, default={}): KEY_VALUE_SCHEMA,
 | 
					        cv.Optional(CONF_DATA_TEMPLATE, default={}): KEY_VALUE_SCHEMA,
 | 
				
			||||||
})
 | 
					        cv.Optional(CONF_VARIABLES, default={}): KEY_VALUE_SCHEMA,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@automation.register_action('homeassistant.event', HomeAssistantServiceCallAction,
 | 
					@automation.register_action(
 | 
				
			||||||
                            HOMEASSISTANT_EVENT_ACTION_SCHEMA)
 | 
					    "homeassistant.event",
 | 
				
			||||||
def homeassistant_event_to_code(config, action_id, template_arg, args):
 | 
					    HomeAssistantServiceCallAction,
 | 
				
			||||||
    serv = yield cg.get_variable(config[CONF_ID])
 | 
					    HOMEASSISTANT_EVENT_ACTION_SCHEMA,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					async def homeassistant_event_to_code(config, action_id, template_arg, args):
 | 
				
			||||||
 | 
					    serv = await cg.get_variable(config[CONF_ID])
 | 
				
			||||||
    var = cg.new_Pvariable(action_id, template_arg, serv, True)
 | 
					    var = cg.new_Pvariable(action_id, template_arg, serv, True)
 | 
				
			||||||
    templ = yield cg.templatable(config[CONF_EVENT], args, None)
 | 
					    templ = await cg.templatable(config[CONF_EVENT], 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 = yield cg.templatable(value, args, None)
 | 
					        templ = await cg.templatable(value, args, None)
 | 
				
			||||||
        cg.add(var.add_data(key, templ))
 | 
					        cg.add(var.add_data(key, templ))
 | 
				
			||||||
    for key, value in config[CONF_DATA_TEMPLATE].items():
 | 
					    for key, value in config[CONF_DATA_TEMPLATE].items():
 | 
				
			||||||
        templ = yield cg.templatable(value, args, None)
 | 
					        templ = await cg.templatable(value, args, None)
 | 
				
			||||||
        cg.add(var.add_data_template(key, templ))
 | 
					        cg.add(var.add_data_template(key, templ))
 | 
				
			||||||
    for key, value in config[CONF_VARIABLES].items():
 | 
					    for key, value in config[CONF_VARIABLES].items():
 | 
				
			||||||
        templ = yield cg.templatable(value, args, None)
 | 
					        templ = await cg.templatable(value, args, None)
 | 
				
			||||||
        cg.add(var.add_variable(key, templ))
 | 
					        cg.add(var.add_variable(key, templ))
 | 
				
			||||||
    yield var
 | 
					    return var
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@automation.register_condition('api.connected', APIConnectedCondition, {})
 | 
					HOMEASSISTANT_TAG_SCANNED_ACTION_SCHEMA = cv.maybe_simple_value(
 | 
				
			||||||
def api_connected_to_code(config, condition_id, template_arg, args):
 | 
					    {
 | 
				
			||||||
    yield cg.new_Pvariable(condition_id, template_arg)
 | 
					        cv.GenerateID(): cv.use_id(APIServer),
 | 
				
			||||||
 | 
					        cv.Required(CONF_TAG): cv.templatable(cv.string_strict),
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    key=CONF_TAG,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@automation.register_action(
 | 
				
			||||||
 | 
					    "homeassistant.tag_scanned",
 | 
				
			||||||
 | 
					    HomeAssistantServiceCallAction,
 | 
				
			||||||
 | 
					    HOMEASSISTANT_TAG_SCANNED_ACTION_SCHEMA,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					async def homeassistant_tag_scanned_to_code(config, action_id, template_arg, args):
 | 
				
			||||||
 | 
					    serv = await cg.get_variable(config[CONF_ID])
 | 
				
			||||||
 | 
					    var = cg.new_Pvariable(action_id, template_arg, serv, True)
 | 
				
			||||||
 | 
					    cg.add(var.set_service("esphome.tag_scanned"))
 | 
				
			||||||
 | 
					    templ = await cg.templatable(config[CONF_TAG], args, cg.std_string)
 | 
				
			||||||
 | 
					    cg.add(var.add_data("tag_id", templ))
 | 
				
			||||||
 | 
					    return var
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@automation.register_condition("api.connected", APIConnectedCondition, {})
 | 
				
			||||||
 | 
					async def api_connected_to_code(config, condition_id, template_arg, args):
 | 
				
			||||||
 | 
					    return cg.new_Pvariable(condition_id, template_arg)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -46,6 +46,7 @@ service APIConnection {
 | 
				
			|||||||
// The Home Assistant protocol is structured as a simple
 | 
					// The Home Assistant protocol is structured as a simple
 | 
				
			||||||
// TCP socket with short binary messages encoded in the protocol buffers format
 | 
					// TCP socket with short binary messages encoded in the protocol buffers format
 | 
				
			||||||
// First, a message in this protocol has a specific format:
 | 
					// First, a message in this protocol has a specific format:
 | 
				
			||||||
 | 
					//  * A zero byte.
 | 
				
			||||||
//  * VarInt denoting the size of the message object. (type is not part of this)
 | 
					//  * VarInt denoting the size of the message object. (type is not part of this)
 | 
				
			||||||
//  * VarInt denoting the type of message.
 | 
					//  * VarInt denoting the type of message.
 | 
				
			||||||
//  * The message object encoded as a ProtoBuf message
 | 
					//  * The message object encoded as a ProtoBuf message
 | 
				
			||||||
@@ -175,6 +176,10 @@ message DeviceInfoResponse {
 | 
				
			|||||||
  string model = 6;
 | 
					  string model = 6;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  bool has_deep_sleep = 7;
 | 
					  bool has_deep_sleep = 7;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // The esphome project details if set
 | 
				
			||||||
 | 
					  string project_name = 8;
 | 
				
			||||||
 | 
					  string project_version = 9;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
message ListEntitiesRequest {
 | 
					message ListEntitiesRequest {
 | 
				
			||||||
@@ -301,12 +306,18 @@ message ListEntitiesFanResponse {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  bool supports_oscillation = 5;
 | 
					  bool supports_oscillation = 5;
 | 
				
			||||||
  bool supports_speed = 6;
 | 
					  bool supports_speed = 6;
 | 
				
			||||||
 | 
					  bool supports_direction = 7;
 | 
				
			||||||
 | 
					  int32 supported_speed_count = 8;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
enum FanSpeed {
 | 
					enum FanSpeed {
 | 
				
			||||||
  FAN_SPEED_LOW = 0;
 | 
					  FAN_SPEED_LOW = 0;
 | 
				
			||||||
  FAN_SPEED_MEDIUM = 1;
 | 
					  FAN_SPEED_MEDIUM = 1;
 | 
				
			||||||
  FAN_SPEED_HIGH = 2;
 | 
					  FAN_SPEED_HIGH = 2;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					enum FanDirection {
 | 
				
			||||||
 | 
					  FAN_DIRECTION_FORWARD = 0;
 | 
				
			||||||
 | 
					  FAN_DIRECTION_REVERSE = 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
message FanStateResponse {
 | 
					message FanStateResponse {
 | 
				
			||||||
  option (id) = 23;
 | 
					  option (id) = 23;
 | 
				
			||||||
  option (source) = SOURCE_SERVER;
 | 
					  option (source) = SOURCE_SERVER;
 | 
				
			||||||
@@ -316,7 +327,9 @@ message FanStateResponse {
 | 
				
			|||||||
  fixed32 key = 1;
 | 
					  fixed32 key = 1;
 | 
				
			||||||
  bool state = 2;
 | 
					  bool state = 2;
 | 
				
			||||||
  bool oscillating = 3;
 | 
					  bool oscillating = 3;
 | 
				
			||||||
  FanSpeed speed = 4;
 | 
					  FanSpeed speed = 4 [deprecated = true];
 | 
				
			||||||
 | 
					  FanDirection direction = 5;
 | 
				
			||||||
 | 
					  int32 speed_level = 6;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
message FanCommandRequest {
 | 
					message FanCommandRequest {
 | 
				
			||||||
  option (id) = 31;
 | 
					  option (id) = 31;
 | 
				
			||||||
@@ -327,10 +340,14 @@ message FanCommandRequest {
 | 
				
			|||||||
  fixed32 key = 1;
 | 
					  fixed32 key = 1;
 | 
				
			||||||
  bool has_state = 2;
 | 
					  bool has_state = 2;
 | 
				
			||||||
  bool state = 3;
 | 
					  bool state = 3;
 | 
				
			||||||
  bool has_speed = 4;
 | 
					  bool has_speed = 4 [deprecated = true];
 | 
				
			||||||
  FanSpeed speed = 5;
 | 
					  FanSpeed speed = 5 [deprecated = true];
 | 
				
			||||||
  bool has_oscillating = 6;
 | 
					  bool has_oscillating = 6;
 | 
				
			||||||
  bool oscillating = 7;
 | 
					  bool oscillating = 7;
 | 
				
			||||||
 | 
					  bool has_direction = 8;
 | 
				
			||||||
 | 
					  FanDirection direction = 9;
 | 
				
			||||||
 | 
					  bool has_speed_level = 10;
 | 
				
			||||||
 | 
					  int32 speed_level = 11;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ==================== LIGHT ====================
 | 
					// ==================== LIGHT ====================
 | 
				
			||||||
@@ -396,6 +413,11 @@ message LightCommandRequest {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ==================== SENSOR ====================
 | 
					// ==================== SENSOR ====================
 | 
				
			||||||
 | 
					enum SensorStateClass {
 | 
				
			||||||
 | 
					  STATE_CLASS_NONE = 0;
 | 
				
			||||||
 | 
					  STATE_CLASS_MEASUREMENT = 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
message ListEntitiesSensorResponse {
 | 
					message ListEntitiesSensorResponse {
 | 
				
			||||||
  option (id) = 16;
 | 
					  option (id) = 16;
 | 
				
			||||||
  option (source) = SOURCE_SERVER;
 | 
					  option (source) = SOURCE_SERVER;
 | 
				
			||||||
@@ -410,6 +432,8 @@ message ListEntitiesSensorResponse {
 | 
				
			|||||||
  string unit_of_measurement = 6;
 | 
					  string unit_of_measurement = 6;
 | 
				
			||||||
  int32 accuracy_decimals = 7;
 | 
					  int32 accuracy_decimals = 7;
 | 
				
			||||||
  bool force_update = 8;
 | 
					  bool force_update = 8;
 | 
				
			||||||
 | 
					  string device_class = 9;
 | 
				
			||||||
 | 
					  SensorStateClass state_class = 10;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
message SensorStateResponse {
 | 
					message SensorStateResponse {
 | 
				
			||||||
  option (id) = 25;
 | 
					  option (id) = 25;
 | 
				
			||||||
@@ -547,6 +571,7 @@ message SubscribeHomeAssistantStateResponse {
 | 
				
			|||||||
  option (id) = 39;
 | 
					  option (id) = 39;
 | 
				
			||||||
  option (source) = SOURCE_SERVER;
 | 
					  option (source) = SOURCE_SERVER;
 | 
				
			||||||
  string entity_id = 1;
 | 
					  string entity_id = 1;
 | 
				
			||||||
 | 
					  string attribute = 2;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
message HomeAssistantStateResponse {
 | 
					message HomeAssistantStateResponse {
 | 
				
			||||||
@@ -556,6 +581,7 @@ message HomeAssistantStateResponse {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  string entity_id = 1;
 | 
					  string entity_id = 1;
 | 
				
			||||||
  string state = 2;
 | 
					  string state = 2;
 | 
				
			||||||
 | 
					  string attribute = 3;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ==================== IMPORT TIME ====================
 | 
					// ==================== IMPORT TIME ====================
 | 
				
			||||||
@@ -650,15 +676,47 @@ message CameraImageRequest {
 | 
				
			|||||||
// ==================== CLIMATE ====================
 | 
					// ==================== CLIMATE ====================
 | 
				
			||||||
enum ClimateMode {
 | 
					enum ClimateMode {
 | 
				
			||||||
  CLIMATE_MODE_OFF = 0;
 | 
					  CLIMATE_MODE_OFF = 0;
 | 
				
			||||||
  CLIMATE_MODE_AUTO = 1;
 | 
					  CLIMATE_MODE_HEAT_COOL = 1;
 | 
				
			||||||
  CLIMATE_MODE_COOL = 2;
 | 
					  CLIMATE_MODE_COOL = 2;
 | 
				
			||||||
  CLIMATE_MODE_HEAT = 3;
 | 
					  CLIMATE_MODE_HEAT = 3;
 | 
				
			||||||
 | 
					  CLIMATE_MODE_FAN_ONLY = 4;
 | 
				
			||||||
 | 
					  CLIMATE_MODE_DRY = 5;
 | 
				
			||||||
 | 
					  CLIMATE_MODE_AUTO = 6;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					enum ClimateFanMode {
 | 
				
			||||||
 | 
					  CLIMATE_FAN_ON = 0;
 | 
				
			||||||
 | 
					  CLIMATE_FAN_OFF = 1;
 | 
				
			||||||
 | 
					  CLIMATE_FAN_AUTO = 2;
 | 
				
			||||||
 | 
					  CLIMATE_FAN_LOW = 3;
 | 
				
			||||||
 | 
					  CLIMATE_FAN_MEDIUM = 4;
 | 
				
			||||||
 | 
					  CLIMATE_FAN_HIGH = 5;
 | 
				
			||||||
 | 
					  CLIMATE_FAN_MIDDLE = 6;
 | 
				
			||||||
 | 
					  CLIMATE_FAN_FOCUS = 7;
 | 
				
			||||||
 | 
					  CLIMATE_FAN_DIFFUSE = 8;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					enum ClimateSwingMode {
 | 
				
			||||||
 | 
					  CLIMATE_SWING_OFF = 0;
 | 
				
			||||||
 | 
					  CLIMATE_SWING_BOTH = 1;
 | 
				
			||||||
 | 
					  CLIMATE_SWING_VERTICAL = 2;
 | 
				
			||||||
 | 
					  CLIMATE_SWING_HORIZONTAL = 3;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
enum ClimateAction {
 | 
					enum ClimateAction {
 | 
				
			||||||
  CLIMATE_ACTION_OFF = 0;
 | 
					  CLIMATE_ACTION_OFF = 0;
 | 
				
			||||||
  // values same as mode for readability
 | 
					  // values same as mode for readability
 | 
				
			||||||
  CLIMATE_ACTION_COOLING = 2;
 | 
					  CLIMATE_ACTION_COOLING = 2;
 | 
				
			||||||
  CLIMATE_ACTION_HEATING = 3;
 | 
					  CLIMATE_ACTION_HEATING = 3;
 | 
				
			||||||
 | 
					  CLIMATE_ACTION_IDLE = 4;
 | 
				
			||||||
 | 
					  CLIMATE_ACTION_DRYING = 5;
 | 
				
			||||||
 | 
					  CLIMATE_ACTION_FAN = 6;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					enum ClimatePreset {
 | 
				
			||||||
 | 
					  CLIMATE_PRESET_ECO = 0;
 | 
				
			||||||
 | 
					  CLIMATE_PRESET_AWAY = 1;
 | 
				
			||||||
 | 
					  CLIMATE_PRESET_BOOST = 2;
 | 
				
			||||||
 | 
					  CLIMATE_PRESET_COMFORT = 3;
 | 
				
			||||||
 | 
					  CLIMATE_PRESET_HOME = 4;
 | 
				
			||||||
 | 
					  CLIMATE_PRESET_SLEEP = 5;
 | 
				
			||||||
 | 
					  CLIMATE_PRESET_ACTIVITY = 6;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
message ListEntitiesClimateResponse {
 | 
					message ListEntitiesClimateResponse {
 | 
				
			||||||
  option (id) = 46;
 | 
					  option (id) = 46;
 | 
				
			||||||
@@ -678,6 +736,11 @@ message ListEntitiesClimateResponse {
 | 
				
			|||||||
  float visual_temperature_step = 10;
 | 
					  float visual_temperature_step = 10;
 | 
				
			||||||
  bool supports_away = 11;
 | 
					  bool supports_away = 11;
 | 
				
			||||||
  bool supports_action = 12;
 | 
					  bool supports_action = 12;
 | 
				
			||||||
 | 
					  repeated ClimateFanMode supported_fan_modes = 13;
 | 
				
			||||||
 | 
					  repeated ClimateSwingMode supported_swing_modes = 14;
 | 
				
			||||||
 | 
					  repeated string supported_custom_fan_modes = 15;
 | 
				
			||||||
 | 
					  repeated ClimatePreset supported_presets = 16;
 | 
				
			||||||
 | 
					  repeated string supported_custom_presets = 17;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
message ClimateStateResponse {
 | 
					message ClimateStateResponse {
 | 
				
			||||||
  option (id) = 47;
 | 
					  option (id) = 47;
 | 
				
			||||||
@@ -693,6 +756,11 @@ message ClimateStateResponse {
 | 
				
			|||||||
  float target_temperature_high = 6;
 | 
					  float target_temperature_high = 6;
 | 
				
			||||||
  bool away = 7;
 | 
					  bool away = 7;
 | 
				
			||||||
  ClimateAction action = 8;
 | 
					  ClimateAction action = 8;
 | 
				
			||||||
 | 
					  ClimateFanMode fan_mode = 9;
 | 
				
			||||||
 | 
					  ClimateSwingMode swing_mode = 10;
 | 
				
			||||||
 | 
					  string custom_fan_mode = 11;
 | 
				
			||||||
 | 
					  ClimatePreset preset = 12;
 | 
				
			||||||
 | 
					  string custom_preset = 13;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
message ClimateCommandRequest {
 | 
					message ClimateCommandRequest {
 | 
				
			||||||
  option (id) = 48;
 | 
					  option (id) = 48;
 | 
				
			||||||
@@ -711,4 +779,14 @@ message ClimateCommandRequest {
 | 
				
			|||||||
  float target_temperature_high = 9;
 | 
					  float target_temperature_high = 9;
 | 
				
			||||||
  bool has_away = 10;
 | 
					  bool has_away = 10;
 | 
				
			||||||
  bool away = 11;
 | 
					  bool away = 11;
 | 
				
			||||||
 | 
					  bool has_fan_mode = 12;
 | 
				
			||||||
 | 
					  ClimateFanMode fan_mode = 13;
 | 
				
			||||||
 | 
					  bool has_swing_mode = 14;
 | 
				
			||||||
 | 
					  ClimateSwingMode swing_mode = 15;
 | 
				
			||||||
 | 
					  bool has_custom_fan_mode = 16;
 | 
				
			||||||
 | 
					  string custom_fan_mode = 17;
 | 
				
			||||||
 | 
					  bool has_preset = 18;
 | 
				
			||||||
 | 
					  ClimatePreset preset = 19;
 | 
				
			||||||
 | 
					  bool has_custom_preset = 20;
 | 
				
			||||||
 | 
					  string custom_preset = 21;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,6 +9,9 @@
 | 
				
			|||||||
#ifdef USE_HOMEASSISTANT_TIME
 | 
					#ifdef USE_HOMEASSISTANT_TIME
 | 
				
			||||||
#include "esphome/components/homeassistant/time/homeassistant_time.h"
 | 
					#include "esphome/components/homeassistant/time/homeassistant_time.h"
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					#ifdef USE_FAN
 | 
				
			||||||
 | 
					#include "esphome/components/fan/fan_helpers.h"
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace esphome {
 | 
					namespace esphome {
 | 
				
			||||||
namespace api {
 | 
					namespace api {
 | 
				
			||||||
@@ -137,7 +140,6 @@ void APIConnection::loop() {
 | 
				
			|||||||
      // bool done = 3;
 | 
					      // bool done = 3;
 | 
				
			||||||
      bool done = this->image_reader_.available() == to_send;
 | 
					      bool done = this->image_reader_.available() == to_send;
 | 
				
			||||||
      buffer.encode_bool(3, done);
 | 
					      buffer.encode_bool(3, done);
 | 
				
			||||||
      this->set_nodelay(false);
 | 
					 | 
				
			||||||
      bool success = this->send_buffer(buffer, 44);
 | 
					      bool success = this->send_buffer(buffer, 44);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (success) {
 | 
					      if (success) {
 | 
				
			||||||
@@ -247,8 +249,12 @@ bool APIConnection::send_fan_state(fan::FanState *fan) {
 | 
				
			|||||||
  resp.state = fan->state;
 | 
					  resp.state = fan->state;
 | 
				
			||||||
  if (traits.supports_oscillation())
 | 
					  if (traits.supports_oscillation())
 | 
				
			||||||
    resp.oscillating = fan->oscillating;
 | 
					    resp.oscillating = fan->oscillating;
 | 
				
			||||||
  if (traits.supports_speed())
 | 
					  if (traits.supports_speed()) {
 | 
				
			||||||
    resp.speed = static_cast<enums::FanSpeed>(fan->speed);
 | 
					    resp.speed_level = fan->speed;
 | 
				
			||||||
 | 
					    resp.speed = static_cast<enums::FanSpeed>(fan::speed_level_to_enum(fan->speed, traits.supported_speed_count()));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (traits.supports_direction())
 | 
				
			||||||
 | 
					    resp.direction = static_cast<enums::FanDirection>(fan->direction);
 | 
				
			||||||
  return this->send_fan_state_response(resp);
 | 
					  return this->send_fan_state_response(resp);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
bool APIConnection::send_fan_info(fan::FanState *fan) {
 | 
					bool APIConnection::send_fan_info(fan::FanState *fan) {
 | 
				
			||||||
@@ -260,6 +266,8 @@ bool APIConnection::send_fan_info(fan::FanState *fan) {
 | 
				
			|||||||
  msg.unique_id = get_default_unique_id("fan", fan);
 | 
					  msg.unique_id = get_default_unique_id("fan", fan);
 | 
				
			||||||
  msg.supports_oscillation = traits.supports_oscillation();
 | 
					  msg.supports_oscillation = traits.supports_oscillation();
 | 
				
			||||||
  msg.supports_speed = traits.supports_speed();
 | 
					  msg.supports_speed = traits.supports_speed();
 | 
				
			||||||
 | 
					  msg.supports_direction = traits.supports_direction();
 | 
				
			||||||
 | 
					  msg.supported_speed_count = traits.supported_speed_count();
 | 
				
			||||||
  return this->send_list_entities_fan_response(msg);
 | 
					  return this->send_list_entities_fan_response(msg);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
void APIConnection::fan_command(const FanCommandRequest &msg) {
 | 
					void APIConnection::fan_command(const FanCommandRequest &msg) {
 | 
				
			||||||
@@ -267,13 +275,21 @@ void APIConnection::fan_command(const FanCommandRequest &msg) {
 | 
				
			|||||||
  if (fan == nullptr)
 | 
					  if (fan == nullptr)
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  auto traits = fan->get_traits();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  auto call = fan->make_call();
 | 
					  auto call = fan->make_call();
 | 
				
			||||||
  if (msg.has_state)
 | 
					  if (msg.has_state)
 | 
				
			||||||
    call.set_state(msg.state);
 | 
					    call.set_state(msg.state);
 | 
				
			||||||
  if (msg.has_oscillating)
 | 
					  if (msg.has_oscillating)
 | 
				
			||||||
    call.set_oscillating(msg.oscillating);
 | 
					    call.set_oscillating(msg.oscillating);
 | 
				
			||||||
  if (msg.has_speed)
 | 
					  if (msg.has_speed_level) {
 | 
				
			||||||
    call.set_speed(static_cast<fan::FanSpeed>(msg.speed));
 | 
					    // Prefer level
 | 
				
			||||||
 | 
					    call.set_speed(msg.speed_level);
 | 
				
			||||||
 | 
					  } else if (msg.has_speed) {
 | 
				
			||||||
 | 
					    call.set_speed(fan::speed_enum_to_level(static_cast<fan::FanSpeed>(msg.speed), traits.supported_speed_count()));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (msg.has_direction)
 | 
				
			||||||
 | 
					    call.set_direction(static_cast<fan::FanDirection>(msg.direction));
 | 
				
			||||||
  call.perform();
 | 
					  call.perform();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
@@ -378,6 +394,9 @@ bool APIConnection::send_sensor_info(sensor::Sensor *sensor) {
 | 
				
			|||||||
  msg.unit_of_measurement = sensor->get_unit_of_measurement();
 | 
					  msg.unit_of_measurement = sensor->get_unit_of_measurement();
 | 
				
			||||||
  msg.accuracy_decimals = sensor->get_accuracy_decimals();
 | 
					  msg.accuracy_decimals = sensor->get_accuracy_decimals();
 | 
				
			||||||
  msg.force_update = sensor->get_force_update();
 | 
					  msg.force_update = sensor->get_force_update();
 | 
				
			||||||
 | 
					  msg.device_class = sensor->get_device_class();
 | 
				
			||||||
 | 
					  msg.state_class = static_cast<enums::SensorStateClass>(sensor->state_class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return this->send_list_entities_sensor_response(msg);
 | 
					  return this->send_list_entities_sensor_response(msg);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
@@ -458,6 +477,16 @@ bool APIConnection::send_climate_state(climate::Climate *climate) {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
  if (traits.get_supports_away())
 | 
					  if (traits.get_supports_away())
 | 
				
			||||||
    resp.away = climate->away;
 | 
					    resp.away = climate->away;
 | 
				
			||||||
 | 
					  if (traits.get_supports_fan_modes() && climate->fan_mode.has_value())
 | 
				
			||||||
 | 
					    resp.fan_mode = static_cast<enums::ClimateFanMode>(climate->fan_mode.value());
 | 
				
			||||||
 | 
					  if (!traits.get_supported_custom_fan_modes().empty() && climate->custom_fan_mode.has_value())
 | 
				
			||||||
 | 
					    resp.custom_fan_mode = climate->custom_fan_mode.value();
 | 
				
			||||||
 | 
					  if (traits.get_supports_presets() && climate->preset.has_value())
 | 
				
			||||||
 | 
					    resp.preset = static_cast<enums::ClimatePreset>(climate->preset.value());
 | 
				
			||||||
 | 
					  if (!traits.get_supported_custom_presets().empty() && climate->custom_preset.has_value())
 | 
				
			||||||
 | 
					    resp.custom_preset = climate->custom_preset.value();
 | 
				
			||||||
 | 
					  if (traits.get_supports_swing_modes())
 | 
				
			||||||
 | 
					    resp.swing_mode = static_cast<enums::ClimateSwingMode>(climate->swing_mode);
 | 
				
			||||||
  return this->send_climate_state_response(resp);
 | 
					  return this->send_climate_state_response(resp);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
bool APIConnection::send_climate_info(climate::Climate *climate) {
 | 
					bool APIConnection::send_climate_info(climate::Climate *climate) {
 | 
				
			||||||
@@ -469,8 +498,9 @@ bool APIConnection::send_climate_info(climate::Climate *climate) {
 | 
				
			|||||||
  msg.unique_id = get_default_unique_id("climate", climate);
 | 
					  msg.unique_id = get_default_unique_id("climate", climate);
 | 
				
			||||||
  msg.supports_current_temperature = traits.get_supports_current_temperature();
 | 
					  msg.supports_current_temperature = traits.get_supports_current_temperature();
 | 
				
			||||||
  msg.supports_two_point_target_temperature = traits.get_supports_two_point_target_temperature();
 | 
					  msg.supports_two_point_target_temperature = traits.get_supports_two_point_target_temperature();
 | 
				
			||||||
  for (auto mode : {climate::CLIMATE_MODE_AUTO, climate::CLIMATE_MODE_OFF, climate::CLIMATE_MODE_COOL,
 | 
					  for (auto mode :
 | 
				
			||||||
                    climate::CLIMATE_MODE_HEAT}) {
 | 
					       {climate::CLIMATE_MODE_AUTO, climate::CLIMATE_MODE_OFF, climate::CLIMATE_MODE_COOL, climate::CLIMATE_MODE_HEAT,
 | 
				
			||||||
 | 
					        climate::CLIMATE_MODE_DRY, climate::CLIMATE_MODE_FAN_ONLY, climate::CLIMATE_MODE_HEAT_COOL}) {
 | 
				
			||||||
    if (traits.supports_mode(mode))
 | 
					    if (traits.supports_mode(mode))
 | 
				
			||||||
      msg.supported_modes.push_back(static_cast<enums::ClimateMode>(mode));
 | 
					      msg.supported_modes.push_back(static_cast<enums::ClimateMode>(mode));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -479,6 +509,29 @@ bool APIConnection::send_climate_info(climate::Climate *climate) {
 | 
				
			|||||||
  msg.visual_temperature_step = traits.get_visual_temperature_step();
 | 
					  msg.visual_temperature_step = traits.get_visual_temperature_step();
 | 
				
			||||||
  msg.supports_away = traits.get_supports_away();
 | 
					  msg.supports_away = traits.get_supports_away();
 | 
				
			||||||
  msg.supports_action = traits.get_supports_action();
 | 
					  msg.supports_action = traits.get_supports_action();
 | 
				
			||||||
 | 
					  for (auto fan_mode : {climate::CLIMATE_FAN_ON, climate::CLIMATE_FAN_OFF, climate::CLIMATE_FAN_AUTO,
 | 
				
			||||||
 | 
					                        climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM, climate::CLIMATE_FAN_HIGH,
 | 
				
			||||||
 | 
					                        climate::CLIMATE_FAN_MIDDLE, climate::CLIMATE_FAN_FOCUS, climate::CLIMATE_FAN_DIFFUSE}) {
 | 
				
			||||||
 | 
					    if (traits.supports_fan_mode(fan_mode))
 | 
				
			||||||
 | 
					      msg.supported_fan_modes.push_back(static_cast<enums::ClimateFanMode>(fan_mode));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  for (auto const &custom_fan_mode : traits.get_supported_custom_fan_modes()) {
 | 
				
			||||||
 | 
					    msg.supported_custom_fan_modes.push_back(custom_fan_mode);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  for (auto preset : {climate::CLIMATE_PRESET_ECO, climate::CLIMATE_PRESET_AWAY, climate::CLIMATE_PRESET_BOOST,
 | 
				
			||||||
 | 
					                      climate::CLIMATE_PRESET_COMFORT, climate::CLIMATE_PRESET_HOME, climate::CLIMATE_PRESET_SLEEP,
 | 
				
			||||||
 | 
					                      climate::CLIMATE_PRESET_ACTIVITY}) {
 | 
				
			||||||
 | 
					    if (traits.supports_preset(preset))
 | 
				
			||||||
 | 
					      msg.supported_presets.push_back(static_cast<enums::ClimatePreset>(preset));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  for (auto const &custom_preset : traits.get_supported_custom_presets()) {
 | 
				
			||||||
 | 
					    msg.supported_custom_presets.push_back(custom_preset);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  for (auto swing_mode : {climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_BOTH, climate::CLIMATE_SWING_VERTICAL,
 | 
				
			||||||
 | 
					                          climate::CLIMATE_SWING_HORIZONTAL}) {
 | 
				
			||||||
 | 
					    if (traits.supports_swing_mode(swing_mode))
 | 
				
			||||||
 | 
					      msg.supported_swing_modes.push_back(static_cast<enums::ClimateSwingMode>(swing_mode));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  return this->send_list_entities_climate_response(msg);
 | 
					  return this->send_list_entities_climate_response(msg);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
void APIConnection::climate_command(const ClimateCommandRequest &msg) {
 | 
					void APIConnection::climate_command(const ClimateCommandRequest &msg) {
 | 
				
			||||||
@@ -497,6 +550,16 @@ void APIConnection::climate_command(const ClimateCommandRequest &msg) {
 | 
				
			|||||||
    call.set_target_temperature_high(msg.target_temperature_high);
 | 
					    call.set_target_temperature_high(msg.target_temperature_high);
 | 
				
			||||||
  if (msg.has_away)
 | 
					  if (msg.has_away)
 | 
				
			||||||
    call.set_away(msg.away);
 | 
					    call.set_away(msg.away);
 | 
				
			||||||
 | 
					  if (msg.has_fan_mode)
 | 
				
			||||||
 | 
					    call.set_fan_mode(static_cast<climate::ClimateFanMode>(msg.fan_mode));
 | 
				
			||||||
 | 
					  if (msg.has_custom_fan_mode)
 | 
				
			||||||
 | 
					    call.set_fan_mode(msg.custom_fan_mode);
 | 
				
			||||||
 | 
					  if (msg.has_preset)
 | 
				
			||||||
 | 
					    call.set_preset(static_cast<climate::ClimatePreset>(msg.preset));
 | 
				
			||||||
 | 
					  if (msg.has_custom_preset)
 | 
				
			||||||
 | 
					    call.set_preset(msg.custom_preset);
 | 
				
			||||||
 | 
					  if (msg.has_swing_mode)
 | 
				
			||||||
 | 
					    call.set_swing_mode(static_cast<climate::ClimateSwingMode>(msg.swing_mode));
 | 
				
			||||||
  call.perform();
 | 
					  call.perform();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
@@ -539,8 +602,6 @@ bool APIConnection::send_log_message(int level, const char *tag, const char *lin
 | 
				
			|||||||
  if (this->log_subscription_ < level)
 | 
					  if (this->log_subscription_ < level)
 | 
				
			||||||
    return false;
 | 
					    return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  this->set_nodelay(false);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Send raw so that we don't copy too much
 | 
					  // Send raw so that we don't copy too much
 | 
				
			||||||
  auto buffer = this->create_buffer();
 | 
					  auto buffer = this->create_buffer();
 | 
				
			||||||
  // LogLevel level = 1;
 | 
					  // LogLevel level = 1;
 | 
				
			||||||
@@ -568,7 +629,7 @@ HelloResponse APIConnection::hello(const HelloRequest &msg) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  HelloResponse resp;
 | 
					  HelloResponse resp;
 | 
				
			||||||
  resp.api_version_major = 1;
 | 
					  resp.api_version_major = 1;
 | 
				
			||||||
  resp.api_version_minor = 3;
 | 
					  resp.api_version_minor = 4;
 | 
				
			||||||
  resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")";
 | 
					  resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")";
 | 
				
			||||||
  this->connection_state_ = ConnectionState::CONNECTED;
 | 
					  this->connection_state_ = ConnectionState::CONNECTED;
 | 
				
			||||||
  return resp;
 | 
					  return resp;
 | 
				
			||||||
@@ -603,13 +664,18 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
 | 
				
			|||||||
#endif
 | 
					#endif
 | 
				
			||||||
#ifdef USE_DEEP_SLEEP
 | 
					#ifdef USE_DEEP_SLEEP
 | 
				
			||||||
  resp.has_deep_sleep = deep_sleep::global_has_deep_sleep;
 | 
					  resp.has_deep_sleep = deep_sleep::global_has_deep_sleep;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#ifdef ESPHOME_PROJECT_NAME
 | 
				
			||||||
 | 
					  resp.project_name = ESPHOME_PROJECT_NAME;
 | 
				
			||||||
 | 
					  resp.project_version = ESPHOME_PROJECT_VERSION;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
  return resp;
 | 
					  return resp;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
void APIConnection::on_home_assistant_state_response(const HomeAssistantStateResponse &msg) {
 | 
					void APIConnection::on_home_assistant_state_response(const HomeAssistantStateResponse &msg) {
 | 
				
			||||||
  for (auto &it : this->parent_->get_state_subs())
 | 
					  for (auto &it : this->parent_->get_state_subs())
 | 
				
			||||||
    if (it.entity_id == msg.entity_id)
 | 
					    if (it.entity_id == msg.entity_id && it.attribute.value() == msg.attribute) {
 | 
				
			||||||
      it.callback(msg.state);
 | 
					      it.callback(msg.state);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
void APIConnection::execute_service(const ExecuteServiceRequest &msg) {
 | 
					void APIConnection::execute_service(const ExecuteServiceRequest &msg) {
 | 
				
			||||||
  bool found = false;
 | 
					  bool found = false;
 | 
				
			||||||
@@ -626,6 +692,7 @@ void APIConnection::subscribe_home_assistant_states(const SubscribeHomeAssistant
 | 
				
			|||||||
  for (auto &it : this->parent_->get_state_subs()) {
 | 
					  for (auto &it : this->parent_->get_state_subs()) {
 | 
				
			||||||
    SubscribeHomeAssistantStateResponse resp;
 | 
					    SubscribeHomeAssistantStateResponse resp;
 | 
				
			||||||
    resp.entity_id = it.entity_id;
 | 
					    resp.entity_id = it.entity_id;
 | 
				
			||||||
 | 
					    resp.attribute = it.attribute.value();
 | 
				
			||||||
    if (!this->send_subscribe_home_assistant_state_response(resp)) {
 | 
					    if (!this->send_subscribe_home_assistant_state_response(resp)) {
 | 
				
			||||||
      this->on_fatal_error();
 | 
					      this->on_fatal_error();
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
@@ -655,8 +722,10 @@ bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint32_t message_type)
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  this->client_->add(reinterpret_cast<char *>(header.data()), header.size());
 | 
					  this->client_->add(reinterpret_cast<char *>(header.data()), header.size(),
 | 
				
			||||||
  this->client_->add(reinterpret_cast<char *>(buffer.get_buffer()->data()), buffer.get_buffer()->size());
 | 
					                     ASYNC_WRITE_FLAG_COPY | ASYNC_WRITE_FLAG_MORE);
 | 
				
			||||||
 | 
					  this->client_->add(reinterpret_cast<char *>(buffer.get_buffer()->data()), buffer.get_buffer()->size(),
 | 
				
			||||||
 | 
					                     ASYNC_WRITE_FLAG_COPY);
 | 
				
			||||||
  bool ret = this->client_->send();
 | 
					  bool ret = this->client_->send();
 | 
				
			||||||
  return ret;
 | 
					  return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -138,12 +138,6 @@ class APIConnection : public APIServerConnection {
 | 
				
			|||||||
  void on_timeout_(uint32_t time);
 | 
					  void on_timeout_(uint32_t time);
 | 
				
			||||||
  void on_data_(uint8_t *buf, size_t len);
 | 
					  void on_data_(uint8_t *buf, size_t len);
 | 
				
			||||||
  void parse_recv_buffer_();
 | 
					  void parse_recv_buffer_();
 | 
				
			||||||
  void set_nodelay(bool nodelay) override {
 | 
					 | 
				
			||||||
    if (nodelay == this->current_nodelay_)
 | 
					 | 
				
			||||||
      return;
 | 
					 | 
				
			||||||
    this->client_->setNoDelay(nodelay);
 | 
					 | 
				
			||||||
    this->current_nodelay_ = nodelay;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  enum class ConnectionState {
 | 
					  enum class ConnectionState {
 | 
				
			||||||
    WAITING_FOR_HELLO,
 | 
					    WAITING_FOR_HELLO,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,5 @@
 | 
				
			|||||||
 | 
					// This file was automatically generated with a tool.
 | 
				
			||||||
 | 
					// See scripts/api_protobuf/api_protobuf.py
 | 
				
			||||||
#include "api_pb2.h"
 | 
					#include "api_pb2.h"
 | 
				
			||||||
#include "esphome/core/log.h"
 | 
					#include "esphome/core/log.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -50,6 +52,26 @@ template<> const char *proto_enum_to_string<enums::FanSpeed>(enums::FanSpeed val
 | 
				
			|||||||
      return "UNKNOWN";
 | 
					      return "UNKNOWN";
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					template<> const char *proto_enum_to_string<enums::FanDirection>(enums::FanDirection value) {
 | 
				
			||||||
 | 
					  switch (value) {
 | 
				
			||||||
 | 
					    case enums::FAN_DIRECTION_FORWARD:
 | 
				
			||||||
 | 
					      return "FAN_DIRECTION_FORWARD";
 | 
				
			||||||
 | 
					    case enums::FAN_DIRECTION_REVERSE:
 | 
				
			||||||
 | 
					      return "FAN_DIRECTION_REVERSE";
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					      return "UNKNOWN";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					template<> const char *proto_enum_to_string<enums::SensorStateClass>(enums::SensorStateClass value) {
 | 
				
			||||||
 | 
					  switch (value) {
 | 
				
			||||||
 | 
					    case enums::STATE_CLASS_NONE:
 | 
				
			||||||
 | 
					      return "STATE_CLASS_NONE";
 | 
				
			||||||
 | 
					    case enums::STATE_CLASS_MEASUREMENT:
 | 
				
			||||||
 | 
					      return "STATE_CLASS_MEASUREMENT";
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					      return "UNKNOWN";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
template<> const char *proto_enum_to_string<enums::LogLevel>(enums::LogLevel value) {
 | 
					template<> const char *proto_enum_to_string<enums::LogLevel>(enums::LogLevel value) {
 | 
				
			||||||
  switch (value) {
 | 
					  switch (value) {
 | 
				
			||||||
    case enums::LOG_LEVEL_NONE:
 | 
					    case enums::LOG_LEVEL_NONE:
 | 
				
			||||||
@@ -96,12 +118,56 @@ template<> const char *proto_enum_to_string<enums::ClimateMode>(enums::ClimateMo
 | 
				
			|||||||
  switch (value) {
 | 
					  switch (value) {
 | 
				
			||||||
    case enums::CLIMATE_MODE_OFF:
 | 
					    case enums::CLIMATE_MODE_OFF:
 | 
				
			||||||
      return "CLIMATE_MODE_OFF";
 | 
					      return "CLIMATE_MODE_OFF";
 | 
				
			||||||
    case enums::CLIMATE_MODE_AUTO:
 | 
					    case enums::CLIMATE_MODE_HEAT_COOL:
 | 
				
			||||||
      return "CLIMATE_MODE_AUTO";
 | 
					      return "CLIMATE_MODE_HEAT_COOL";
 | 
				
			||||||
    case enums::CLIMATE_MODE_COOL:
 | 
					    case enums::CLIMATE_MODE_COOL:
 | 
				
			||||||
      return "CLIMATE_MODE_COOL";
 | 
					      return "CLIMATE_MODE_COOL";
 | 
				
			||||||
    case enums::CLIMATE_MODE_HEAT:
 | 
					    case enums::CLIMATE_MODE_HEAT:
 | 
				
			||||||
      return "CLIMATE_MODE_HEAT";
 | 
					      return "CLIMATE_MODE_HEAT";
 | 
				
			||||||
 | 
					    case enums::CLIMATE_MODE_FAN_ONLY:
 | 
				
			||||||
 | 
					      return "CLIMATE_MODE_FAN_ONLY";
 | 
				
			||||||
 | 
					    case enums::CLIMATE_MODE_DRY:
 | 
				
			||||||
 | 
					      return "CLIMATE_MODE_DRY";
 | 
				
			||||||
 | 
					    case enums::CLIMATE_MODE_AUTO:
 | 
				
			||||||
 | 
					      return "CLIMATE_MODE_AUTO";
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					      return "UNKNOWN";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					template<> const char *proto_enum_to_string<enums::ClimateFanMode>(enums::ClimateFanMode value) {
 | 
				
			||||||
 | 
					  switch (value) {
 | 
				
			||||||
 | 
					    case enums::CLIMATE_FAN_ON:
 | 
				
			||||||
 | 
					      return "CLIMATE_FAN_ON";
 | 
				
			||||||
 | 
					    case enums::CLIMATE_FAN_OFF:
 | 
				
			||||||
 | 
					      return "CLIMATE_FAN_OFF";
 | 
				
			||||||
 | 
					    case enums::CLIMATE_FAN_AUTO:
 | 
				
			||||||
 | 
					      return "CLIMATE_FAN_AUTO";
 | 
				
			||||||
 | 
					    case enums::CLIMATE_FAN_LOW:
 | 
				
			||||||
 | 
					      return "CLIMATE_FAN_LOW";
 | 
				
			||||||
 | 
					    case enums::CLIMATE_FAN_MEDIUM:
 | 
				
			||||||
 | 
					      return "CLIMATE_FAN_MEDIUM";
 | 
				
			||||||
 | 
					    case enums::CLIMATE_FAN_HIGH:
 | 
				
			||||||
 | 
					      return "CLIMATE_FAN_HIGH";
 | 
				
			||||||
 | 
					    case enums::CLIMATE_FAN_MIDDLE:
 | 
				
			||||||
 | 
					      return "CLIMATE_FAN_MIDDLE";
 | 
				
			||||||
 | 
					    case enums::CLIMATE_FAN_FOCUS:
 | 
				
			||||||
 | 
					      return "CLIMATE_FAN_FOCUS";
 | 
				
			||||||
 | 
					    case enums::CLIMATE_FAN_DIFFUSE:
 | 
				
			||||||
 | 
					      return "CLIMATE_FAN_DIFFUSE";
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					      return "UNKNOWN";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					template<> const char *proto_enum_to_string<enums::ClimateSwingMode>(enums::ClimateSwingMode value) {
 | 
				
			||||||
 | 
					  switch (value) {
 | 
				
			||||||
 | 
					    case enums::CLIMATE_SWING_OFF:
 | 
				
			||||||
 | 
					      return "CLIMATE_SWING_OFF";
 | 
				
			||||||
 | 
					    case enums::CLIMATE_SWING_BOTH:
 | 
				
			||||||
 | 
					      return "CLIMATE_SWING_BOTH";
 | 
				
			||||||
 | 
					    case enums::CLIMATE_SWING_VERTICAL:
 | 
				
			||||||
 | 
					      return "CLIMATE_SWING_VERTICAL";
 | 
				
			||||||
 | 
					    case enums::CLIMATE_SWING_HORIZONTAL:
 | 
				
			||||||
 | 
					      return "CLIMATE_SWING_HORIZONTAL";
 | 
				
			||||||
    default:
 | 
					    default:
 | 
				
			||||||
      return "UNKNOWN";
 | 
					      return "UNKNOWN";
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -114,6 +180,32 @@ template<> const char *proto_enum_to_string<enums::ClimateAction>(enums::Climate
 | 
				
			|||||||
      return "CLIMATE_ACTION_COOLING";
 | 
					      return "CLIMATE_ACTION_COOLING";
 | 
				
			||||||
    case enums::CLIMATE_ACTION_HEATING:
 | 
					    case enums::CLIMATE_ACTION_HEATING:
 | 
				
			||||||
      return "CLIMATE_ACTION_HEATING";
 | 
					      return "CLIMATE_ACTION_HEATING";
 | 
				
			||||||
 | 
					    case enums::CLIMATE_ACTION_IDLE:
 | 
				
			||||||
 | 
					      return "CLIMATE_ACTION_IDLE";
 | 
				
			||||||
 | 
					    case enums::CLIMATE_ACTION_DRYING:
 | 
				
			||||||
 | 
					      return "CLIMATE_ACTION_DRYING";
 | 
				
			||||||
 | 
					    case enums::CLIMATE_ACTION_FAN:
 | 
				
			||||||
 | 
					      return "CLIMATE_ACTION_FAN";
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					      return "UNKNOWN";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					template<> const char *proto_enum_to_string<enums::ClimatePreset>(enums::ClimatePreset value) {
 | 
				
			||||||
 | 
					  switch (value) {
 | 
				
			||||||
 | 
					    case enums::CLIMATE_PRESET_ECO:
 | 
				
			||||||
 | 
					      return "CLIMATE_PRESET_ECO";
 | 
				
			||||||
 | 
					    case enums::CLIMATE_PRESET_AWAY:
 | 
				
			||||||
 | 
					      return "CLIMATE_PRESET_AWAY";
 | 
				
			||||||
 | 
					    case enums::CLIMATE_PRESET_BOOST:
 | 
				
			||||||
 | 
					      return "CLIMATE_PRESET_BOOST";
 | 
				
			||||||
 | 
					    case enums::CLIMATE_PRESET_COMFORT:
 | 
				
			||||||
 | 
					      return "CLIMATE_PRESET_COMFORT";
 | 
				
			||||||
 | 
					    case enums::CLIMATE_PRESET_HOME:
 | 
				
			||||||
 | 
					      return "CLIMATE_PRESET_HOME";
 | 
				
			||||||
 | 
					    case enums::CLIMATE_PRESET_SLEEP:
 | 
				
			||||||
 | 
					      return "CLIMATE_PRESET_SLEEP";
 | 
				
			||||||
 | 
					    case enums::CLIMATE_PRESET_ACTIVITY:
 | 
				
			||||||
 | 
					      return "CLIMATE_PRESET_ACTIVITY";
 | 
				
			||||||
    default:
 | 
					    default:
 | 
				
			||||||
      return "UNKNOWN";
 | 
					      return "UNKNOWN";
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -268,6 +360,14 @@ bool DeviceInfoResponse::decode_length(uint32_t field_id, ProtoLengthDelimited v
 | 
				
			|||||||
      this->model = value.as_string();
 | 
					      this->model = value.as_string();
 | 
				
			||||||
      return true;
 | 
					      return true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    case 8: {
 | 
				
			||||||
 | 
					      this->project_name = value.as_string();
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    case 9: {
 | 
				
			||||||
 | 
					      this->project_version = value.as_string();
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    default:
 | 
					    default:
 | 
				
			||||||
      return false;
 | 
					      return false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -280,6 +380,8 @@ void DeviceInfoResponse::encode(ProtoWriteBuffer buffer) const {
 | 
				
			|||||||
  buffer.encode_string(5, this->compilation_time);
 | 
					  buffer.encode_string(5, this->compilation_time);
 | 
				
			||||||
  buffer.encode_string(6, this->model);
 | 
					  buffer.encode_string(6, this->model);
 | 
				
			||||||
  buffer.encode_bool(7, this->has_deep_sleep);
 | 
					  buffer.encode_bool(7, this->has_deep_sleep);
 | 
				
			||||||
 | 
					  buffer.encode_string(8, this->project_name);
 | 
				
			||||||
 | 
					  buffer.encode_string(9, this->project_version);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
void DeviceInfoResponse::dump_to(std::string &out) const {
 | 
					void DeviceInfoResponse::dump_to(std::string &out) const {
 | 
				
			||||||
  char buffer[64];
 | 
					  char buffer[64];
 | 
				
			||||||
@@ -311,6 +413,14 @@ void DeviceInfoResponse::dump_to(std::string &out) const {
 | 
				
			|||||||
  out.append("  has_deep_sleep: ");
 | 
					  out.append("  has_deep_sleep: ");
 | 
				
			||||||
  out.append(YESNO(this->has_deep_sleep));
 | 
					  out.append(YESNO(this->has_deep_sleep));
 | 
				
			||||||
  out.append("\n");
 | 
					  out.append("\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  out.append("  project_name: ");
 | 
				
			||||||
 | 
					  out.append("'").append(this->project_name).append("'");
 | 
				
			||||||
 | 
					  out.append("\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  out.append("  project_version: ");
 | 
				
			||||||
 | 
					  out.append("'").append(this->project_version).append("'");
 | 
				
			||||||
 | 
					  out.append("\n");
 | 
				
			||||||
  out.append("}");
 | 
					  out.append("}");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
void ListEntitiesRequest::encode(ProtoWriteBuffer buffer) const {}
 | 
					void ListEntitiesRequest::encode(ProtoWriteBuffer buffer) const {}
 | 
				
			||||||
@@ -710,6 +820,14 @@ bool ListEntitiesFanResponse::decode_varint(uint32_t field_id, ProtoVarInt value
 | 
				
			|||||||
      this->supports_speed = value.as_bool();
 | 
					      this->supports_speed = value.as_bool();
 | 
				
			||||||
      return true;
 | 
					      return true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    case 7: {
 | 
				
			||||||
 | 
					      this->supports_direction = value.as_bool();
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    case 8: {
 | 
				
			||||||
 | 
					      this->supported_speed_count = value.as_int32();
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    default:
 | 
					    default:
 | 
				
			||||||
      return false;
 | 
					      return false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -749,6 +867,8 @@ void ListEntitiesFanResponse::encode(ProtoWriteBuffer buffer) const {
 | 
				
			|||||||
  buffer.encode_string(4, this->unique_id);
 | 
					  buffer.encode_string(4, this->unique_id);
 | 
				
			||||||
  buffer.encode_bool(5, this->supports_oscillation);
 | 
					  buffer.encode_bool(5, this->supports_oscillation);
 | 
				
			||||||
  buffer.encode_bool(6, this->supports_speed);
 | 
					  buffer.encode_bool(6, this->supports_speed);
 | 
				
			||||||
 | 
					  buffer.encode_bool(7, this->supports_direction);
 | 
				
			||||||
 | 
					  buffer.encode_int32(8, this->supported_speed_count);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
void ListEntitiesFanResponse::dump_to(std::string &out) const {
 | 
					void ListEntitiesFanResponse::dump_to(std::string &out) const {
 | 
				
			||||||
  char buffer[64];
 | 
					  char buffer[64];
 | 
				
			||||||
@@ -777,6 +897,15 @@ void ListEntitiesFanResponse::dump_to(std::string &out) const {
 | 
				
			|||||||
  out.append("  supports_speed: ");
 | 
					  out.append("  supports_speed: ");
 | 
				
			||||||
  out.append(YESNO(this->supports_speed));
 | 
					  out.append(YESNO(this->supports_speed));
 | 
				
			||||||
  out.append("\n");
 | 
					  out.append("\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  out.append("  supports_direction: ");
 | 
				
			||||||
 | 
					  out.append(YESNO(this->supports_direction));
 | 
				
			||||||
 | 
					  out.append("\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  out.append("  supported_speed_count: ");
 | 
				
			||||||
 | 
					  sprintf(buffer, "%d", this->supported_speed_count);
 | 
				
			||||||
 | 
					  out.append(buffer);
 | 
				
			||||||
 | 
					  out.append("\n");
 | 
				
			||||||
  out.append("}");
 | 
					  out.append("}");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
bool FanStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
					bool FanStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
				
			||||||
@@ -793,6 +922,14 @@ bool FanStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
				
			|||||||
      this->speed = value.as_enum<enums::FanSpeed>();
 | 
					      this->speed = value.as_enum<enums::FanSpeed>();
 | 
				
			||||||
      return true;
 | 
					      return true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    case 5: {
 | 
				
			||||||
 | 
					      this->direction = value.as_enum<enums::FanDirection>();
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    case 6: {
 | 
				
			||||||
 | 
					      this->speed_level = value.as_int32();
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    default:
 | 
					    default:
 | 
				
			||||||
      return false;
 | 
					      return false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -812,6 +949,8 @@ void FanStateResponse::encode(ProtoWriteBuffer buffer) const {
 | 
				
			|||||||
  buffer.encode_bool(2, this->state);
 | 
					  buffer.encode_bool(2, this->state);
 | 
				
			||||||
  buffer.encode_bool(3, this->oscillating);
 | 
					  buffer.encode_bool(3, this->oscillating);
 | 
				
			||||||
  buffer.encode_enum<enums::FanSpeed>(4, this->speed);
 | 
					  buffer.encode_enum<enums::FanSpeed>(4, this->speed);
 | 
				
			||||||
 | 
					  buffer.encode_enum<enums::FanDirection>(5, this->direction);
 | 
				
			||||||
 | 
					  buffer.encode_int32(6, this->speed_level);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
void FanStateResponse::dump_to(std::string &out) const {
 | 
					void FanStateResponse::dump_to(std::string &out) const {
 | 
				
			||||||
  char buffer[64];
 | 
					  char buffer[64];
 | 
				
			||||||
@@ -832,6 +971,15 @@ void FanStateResponse::dump_to(std::string &out) const {
 | 
				
			|||||||
  out.append("  speed: ");
 | 
					  out.append("  speed: ");
 | 
				
			||||||
  out.append(proto_enum_to_string<enums::FanSpeed>(this->speed));
 | 
					  out.append(proto_enum_to_string<enums::FanSpeed>(this->speed));
 | 
				
			||||||
  out.append("\n");
 | 
					  out.append("\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  out.append("  direction: ");
 | 
				
			||||||
 | 
					  out.append(proto_enum_to_string<enums::FanDirection>(this->direction));
 | 
				
			||||||
 | 
					  out.append("\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  out.append("  speed_level: ");
 | 
				
			||||||
 | 
					  sprintf(buffer, "%d", this->speed_level);
 | 
				
			||||||
 | 
					  out.append(buffer);
 | 
				
			||||||
 | 
					  out.append("\n");
 | 
				
			||||||
  out.append("}");
 | 
					  out.append("}");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
bool FanCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
					bool FanCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
				
			||||||
@@ -860,6 +1008,22 @@ bool FanCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
				
			|||||||
      this->oscillating = value.as_bool();
 | 
					      this->oscillating = value.as_bool();
 | 
				
			||||||
      return true;
 | 
					      return true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    case 8: {
 | 
				
			||||||
 | 
					      this->has_direction = value.as_bool();
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    case 9: {
 | 
				
			||||||
 | 
					      this->direction = value.as_enum<enums::FanDirection>();
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    case 10: {
 | 
				
			||||||
 | 
					      this->has_speed_level = value.as_bool();
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    case 11: {
 | 
				
			||||||
 | 
					      this->speed_level = value.as_int32();
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    default:
 | 
					    default:
 | 
				
			||||||
      return false;
 | 
					      return false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -882,6 +1046,10 @@ void FanCommandRequest::encode(ProtoWriteBuffer buffer) const {
 | 
				
			|||||||
  buffer.encode_enum<enums::FanSpeed>(5, this->speed);
 | 
					  buffer.encode_enum<enums::FanSpeed>(5, this->speed);
 | 
				
			||||||
  buffer.encode_bool(6, this->has_oscillating);
 | 
					  buffer.encode_bool(6, this->has_oscillating);
 | 
				
			||||||
  buffer.encode_bool(7, this->oscillating);
 | 
					  buffer.encode_bool(7, this->oscillating);
 | 
				
			||||||
 | 
					  buffer.encode_bool(8, this->has_direction);
 | 
				
			||||||
 | 
					  buffer.encode_enum<enums::FanDirection>(9, this->direction);
 | 
				
			||||||
 | 
					  buffer.encode_bool(10, this->has_speed_level);
 | 
				
			||||||
 | 
					  buffer.encode_int32(11, this->speed_level);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
void FanCommandRequest::dump_to(std::string &out) const {
 | 
					void FanCommandRequest::dump_to(std::string &out) const {
 | 
				
			||||||
  char buffer[64];
 | 
					  char buffer[64];
 | 
				
			||||||
@@ -914,6 +1082,23 @@ void FanCommandRequest::dump_to(std::string &out) const {
 | 
				
			|||||||
  out.append("  oscillating: ");
 | 
					  out.append("  oscillating: ");
 | 
				
			||||||
  out.append(YESNO(this->oscillating));
 | 
					  out.append(YESNO(this->oscillating));
 | 
				
			||||||
  out.append("\n");
 | 
					  out.append("\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  out.append("  has_direction: ");
 | 
				
			||||||
 | 
					  out.append(YESNO(this->has_direction));
 | 
				
			||||||
 | 
					  out.append("\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  out.append("  direction: ");
 | 
				
			||||||
 | 
					  out.append(proto_enum_to_string<enums::FanDirection>(this->direction));
 | 
				
			||||||
 | 
					  out.append("\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  out.append("  has_speed_level: ");
 | 
				
			||||||
 | 
					  out.append(YESNO(this->has_speed_level));
 | 
				
			||||||
 | 
					  out.append("\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  out.append("  speed_level: ");
 | 
				
			||||||
 | 
					  sprintf(buffer, "%d", this->speed_level);
 | 
				
			||||||
 | 
					  out.append(buffer);
 | 
				
			||||||
 | 
					  out.append("\n");
 | 
				
			||||||
  out.append("}");
 | 
					  out.append("}");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
bool ListEntitiesLightResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
					bool ListEntitiesLightResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
				
			||||||
@@ -1372,6 +1557,10 @@ bool ListEntitiesSensorResponse::decode_varint(uint32_t field_id, ProtoVarInt va
 | 
				
			|||||||
      this->force_update = value.as_bool();
 | 
					      this->force_update = value.as_bool();
 | 
				
			||||||
      return true;
 | 
					      return true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    case 10: {
 | 
				
			||||||
 | 
					      this->state_class = value.as_enum<enums::SensorStateClass>();
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    default:
 | 
					    default:
 | 
				
			||||||
      return false;
 | 
					      return false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -1398,6 +1587,10 @@ bool ListEntitiesSensorResponse::decode_length(uint32_t field_id, ProtoLengthDel
 | 
				
			|||||||
      this->unit_of_measurement = value.as_string();
 | 
					      this->unit_of_measurement = value.as_string();
 | 
				
			||||||
      return true;
 | 
					      return true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    case 9: {
 | 
				
			||||||
 | 
					      this->device_class = value.as_string();
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    default:
 | 
					    default:
 | 
				
			||||||
      return false;
 | 
					      return false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -1421,6 +1614,8 @@ void ListEntitiesSensorResponse::encode(ProtoWriteBuffer buffer) const {
 | 
				
			|||||||
  buffer.encode_string(6, this->unit_of_measurement);
 | 
					  buffer.encode_string(6, this->unit_of_measurement);
 | 
				
			||||||
  buffer.encode_int32(7, this->accuracy_decimals);
 | 
					  buffer.encode_int32(7, this->accuracy_decimals);
 | 
				
			||||||
  buffer.encode_bool(8, this->force_update);
 | 
					  buffer.encode_bool(8, this->force_update);
 | 
				
			||||||
 | 
					  buffer.encode_string(9, this->device_class);
 | 
				
			||||||
 | 
					  buffer.encode_enum<enums::SensorStateClass>(10, this->state_class);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
void ListEntitiesSensorResponse::dump_to(std::string &out) const {
 | 
					void ListEntitiesSensorResponse::dump_to(std::string &out) const {
 | 
				
			||||||
  char buffer[64];
 | 
					  char buffer[64];
 | 
				
			||||||
@@ -1458,6 +1653,14 @@ void ListEntitiesSensorResponse::dump_to(std::string &out) const {
 | 
				
			|||||||
  out.append("  force_update: ");
 | 
					  out.append("  force_update: ");
 | 
				
			||||||
  out.append(YESNO(this->force_update));
 | 
					  out.append(YESNO(this->force_update));
 | 
				
			||||||
  out.append("\n");
 | 
					  out.append("\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  out.append("  device_class: ");
 | 
				
			||||||
 | 
					  out.append("'").append(this->device_class).append("'");
 | 
				
			||||||
 | 
					  out.append("\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  out.append("  state_class: ");
 | 
				
			||||||
 | 
					  out.append(proto_enum_to_string<enums::SensorStateClass>(this->state_class));
 | 
				
			||||||
 | 
					  out.append("\n");
 | 
				
			||||||
  out.append("}");
 | 
					  out.append("}");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
bool SensorStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
					bool SensorStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
				
			||||||
@@ -1979,12 +2182,17 @@ bool SubscribeHomeAssistantStateResponse::decode_length(uint32_t field_id, Proto
 | 
				
			|||||||
      this->entity_id = value.as_string();
 | 
					      this->entity_id = value.as_string();
 | 
				
			||||||
      return true;
 | 
					      return true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    case 2: {
 | 
				
			||||||
 | 
					      this->attribute = value.as_string();
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    default:
 | 
					    default:
 | 
				
			||||||
      return false;
 | 
					      return false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
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);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
void SubscribeHomeAssistantStateResponse::dump_to(std::string &out) const {
 | 
					void SubscribeHomeAssistantStateResponse::dump_to(std::string &out) const {
 | 
				
			||||||
  char buffer[64];
 | 
					  char buffer[64];
 | 
				
			||||||
@@ -1992,6 +2200,10 @@ void SubscribeHomeAssistantStateResponse::dump_to(std::string &out) const {
 | 
				
			|||||||
  out.append("  entity_id: ");
 | 
					  out.append("  entity_id: ");
 | 
				
			||||||
  out.append("'").append(this->entity_id).append("'");
 | 
					  out.append("'").append(this->entity_id).append("'");
 | 
				
			||||||
  out.append("\n");
 | 
					  out.append("\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  out.append("  attribute: ");
 | 
				
			||||||
 | 
					  out.append("'").append(this->attribute).append("'");
 | 
				
			||||||
 | 
					  out.append("\n");
 | 
				
			||||||
  out.append("}");
 | 
					  out.append("}");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
bool HomeAssistantStateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
 | 
					bool HomeAssistantStateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
 | 
				
			||||||
@@ -2004,6 +2216,10 @@ bool HomeAssistantStateResponse::decode_length(uint32_t field_id, ProtoLengthDel
 | 
				
			|||||||
      this->state = value.as_string();
 | 
					      this->state = value.as_string();
 | 
				
			||||||
      return true;
 | 
					      return true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    case 3: {
 | 
				
			||||||
 | 
					      this->attribute = value.as_string();
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    default:
 | 
					    default:
 | 
				
			||||||
      return false;
 | 
					      return false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -2011,6 +2227,7 @@ bool HomeAssistantStateResponse::decode_length(uint32_t field_id, ProtoLengthDel
 | 
				
			|||||||
void HomeAssistantStateResponse::encode(ProtoWriteBuffer buffer) const {
 | 
					void HomeAssistantStateResponse::encode(ProtoWriteBuffer buffer) const {
 | 
				
			||||||
  buffer.encode_string(1, this->entity_id);
 | 
					  buffer.encode_string(1, this->entity_id);
 | 
				
			||||||
  buffer.encode_string(2, this->state);
 | 
					  buffer.encode_string(2, this->state);
 | 
				
			||||||
 | 
					  buffer.encode_string(3, this->attribute);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
void HomeAssistantStateResponse::dump_to(std::string &out) const {
 | 
					void HomeAssistantStateResponse::dump_to(std::string &out) const {
 | 
				
			||||||
  char buffer[64];
 | 
					  char buffer[64];
 | 
				
			||||||
@@ -2022,6 +2239,10 @@ void HomeAssistantStateResponse::dump_to(std::string &out) const {
 | 
				
			|||||||
  out.append("  state: ");
 | 
					  out.append("  state: ");
 | 
				
			||||||
  out.append("'").append(this->state).append("'");
 | 
					  out.append("'").append(this->state).append("'");
 | 
				
			||||||
  out.append("\n");
 | 
					  out.append("\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  out.append("  attribute: ");
 | 
				
			||||||
 | 
					  out.append("'").append(this->attribute).append("'");
 | 
				
			||||||
 | 
					  out.append("\n");
 | 
				
			||||||
  out.append("}");
 | 
					  out.append("}");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
void GetTimeRequest::encode(ProtoWriteBuffer buffer) const {}
 | 
					void GetTimeRequest::encode(ProtoWriteBuffer buffer) const {}
 | 
				
			||||||
@@ -2458,6 +2679,18 @@ bool ListEntitiesClimateResponse::decode_varint(uint32_t field_id, ProtoVarInt v
 | 
				
			|||||||
      this->supports_action = value.as_bool();
 | 
					      this->supports_action = value.as_bool();
 | 
				
			||||||
      return true;
 | 
					      return true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    case 13: {
 | 
				
			||||||
 | 
					      this->supported_fan_modes.push_back(value.as_enum<enums::ClimateFanMode>());
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    case 14: {
 | 
				
			||||||
 | 
					      this->supported_swing_modes.push_back(value.as_enum<enums::ClimateSwingMode>());
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    case 16: {
 | 
				
			||||||
 | 
					      this->supported_presets.push_back(value.as_enum<enums::ClimatePreset>());
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    default:
 | 
					    default:
 | 
				
			||||||
      return false;
 | 
					      return false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -2476,6 +2709,14 @@ bool ListEntitiesClimateResponse::decode_length(uint32_t field_id, ProtoLengthDe
 | 
				
			|||||||
      this->unique_id = value.as_string();
 | 
					      this->unique_id = value.as_string();
 | 
				
			||||||
      return true;
 | 
					      return true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    case 15: {
 | 
				
			||||||
 | 
					      this->supported_custom_fan_modes.push_back(value.as_string());
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    case 17: {
 | 
				
			||||||
 | 
					      this->supported_custom_presets.push_back(value.as_string());
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    default:
 | 
					    default:
 | 
				
			||||||
      return false;
 | 
					      return false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -2517,6 +2758,21 @@ void ListEntitiesClimateResponse::encode(ProtoWriteBuffer buffer) const {
 | 
				
			|||||||
  buffer.encode_float(10, this->visual_temperature_step);
 | 
					  buffer.encode_float(10, this->visual_temperature_step);
 | 
				
			||||||
  buffer.encode_bool(11, this->supports_away);
 | 
					  buffer.encode_bool(11, this->supports_away);
 | 
				
			||||||
  buffer.encode_bool(12, this->supports_action);
 | 
					  buffer.encode_bool(12, this->supports_action);
 | 
				
			||||||
 | 
					  for (auto &it : this->supported_fan_modes) {
 | 
				
			||||||
 | 
					    buffer.encode_enum<enums::ClimateFanMode>(13, it, true);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  for (auto &it : this->supported_swing_modes) {
 | 
				
			||||||
 | 
					    buffer.encode_enum<enums::ClimateSwingMode>(14, it, true);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  for (auto &it : this->supported_custom_fan_modes) {
 | 
				
			||||||
 | 
					    buffer.encode_string(15, it, true);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  for (auto &it : this->supported_presets) {
 | 
				
			||||||
 | 
					    buffer.encode_enum<enums::ClimatePreset>(16, it, true);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  for (auto &it : this->supported_custom_presets) {
 | 
				
			||||||
 | 
					    buffer.encode_string(17, it, true);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
void ListEntitiesClimateResponse::dump_to(std::string &out) const {
 | 
					void ListEntitiesClimateResponse::dump_to(std::string &out) const {
 | 
				
			||||||
  char buffer[64];
 | 
					  char buffer[64];
 | 
				
			||||||
@@ -2574,6 +2830,36 @@ void ListEntitiesClimateResponse::dump_to(std::string &out) const {
 | 
				
			|||||||
  out.append("  supports_action: ");
 | 
					  out.append("  supports_action: ");
 | 
				
			||||||
  out.append(YESNO(this->supports_action));
 | 
					  out.append(YESNO(this->supports_action));
 | 
				
			||||||
  out.append("\n");
 | 
					  out.append("\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (const auto &it : this->supported_fan_modes) {
 | 
				
			||||||
 | 
					    out.append("  supported_fan_modes: ");
 | 
				
			||||||
 | 
					    out.append(proto_enum_to_string<enums::ClimateFanMode>(it));
 | 
				
			||||||
 | 
					    out.append("\n");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (const auto &it : this->supported_swing_modes) {
 | 
				
			||||||
 | 
					    out.append("  supported_swing_modes: ");
 | 
				
			||||||
 | 
					    out.append(proto_enum_to_string<enums::ClimateSwingMode>(it));
 | 
				
			||||||
 | 
					    out.append("\n");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (const auto &it : this->supported_custom_fan_modes) {
 | 
				
			||||||
 | 
					    out.append("  supported_custom_fan_modes: ");
 | 
				
			||||||
 | 
					    out.append("'").append(it).append("'");
 | 
				
			||||||
 | 
					    out.append("\n");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (const auto &it : this->supported_presets) {
 | 
				
			||||||
 | 
					    out.append("  supported_presets: ");
 | 
				
			||||||
 | 
					    out.append(proto_enum_to_string<enums::ClimatePreset>(it));
 | 
				
			||||||
 | 
					    out.append("\n");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (const auto &it : this->supported_custom_presets) {
 | 
				
			||||||
 | 
					    out.append("  supported_custom_presets: ");
 | 
				
			||||||
 | 
					    out.append("'").append(it).append("'");
 | 
				
			||||||
 | 
					    out.append("\n");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  out.append("}");
 | 
					  out.append("}");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
bool ClimateStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
					bool ClimateStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
				
			||||||
@@ -2590,6 +2876,32 @@ bool ClimateStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
				
			|||||||
      this->action = value.as_enum<enums::ClimateAction>();
 | 
					      this->action = value.as_enum<enums::ClimateAction>();
 | 
				
			||||||
      return true;
 | 
					      return true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    case 9: {
 | 
				
			||||||
 | 
					      this->fan_mode = value.as_enum<enums::ClimateFanMode>();
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    case 10: {
 | 
				
			||||||
 | 
					      this->swing_mode = value.as_enum<enums::ClimateSwingMode>();
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    case 12: {
 | 
				
			||||||
 | 
					      this->preset = value.as_enum<enums::ClimatePreset>();
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					bool ClimateStateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
 | 
				
			||||||
 | 
					  switch (field_id) {
 | 
				
			||||||
 | 
					    case 11: {
 | 
				
			||||||
 | 
					      this->custom_fan_mode = value.as_string();
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    case 13: {
 | 
				
			||||||
 | 
					      this->custom_preset = value.as_string();
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    default:
 | 
					    default:
 | 
				
			||||||
      return false;
 | 
					      return false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -2629,6 +2941,11 @@ void ClimateStateResponse::encode(ProtoWriteBuffer buffer) const {
 | 
				
			|||||||
  buffer.encode_float(6, this->target_temperature_high);
 | 
					  buffer.encode_float(6, this->target_temperature_high);
 | 
				
			||||||
  buffer.encode_bool(7, this->away);
 | 
					  buffer.encode_bool(7, this->away);
 | 
				
			||||||
  buffer.encode_enum<enums::ClimateAction>(8, this->action);
 | 
					  buffer.encode_enum<enums::ClimateAction>(8, this->action);
 | 
				
			||||||
 | 
					  buffer.encode_enum<enums::ClimateFanMode>(9, this->fan_mode);
 | 
				
			||||||
 | 
					  buffer.encode_enum<enums::ClimateSwingMode>(10, this->swing_mode);
 | 
				
			||||||
 | 
					  buffer.encode_string(11, this->custom_fan_mode);
 | 
				
			||||||
 | 
					  buffer.encode_enum<enums::ClimatePreset>(12, this->preset);
 | 
				
			||||||
 | 
					  buffer.encode_string(13, this->custom_preset);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
void ClimateStateResponse::dump_to(std::string &out) const {
 | 
					void ClimateStateResponse::dump_to(std::string &out) const {
 | 
				
			||||||
  char buffer[64];
 | 
					  char buffer[64];
 | 
				
			||||||
@@ -2669,6 +2986,26 @@ void ClimateStateResponse::dump_to(std::string &out) const {
 | 
				
			|||||||
  out.append("  action: ");
 | 
					  out.append("  action: ");
 | 
				
			||||||
  out.append(proto_enum_to_string<enums::ClimateAction>(this->action));
 | 
					  out.append(proto_enum_to_string<enums::ClimateAction>(this->action));
 | 
				
			||||||
  out.append("\n");
 | 
					  out.append("\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  out.append("  fan_mode: ");
 | 
				
			||||||
 | 
					  out.append(proto_enum_to_string<enums::ClimateFanMode>(this->fan_mode));
 | 
				
			||||||
 | 
					  out.append("\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  out.append("  swing_mode: ");
 | 
				
			||||||
 | 
					  out.append(proto_enum_to_string<enums::ClimateSwingMode>(this->swing_mode));
 | 
				
			||||||
 | 
					  out.append("\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  out.append("  custom_fan_mode: ");
 | 
				
			||||||
 | 
					  out.append("'").append(this->custom_fan_mode).append("'");
 | 
				
			||||||
 | 
					  out.append("\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  out.append("  preset: ");
 | 
				
			||||||
 | 
					  out.append(proto_enum_to_string<enums::ClimatePreset>(this->preset));
 | 
				
			||||||
 | 
					  out.append("\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  out.append("  custom_preset: ");
 | 
				
			||||||
 | 
					  out.append("'").append(this->custom_preset).append("'");
 | 
				
			||||||
 | 
					  out.append("\n");
 | 
				
			||||||
  out.append("}");
 | 
					  out.append("}");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
bool ClimateCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
					bool ClimateCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
				
			||||||
@@ -2701,6 +3038,52 @@ bool ClimateCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value)
 | 
				
			|||||||
      this->away = value.as_bool();
 | 
					      this->away = value.as_bool();
 | 
				
			||||||
      return true;
 | 
					      return true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    case 12: {
 | 
				
			||||||
 | 
					      this->has_fan_mode = value.as_bool();
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    case 13: {
 | 
				
			||||||
 | 
					      this->fan_mode = value.as_enum<enums::ClimateFanMode>();
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    case 14: {
 | 
				
			||||||
 | 
					      this->has_swing_mode = value.as_bool();
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    case 15: {
 | 
				
			||||||
 | 
					      this->swing_mode = value.as_enum<enums::ClimateSwingMode>();
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    case 16: {
 | 
				
			||||||
 | 
					      this->has_custom_fan_mode = value.as_bool();
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    case 18: {
 | 
				
			||||||
 | 
					      this->has_preset = value.as_bool();
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    case 19: {
 | 
				
			||||||
 | 
					      this->preset = value.as_enum<enums::ClimatePreset>();
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    case 20: {
 | 
				
			||||||
 | 
					      this->has_custom_preset = value.as_bool();
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					bool ClimateCommandRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
 | 
				
			||||||
 | 
					  switch (field_id) {
 | 
				
			||||||
 | 
					    case 17: {
 | 
				
			||||||
 | 
					      this->custom_fan_mode = value.as_string();
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    case 21: {
 | 
				
			||||||
 | 
					      this->custom_preset = value.as_string();
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    default:
 | 
					    default:
 | 
				
			||||||
      return false;
 | 
					      return false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -2739,6 +3122,16 @@ void ClimateCommandRequest::encode(ProtoWriteBuffer buffer) const {
 | 
				
			|||||||
  buffer.encode_float(9, this->target_temperature_high);
 | 
					  buffer.encode_float(9, this->target_temperature_high);
 | 
				
			||||||
  buffer.encode_bool(10, this->has_away);
 | 
					  buffer.encode_bool(10, this->has_away);
 | 
				
			||||||
  buffer.encode_bool(11, this->away);
 | 
					  buffer.encode_bool(11, this->away);
 | 
				
			||||||
 | 
					  buffer.encode_bool(12, this->has_fan_mode);
 | 
				
			||||||
 | 
					  buffer.encode_enum<enums::ClimateFanMode>(13, this->fan_mode);
 | 
				
			||||||
 | 
					  buffer.encode_bool(14, this->has_swing_mode);
 | 
				
			||||||
 | 
					  buffer.encode_enum<enums::ClimateSwingMode>(15, this->swing_mode);
 | 
				
			||||||
 | 
					  buffer.encode_bool(16, this->has_custom_fan_mode);
 | 
				
			||||||
 | 
					  buffer.encode_string(17, this->custom_fan_mode);
 | 
				
			||||||
 | 
					  buffer.encode_bool(18, this->has_preset);
 | 
				
			||||||
 | 
					  buffer.encode_enum<enums::ClimatePreset>(19, this->preset);
 | 
				
			||||||
 | 
					  buffer.encode_bool(20, this->has_custom_preset);
 | 
				
			||||||
 | 
					  buffer.encode_string(21, this->custom_preset);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
void ClimateCommandRequest::dump_to(std::string &out) const {
 | 
					void ClimateCommandRequest::dump_to(std::string &out) const {
 | 
				
			||||||
  char buffer[64];
 | 
					  char buffer[64];
 | 
				
			||||||
@@ -2790,6 +3183,46 @@ void ClimateCommandRequest::dump_to(std::string &out) const {
 | 
				
			|||||||
  out.append("  away: ");
 | 
					  out.append("  away: ");
 | 
				
			||||||
  out.append(YESNO(this->away));
 | 
					  out.append(YESNO(this->away));
 | 
				
			||||||
  out.append("\n");
 | 
					  out.append("\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  out.append("  has_fan_mode: ");
 | 
				
			||||||
 | 
					  out.append(YESNO(this->has_fan_mode));
 | 
				
			||||||
 | 
					  out.append("\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  out.append("  fan_mode: ");
 | 
				
			||||||
 | 
					  out.append(proto_enum_to_string<enums::ClimateFanMode>(this->fan_mode));
 | 
				
			||||||
 | 
					  out.append("\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  out.append("  has_swing_mode: ");
 | 
				
			||||||
 | 
					  out.append(YESNO(this->has_swing_mode));
 | 
				
			||||||
 | 
					  out.append("\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  out.append("  swing_mode: ");
 | 
				
			||||||
 | 
					  out.append(proto_enum_to_string<enums::ClimateSwingMode>(this->swing_mode));
 | 
				
			||||||
 | 
					  out.append("\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  out.append("  has_custom_fan_mode: ");
 | 
				
			||||||
 | 
					  out.append(YESNO(this->has_custom_fan_mode));
 | 
				
			||||||
 | 
					  out.append("\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  out.append("  custom_fan_mode: ");
 | 
				
			||||||
 | 
					  out.append("'").append(this->custom_fan_mode).append("'");
 | 
				
			||||||
 | 
					  out.append("\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  out.append("  has_preset: ");
 | 
				
			||||||
 | 
					  out.append(YESNO(this->has_preset));
 | 
				
			||||||
 | 
					  out.append("\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  out.append("  preset: ");
 | 
				
			||||||
 | 
					  out.append(proto_enum_to_string<enums::ClimatePreset>(this->preset));
 | 
				
			||||||
 | 
					  out.append("\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  out.append("  has_custom_preset: ");
 | 
				
			||||||
 | 
					  out.append(YESNO(this->has_custom_preset));
 | 
				
			||||||
 | 
					  out.append("\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  out.append("  custom_preset: ");
 | 
				
			||||||
 | 
					  out.append("'").append(this->custom_preset).append("'");
 | 
				
			||||||
 | 
					  out.append("\n");
 | 
				
			||||||
  out.append("}");
 | 
					  out.append("}");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,5 @@
 | 
				
			|||||||
 | 
					// This file was automatically generated with a tool.
 | 
				
			||||||
 | 
					// See scripts/api_protobuf/api_protobuf.py
 | 
				
			||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "proto.h"
 | 
					#include "proto.h"
 | 
				
			||||||
@@ -26,6 +28,14 @@ enum FanSpeed : uint32_t {
 | 
				
			|||||||
  FAN_SPEED_MEDIUM = 1,
 | 
					  FAN_SPEED_MEDIUM = 1,
 | 
				
			||||||
  FAN_SPEED_HIGH = 2,
 | 
					  FAN_SPEED_HIGH = 2,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					enum FanDirection : uint32_t {
 | 
				
			||||||
 | 
					  FAN_DIRECTION_FORWARD = 0,
 | 
				
			||||||
 | 
					  FAN_DIRECTION_REVERSE = 1,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					enum SensorStateClass : uint32_t {
 | 
				
			||||||
 | 
					  STATE_CLASS_NONE = 0,
 | 
				
			||||||
 | 
					  STATE_CLASS_MEASUREMENT = 1,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
enum LogLevel : uint32_t {
 | 
					enum LogLevel : uint32_t {
 | 
				
			||||||
  LOG_LEVEL_NONE = 0,
 | 
					  LOG_LEVEL_NONE = 0,
 | 
				
			||||||
  LOG_LEVEL_ERROR = 1,
 | 
					  LOG_LEVEL_ERROR = 1,
 | 
				
			||||||
@@ -47,21 +57,53 @@ enum ServiceArgType : uint32_t {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
enum ClimateMode : uint32_t {
 | 
					enum ClimateMode : uint32_t {
 | 
				
			||||||
  CLIMATE_MODE_OFF = 0,
 | 
					  CLIMATE_MODE_OFF = 0,
 | 
				
			||||||
  CLIMATE_MODE_AUTO = 1,
 | 
					  CLIMATE_MODE_HEAT_COOL = 1,
 | 
				
			||||||
  CLIMATE_MODE_COOL = 2,
 | 
					  CLIMATE_MODE_COOL = 2,
 | 
				
			||||||
  CLIMATE_MODE_HEAT = 3,
 | 
					  CLIMATE_MODE_HEAT = 3,
 | 
				
			||||||
 | 
					  CLIMATE_MODE_FAN_ONLY = 4,
 | 
				
			||||||
 | 
					  CLIMATE_MODE_DRY = 5,
 | 
				
			||||||
 | 
					  CLIMATE_MODE_AUTO = 6,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					enum ClimateFanMode : uint32_t {
 | 
				
			||||||
 | 
					  CLIMATE_FAN_ON = 0,
 | 
				
			||||||
 | 
					  CLIMATE_FAN_OFF = 1,
 | 
				
			||||||
 | 
					  CLIMATE_FAN_AUTO = 2,
 | 
				
			||||||
 | 
					  CLIMATE_FAN_LOW = 3,
 | 
				
			||||||
 | 
					  CLIMATE_FAN_MEDIUM = 4,
 | 
				
			||||||
 | 
					  CLIMATE_FAN_HIGH = 5,
 | 
				
			||||||
 | 
					  CLIMATE_FAN_MIDDLE = 6,
 | 
				
			||||||
 | 
					  CLIMATE_FAN_FOCUS = 7,
 | 
				
			||||||
 | 
					  CLIMATE_FAN_DIFFUSE = 8,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					enum ClimateSwingMode : uint32_t {
 | 
				
			||||||
 | 
					  CLIMATE_SWING_OFF = 0,
 | 
				
			||||||
 | 
					  CLIMATE_SWING_BOTH = 1,
 | 
				
			||||||
 | 
					  CLIMATE_SWING_VERTICAL = 2,
 | 
				
			||||||
 | 
					  CLIMATE_SWING_HORIZONTAL = 3,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
enum ClimateAction : uint32_t {
 | 
					enum ClimateAction : uint32_t {
 | 
				
			||||||
  CLIMATE_ACTION_OFF = 0,
 | 
					  CLIMATE_ACTION_OFF = 0,
 | 
				
			||||||
  CLIMATE_ACTION_COOLING = 2,
 | 
					  CLIMATE_ACTION_COOLING = 2,
 | 
				
			||||||
  CLIMATE_ACTION_HEATING = 3,
 | 
					  CLIMATE_ACTION_HEATING = 3,
 | 
				
			||||||
 | 
					  CLIMATE_ACTION_IDLE = 4,
 | 
				
			||||||
 | 
					  CLIMATE_ACTION_DRYING = 5,
 | 
				
			||||||
 | 
					  CLIMATE_ACTION_FAN = 6,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					enum ClimatePreset : uint32_t {
 | 
				
			||||||
 | 
					  CLIMATE_PRESET_ECO = 0,
 | 
				
			||||||
 | 
					  CLIMATE_PRESET_AWAY = 1,
 | 
				
			||||||
 | 
					  CLIMATE_PRESET_BOOST = 2,
 | 
				
			||||||
 | 
					  CLIMATE_PRESET_COMFORT = 3,
 | 
				
			||||||
 | 
					  CLIMATE_PRESET_HOME = 4,
 | 
				
			||||||
 | 
					  CLIMATE_PRESET_SLEEP = 5,
 | 
				
			||||||
 | 
					  CLIMATE_PRESET_ACTIVITY = 6,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}  // namespace enums
 | 
					}  // namespace enums
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class HelloRequest : public ProtoMessage {
 | 
					class HelloRequest : public ProtoMessage {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  std::string client_info{};  // NOLINT
 | 
					  std::string client_info{};
 | 
				
			||||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
					  void encode(ProtoWriteBuffer buffer) const override;
 | 
				
			||||||
  void dump_to(std::string &out) const override;
 | 
					  void dump_to(std::string &out) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -70,9 +112,9 @@ class HelloRequest : public ProtoMessage {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
class HelloResponse : public ProtoMessage {
 | 
					class HelloResponse : public ProtoMessage {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  uint32_t api_version_major{0};  // NOLINT
 | 
					  uint32_t api_version_major{0};
 | 
				
			||||||
  uint32_t api_version_minor{0};  // NOLINT
 | 
					  uint32_t api_version_minor{0};
 | 
				
			||||||
  std::string server_info{};      // NOLINT
 | 
					  std::string server_info{};
 | 
				
			||||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
					  void encode(ProtoWriteBuffer buffer) const override;
 | 
				
			||||||
  void dump_to(std::string &out) const override;
 | 
					  void dump_to(std::string &out) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -82,7 +124,7 @@ class HelloResponse : public ProtoMessage {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
class ConnectRequest : public ProtoMessage {
 | 
					class ConnectRequest : public ProtoMessage {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  std::string password{};  // NOLINT
 | 
					  std::string password{};
 | 
				
			||||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
					  void encode(ProtoWriteBuffer buffer) const override;
 | 
				
			||||||
  void dump_to(std::string &out) const override;
 | 
					  void dump_to(std::string &out) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -91,7 +133,7 @@ class ConnectRequest : public ProtoMessage {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
class ConnectResponse : public ProtoMessage {
 | 
					class ConnectResponse : public ProtoMessage {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  bool invalid_password{false};  // NOLINT
 | 
					  bool invalid_password{false};
 | 
				
			||||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
					  void encode(ProtoWriteBuffer buffer) const override;
 | 
				
			||||||
  void dump_to(std::string &out) const override;
 | 
					  void dump_to(std::string &out) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -135,13 +177,15 @@ class DeviceInfoRequest : public ProtoMessage {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
class DeviceInfoResponse : public ProtoMessage {
 | 
					class DeviceInfoResponse : public ProtoMessage {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  bool uses_password{false};       // NOLINT
 | 
					  bool uses_password{false};
 | 
				
			||||||
  std::string name{};              // NOLINT
 | 
					  std::string name{};
 | 
				
			||||||
  std::string mac_address{};       // NOLINT
 | 
					  std::string mac_address{};
 | 
				
			||||||
  std::string esphome_version{};   // NOLINT
 | 
					  std::string esphome_version{};
 | 
				
			||||||
  std::string compilation_time{};  // NOLINT
 | 
					  std::string compilation_time{};
 | 
				
			||||||
  std::string model{};             // NOLINT
 | 
					  std::string model{};
 | 
				
			||||||
  bool has_deep_sleep{false};      // NOLINT
 | 
					  bool has_deep_sleep{false};
 | 
				
			||||||
 | 
					  std::string project_name{};
 | 
				
			||||||
 | 
					  std::string project_version{};
 | 
				
			||||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
					  void encode(ProtoWriteBuffer buffer) const override;
 | 
				
			||||||
  void dump_to(std::string &out) const override;
 | 
					  void dump_to(std::string &out) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -172,12 +216,12 @@ class SubscribeStatesRequest : public ProtoMessage {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
class ListEntitiesBinarySensorResponse : public ProtoMessage {
 | 
					class ListEntitiesBinarySensorResponse : public ProtoMessage {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  std::string object_id{};              // NOLINT
 | 
					  std::string object_id{};
 | 
				
			||||||
  uint32_t key{0};                      // NOLINT
 | 
					  uint32_t key{0};
 | 
				
			||||||
  std::string name{};                   // NOLINT
 | 
					  std::string name{};
 | 
				
			||||||
  std::string unique_id{};              // NOLINT
 | 
					  std::string unique_id{};
 | 
				
			||||||
  std::string device_class{};           // NOLINT
 | 
					  std::string device_class{};
 | 
				
			||||||
  bool is_status_binary_sensor{false};  // NOLINT
 | 
					  bool is_status_binary_sensor{false};
 | 
				
			||||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
					  void encode(ProtoWriteBuffer buffer) const override;
 | 
				
			||||||
  void dump_to(std::string &out) const override;
 | 
					  void dump_to(std::string &out) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -188,9 +232,9 @@ class ListEntitiesBinarySensorResponse : public ProtoMessage {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
class BinarySensorStateResponse : public ProtoMessage {
 | 
					class BinarySensorStateResponse : public ProtoMessage {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  uint32_t key{0};            // NOLINT
 | 
					  uint32_t key{0};
 | 
				
			||||||
  bool state{false};          // NOLINT
 | 
					  bool state{false};
 | 
				
			||||||
  bool missing_state{false};  // NOLINT
 | 
					  bool missing_state{false};
 | 
				
			||||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
					  void encode(ProtoWriteBuffer buffer) const override;
 | 
				
			||||||
  void dump_to(std::string &out) const override;
 | 
					  void dump_to(std::string &out) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -200,14 +244,14 @@ class BinarySensorStateResponse : public ProtoMessage {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
class ListEntitiesCoverResponse : public ProtoMessage {
 | 
					class ListEntitiesCoverResponse : public ProtoMessage {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  std::string object_id{};        // NOLINT
 | 
					  std::string object_id{};
 | 
				
			||||||
  uint32_t key{0};                // NOLINT
 | 
					  uint32_t key{0};
 | 
				
			||||||
  std::string name{};             // NOLINT
 | 
					  std::string name{};
 | 
				
			||||||
  std::string unique_id{};        // NOLINT
 | 
					  std::string unique_id{};
 | 
				
			||||||
  bool assumed_state{false};      // NOLINT
 | 
					  bool assumed_state{false};
 | 
				
			||||||
  bool supports_position{false};  // NOLINT
 | 
					  bool supports_position{false};
 | 
				
			||||||
  bool supports_tilt{false};      // NOLINT
 | 
					  bool supports_tilt{false};
 | 
				
			||||||
  std::string device_class{};     // NOLINT
 | 
					  std::string device_class{};
 | 
				
			||||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
					  void encode(ProtoWriteBuffer buffer) const override;
 | 
				
			||||||
  void dump_to(std::string &out) const override;
 | 
					  void dump_to(std::string &out) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -218,11 +262,11 @@ class ListEntitiesCoverResponse : public ProtoMessage {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
class CoverStateResponse : public ProtoMessage {
 | 
					class CoverStateResponse : public ProtoMessage {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  uint32_t key{0};                            // NOLINT
 | 
					  uint32_t key{0};
 | 
				
			||||||
  enums::LegacyCoverState legacy_state{};     // NOLINT
 | 
					  enums::LegacyCoverState legacy_state{};
 | 
				
			||||||
  float position{0.0f};                       // NOLINT
 | 
					  float position{0.0f};
 | 
				
			||||||
  float tilt{0.0f};                           // NOLINT
 | 
					  float tilt{0.0f};
 | 
				
			||||||
  enums::CoverOperation current_operation{};  // NOLINT
 | 
					  enums::CoverOperation current_operation{};
 | 
				
			||||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
					  void encode(ProtoWriteBuffer buffer) const override;
 | 
				
			||||||
  void dump_to(std::string &out) const override;
 | 
					  void dump_to(std::string &out) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -232,14 +276,14 @@ class CoverStateResponse : public ProtoMessage {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
class CoverCommandRequest : public ProtoMessage {
 | 
					class CoverCommandRequest : public ProtoMessage {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  uint32_t key{0};                             // NOLINT
 | 
					  uint32_t key{0};
 | 
				
			||||||
  bool has_legacy_command{false};              // NOLINT
 | 
					  bool has_legacy_command{false};
 | 
				
			||||||
  enums::LegacyCoverCommand legacy_command{};  // NOLINT
 | 
					  enums::LegacyCoverCommand legacy_command{};
 | 
				
			||||||
  bool has_position{false};                    // NOLINT
 | 
					  bool has_position{false};
 | 
				
			||||||
  float position{0.0f};                        // NOLINT
 | 
					  float position{0.0f};
 | 
				
			||||||
  bool has_tilt{false};                        // NOLINT
 | 
					  bool has_tilt{false};
 | 
				
			||||||
  float tilt{0.0f};                            // NOLINT
 | 
					  float tilt{0.0f};
 | 
				
			||||||
  bool stop{false};                            // NOLINT
 | 
					  bool stop{false};
 | 
				
			||||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
					  void encode(ProtoWriteBuffer buffer) const override;
 | 
				
			||||||
  void dump_to(std::string &out) const override;
 | 
					  void dump_to(std::string &out) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -249,12 +293,14 @@ class CoverCommandRequest : public ProtoMessage {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
class ListEntitiesFanResponse : public ProtoMessage {
 | 
					class ListEntitiesFanResponse : public ProtoMessage {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  std::string object_id{};           // NOLINT
 | 
					  std::string object_id{};
 | 
				
			||||||
  uint32_t key{0};                   // NOLINT
 | 
					  uint32_t key{0};
 | 
				
			||||||
  std::string name{};                // NOLINT
 | 
					  std::string name{};
 | 
				
			||||||
  std::string unique_id{};           // NOLINT
 | 
					  std::string unique_id{};
 | 
				
			||||||
  bool supports_oscillation{false};  // NOLINT
 | 
					  bool supports_oscillation{false};
 | 
				
			||||||
  bool supports_speed{false};        // NOLINT
 | 
					  bool supports_speed{false};
 | 
				
			||||||
 | 
					  bool supports_direction{false};
 | 
				
			||||||
 | 
					  int32_t supported_speed_count{0};
 | 
				
			||||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
					  void encode(ProtoWriteBuffer buffer) const override;
 | 
				
			||||||
  void dump_to(std::string &out) const override;
 | 
					  void dump_to(std::string &out) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -265,10 +311,12 @@ class ListEntitiesFanResponse : public ProtoMessage {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
class FanStateResponse : public ProtoMessage {
 | 
					class FanStateResponse : public ProtoMessage {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  uint32_t key{0};          // NOLINT
 | 
					  uint32_t key{0};
 | 
				
			||||||
  bool state{false};        // NOLINT
 | 
					  bool state{false};
 | 
				
			||||||
  bool oscillating{false};  // NOLINT
 | 
					  bool oscillating{false};
 | 
				
			||||||
  enums::FanSpeed speed{};  // NOLINT
 | 
					  enums::FanSpeed speed{};
 | 
				
			||||||
 | 
					  enums::FanDirection direction{};
 | 
				
			||||||
 | 
					  int32_t speed_level{0};
 | 
				
			||||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
					  void encode(ProtoWriteBuffer buffer) const override;
 | 
				
			||||||
  void dump_to(std::string &out) const override;
 | 
					  void dump_to(std::string &out) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -278,13 +326,17 @@ class FanStateResponse : public ProtoMessage {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
class FanCommandRequest : public ProtoMessage {
 | 
					class FanCommandRequest : public ProtoMessage {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  uint32_t key{0};              // NOLINT
 | 
					  uint32_t key{0};
 | 
				
			||||||
  bool has_state{false};        // NOLINT
 | 
					  bool has_state{false};
 | 
				
			||||||
  bool state{false};            // NOLINT
 | 
					  bool state{false};
 | 
				
			||||||
  bool has_speed{false};        // NOLINT
 | 
					  bool has_speed{false};
 | 
				
			||||||
  enums::FanSpeed speed{};      // NOLINT
 | 
					  enums::FanSpeed speed{};
 | 
				
			||||||
  bool has_oscillating{false};  // NOLINT
 | 
					  bool has_oscillating{false};
 | 
				
			||||||
  bool oscillating{false};      // NOLINT
 | 
					  bool oscillating{false};
 | 
				
			||||||
 | 
					  bool has_direction{false};
 | 
				
			||||||
 | 
					  enums::FanDirection direction{};
 | 
				
			||||||
 | 
					  bool has_speed_level{false};
 | 
				
			||||||
 | 
					  int32_t speed_level{0};
 | 
				
			||||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
					  void encode(ProtoWriteBuffer buffer) const override;
 | 
				
			||||||
  void dump_to(std::string &out) const override;
 | 
					  void dump_to(std::string &out) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -294,17 +346,17 @@ class FanCommandRequest : public ProtoMessage {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
class ListEntitiesLightResponse : public ProtoMessage {
 | 
					class ListEntitiesLightResponse : public ProtoMessage {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  std::string object_id{};                 // NOLINT
 | 
					  std::string object_id{};
 | 
				
			||||||
  uint32_t key{0};                         // NOLINT
 | 
					  uint32_t key{0};
 | 
				
			||||||
  std::string name{};                      // NOLINT
 | 
					  std::string name{};
 | 
				
			||||||
  std::string unique_id{};                 // NOLINT
 | 
					  std::string unique_id{};
 | 
				
			||||||
  bool supports_brightness{false};         // NOLINT
 | 
					  bool supports_brightness{false};
 | 
				
			||||||
  bool supports_rgb{false};                // NOLINT
 | 
					  bool supports_rgb{false};
 | 
				
			||||||
  bool supports_white_value{false};        // NOLINT
 | 
					  bool supports_white_value{false};
 | 
				
			||||||
  bool supports_color_temperature{false};  // NOLINT
 | 
					  bool supports_color_temperature{false};
 | 
				
			||||||
  float min_mireds{0.0f};                  // NOLINT
 | 
					  float min_mireds{0.0f};
 | 
				
			||||||
  float max_mireds{0.0f};                  // NOLINT
 | 
					  float max_mireds{0.0f};
 | 
				
			||||||
  std::vector<std::string> effects{};      // NOLINT
 | 
					  std::vector<std::string> effects{};
 | 
				
			||||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
					  void encode(ProtoWriteBuffer buffer) const override;
 | 
				
			||||||
  void dump_to(std::string &out) const override;
 | 
					  void dump_to(std::string &out) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -315,15 +367,15 @@ class ListEntitiesLightResponse : public ProtoMessage {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
class LightStateResponse : public ProtoMessage {
 | 
					class LightStateResponse : public ProtoMessage {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  uint32_t key{0};                // NOLINT
 | 
					  uint32_t key{0};
 | 
				
			||||||
  bool state{false};              // NOLINT
 | 
					  bool state{false};
 | 
				
			||||||
  float brightness{0.0f};         // NOLINT
 | 
					  float brightness{0.0f};
 | 
				
			||||||
  float red{0.0f};                // NOLINT
 | 
					  float red{0.0f};
 | 
				
			||||||
  float green{0.0f};              // NOLINT
 | 
					  float green{0.0f};
 | 
				
			||||||
  float blue{0.0f};               // NOLINT
 | 
					  float blue{0.0f};
 | 
				
			||||||
  float white{0.0f};              // NOLINT
 | 
					  float white{0.0f};
 | 
				
			||||||
  float color_temperature{0.0f};  // NOLINT
 | 
					  float color_temperature{0.0f};
 | 
				
			||||||
  std::string effect{};           // NOLINT
 | 
					  std::string effect{};
 | 
				
			||||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
					  void encode(ProtoWriteBuffer buffer) const override;
 | 
				
			||||||
  void dump_to(std::string &out) const override;
 | 
					  void dump_to(std::string &out) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -334,25 +386,25 @@ class LightStateResponse : public ProtoMessage {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
class LightCommandRequest : public ProtoMessage {
 | 
					class LightCommandRequest : public ProtoMessage {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  uint32_t key{0};                    // NOLINT
 | 
					  uint32_t key{0};
 | 
				
			||||||
  bool has_state{false};              // NOLINT
 | 
					  bool has_state{false};
 | 
				
			||||||
  bool state{false};                  // NOLINT
 | 
					  bool state{false};
 | 
				
			||||||
  bool has_brightness{false};         // NOLINT
 | 
					  bool has_brightness{false};
 | 
				
			||||||
  float brightness{0.0f};             // NOLINT
 | 
					  float brightness{0.0f};
 | 
				
			||||||
  bool has_rgb{false};                // NOLINT
 | 
					  bool has_rgb{false};
 | 
				
			||||||
  float red{0.0f};                    // NOLINT
 | 
					  float red{0.0f};
 | 
				
			||||||
  float green{0.0f};                  // NOLINT
 | 
					  float green{0.0f};
 | 
				
			||||||
  float blue{0.0f};                   // NOLINT
 | 
					  float blue{0.0f};
 | 
				
			||||||
  bool has_white{false};              // NOLINT
 | 
					  bool has_white{false};
 | 
				
			||||||
  float white{0.0f};                  // NOLINT
 | 
					  float white{0.0f};
 | 
				
			||||||
  bool has_color_temperature{false};  // NOLINT
 | 
					  bool has_color_temperature{false};
 | 
				
			||||||
  float color_temperature{0.0f};      // NOLINT
 | 
					  float color_temperature{0.0f};
 | 
				
			||||||
  bool has_transition_length{false};  // NOLINT
 | 
					  bool has_transition_length{false};
 | 
				
			||||||
  uint32_t transition_length{0};      // NOLINT
 | 
					  uint32_t transition_length{0};
 | 
				
			||||||
  bool has_flash_length{false};       // NOLINT
 | 
					  bool has_flash_length{false};
 | 
				
			||||||
  uint32_t flash_length{0};           // NOLINT
 | 
					  uint32_t flash_length{0};
 | 
				
			||||||
  bool has_effect{false};             // NOLINT
 | 
					  bool has_effect{false};
 | 
				
			||||||
  std::string effect{};               // NOLINT
 | 
					  std::string effect{};
 | 
				
			||||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
					  void encode(ProtoWriteBuffer buffer) const override;
 | 
				
			||||||
  void dump_to(std::string &out) const override;
 | 
					  void dump_to(std::string &out) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -363,14 +415,16 @@ class LightCommandRequest : public ProtoMessage {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
class ListEntitiesSensorResponse : public ProtoMessage {
 | 
					class ListEntitiesSensorResponse : public ProtoMessage {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  std::string object_id{};            // NOLINT
 | 
					  std::string object_id{};
 | 
				
			||||||
  uint32_t key{0};                    // NOLINT
 | 
					  uint32_t key{0};
 | 
				
			||||||
  std::string name{};                 // NOLINT
 | 
					  std::string name{};
 | 
				
			||||||
  std::string unique_id{};            // NOLINT
 | 
					  std::string unique_id{};
 | 
				
			||||||
  std::string icon{};                 // NOLINT
 | 
					  std::string icon{};
 | 
				
			||||||
  std::string unit_of_measurement{};  // NOLINT
 | 
					  std::string unit_of_measurement{};
 | 
				
			||||||
  int32_t accuracy_decimals{0};       // NOLINT
 | 
					  int32_t accuracy_decimals{0};
 | 
				
			||||||
  bool force_update{false};           // NOLINT
 | 
					  bool force_update{false};
 | 
				
			||||||
 | 
					  std::string device_class{};
 | 
				
			||||||
 | 
					  enums::SensorStateClass state_class{};
 | 
				
			||||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
					  void encode(ProtoWriteBuffer buffer) const override;
 | 
				
			||||||
  void dump_to(std::string &out) const override;
 | 
					  void dump_to(std::string &out) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -381,9 +435,9 @@ class ListEntitiesSensorResponse : public ProtoMessage {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
class SensorStateResponse : public ProtoMessage {
 | 
					class SensorStateResponse : public ProtoMessage {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  uint32_t key{0};            // NOLINT
 | 
					  uint32_t key{0};
 | 
				
			||||||
  float state{0.0f};          // NOLINT
 | 
					  float state{0.0f};
 | 
				
			||||||
  bool missing_state{false};  // NOLINT
 | 
					  bool missing_state{false};
 | 
				
			||||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
					  void encode(ProtoWriteBuffer buffer) const override;
 | 
				
			||||||
  void dump_to(std::string &out) const override;
 | 
					  void dump_to(std::string &out) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -393,12 +447,12 @@ class SensorStateResponse : public ProtoMessage {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
class ListEntitiesSwitchResponse : public ProtoMessage {
 | 
					class ListEntitiesSwitchResponse : public ProtoMessage {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  std::string object_id{};    // NOLINT
 | 
					  std::string object_id{};
 | 
				
			||||||
  uint32_t key{0};            // NOLINT
 | 
					  uint32_t key{0};
 | 
				
			||||||
  std::string name{};         // NOLINT
 | 
					  std::string name{};
 | 
				
			||||||
  std::string unique_id{};    // NOLINT
 | 
					  std::string unique_id{};
 | 
				
			||||||
  std::string icon{};         // NOLINT
 | 
					  std::string icon{};
 | 
				
			||||||
  bool assumed_state{false};  // NOLINT
 | 
					  bool assumed_state{false};
 | 
				
			||||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
					  void encode(ProtoWriteBuffer buffer) const override;
 | 
				
			||||||
  void dump_to(std::string &out) const override;
 | 
					  void dump_to(std::string &out) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -409,8 +463,8 @@ class ListEntitiesSwitchResponse : public ProtoMessage {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
class SwitchStateResponse : public ProtoMessage {
 | 
					class SwitchStateResponse : public ProtoMessage {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  uint32_t key{0};    // NOLINT
 | 
					  uint32_t key{0};
 | 
				
			||||||
  bool state{false};  // NOLINT
 | 
					  bool state{false};
 | 
				
			||||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
					  void encode(ProtoWriteBuffer buffer) const override;
 | 
				
			||||||
  void dump_to(std::string &out) const override;
 | 
					  void dump_to(std::string &out) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -420,8 +474,8 @@ class SwitchStateResponse : public ProtoMessage {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
class SwitchCommandRequest : public ProtoMessage {
 | 
					class SwitchCommandRequest : public ProtoMessage {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  uint32_t key{0};    // NOLINT
 | 
					  uint32_t key{0};
 | 
				
			||||||
  bool state{false};  // NOLINT
 | 
					  bool state{false};
 | 
				
			||||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
					  void encode(ProtoWriteBuffer buffer) const override;
 | 
				
			||||||
  void dump_to(std::string &out) const override;
 | 
					  void dump_to(std::string &out) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -431,11 +485,11 @@ class SwitchCommandRequest : public ProtoMessage {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
class ListEntitiesTextSensorResponse : public ProtoMessage {
 | 
					class ListEntitiesTextSensorResponse : public ProtoMessage {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  std::string object_id{};  // NOLINT
 | 
					  std::string object_id{};
 | 
				
			||||||
  uint32_t key{0};          // NOLINT
 | 
					  uint32_t key{0};
 | 
				
			||||||
  std::string name{};       // NOLINT
 | 
					  std::string name{};
 | 
				
			||||||
  std::string unique_id{};  // NOLINT
 | 
					  std::string unique_id{};
 | 
				
			||||||
  std::string icon{};       // NOLINT
 | 
					  std::string icon{};
 | 
				
			||||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
					  void encode(ProtoWriteBuffer buffer) const override;
 | 
				
			||||||
  void dump_to(std::string &out) const override;
 | 
					  void dump_to(std::string &out) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -445,9 +499,9 @@ class ListEntitiesTextSensorResponse : public ProtoMessage {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
class TextSensorStateResponse : public ProtoMessage {
 | 
					class TextSensorStateResponse : public ProtoMessage {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  uint32_t key{0};            // NOLINT
 | 
					  uint32_t key{0};
 | 
				
			||||||
  std::string state{};        // NOLINT
 | 
					  std::string state{};
 | 
				
			||||||
  bool missing_state{false};  // NOLINT
 | 
					  bool missing_state{false};
 | 
				
			||||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
					  void encode(ProtoWriteBuffer buffer) const override;
 | 
				
			||||||
  void dump_to(std::string &out) const override;
 | 
					  void dump_to(std::string &out) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -458,8 +512,8 @@ class TextSensorStateResponse : public ProtoMessage {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
class SubscribeLogsRequest : public ProtoMessage {
 | 
					class SubscribeLogsRequest : public ProtoMessage {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  enums::LogLevel level{};  // NOLINT
 | 
					  enums::LogLevel level{};
 | 
				
			||||||
  bool dump_config{false};  // NOLINT
 | 
					  bool dump_config{false};
 | 
				
			||||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
					  void encode(ProtoWriteBuffer buffer) const override;
 | 
				
			||||||
  void dump_to(std::string &out) const override;
 | 
					  void dump_to(std::string &out) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -468,10 +522,10 @@ class SubscribeLogsRequest : public ProtoMessage {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
class SubscribeLogsResponse : public ProtoMessage {
 | 
					class SubscribeLogsResponse : public ProtoMessage {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  enums::LogLevel level{};  // NOLINT
 | 
					  enums::LogLevel level{};
 | 
				
			||||||
  std::string tag{};        // NOLINT
 | 
					  std::string tag{};
 | 
				
			||||||
  std::string message{};    // NOLINT
 | 
					  std::string message{};
 | 
				
			||||||
  bool send_failed{false};  // NOLINT
 | 
					  bool send_failed{false};
 | 
				
			||||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
					  void encode(ProtoWriteBuffer buffer) const override;
 | 
				
			||||||
  void dump_to(std::string &out) const override;
 | 
					  void dump_to(std::string &out) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -488,8 +542,8 @@ class SubscribeHomeassistantServicesRequest : public ProtoMessage {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
class HomeassistantServiceMap : public ProtoMessage {
 | 
					class HomeassistantServiceMap : public ProtoMessage {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  std::string key{};    // NOLINT
 | 
					  std::string key{};
 | 
				
			||||||
  std::string value{};  // NOLINT
 | 
					  std::string value{};
 | 
				
			||||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
					  void encode(ProtoWriteBuffer buffer) const override;
 | 
				
			||||||
  void dump_to(std::string &out) const override;
 | 
					  void dump_to(std::string &out) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -498,11 +552,11 @@ class HomeassistantServiceMap : public ProtoMessage {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
class HomeassistantServiceResponse : public ProtoMessage {
 | 
					class HomeassistantServiceResponse : public ProtoMessage {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  std::string service{};                                 // NOLINT
 | 
					  std::string service{};
 | 
				
			||||||
  std::vector<HomeassistantServiceMap> data{};           // NOLINT
 | 
					  std::vector<HomeassistantServiceMap> data{};
 | 
				
			||||||
  std::vector<HomeassistantServiceMap> data_template{};  // NOLINT
 | 
					  std::vector<HomeassistantServiceMap> data_template{};
 | 
				
			||||||
  std::vector<HomeassistantServiceMap> variables{};      // NOLINT
 | 
					  std::vector<HomeassistantServiceMap> variables{};
 | 
				
			||||||
  bool is_event{false};                                  // NOLINT
 | 
					  bool is_event{false};
 | 
				
			||||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
					  void encode(ProtoWriteBuffer buffer) const override;
 | 
				
			||||||
  void dump_to(std::string &out) const override;
 | 
					  void dump_to(std::string &out) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -519,7 +573,8 @@ class SubscribeHomeAssistantStatesRequest : public ProtoMessage {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
class SubscribeHomeAssistantStateResponse : public ProtoMessage {
 | 
					class SubscribeHomeAssistantStateResponse : public ProtoMessage {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  std::string entity_id{};  // NOLINT
 | 
					  std::string entity_id{};
 | 
				
			||||||
 | 
					  std::string attribute{};
 | 
				
			||||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
					  void encode(ProtoWriteBuffer buffer) const override;
 | 
				
			||||||
  void dump_to(std::string &out) const override;
 | 
					  void dump_to(std::string &out) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -528,8 +583,9 @@ class SubscribeHomeAssistantStateResponse : public ProtoMessage {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
class HomeAssistantStateResponse : public ProtoMessage {
 | 
					class HomeAssistantStateResponse : public ProtoMessage {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  std::string entity_id{};  // NOLINT
 | 
					  std::string entity_id{};
 | 
				
			||||||
  std::string state{};      // NOLINT
 | 
					  std::string state{};
 | 
				
			||||||
 | 
					  std::string attribute{};
 | 
				
			||||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
					  void encode(ProtoWriteBuffer buffer) const override;
 | 
				
			||||||
  void dump_to(std::string &out) const override;
 | 
					  void dump_to(std::string &out) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -545,7 +601,7 @@ class GetTimeRequest : public ProtoMessage {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
class GetTimeResponse : public ProtoMessage {
 | 
					class GetTimeResponse : public ProtoMessage {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  uint32_t epoch_seconds{0};  // NOLINT
 | 
					  uint32_t epoch_seconds{0};
 | 
				
			||||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
					  void encode(ProtoWriteBuffer buffer) const override;
 | 
				
			||||||
  void dump_to(std::string &out) const override;
 | 
					  void dump_to(std::string &out) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -554,8 +610,8 @@ class GetTimeResponse : public ProtoMessage {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
class ListEntitiesServicesArgument : public ProtoMessage {
 | 
					class ListEntitiesServicesArgument : public ProtoMessage {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  std::string name{};            // NOLINT
 | 
					  std::string name{};
 | 
				
			||||||
  enums::ServiceArgType type{};  // NOLINT
 | 
					  enums::ServiceArgType type{};
 | 
				
			||||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
					  void encode(ProtoWriteBuffer buffer) const override;
 | 
				
			||||||
  void dump_to(std::string &out) const override;
 | 
					  void dump_to(std::string &out) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -565,9 +621,9 @@ class ListEntitiesServicesArgument : public ProtoMessage {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
class ListEntitiesServicesResponse : public ProtoMessage {
 | 
					class ListEntitiesServicesResponse : public ProtoMessage {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  std::string name{};                                // NOLINT
 | 
					  std::string name{};
 | 
				
			||||||
  uint32_t key{0};                                   // NOLINT
 | 
					  uint32_t key{0};
 | 
				
			||||||
  std::vector<ListEntitiesServicesArgument> args{};  // NOLINT
 | 
					  std::vector<ListEntitiesServicesArgument> args{};
 | 
				
			||||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
					  void encode(ProtoWriteBuffer buffer) const override;
 | 
				
			||||||
  void dump_to(std::string &out) const override;
 | 
					  void dump_to(std::string &out) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -577,15 +633,15 @@ class ListEntitiesServicesResponse : public ProtoMessage {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
class ExecuteServiceArgument : public ProtoMessage {
 | 
					class ExecuteServiceArgument : public ProtoMessage {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  bool bool_{false};                        // NOLINT
 | 
					  bool bool_{false};
 | 
				
			||||||
  int32_t legacy_int{0};                    // NOLINT
 | 
					  int32_t legacy_int{0};
 | 
				
			||||||
  float float_{0.0f};                       // NOLINT
 | 
					  float float_{0.0f};
 | 
				
			||||||
  std::string string_{};                    // NOLINT
 | 
					  std::string string_{};
 | 
				
			||||||
  int32_t int_{0};                          // NOLINT
 | 
					  int32_t int_{0};
 | 
				
			||||||
  std::vector<bool> bool_array{};           // NOLINT
 | 
					  std::vector<bool> bool_array{};
 | 
				
			||||||
  std::vector<int32_t> int_array{};         // NOLINT
 | 
					  std::vector<int32_t> int_array{};
 | 
				
			||||||
  std::vector<float> float_array{};         // NOLINT
 | 
					  std::vector<float> float_array{};
 | 
				
			||||||
  std::vector<std::string> string_array{};  // NOLINT
 | 
					  std::vector<std::string> string_array{};
 | 
				
			||||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
					  void encode(ProtoWriteBuffer buffer) const override;
 | 
				
			||||||
  void dump_to(std::string &out) const override;
 | 
					  void dump_to(std::string &out) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -596,8 +652,8 @@ class ExecuteServiceArgument : public ProtoMessage {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
class ExecuteServiceRequest : public ProtoMessage {
 | 
					class ExecuteServiceRequest : public ProtoMessage {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  uint32_t key{0};                             // NOLINT
 | 
					  uint32_t key{0};
 | 
				
			||||||
  std::vector<ExecuteServiceArgument> args{};  // NOLINT
 | 
					  std::vector<ExecuteServiceArgument> args{};
 | 
				
			||||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
					  void encode(ProtoWriteBuffer buffer) const override;
 | 
				
			||||||
  void dump_to(std::string &out) const override;
 | 
					  void dump_to(std::string &out) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -607,10 +663,10 @@ class ExecuteServiceRequest : public ProtoMessage {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
class ListEntitiesCameraResponse : public ProtoMessage {
 | 
					class ListEntitiesCameraResponse : public ProtoMessage {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  std::string object_id{};  // NOLINT
 | 
					  std::string object_id{};
 | 
				
			||||||
  uint32_t key{0};          // NOLINT
 | 
					  uint32_t key{0};
 | 
				
			||||||
  std::string name{};       // NOLINT
 | 
					  std::string name{};
 | 
				
			||||||
  std::string unique_id{};  // NOLINT
 | 
					  std::string unique_id{};
 | 
				
			||||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
					  void encode(ProtoWriteBuffer buffer) const override;
 | 
				
			||||||
  void dump_to(std::string &out) const override;
 | 
					  void dump_to(std::string &out) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -620,9 +676,9 @@ class ListEntitiesCameraResponse : public ProtoMessage {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
class CameraImageResponse : public ProtoMessage {
 | 
					class CameraImageResponse : public ProtoMessage {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  uint32_t key{0};     // NOLINT
 | 
					  uint32_t key{0};
 | 
				
			||||||
  std::string data{};  // NOLINT
 | 
					  std::string data{};
 | 
				
			||||||
  bool done{false};    // NOLINT
 | 
					  bool done{false};
 | 
				
			||||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
					  void encode(ProtoWriteBuffer buffer) const override;
 | 
				
			||||||
  void dump_to(std::string &out) const override;
 | 
					  void dump_to(std::string &out) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -633,8 +689,8 @@ class CameraImageResponse : public ProtoMessage {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
class CameraImageRequest : public ProtoMessage {
 | 
					class CameraImageRequest : public ProtoMessage {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  bool single{false};  // NOLINT
 | 
					  bool single{false};
 | 
				
			||||||
  bool stream{false};  // NOLINT
 | 
					  bool stream{false};
 | 
				
			||||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
					  void encode(ProtoWriteBuffer buffer) const override;
 | 
				
			||||||
  void dump_to(std::string &out) const override;
 | 
					  void dump_to(std::string &out) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -643,18 +699,23 @@ class CameraImageRequest : public ProtoMessage {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
class ListEntitiesClimateResponse : public ProtoMessage {
 | 
					class ListEntitiesClimateResponse : public ProtoMessage {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  std::string object_id{};                            // NOLINT
 | 
					  std::string object_id{};
 | 
				
			||||||
  uint32_t key{0};                                    // NOLINT
 | 
					  uint32_t key{0};
 | 
				
			||||||
  std::string name{};                                 // NOLINT
 | 
					  std::string name{};
 | 
				
			||||||
  std::string unique_id{};                            // NOLINT
 | 
					  std::string unique_id{};
 | 
				
			||||||
  bool supports_current_temperature{false};           // NOLINT
 | 
					  bool supports_current_temperature{false};
 | 
				
			||||||
  bool supports_two_point_target_temperature{false};  // NOLINT
 | 
					  bool supports_two_point_target_temperature{false};
 | 
				
			||||||
  std::vector<enums::ClimateMode> supported_modes{};  // NOLINT
 | 
					  std::vector<enums::ClimateMode> supported_modes{};
 | 
				
			||||||
  float visual_min_temperature{0.0f};                 // NOLINT
 | 
					  float visual_min_temperature{0.0f};
 | 
				
			||||||
  float visual_max_temperature{0.0f};                 // NOLINT
 | 
					  float visual_max_temperature{0.0f};
 | 
				
			||||||
  float visual_temperature_step{0.0f};                // NOLINT
 | 
					  float visual_temperature_step{0.0f};
 | 
				
			||||||
  bool supports_away{false};                          // NOLINT
 | 
					  bool supports_away{false};
 | 
				
			||||||
  bool supports_action{false};                        // NOLINT
 | 
					  bool supports_action{false};
 | 
				
			||||||
 | 
					  std::vector<enums::ClimateFanMode> supported_fan_modes{};
 | 
				
			||||||
 | 
					  std::vector<enums::ClimateSwingMode> supported_swing_modes{};
 | 
				
			||||||
 | 
					  std::vector<std::string> supported_custom_fan_modes{};
 | 
				
			||||||
 | 
					  std::vector<enums::ClimatePreset> supported_presets{};
 | 
				
			||||||
 | 
					  std::vector<std::string> supported_custom_presets{};
 | 
				
			||||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
					  void encode(ProtoWriteBuffer buffer) const override;
 | 
				
			||||||
  void dump_to(std::string &out) const override;
 | 
					  void dump_to(std::string &out) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -665,39 +726,56 @@ class ListEntitiesClimateResponse : public ProtoMessage {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
class ClimateStateResponse : public ProtoMessage {
 | 
					class ClimateStateResponse : public ProtoMessage {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  uint32_t key{0};                      // NOLINT
 | 
					  uint32_t key{0};
 | 
				
			||||||
  enums::ClimateMode mode{};            // NOLINT
 | 
					  enums::ClimateMode mode{};
 | 
				
			||||||
  float current_temperature{0.0f};      // NOLINT
 | 
					  float current_temperature{0.0f};
 | 
				
			||||||
  float target_temperature{0.0f};       // NOLINT
 | 
					  float target_temperature{0.0f};
 | 
				
			||||||
  float target_temperature_low{0.0f};   // NOLINT
 | 
					  float target_temperature_low{0.0f};
 | 
				
			||||||
  float target_temperature_high{0.0f};  // NOLINT
 | 
					  float target_temperature_high{0.0f};
 | 
				
			||||||
  bool away{false};                     // NOLINT
 | 
					  bool away{false};
 | 
				
			||||||
  enums::ClimateAction action{};        // NOLINT
 | 
					  enums::ClimateAction action{};
 | 
				
			||||||
 | 
					  enums::ClimateFanMode fan_mode{};
 | 
				
			||||||
 | 
					  enums::ClimateSwingMode swing_mode{};
 | 
				
			||||||
 | 
					  std::string custom_fan_mode{};
 | 
				
			||||||
 | 
					  enums::ClimatePreset preset{};
 | 
				
			||||||
 | 
					  std::string custom_preset{};
 | 
				
			||||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
					  void encode(ProtoWriteBuffer buffer) const override;
 | 
				
			||||||
  void dump_to(std::string &out) const override;
 | 
					  void dump_to(std::string &out) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 protected:
 | 
					 protected:
 | 
				
			||||||
  bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
 | 
					  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;
 | 
					  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
class ClimateCommandRequest : public ProtoMessage {
 | 
					class ClimateCommandRequest : public ProtoMessage {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  uint32_t key{0};                          // NOLINT
 | 
					  uint32_t key{0};
 | 
				
			||||||
  bool has_mode{false};                     // NOLINT
 | 
					  bool has_mode{false};
 | 
				
			||||||
  enums::ClimateMode mode{};                // NOLINT
 | 
					  enums::ClimateMode mode{};
 | 
				
			||||||
  bool has_target_temperature{false};       // NOLINT
 | 
					  bool has_target_temperature{false};
 | 
				
			||||||
  float target_temperature{0.0f};           // NOLINT
 | 
					  float target_temperature{0.0f};
 | 
				
			||||||
  bool has_target_temperature_low{false};   // NOLINT
 | 
					  bool has_target_temperature_low{false};
 | 
				
			||||||
  float target_temperature_low{0.0f};       // NOLINT
 | 
					  float target_temperature_low{0.0f};
 | 
				
			||||||
  bool has_target_temperature_high{false};  // NOLINT
 | 
					  bool has_target_temperature_high{false};
 | 
				
			||||||
  float target_temperature_high{0.0f};      // NOLINT
 | 
					  float target_temperature_high{0.0f};
 | 
				
			||||||
  bool has_away{false};                     // NOLINT
 | 
					  bool has_away{false};
 | 
				
			||||||
  bool away{false};                         // NOLINT
 | 
					  bool away{false};
 | 
				
			||||||
 | 
					  bool has_fan_mode{false};
 | 
				
			||||||
 | 
					  enums::ClimateFanMode fan_mode{};
 | 
				
			||||||
 | 
					  bool has_swing_mode{false};
 | 
				
			||||||
 | 
					  enums::ClimateSwingMode swing_mode{};
 | 
				
			||||||
 | 
					  bool has_custom_fan_mode{false};
 | 
				
			||||||
 | 
					  std::string custom_fan_mode{};
 | 
				
			||||||
 | 
					  bool has_preset{false};
 | 
				
			||||||
 | 
					  enums::ClimatePreset preset{};
 | 
				
			||||||
 | 
					  bool has_custom_preset{false};
 | 
				
			||||||
 | 
					  std::string custom_preset{};
 | 
				
			||||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
					  void encode(ProtoWriteBuffer buffer) const override;
 | 
				
			||||||
  void dump_to(std::string &out) const override;
 | 
					  void dump_to(std::string &out) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 protected:
 | 
					 protected:
 | 
				
			||||||
  bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
 | 
					  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;
 | 
					  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,5 @@
 | 
				
			|||||||
 | 
					// This file was automatically generated with a tool.
 | 
				
			||||||
 | 
					// See scripts/api_protobuf/api_protobuf.py
 | 
				
			||||||
#include "api_pb2_service.h"
 | 
					#include "api_pb2_service.h"
 | 
				
			||||||
#include "esphome/core/log.h"
 | 
					#include "esphome/core/log.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -8,69 +10,57 @@ static const char *TAG = "api.service";
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
bool APIServerConnectionBase::send_hello_response(const HelloResponse &msg) {
 | 
					bool APIServerConnectionBase::send_hello_response(const HelloResponse &msg) {
 | 
				
			||||||
  ESP_LOGVV(TAG, "send_hello_response: %s", msg.dump().c_str());
 | 
					  ESP_LOGVV(TAG, "send_hello_response: %s", msg.dump().c_str());
 | 
				
			||||||
  this->set_nodelay(true);
 | 
					 | 
				
			||||||
  return this->send_message_<HelloResponse>(msg, 2);
 | 
					  return this->send_message_<HelloResponse>(msg, 2);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
bool APIServerConnectionBase::send_connect_response(const ConnectResponse &msg) {
 | 
					bool APIServerConnectionBase::send_connect_response(const ConnectResponse &msg) {
 | 
				
			||||||
  ESP_LOGVV(TAG, "send_connect_response: %s", msg.dump().c_str());
 | 
					  ESP_LOGVV(TAG, "send_connect_response: %s", msg.dump().c_str());
 | 
				
			||||||
  this->set_nodelay(true);
 | 
					 | 
				
			||||||
  return this->send_message_<ConnectResponse>(msg, 4);
 | 
					  return this->send_message_<ConnectResponse>(msg, 4);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
bool APIServerConnectionBase::send_disconnect_request(const DisconnectRequest &msg) {
 | 
					bool APIServerConnectionBase::send_disconnect_request(const DisconnectRequest &msg) {
 | 
				
			||||||
  ESP_LOGVV(TAG, "send_disconnect_request: %s", msg.dump().c_str());
 | 
					  ESP_LOGVV(TAG, "send_disconnect_request: %s", msg.dump().c_str());
 | 
				
			||||||
  this->set_nodelay(true);
 | 
					 | 
				
			||||||
  return this->send_message_<DisconnectRequest>(msg, 5);
 | 
					  return this->send_message_<DisconnectRequest>(msg, 5);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
bool APIServerConnectionBase::send_disconnect_response(const DisconnectResponse &msg) {
 | 
					bool APIServerConnectionBase::send_disconnect_response(const DisconnectResponse &msg) {
 | 
				
			||||||
  ESP_LOGVV(TAG, "send_disconnect_response: %s", msg.dump().c_str());
 | 
					  ESP_LOGVV(TAG, "send_disconnect_response: %s", msg.dump().c_str());
 | 
				
			||||||
  this->set_nodelay(true);
 | 
					 | 
				
			||||||
  return this->send_message_<DisconnectResponse>(msg, 6);
 | 
					  return this->send_message_<DisconnectResponse>(msg, 6);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
bool APIServerConnectionBase::send_ping_request(const PingRequest &msg) {
 | 
					bool APIServerConnectionBase::send_ping_request(const PingRequest &msg) {
 | 
				
			||||||
  ESP_LOGVV(TAG, "send_ping_request: %s", msg.dump().c_str());
 | 
					  ESP_LOGVV(TAG, "send_ping_request: %s", msg.dump().c_str());
 | 
				
			||||||
  this->set_nodelay(false);
 | 
					 | 
				
			||||||
  return this->send_message_<PingRequest>(msg, 7);
 | 
					  return this->send_message_<PingRequest>(msg, 7);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
bool APIServerConnectionBase::send_ping_response(const PingResponse &msg) {
 | 
					bool APIServerConnectionBase::send_ping_response(const PingResponse &msg) {
 | 
				
			||||||
  ESP_LOGVV(TAG, "send_ping_response: %s", msg.dump().c_str());
 | 
					  ESP_LOGVV(TAG, "send_ping_response: %s", msg.dump().c_str());
 | 
				
			||||||
  this->set_nodelay(false);
 | 
					 | 
				
			||||||
  return this->send_message_<PingResponse>(msg, 8);
 | 
					  return this->send_message_<PingResponse>(msg, 8);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
bool APIServerConnectionBase::send_device_info_response(const DeviceInfoResponse &msg) {
 | 
					bool APIServerConnectionBase::send_device_info_response(const DeviceInfoResponse &msg) {
 | 
				
			||||||
  ESP_LOGVV(TAG, "send_device_info_response: %s", msg.dump().c_str());
 | 
					  ESP_LOGVV(TAG, "send_device_info_response: %s", msg.dump().c_str());
 | 
				
			||||||
  this->set_nodelay(false);
 | 
					 | 
				
			||||||
  return this->send_message_<DeviceInfoResponse>(msg, 10);
 | 
					  return this->send_message_<DeviceInfoResponse>(msg, 10);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
bool APIServerConnectionBase::send_list_entities_done_response(const ListEntitiesDoneResponse &msg) {
 | 
					bool APIServerConnectionBase::send_list_entities_done_response(const ListEntitiesDoneResponse &msg) {
 | 
				
			||||||
  ESP_LOGVV(TAG, "send_list_entities_done_response: %s", msg.dump().c_str());
 | 
					  ESP_LOGVV(TAG, "send_list_entities_done_response: %s", msg.dump().c_str());
 | 
				
			||||||
  this->set_nodelay(true);
 | 
					 | 
				
			||||||
  return this->send_message_<ListEntitiesDoneResponse>(msg, 19);
 | 
					  return this->send_message_<ListEntitiesDoneResponse>(msg, 19);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#ifdef USE_BINARY_SENSOR
 | 
					#ifdef USE_BINARY_SENSOR
 | 
				
			||||||
bool APIServerConnectionBase::send_list_entities_binary_sensor_response(const ListEntitiesBinarySensorResponse &msg) {
 | 
					bool APIServerConnectionBase::send_list_entities_binary_sensor_response(const ListEntitiesBinarySensorResponse &msg) {
 | 
				
			||||||
  ESP_LOGVV(TAG, "send_list_entities_binary_sensor_response: %s", msg.dump().c_str());
 | 
					  ESP_LOGVV(TAG, "send_list_entities_binary_sensor_response: %s", msg.dump().c_str());
 | 
				
			||||||
  this->set_nodelay(false);
 | 
					 | 
				
			||||||
  return this->send_message_<ListEntitiesBinarySensorResponse>(msg, 12);
 | 
					  return this->send_message_<ListEntitiesBinarySensorResponse>(msg, 12);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
#ifdef USE_BINARY_SENSOR
 | 
					#ifdef USE_BINARY_SENSOR
 | 
				
			||||||
bool APIServerConnectionBase::send_binary_sensor_state_response(const BinarySensorStateResponse &msg) {
 | 
					bool APIServerConnectionBase::send_binary_sensor_state_response(const BinarySensorStateResponse &msg) {
 | 
				
			||||||
  ESP_LOGVV(TAG, "send_binary_sensor_state_response: %s", msg.dump().c_str());
 | 
					  ESP_LOGVV(TAG, "send_binary_sensor_state_response: %s", msg.dump().c_str());
 | 
				
			||||||
  this->set_nodelay(true);
 | 
					 | 
				
			||||||
  return this->send_message_<BinarySensorStateResponse>(msg, 21);
 | 
					  return this->send_message_<BinarySensorStateResponse>(msg, 21);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
#ifdef USE_COVER
 | 
					#ifdef USE_COVER
 | 
				
			||||||
bool APIServerConnectionBase::send_list_entities_cover_response(const ListEntitiesCoverResponse &msg) {
 | 
					bool APIServerConnectionBase::send_list_entities_cover_response(const ListEntitiesCoverResponse &msg) {
 | 
				
			||||||
  ESP_LOGVV(TAG, "send_list_entities_cover_response: %s", msg.dump().c_str());
 | 
					  ESP_LOGVV(TAG, "send_list_entities_cover_response: %s", msg.dump().c_str());
 | 
				
			||||||
  this->set_nodelay(false);
 | 
					 | 
				
			||||||
  return this->send_message_<ListEntitiesCoverResponse>(msg, 13);
 | 
					  return this->send_message_<ListEntitiesCoverResponse>(msg, 13);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
#ifdef USE_COVER
 | 
					#ifdef USE_COVER
 | 
				
			||||||
bool APIServerConnectionBase::send_cover_state_response(const CoverStateResponse &msg) {
 | 
					bool APIServerConnectionBase::send_cover_state_response(const CoverStateResponse &msg) {
 | 
				
			||||||
  ESP_LOGVV(TAG, "send_cover_state_response: %s", msg.dump().c_str());
 | 
					  ESP_LOGVV(TAG, "send_cover_state_response: %s", msg.dump().c_str());
 | 
				
			||||||
  this->set_nodelay(true);
 | 
					 | 
				
			||||||
  return this->send_message_<CoverStateResponse>(msg, 22);
 | 
					  return this->send_message_<CoverStateResponse>(msg, 22);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
@@ -79,14 +69,12 @@ bool APIServerConnectionBase::send_cover_state_response(const CoverStateResponse
 | 
				
			|||||||
#ifdef USE_FAN
 | 
					#ifdef USE_FAN
 | 
				
			||||||
bool APIServerConnectionBase::send_list_entities_fan_response(const ListEntitiesFanResponse &msg) {
 | 
					bool APIServerConnectionBase::send_list_entities_fan_response(const ListEntitiesFanResponse &msg) {
 | 
				
			||||||
  ESP_LOGVV(TAG, "send_list_entities_fan_response: %s", msg.dump().c_str());
 | 
					  ESP_LOGVV(TAG, "send_list_entities_fan_response: %s", msg.dump().c_str());
 | 
				
			||||||
  this->set_nodelay(false);
 | 
					 | 
				
			||||||
  return this->send_message_<ListEntitiesFanResponse>(msg, 14);
 | 
					  return this->send_message_<ListEntitiesFanResponse>(msg, 14);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
#ifdef USE_FAN
 | 
					#ifdef USE_FAN
 | 
				
			||||||
bool APIServerConnectionBase::send_fan_state_response(const FanStateResponse &msg) {
 | 
					bool APIServerConnectionBase::send_fan_state_response(const FanStateResponse &msg) {
 | 
				
			||||||
  ESP_LOGVV(TAG, "send_fan_state_response: %s", msg.dump().c_str());
 | 
					  ESP_LOGVV(TAG, "send_fan_state_response: %s", msg.dump().c_str());
 | 
				
			||||||
  this->set_nodelay(true);
 | 
					 | 
				
			||||||
  return this->send_message_<FanStateResponse>(msg, 23);
 | 
					  return this->send_message_<FanStateResponse>(msg, 23);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
@@ -95,14 +83,12 @@ bool APIServerConnectionBase::send_fan_state_response(const FanStateResponse &ms
 | 
				
			|||||||
#ifdef USE_LIGHT
 | 
					#ifdef USE_LIGHT
 | 
				
			||||||
bool APIServerConnectionBase::send_list_entities_light_response(const ListEntitiesLightResponse &msg) {
 | 
					bool APIServerConnectionBase::send_list_entities_light_response(const ListEntitiesLightResponse &msg) {
 | 
				
			||||||
  ESP_LOGVV(TAG, "send_list_entities_light_response: %s", msg.dump().c_str());
 | 
					  ESP_LOGVV(TAG, "send_list_entities_light_response: %s", msg.dump().c_str());
 | 
				
			||||||
  this->set_nodelay(false);
 | 
					 | 
				
			||||||
  return this->send_message_<ListEntitiesLightResponse>(msg, 15);
 | 
					  return this->send_message_<ListEntitiesLightResponse>(msg, 15);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
#ifdef USE_LIGHT
 | 
					#ifdef USE_LIGHT
 | 
				
			||||||
bool APIServerConnectionBase::send_light_state_response(const LightStateResponse &msg) {
 | 
					bool APIServerConnectionBase::send_light_state_response(const LightStateResponse &msg) {
 | 
				
			||||||
  ESP_LOGVV(TAG, "send_light_state_response: %s", msg.dump().c_str());
 | 
					  ESP_LOGVV(TAG, "send_light_state_response: %s", msg.dump().c_str());
 | 
				
			||||||
  this->set_nodelay(true);
 | 
					 | 
				
			||||||
  return this->send_message_<LightStateResponse>(msg, 24);
 | 
					  return this->send_message_<LightStateResponse>(msg, 24);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
@@ -111,28 +97,24 @@ bool APIServerConnectionBase::send_light_state_response(const LightStateResponse
 | 
				
			|||||||
#ifdef USE_SENSOR
 | 
					#ifdef USE_SENSOR
 | 
				
			||||||
bool APIServerConnectionBase::send_list_entities_sensor_response(const ListEntitiesSensorResponse &msg) {
 | 
					bool APIServerConnectionBase::send_list_entities_sensor_response(const ListEntitiesSensorResponse &msg) {
 | 
				
			||||||
  ESP_LOGVV(TAG, "send_list_entities_sensor_response: %s", msg.dump().c_str());
 | 
					  ESP_LOGVV(TAG, "send_list_entities_sensor_response: %s", msg.dump().c_str());
 | 
				
			||||||
  this->set_nodelay(false);
 | 
					 | 
				
			||||||
  return this->send_message_<ListEntitiesSensorResponse>(msg, 16);
 | 
					  return this->send_message_<ListEntitiesSensorResponse>(msg, 16);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
#ifdef USE_SENSOR
 | 
					#ifdef USE_SENSOR
 | 
				
			||||||
bool APIServerConnectionBase::send_sensor_state_response(const SensorStateResponse &msg) {
 | 
					bool APIServerConnectionBase::send_sensor_state_response(const SensorStateResponse &msg) {
 | 
				
			||||||
  ESP_LOGVV(TAG, "send_sensor_state_response: %s", msg.dump().c_str());
 | 
					  ESP_LOGVV(TAG, "send_sensor_state_response: %s", msg.dump().c_str());
 | 
				
			||||||
  this->set_nodelay(true);
 | 
					 | 
				
			||||||
  return this->send_message_<SensorStateResponse>(msg, 25);
 | 
					  return this->send_message_<SensorStateResponse>(msg, 25);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
#ifdef USE_SWITCH
 | 
					#ifdef USE_SWITCH
 | 
				
			||||||
bool APIServerConnectionBase::send_list_entities_switch_response(const ListEntitiesSwitchResponse &msg) {
 | 
					bool APIServerConnectionBase::send_list_entities_switch_response(const ListEntitiesSwitchResponse &msg) {
 | 
				
			||||||
  ESP_LOGVV(TAG, "send_list_entities_switch_response: %s", msg.dump().c_str());
 | 
					  ESP_LOGVV(TAG, "send_list_entities_switch_response: %s", msg.dump().c_str());
 | 
				
			||||||
  this->set_nodelay(false);
 | 
					 | 
				
			||||||
  return this->send_message_<ListEntitiesSwitchResponse>(msg, 17);
 | 
					  return this->send_message_<ListEntitiesSwitchResponse>(msg, 17);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
#ifdef USE_SWITCH
 | 
					#ifdef USE_SWITCH
 | 
				
			||||||
bool APIServerConnectionBase::send_switch_state_response(const SwitchStateResponse &msg) {
 | 
					bool APIServerConnectionBase::send_switch_state_response(const SwitchStateResponse &msg) {
 | 
				
			||||||
  ESP_LOGVV(TAG, "send_switch_state_response: %s", msg.dump().c_str());
 | 
					  ESP_LOGVV(TAG, "send_switch_state_response: %s", msg.dump().c_str());
 | 
				
			||||||
  this->set_nodelay(true);
 | 
					 | 
				
			||||||
  return this->send_message_<SwitchStateResponse>(msg, 26);
 | 
					  return this->send_message_<SwitchStateResponse>(msg, 26);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
@@ -141,58 +123,48 @@ bool APIServerConnectionBase::send_switch_state_response(const SwitchStateRespon
 | 
				
			|||||||
#ifdef USE_TEXT_SENSOR
 | 
					#ifdef USE_TEXT_SENSOR
 | 
				
			||||||
bool APIServerConnectionBase::send_list_entities_text_sensor_response(const ListEntitiesTextSensorResponse &msg) {
 | 
					bool APIServerConnectionBase::send_list_entities_text_sensor_response(const ListEntitiesTextSensorResponse &msg) {
 | 
				
			||||||
  ESP_LOGVV(TAG, "send_list_entities_text_sensor_response: %s", msg.dump().c_str());
 | 
					  ESP_LOGVV(TAG, "send_list_entities_text_sensor_response: %s", msg.dump().c_str());
 | 
				
			||||||
  this->set_nodelay(false);
 | 
					 | 
				
			||||||
  return this->send_message_<ListEntitiesTextSensorResponse>(msg, 18);
 | 
					  return this->send_message_<ListEntitiesTextSensorResponse>(msg, 18);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
#ifdef USE_TEXT_SENSOR
 | 
					#ifdef USE_TEXT_SENSOR
 | 
				
			||||||
bool APIServerConnectionBase::send_text_sensor_state_response(const TextSensorStateResponse &msg) {
 | 
					bool APIServerConnectionBase::send_text_sensor_state_response(const TextSensorStateResponse &msg) {
 | 
				
			||||||
  ESP_LOGVV(TAG, "send_text_sensor_state_response: %s", msg.dump().c_str());
 | 
					  ESP_LOGVV(TAG, "send_text_sensor_state_response: %s", msg.dump().c_str());
 | 
				
			||||||
  this->set_nodelay(true);
 | 
					 | 
				
			||||||
  return this->send_message_<TextSensorStateResponse>(msg, 27);
 | 
					  return this->send_message_<TextSensorStateResponse>(msg, 27);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
bool APIServerConnectionBase::send_subscribe_logs_response(const SubscribeLogsResponse &msg) {
 | 
					bool APIServerConnectionBase::send_subscribe_logs_response(const SubscribeLogsResponse &msg) {
 | 
				
			||||||
  this->set_nodelay(false);
 | 
					 | 
				
			||||||
  return this->send_message_<SubscribeLogsResponse>(msg, 29);
 | 
					  return this->send_message_<SubscribeLogsResponse>(msg, 29);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
bool APIServerConnectionBase::send_homeassistant_service_response(const HomeassistantServiceResponse &msg) {
 | 
					bool APIServerConnectionBase::send_homeassistant_service_response(const HomeassistantServiceResponse &msg) {
 | 
				
			||||||
  ESP_LOGVV(TAG, "send_homeassistant_service_response: %s", msg.dump().c_str());
 | 
					  ESP_LOGVV(TAG, "send_homeassistant_service_response: %s", msg.dump().c_str());
 | 
				
			||||||
  this->set_nodelay(true);
 | 
					 | 
				
			||||||
  return this->send_message_<HomeassistantServiceResponse>(msg, 35);
 | 
					  return this->send_message_<HomeassistantServiceResponse>(msg, 35);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
bool APIServerConnectionBase::send_subscribe_home_assistant_state_response(
 | 
					bool APIServerConnectionBase::send_subscribe_home_assistant_state_response(
 | 
				
			||||||
    const SubscribeHomeAssistantStateResponse &msg) {
 | 
					    const SubscribeHomeAssistantStateResponse &msg) {
 | 
				
			||||||
  ESP_LOGVV(TAG, "send_subscribe_home_assistant_state_response: %s", msg.dump().c_str());
 | 
					  ESP_LOGVV(TAG, "send_subscribe_home_assistant_state_response: %s", msg.dump().c_str());
 | 
				
			||||||
  this->set_nodelay(false);
 | 
					 | 
				
			||||||
  return this->send_message_<SubscribeHomeAssistantStateResponse>(msg, 39);
 | 
					  return this->send_message_<SubscribeHomeAssistantStateResponse>(msg, 39);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
bool APIServerConnectionBase::send_get_time_request(const GetTimeRequest &msg) {
 | 
					bool APIServerConnectionBase::send_get_time_request(const GetTimeRequest &msg) {
 | 
				
			||||||
  ESP_LOGVV(TAG, "send_get_time_request: %s", msg.dump().c_str());
 | 
					  ESP_LOGVV(TAG, "send_get_time_request: %s", msg.dump().c_str());
 | 
				
			||||||
  this->set_nodelay(false);
 | 
					 | 
				
			||||||
  return this->send_message_<GetTimeRequest>(msg, 36);
 | 
					  return this->send_message_<GetTimeRequest>(msg, 36);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
bool APIServerConnectionBase::send_get_time_response(const GetTimeResponse &msg) {
 | 
					bool APIServerConnectionBase::send_get_time_response(const GetTimeResponse &msg) {
 | 
				
			||||||
  ESP_LOGVV(TAG, "send_get_time_response: %s", msg.dump().c_str());
 | 
					  ESP_LOGVV(TAG, "send_get_time_response: %s", msg.dump().c_str());
 | 
				
			||||||
  this->set_nodelay(true);
 | 
					 | 
				
			||||||
  return this->send_message_<GetTimeResponse>(msg, 37);
 | 
					  return this->send_message_<GetTimeResponse>(msg, 37);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
bool APIServerConnectionBase::send_list_entities_services_response(const ListEntitiesServicesResponse &msg) {
 | 
					bool APIServerConnectionBase::send_list_entities_services_response(const ListEntitiesServicesResponse &msg) {
 | 
				
			||||||
  ESP_LOGVV(TAG, "send_list_entities_services_response: %s", msg.dump().c_str());
 | 
					  ESP_LOGVV(TAG, "send_list_entities_services_response: %s", msg.dump().c_str());
 | 
				
			||||||
  this->set_nodelay(false);
 | 
					 | 
				
			||||||
  return this->send_message_<ListEntitiesServicesResponse>(msg, 41);
 | 
					  return this->send_message_<ListEntitiesServicesResponse>(msg, 41);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#ifdef USE_ESP32_CAMERA
 | 
					#ifdef USE_ESP32_CAMERA
 | 
				
			||||||
bool APIServerConnectionBase::send_list_entities_camera_response(const ListEntitiesCameraResponse &msg) {
 | 
					bool APIServerConnectionBase::send_list_entities_camera_response(const ListEntitiesCameraResponse &msg) {
 | 
				
			||||||
  ESP_LOGVV(TAG, "send_list_entities_camera_response: %s", msg.dump().c_str());
 | 
					  ESP_LOGVV(TAG, "send_list_entities_camera_response: %s", msg.dump().c_str());
 | 
				
			||||||
  this->set_nodelay(false);
 | 
					 | 
				
			||||||
  return this->send_message_<ListEntitiesCameraResponse>(msg, 43);
 | 
					  return this->send_message_<ListEntitiesCameraResponse>(msg, 43);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
#ifdef USE_ESP32_CAMERA
 | 
					#ifdef USE_ESP32_CAMERA
 | 
				
			||||||
bool APIServerConnectionBase::send_camera_image_response(const CameraImageResponse &msg) {
 | 
					bool APIServerConnectionBase::send_camera_image_response(const CameraImageResponse &msg) {
 | 
				
			||||||
  ESP_LOGVV(TAG, "send_camera_image_response: %s", msg.dump().c_str());
 | 
					  ESP_LOGVV(TAG, "send_camera_image_response: %s", msg.dump().c_str());
 | 
				
			||||||
  this->set_nodelay(false);
 | 
					 | 
				
			||||||
  return this->send_message_<CameraImageResponse>(msg, 44);
 | 
					  return this->send_message_<CameraImageResponse>(msg, 44);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
@@ -201,14 +173,12 @@ bool APIServerConnectionBase::send_camera_image_response(const CameraImageRespon
 | 
				
			|||||||
#ifdef USE_CLIMATE
 | 
					#ifdef USE_CLIMATE
 | 
				
			||||||
bool APIServerConnectionBase::send_list_entities_climate_response(const ListEntitiesClimateResponse &msg) {
 | 
					bool APIServerConnectionBase::send_list_entities_climate_response(const ListEntitiesClimateResponse &msg) {
 | 
				
			||||||
  ESP_LOGVV(TAG, "send_list_entities_climate_response: %s", msg.dump().c_str());
 | 
					  ESP_LOGVV(TAG, "send_list_entities_climate_response: %s", msg.dump().c_str());
 | 
				
			||||||
  this->set_nodelay(false);
 | 
					 | 
				
			||||||
  return this->send_message_<ListEntitiesClimateResponse>(msg, 46);
 | 
					  return this->send_message_<ListEntitiesClimateResponse>(msg, 46);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
#ifdef USE_CLIMATE
 | 
					#ifdef USE_CLIMATE
 | 
				
			||||||
bool APIServerConnectionBase::send_climate_state_response(const ClimateStateResponse &msg) {
 | 
					bool APIServerConnectionBase::send_climate_state_response(const ClimateStateResponse &msg) {
 | 
				
			||||||
  ESP_LOGVV(TAG, "send_climate_state_response: %s", msg.dump().c_str());
 | 
					  ESP_LOGVV(TAG, "send_climate_state_response: %s", msg.dump().c_str());
 | 
				
			||||||
  this->set_nodelay(true);
 | 
					 | 
				
			||||||
  return this->send_message_<ClimateStateResponse>(msg, 47);
 | 
					  return this->send_message_<ClimateStateResponse>(msg, 47);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,5 @@
 | 
				
			|||||||
 | 
					// This file was automatically generated with a tool.
 | 
				
			||||||
 | 
					// See scripts/api_protobuf/api_protobuf.py
 | 
				
			||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "api_pb2.h"
 | 
					#include "api_pb2.h"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -208,9 +208,11 @@ void APIServer::send_homeassistant_service_call(const HomeassistantServiceRespon
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
APIServer::APIServer() { global_api_server = this; }
 | 
					APIServer::APIServer() { global_api_server = this; }
 | 
				
			||||||
void APIServer::subscribe_home_assistant_state(std::string entity_id, std::function<void(std::string)> f) {
 | 
					void APIServer::subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute,
 | 
				
			||||||
 | 
					                                               std::function<void(std::string)> f) {
 | 
				
			||||||
  this->state_subs_.push_back(HomeAssistantStateSubscription{
 | 
					  this->state_subs_.push_back(HomeAssistantStateSubscription{
 | 
				
			||||||
      .entity_id = std::move(entity_id),
 | 
					      .entity_id = std::move(entity_id),
 | 
				
			||||||
 | 
					      .attribute = std::move(attribute),
 | 
				
			||||||
      .callback = std::move(f),
 | 
					      .callback = std::move(f),
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -71,10 +71,12 @@ class APIServer : public Component, public Controller {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  struct HomeAssistantStateSubscription {
 | 
					  struct HomeAssistantStateSubscription {
 | 
				
			||||||
    std::string entity_id;
 | 
					    std::string entity_id;
 | 
				
			||||||
 | 
					    optional<std::string> attribute;
 | 
				
			||||||
    std::function<void(std::string)> callback;
 | 
					    std::function<void(std::string)> callback;
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void subscribe_home_assistant_state(std::string entity_id, std::function<void(std::string)> f);
 | 
					  void subscribe_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_; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -76,13 +76,13 @@ class CustomAPIDevice {
 | 
				
			|||||||
    global_api_server->register_user_service(service);
 | 
					    global_api_server->register_user_service(service);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Subscribe to the 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, "climate.kitchen", "current_temperature");
 | 
				
			||||||
   * }
 | 
					   * }
 | 
				
			||||||
   *
 | 
					   *
 | 
				
			||||||
   * void on_state_changed(std::string state) {
 | 
					   * void on_state_changed(std::string state) {
 | 
				
			||||||
@@ -93,17 +93,19 @@ class CustomAPIDevice {
 | 
				
			|||||||
   * @tparam T The class type creating the service, automatically deduced from the function pointer.
 | 
					   * @tparam T The class type creating the service, automatically deduced from the function pointer.
 | 
				
			||||||
   * @param callback The member function to call when the entity state changes.
 | 
					   * @param callback The member function to call when the entity state changes.
 | 
				
			||||||
   * @param entity_id The entity_id to track.
 | 
					   * @param entity_id The entity_id to track.
 | 
				
			||||||
 | 
					   * @param attribute The entity state attribute to track.
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  template<typename T>
 | 
					  template<typename T>
 | 
				
			||||||
  void subscribe_homeassistant_state(void (T::*callback)(std::string), const std::string &entity_id) {
 | 
					  void subscribe_homeassistant_state(void (T::*callback)(std::string), const std::string &entity_id,
 | 
				
			||||||
 | 
					                                     const std::string &attribute = "") {
 | 
				
			||||||
    auto f = std::bind(callback, (T *) this, std::placeholders::_1);
 | 
					    auto f = std::bind(callback, (T *) this, std::placeholders::_1);
 | 
				
			||||||
    global_api_server->subscribe_home_assistant_state(entity_id, f);
 | 
					    global_api_server->subscribe_home_assistant_state(entity_id, optional<std::string>(attribute), f);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Subscribe to the 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");
 | 
				
			||||||
@@ -117,11 +119,13 @@ class CustomAPIDevice {
 | 
				
			|||||||
   * @tparam T The class type creating the service, automatically deduced from the function pointer.
 | 
					   * @tparam T The class type creating the service, automatically deduced from the function pointer.
 | 
				
			||||||
   * @param callback The member function to call when the entity state changes.
 | 
					   * @param callback The member function to call when the entity state changes.
 | 
				
			||||||
   * @param entity_id The entity_id to track.
 | 
					   * @param entity_id The entity_id to track.
 | 
				
			||||||
 | 
					   * @param attribute The entity state attribute to track.
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  template<typename T>
 | 
					  template<typename T>
 | 
				
			||||||
  void subscribe_homeassistant_state(void (T::*callback)(std::string, std::string), const std::string &entity_id) {
 | 
					  void subscribe_homeassistant_state(void (T::*callback)(std::string, std::string), const std::string &entity_id,
 | 
				
			||||||
 | 
					                                     const std::string &attribute = "") {
 | 
				
			||||||
    auto f = std::bind(callback, (T *) this, entity_id, std::placeholders::_1);
 | 
					    auto f = std::bind(callback, (T *) this, entity_id, std::placeholders::_1);
 | 
				
			||||||
    global_api_server->subscribe_home_assistant_state(entity_id, f);
 | 
					    global_api_server->subscribe_home_assistant_state(entity_id, optional<std::string>(attribute), f);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Call a Home Assistant service from ESPHome.
 | 
					  /** Call a Home Assistant service from ESPHome.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -29,6 +29,7 @@ template<typename... Ts> class HomeAssistantServiceCallAction : public Action<Ts
 | 
				
			|||||||
  template<typename T> void add_variable(std::string key, T value) {
 | 
					  template<typename T> void add_variable(std::string key, T value) {
 | 
				
			||||||
    this->variables_.push_back(TemplatableKeyValuePair<Ts...>(key, value));
 | 
					    this->variables_.push_back(TemplatableKeyValuePair<Ts...>(key, value));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void play(Ts... x) override {
 | 
					  void play(Ts... x) override {
 | 
				
			||||||
    HomeassistantServiceResponse resp;
 | 
					    HomeassistantServiceResponse resp;
 | 
				
			||||||
    resp.service = this->service_.value(x...);
 | 
					    resp.service = this->service_.value(x...);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -62,8 +62,7 @@ void ProtoMessage::decode(const uint8_t *buffer, size_t length) {
 | 
				
			|||||||
          error = true;
 | 
					          error = true;
 | 
				
			||||||
          break;
 | 
					          break;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        uint32_t val = (uint32_t(buffer[i]) << 0) | (uint32_t(buffer[i + 1]) << 8) | (uint32_t(buffer[i + 2]) << 16) |
 | 
					        uint32_t val = encode_uint32(buffer[i + 3], buffer[i + 2], buffer[i + 1], buffer[i]);
 | 
				
			||||||
                       (uint32_t(buffer[i + 3]) << 24);
 | 
					 | 
				
			||||||
        if (!this->decode_32bit(field_id, Proto32Bit(val))) {
 | 
					        if (!this->decode_32bit(field_id, Proto32Bit(val))) {
 | 
				
			||||||
          ESP_LOGV(TAG, "Cannot decode 32-bit field %u with value %u!", field_id, val);
 | 
					          ESP_LOGV(TAG, "Cannot decode 32-bit field %u with value %u!", field_id, val);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -266,7 +266,6 @@ class ProtoService {
 | 
				
			|||||||
  virtual ProtoWriteBuffer create_buffer() = 0;
 | 
					  virtual ProtoWriteBuffer create_buffer() = 0;
 | 
				
			||||||
  virtual bool send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) = 0;
 | 
					  virtual bool send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) = 0;
 | 
				
			||||||
  virtual bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) = 0;
 | 
					  virtual bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) = 0;
 | 
				
			||||||
  virtual void set_nodelay(bool nodelay) = 0;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  template<class C> bool send_message_(const C &msg, uint32_t message_type) {
 | 
					  template<class C> bool send_message_(const C &msg, uint32_t message_type) {
 | 
				
			||||||
    auto buffer = this->create_buffer();
 | 
					    auto buffer = this->create_buffer();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,40 +1,48 @@
 | 
				
			|||||||
import esphome.codegen as cg
 | 
					import esphome.codegen as cg
 | 
				
			||||||
import esphome.config_validation as cv
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
from esphome import pins
 | 
					from esphome import pins
 | 
				
			||||||
from esphome.const import CONF_INDOOR, CONF_WATCHDOG_THRESHOLD, \
 | 
					from esphome.const import (
 | 
				
			||||||
    CONF_NOISE_LEVEL, CONF_SPIKE_REJECTION, CONF_LIGHTNING_THRESHOLD, \
 | 
					    CONF_INDOOR,
 | 
				
			||||||
    CONF_MASK_DISTURBER, CONF_DIV_RATIO, CONF_CAPACITANCE
 | 
					    CONF_WATCHDOG_THRESHOLD,
 | 
				
			||||||
from esphome.core import coroutine
 | 
					    CONF_NOISE_LEVEL,
 | 
				
			||||||
 | 
					    CONF_SPIKE_REJECTION,
 | 
				
			||||||
 | 
					    CONF_LIGHTNING_THRESHOLD,
 | 
				
			||||||
 | 
					    CONF_MASK_DISTURBER,
 | 
				
			||||||
 | 
					    CONF_DIV_RATIO,
 | 
				
			||||||
 | 
					    CONF_CAPACITANCE,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
AUTO_LOAD = ['sensor', 'binary_sensor']
 | 
					AUTO_LOAD = ["sensor", "binary_sensor"]
 | 
				
			||||||
MULTI_CONF = True
 | 
					MULTI_CONF = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CONF_AS3935_ID = 'as3935_id'
 | 
					CONF_AS3935_ID = "as3935_id"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
as3935_ns = cg.esphome_ns.namespace('as3935')
 | 
					as3935_ns = cg.esphome_ns.namespace("as3935")
 | 
				
			||||||
AS3935 = as3935_ns.class_('AS3935Component', cg.Component)
 | 
					AS3935 = as3935_ns.class_("AS3935Component", cg.Component)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CONF_IRQ_PIN = 'irq_pin'
 | 
					CONF_IRQ_PIN = "irq_pin"
 | 
				
			||||||
AS3935_SCHEMA = cv.Schema({
 | 
					AS3935_SCHEMA = cv.Schema(
 | 
				
			||||||
    cv.GenerateID(): cv.declare_id(AS3935),
 | 
					    {
 | 
				
			||||||
    cv.Required(CONF_IRQ_PIN): pins.gpio_input_pin_schema,
 | 
					        cv.GenerateID(): cv.declare_id(AS3935),
 | 
				
			||||||
 | 
					        cv.Required(CONF_IRQ_PIN): pins.gpio_input_pin_schema,
 | 
				
			||||||
    cv.Optional(CONF_INDOOR, default=True): cv.boolean,
 | 
					        cv.Optional(CONF_INDOOR, default=True): cv.boolean,
 | 
				
			||||||
    cv.Optional(CONF_NOISE_LEVEL, default=2): cv.int_range(min=1, max=7),
 | 
					        cv.Optional(CONF_NOISE_LEVEL, default=2): cv.int_range(min=1, max=7),
 | 
				
			||||||
    cv.Optional(CONF_WATCHDOG_THRESHOLD, default=2): cv.int_range(min=1, max=10),
 | 
					        cv.Optional(CONF_WATCHDOG_THRESHOLD, default=2): cv.int_range(min=1, max=10),
 | 
				
			||||||
    cv.Optional(CONF_SPIKE_REJECTION, default=2): cv.int_range(min=1, max=11),
 | 
					        cv.Optional(CONF_SPIKE_REJECTION, default=2): cv.int_range(min=1, max=11),
 | 
				
			||||||
    cv.Optional(CONF_LIGHTNING_THRESHOLD, default=1): cv.one_of(1, 5, 9, 16, int=True),
 | 
					        cv.Optional(CONF_LIGHTNING_THRESHOLD, default=1): cv.one_of(
 | 
				
			||||||
    cv.Optional(CONF_MASK_DISTURBER, default=False): cv.boolean,
 | 
					            1, 5, 9, 16, int=True
 | 
				
			||||||
    cv.Optional(CONF_DIV_RATIO, default=0): cv.one_of(0, 16, 22, 64, 128, int=True),
 | 
					        ),
 | 
				
			||||||
    cv.Optional(CONF_CAPACITANCE, default=0): cv.int_range(min=0, max=15),
 | 
					        cv.Optional(CONF_MASK_DISTURBER, default=False): cv.boolean,
 | 
				
			||||||
})
 | 
					        cv.Optional(CONF_DIV_RATIO, default=0): cv.one_of(0, 16, 32, 64, 128, int=True),
 | 
				
			||||||
 | 
					        cv.Optional(CONF_CAPACITANCE, default=0): cv.int_range(min=0, max=15),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@coroutine
 | 
					async def setup_as3935(var, config):
 | 
				
			||||||
def setup_as3935(var, config):
 | 
					    await cg.register_component(var, config)
 | 
				
			||||||
    yield cg.register_component(var, config)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    irq_pin = yield cg.gpio_pin_expression(config[CONF_IRQ_PIN])
 | 
					    irq_pin = await cg.gpio_pin_expression(config[CONF_IRQ_PIN])
 | 
				
			||||||
    cg.add(var.set_irq_pin(irq_pin))
 | 
					    cg.add(var.set_irq_pin(irq_pin))
 | 
				
			||||||
    cg.add(var.set_indoor(config[CONF_INDOOR]))
 | 
					    cg.add(var.set_indoor(config[CONF_INDOOR]))
 | 
				
			||||||
    cg.add(var.set_noise_level(config[CONF_NOISE_LEVEL]))
 | 
					    cg.add(var.set_noise_level(config[CONF_NOISE_LEVEL]))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -26,6 +26,9 @@ void AS3935Component::setup() {
 | 
				
			|||||||
void AS3935Component::dump_config() {
 | 
					void AS3935Component::dump_config() {
 | 
				
			||||||
  ESP_LOGCONFIG(TAG, "AS3935:");
 | 
					  ESP_LOGCONFIG(TAG, "AS3935:");
 | 
				
			||||||
  LOG_PIN("  Interrupt Pin: ", this->irq_pin_);
 | 
					  LOG_PIN("  Interrupt Pin: ", this->irq_pin_);
 | 
				
			||||||
 | 
					  LOG_BINARY_SENSOR("  ", "Thunder alert", this->thunder_alert_binary_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Distance", this->distance_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Lightning energy", this->energy_sensor_);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
float AS3935Component::get_setup_priority() const { return setup_priority::DATA; }
 | 
					float AS3935Component::get_setup_priority() const { return setup_priority::DATA; }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,14 +3,16 @@ import esphome.config_validation as cv
 | 
				
			|||||||
from esphome.components import binary_sensor
 | 
					from esphome.components import binary_sensor
 | 
				
			||||||
from . import AS3935, CONF_AS3935_ID
 | 
					from . import AS3935, CONF_AS3935_ID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
DEPENDENCIES = ['as3935']
 | 
					DEPENDENCIES = ["as3935"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({
 | 
					CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend(
 | 
				
			||||||
    cv.GenerateID(CONF_AS3935_ID): cv.use_id(AS3935),
 | 
					    {
 | 
				
			||||||
})
 | 
					        cv.GenerateID(CONF_AS3935_ID): cv.use_id(AS3935),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def to_code(config):
 | 
					async def to_code(config):
 | 
				
			||||||
    hub = yield cg.get_variable(config[CONF_AS3935_ID])
 | 
					    hub = await cg.get_variable(config[CONF_AS3935_ID])
 | 
				
			||||||
    var = yield binary_sensor.new_binary_sensor(config)
 | 
					    var = await binary_sensor.new_binary_sensor(config)
 | 
				
			||||||
    cg.add(hub.set_thunder_alert_binary_sensor(var))
 | 
					    cg.add(hub.set_thunder_alert_binary_sensor(var))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,30 +1,46 @@
 | 
				
			|||||||
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 sensor
 | 
					from esphome.components import sensor
 | 
				
			||||||
from esphome.const import CONF_DISTANCE, CONF_LIGHTNING_ENERGY, \
 | 
					from esphome.const import (
 | 
				
			||||||
    UNIT_KILOMETER, UNIT_EMPTY, ICON_SIGNAL_DISTANCE_VARIANT, ICON_FLASH
 | 
					    CONF_DISTANCE,
 | 
				
			||||||
 | 
					    CONF_LIGHTNING_ENERGY,
 | 
				
			||||||
 | 
					    DEVICE_CLASS_EMPTY,
 | 
				
			||||||
 | 
					    STATE_CLASS_NONE,
 | 
				
			||||||
 | 
					    UNIT_KILOMETER,
 | 
				
			||||||
 | 
					    UNIT_EMPTY,
 | 
				
			||||||
 | 
					    ICON_SIGNAL_DISTANCE_VARIANT,
 | 
				
			||||||
 | 
					    ICON_FLASH,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
from . import AS3935, CONF_AS3935_ID
 | 
					from . import AS3935, CONF_AS3935_ID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
DEPENDENCIES = ['as3935']
 | 
					DEPENDENCIES = ["as3935"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CONFIG_SCHEMA = cv.Schema({
 | 
					CONFIG_SCHEMA = cv.Schema(
 | 
				
			||||||
    cv.GenerateID(CONF_AS3935_ID): cv.use_id(AS3935),
 | 
					    {
 | 
				
			||||||
    cv.Optional(CONF_DISTANCE):
 | 
					        cv.GenerateID(CONF_AS3935_ID): cv.use_id(AS3935),
 | 
				
			||||||
        sensor.sensor_schema(UNIT_KILOMETER, ICON_SIGNAL_DISTANCE_VARIANT, 1),
 | 
					        cv.Optional(CONF_DISTANCE): sensor.sensor_schema(
 | 
				
			||||||
    cv.Optional(CONF_LIGHTNING_ENERGY):
 | 
					            UNIT_KILOMETER,
 | 
				
			||||||
        sensor.sensor_schema(UNIT_EMPTY, ICON_FLASH, 1),
 | 
					            ICON_SIGNAL_DISTANCE_VARIANT,
 | 
				
			||||||
}).extend(cv.COMPONENT_SCHEMA)
 | 
					            1,
 | 
				
			||||||
 | 
					            DEVICE_CLASS_EMPTY,
 | 
				
			||||||
 | 
					            STATE_CLASS_NONE,
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        cv.Optional(CONF_LIGHTNING_ENERGY): sensor.sensor_schema(
 | 
				
			||||||
 | 
					            UNIT_EMPTY, ICON_FLASH, 1, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					).extend(cv.COMPONENT_SCHEMA)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def to_code(config):
 | 
					async def to_code(config):
 | 
				
			||||||
    hub = yield cg.get_variable(config[CONF_AS3935_ID])
 | 
					    hub = await cg.get_variable(config[CONF_AS3935_ID])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if CONF_DISTANCE in config:
 | 
					    if CONF_DISTANCE in config:
 | 
				
			||||||
        conf = config[CONF_DISTANCE]
 | 
					        conf = config[CONF_DISTANCE]
 | 
				
			||||||
        distance_sensor = yield sensor.new_sensor(conf)
 | 
					        distance_sensor = await sensor.new_sensor(conf)
 | 
				
			||||||
        cg.add(hub.set_distance_sensor(distance_sensor))
 | 
					        cg.add(hub.set_distance_sensor(distance_sensor))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if CONF_LIGHTNING_ENERGY in config:
 | 
					    if CONF_LIGHTNING_ENERGY in config:
 | 
				
			||||||
        conf = config[CONF_LIGHTNING_ENERGY]
 | 
					        conf = config[CONF_LIGHTNING_ENERGY]
 | 
				
			||||||
        lightning_energy_sensor = yield sensor.new_sensor(conf)
 | 
					        lightning_energy_sensor = await sensor.new_sensor(conf)
 | 
				
			||||||
        cg.add(hub.set_distance_sensor(lightning_energy_sensor))
 | 
					        cg.add(hub.set_energy_sensor(lightning_energy_sensor))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,18 +3,24 @@ import esphome.config_validation as cv
 | 
				
			|||||||
from esphome.components import as3935, i2c
 | 
					from esphome.components import as3935, i2c
 | 
				
			||||||
from esphome.const import CONF_ID
 | 
					from esphome.const import CONF_ID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
AUTO_LOAD = ['as3935']
 | 
					AUTO_LOAD = ["as3935"]
 | 
				
			||||||
DEPENDENCIES = ['i2c']
 | 
					DEPENDENCIES = ["i2c"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
as3935_i2c_ns = cg.esphome_ns.namespace('as3935_i2c')
 | 
					as3935_i2c_ns = cg.esphome_ns.namespace("as3935_i2c")
 | 
				
			||||||
I2CAS3935 = as3935_i2c_ns.class_('I2CAS3935Component', as3935.AS3935, i2c.I2CDevice)
 | 
					I2CAS3935 = as3935_i2c_ns.class_("I2CAS3935Component", as3935.AS3935, i2c.I2CDevice)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CONFIG_SCHEMA = cv.All(as3935.AS3935_SCHEMA.extend({
 | 
					CONFIG_SCHEMA = cv.All(
 | 
				
			||||||
    cv.GenerateID(): cv.declare_id(I2CAS3935),
 | 
					    as3935.AS3935_SCHEMA.extend(
 | 
				
			||||||
}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x03)))
 | 
					        {
 | 
				
			||||||
 | 
					            cv.GenerateID(): cv.declare_id(I2CAS3935),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    .extend(cv.COMPONENT_SCHEMA)
 | 
				
			||||||
 | 
					    .extend(i2c.i2c_device_schema(0x03))
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def to_code(config):
 | 
					async def to_code(config):
 | 
				
			||||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
					    var = cg.new_Pvariable(config[CONF_ID])
 | 
				
			||||||
    yield as3935.setup_as3935(var, config)
 | 
					    await as3935.setup_as3935(var, config)
 | 
				
			||||||
    yield i2c.register_i2c_device(var, config)
 | 
					    await i2c.register_i2c_device(var, config)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,18 +3,24 @@ import esphome.config_validation as cv
 | 
				
			|||||||
from esphome.components import as3935, spi
 | 
					from esphome.components import as3935, spi
 | 
				
			||||||
from esphome.const import CONF_ID
 | 
					from esphome.const import CONF_ID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
AUTO_LOAD = ['as3935']
 | 
					AUTO_LOAD = ["as3935"]
 | 
				
			||||||
DEPENDENCIES = ['spi']
 | 
					DEPENDENCIES = ["spi"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
as3935_spi_ns = cg.esphome_ns.namespace('as3935_spi')
 | 
					as3935_spi_ns = cg.esphome_ns.namespace("as3935_spi")
 | 
				
			||||||
SPIAS3935 = as3935_spi_ns.class_('SPIAS3935Component', as3935.AS3935, spi.SPIDevice)
 | 
					SPIAS3935 = as3935_spi_ns.class_("SPIAS3935Component", as3935.AS3935, spi.SPIDevice)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CONFIG_SCHEMA = cv.All(as3935.AS3935_SCHEMA.extend({
 | 
					CONFIG_SCHEMA = cv.All(
 | 
				
			||||||
    cv.GenerateID(): cv.declare_id(SPIAS3935)
 | 
					    as3935.AS3935_SCHEMA.extend(
 | 
				
			||||||
}).extend(cv.COMPONENT_SCHEMA).extend(spi.SPI_DEVICE_SCHEMA))
 | 
					        {
 | 
				
			||||||
 | 
					            cv.GenerateID(): cv.declare_id(SPIAS3935),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    .extend(cv.COMPONENT_SCHEMA)
 | 
				
			||||||
 | 
					    .extend(spi.spi_device_schema(cs_pin_required=True))
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def to_code(config):
 | 
					async def to_code(config):
 | 
				
			||||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
					    var = cg.new_Pvariable(config[CONF_ID])
 | 
				
			||||||
    yield as3935.setup_as3935(var, config)
 | 
					    await as3935.setup_as3935(var, config)
 | 
				
			||||||
    yield spi.register_spi_device(var, config)
 | 
					    await spi.register_spi_device(var, config)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,7 +33,7 @@ void SPIAS3935Component::write_register(uint8_t reg, uint8_t mask, uint8_t bits,
 | 
				
			|||||||
uint8_t SPIAS3935Component::read_register(uint8_t reg) {
 | 
					uint8_t SPIAS3935Component::read_register(uint8_t reg) {
 | 
				
			||||||
  uint8_t value = 0;
 | 
					  uint8_t value = 0;
 | 
				
			||||||
  this->enable();
 | 
					  this->enable();
 | 
				
			||||||
  this->write_byte(reg |= SPI_READ_M);
 | 
					  this->write_byte(reg | SPI_READ_M);
 | 
				
			||||||
  value = this->read_byte();
 | 
					  value = this->read_byte();
 | 
				
			||||||
  // According to datsheet, the chip select must be written HIGH, LOW, HIGH
 | 
					  // According to datsheet, the chip select must be written HIGH, LOW, HIGH
 | 
				
			||||||
  // to correctly end the READ command.
 | 
					  // to correctly end the READ command.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,12 +2,14 @@
 | 
				
			|||||||
import esphome.codegen as cg
 | 
					import esphome.codegen as cg
 | 
				
			||||||
from esphome.core import CORE, coroutine_with_priority
 | 
					from esphome.core import CORE, coroutine_with_priority
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CODEOWNERS = ["@OttoWinter"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@coroutine_with_priority(200.0)
 | 
					@coroutine_with_priority(200.0)
 | 
				
			||||||
def to_code(config):
 | 
					async def to_code(config):
 | 
				
			||||||
    if CORE.is_esp32:
 | 
					    if CORE.is_esp32:
 | 
				
			||||||
        # https://github.com/OttoWinter/AsyncTCP/blob/master/library.json
 | 
					        # https://github.com/esphome/AsyncTCP/blob/master/library.json
 | 
				
			||||||
        cg.add_library('AsyncTCP-esphome', '1.1.1')
 | 
					        cg.add_library("esphome/AsyncTCP-esphome", "1.2.2")
 | 
				
			||||||
    elif CORE.is_esp8266:
 | 
					    elif CORE.is_esp8266:
 | 
				
			||||||
        # https://github.com/OttoWinter/ESPAsyncTCP
 | 
					        # https://github.com/OttoWinter/ESPAsyncTCP
 | 
				
			||||||
        cg.add_library('ESPAsyncTCP-esphome', '1.2.2')
 | 
					        cg.add_library("ESPAsyncTCP-esphome", "1.2.3")
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										0
									
								
								esphome/components/atc_mithermometer/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								esphome/components/atc_mithermometer/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										137
									
								
								esphome/components/atc_mithermometer/atc_mithermometer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								esphome/components/atc_mithermometer/atc_mithermometer.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,137 @@
 | 
				
			|||||||
 | 
					#include "atc_mithermometer.h"
 | 
				
			||||||
 | 
					#include "esphome/core/log.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef ARDUINO_ARCH_ESP32
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace esphome {
 | 
				
			||||||
 | 
					namespace atc_mithermometer {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char *TAG = "atc_mithermometer";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ATCMiThermometer::dump_config() {
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "ATC MiThermometer");
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Temperature", this->temperature_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Humidity", this->humidity_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Battery Level", this->battery_level_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Battery Voltage", this->battery_voltage_);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool ATCMiThermometer::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
 | 
				
			||||||
 | 
					  if (device.address_uint64() != this->address_) {
 | 
				
			||||||
 | 
					    ESP_LOGVV(TAG, "parse_device(): unknown MAC address.");
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  ESP_LOGVV(TAG, "parse_device(): MAC address %s found.", device.address_str().c_str());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  bool success = false;
 | 
				
			||||||
 | 
					  for (auto &service_data : device.get_service_datas()) {
 | 
				
			||||||
 | 
					    auto res = parse_header(service_data);
 | 
				
			||||||
 | 
					    if (res->is_duplicate) {
 | 
				
			||||||
 | 
					      continue;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (!(parse_message(service_data.data, *res))) {
 | 
				
			||||||
 | 
					      continue;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (!(report_results(res, device.address_str()))) {
 | 
				
			||||||
 | 
					      continue;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (res->temperature.has_value() && this->temperature_ != nullptr)
 | 
				
			||||||
 | 
					      this->temperature_->publish_state(*res->temperature);
 | 
				
			||||||
 | 
					    if (res->humidity.has_value() && this->humidity_ != nullptr)
 | 
				
			||||||
 | 
					      this->humidity_->publish_state(*res->humidity);
 | 
				
			||||||
 | 
					    if (res->battery_level.has_value() && this->battery_level_ != nullptr)
 | 
				
			||||||
 | 
					      this->battery_level_->publish_state(*res->battery_level);
 | 
				
			||||||
 | 
					    if (res->battery_voltage.has_value() && this->battery_voltage_ != nullptr)
 | 
				
			||||||
 | 
					      this->battery_voltage_->publish_state(*res->battery_voltage);
 | 
				
			||||||
 | 
					    success = true;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!success) {
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					optional<ParseResult> ATCMiThermometer::parse_header(const esp32_ble_tracker::ServiceData &service_data) {
 | 
				
			||||||
 | 
					  ParseResult result;
 | 
				
			||||||
 | 
					  if (!service_data.uuid.contains(0x1A, 0x18)) {
 | 
				
			||||||
 | 
					    ESP_LOGVV(TAG, "parse_header(): no service data UUID magic bytes.");
 | 
				
			||||||
 | 
					    return {};
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  auto raw = service_data.data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static uint8_t last_frame_count = 0;
 | 
				
			||||||
 | 
					  if (last_frame_count == raw[12]) {
 | 
				
			||||||
 | 
					    ESP_LOGVV(TAG, "parse_header(): duplicate data packet received (%d).", static_cast<int>(last_frame_count));
 | 
				
			||||||
 | 
					    result.is_duplicate = true;
 | 
				
			||||||
 | 
					    return {};
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  last_frame_count = raw[12];
 | 
				
			||||||
 | 
					  result.is_duplicate = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool ATCMiThermometer::parse_message(const std::vector<uint8_t> &message, ParseResult &result) {
 | 
				
			||||||
 | 
					  // Byte 0-5 mac in correct order
 | 
				
			||||||
 | 
					  // Byte 6-7 Temperature in uint16
 | 
				
			||||||
 | 
					  // Byte 8 Humidity in percent
 | 
				
			||||||
 | 
					  // Byte 9 Battery in percent
 | 
				
			||||||
 | 
					  // Byte 10-11 Battery in mV uint16_t
 | 
				
			||||||
 | 
					  // Byte 12 frame packet counter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const uint8_t *data = message.data();
 | 
				
			||||||
 | 
					  const int data_length = 13;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (message.size() != data_length) {
 | 
				
			||||||
 | 
					    ESP_LOGVV(TAG, "parse_message(): payload has wrong size (%d)!", message.size());
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // temperature, 2 bytes, 16-bit signed integer (LE), 0.1 °C
 | 
				
			||||||
 | 
					  const int16_t temperature = uint16_t(data[7]) | (uint16_t(data[6]) << 8);
 | 
				
			||||||
 | 
					  result.temperature = temperature / 10.0f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // humidity, 1 byte, 8-bit unsigned integer, 1.0 %
 | 
				
			||||||
 | 
					  result.humidity = data[8];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // battery, 1 byte, 8-bit unsigned integer,  1.0 %
 | 
				
			||||||
 | 
					  result.battery_level = data[9];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // battery, 2 bytes, 16-bit unsigned integer,  0.001 V
 | 
				
			||||||
 | 
					  const int16_t battery_voltage = uint16_t(data[11]) | (uint16_t(data[10]) << 8);
 | 
				
			||||||
 | 
					  result.battery_voltage = battery_voltage / 1.0e3f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool ATCMiThermometer::report_results(const optional<ParseResult> &result, const std::string &address) {
 | 
				
			||||||
 | 
					  if (!result.has_value()) {
 | 
				
			||||||
 | 
					    ESP_LOGVV(TAG, "report_results(): no results available.");
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ESP_LOGD(TAG, "Got ATC MiThermometer (%s):", address.c_str());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (result->temperature.has_value()) {
 | 
				
			||||||
 | 
					    ESP_LOGD(TAG, "  Temperature: %.1f °C", *result->temperature);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (result->humidity.has_value()) {
 | 
				
			||||||
 | 
					    ESP_LOGD(TAG, "  Humidity: %.0f %%", *result->humidity);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (result->battery_level.has_value()) {
 | 
				
			||||||
 | 
					    ESP_LOGD(TAG, "  Battery Level: %.0f %%", *result->battery_level);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (result->battery_voltage.has_value()) {
 | 
				
			||||||
 | 
					    ESP_LOGD(TAG, "  Battery Voltage: %.3f V", *result->battery_voltage);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace atc_mithermometer
 | 
				
			||||||
 | 
					}  // namespace esphome
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										48
									
								
								esphome/components/atc_mithermometer/atc_mithermometer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								esphome/components/atc_mithermometer/atc_mithermometer.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,48 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "esphome/core/component.h"
 | 
				
			||||||
 | 
					#include "esphome/components/sensor/sensor.h"
 | 
				
			||||||
 | 
					#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef ARDUINO_ARCH_ESP32
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace esphome {
 | 
				
			||||||
 | 
					namespace atc_mithermometer {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct ParseResult {
 | 
				
			||||||
 | 
					  optional<float> temperature;
 | 
				
			||||||
 | 
					  optional<float> humidity;
 | 
				
			||||||
 | 
					  optional<float> battery_level;
 | 
				
			||||||
 | 
					  optional<float> battery_voltage;
 | 
				
			||||||
 | 
					  bool is_duplicate;
 | 
				
			||||||
 | 
					  int raw_offset;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ATCMiThermometer : public Component, public esp32_ble_tracker::ESPBTDeviceListener {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  void set_address(uint64_t address) { address_ = address; };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
 | 
				
			||||||
 | 
					  void dump_config() override;
 | 
				
			||||||
 | 
					  float get_setup_priority() const override { return setup_priority::DATA; }
 | 
				
			||||||
 | 
					  void set_temperature(sensor::Sensor *temperature) { temperature_ = temperature; }
 | 
				
			||||||
 | 
					  void set_humidity(sensor::Sensor *humidity) { humidity_ = humidity; }
 | 
				
			||||||
 | 
					  void set_battery_level(sensor::Sensor *battery_level) { battery_level_ = battery_level; }
 | 
				
			||||||
 | 
					  void set_battery_voltage(sensor::Sensor *battery_voltage) { battery_voltage_ = battery_voltage; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 protected:
 | 
				
			||||||
 | 
					  uint64_t address_;
 | 
				
			||||||
 | 
					  sensor::Sensor *temperature_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *humidity_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *battery_level_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *battery_voltage_{nullptr};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  optional<ParseResult> parse_header(const esp32_ble_tracker::ServiceData &service_data);
 | 
				
			||||||
 | 
					  bool parse_message(const std::vector<uint8_t> &message, ParseResult &result);
 | 
				
			||||||
 | 
					  bool report_results(const optional<ParseResult> &result, const std::string &address);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace atc_mithermometer
 | 
				
			||||||
 | 
					}  // namespace esphome
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										85
									
								
								esphome/components/atc_mithermometer/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								esphome/components/atc_mithermometer/sensor.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,85 @@
 | 
				
			|||||||
 | 
					import esphome.codegen as cg
 | 
				
			||||||
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
 | 
					from esphome.components import sensor, esp32_ble_tracker
 | 
				
			||||||
 | 
					from esphome.const import (
 | 
				
			||||||
 | 
					    CONF_BATTERY_LEVEL,
 | 
				
			||||||
 | 
					    CONF_BATTERY_VOLTAGE,
 | 
				
			||||||
 | 
					    CONF_MAC_ADDRESS,
 | 
				
			||||||
 | 
					    CONF_HUMIDITY,
 | 
				
			||||||
 | 
					    CONF_TEMPERATURE,
 | 
				
			||||||
 | 
					    CONF_ID,
 | 
				
			||||||
 | 
					    DEVICE_CLASS_BATTERY,
 | 
				
			||||||
 | 
					    DEVICE_CLASS_HUMIDITY,
 | 
				
			||||||
 | 
					    DEVICE_CLASS_TEMPERATURE,
 | 
				
			||||||
 | 
					    DEVICE_CLASS_VOLTAGE,
 | 
				
			||||||
 | 
					    ICON_EMPTY,
 | 
				
			||||||
 | 
					    STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					    UNIT_CELSIUS,
 | 
				
			||||||
 | 
					    UNIT_PERCENT,
 | 
				
			||||||
 | 
					    UNIT_VOLT,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CODEOWNERS = ["@ahpohl"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEPENDENCIES = ["esp32_ble_tracker"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					atc_mithermometer_ns = cg.esphome_ns.namespace("atc_mithermometer")
 | 
				
			||||||
 | 
					ATCMiThermometer = atc_mithermometer_ns.class_(
 | 
				
			||||||
 | 
					    "ATCMiThermometer", esp32_ble_tracker.ESPBTDeviceListener, cg.Component
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CONFIG_SCHEMA = (
 | 
				
			||||||
 | 
					    cv.Schema(
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            cv.GenerateID(): cv.declare_id(ATCMiThermometer),
 | 
				
			||||||
 | 
					            cv.Required(CONF_MAC_ADDRESS): cv.mac_address,
 | 
				
			||||||
 | 
					            cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                UNIT_CELSIUS,
 | 
				
			||||||
 | 
					                ICON_EMPTY,
 | 
				
			||||||
 | 
					                1,
 | 
				
			||||||
 | 
					                DEVICE_CLASS_TEMPERATURE,
 | 
				
			||||||
 | 
					                STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                UNIT_PERCENT,
 | 
				
			||||||
 | 
					                ICON_EMPTY,
 | 
				
			||||||
 | 
					                0,
 | 
				
			||||||
 | 
					                DEVICE_CLASS_HUMIDITY,
 | 
				
			||||||
 | 
					                STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                UNIT_PERCENT,
 | 
				
			||||||
 | 
					                ICON_EMPTY,
 | 
				
			||||||
 | 
					                0,
 | 
				
			||||||
 | 
					                DEVICE_CLASS_BATTERY,
 | 
				
			||||||
 | 
					                STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_BATTERY_VOLTAGE): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                UNIT_VOLT, ICON_EMPTY, 3, DEVICE_CLASS_VOLTAGE, STATE_CLASS_MEASUREMENT
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    .extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
 | 
				
			||||||
 | 
					    .extend(cv.COMPONENT_SCHEMA)
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async def to_code(config):
 | 
				
			||||||
 | 
					    var = cg.new_Pvariable(config[CONF_ID])
 | 
				
			||||||
 | 
					    await cg.register_component(var, config)
 | 
				
			||||||
 | 
					    await esp32_ble_tracker.register_ble_device(var, config)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if CONF_TEMPERATURE in config:
 | 
				
			||||||
 | 
					        sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
 | 
				
			||||||
 | 
					        cg.add(var.set_temperature(sens))
 | 
				
			||||||
 | 
					    if CONF_HUMIDITY in config:
 | 
				
			||||||
 | 
					        sens = await sensor.new_sensor(config[CONF_HUMIDITY])
 | 
				
			||||||
 | 
					        cg.add(var.set_humidity(sens))
 | 
				
			||||||
 | 
					    if CONF_BATTERY_LEVEL in config:
 | 
				
			||||||
 | 
					        sens = await sensor.new_sensor(config[CONF_BATTERY_LEVEL])
 | 
				
			||||||
 | 
					        cg.add(var.set_battery_level(sens))
 | 
				
			||||||
 | 
					    if CONF_BATTERY_VOLTAGE in config:
 | 
				
			||||||
 | 
					        sens = await sensor.new_sensor(config[CONF_BATTERY_VOLTAGE])
 | 
				
			||||||
 | 
					        cg.add(var.set_battery_voltage(sens))
 | 
				
			||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user