mirror of
				https://github.com/esphome/esphome.git
				synced 2025-11-03 08:31:47 +00:00 
			
		
		
		
	Compare commits
	
		
			1031 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					925a992d1b | ||
| 
						 | 
					61ecbe4273 | ||
| 
						 | 
					65c7e27a43 | ||
| 
						 | 
					57b56010da | ||
| 
						 | 
					ef89249019 | ||
| 
						 | 
					bc64cf3e47 | ||
| 
						 | 
					def70dde72 | ||
| 
						 | 
					d238e06f86 | ||
| 
						 | 
					2fc59ecc30 | ||
| 
						 | 
					0047d24698 | ||
| 
						 | 
					89a89e1785 | ||
| 
						 | 
					1952d275f7 | ||
| 
						 | 
					043095b605 | ||
| 
						 | 
					431d3578a5 | ||
| 
						 | 
					5f02d86841 | ||
| 
						 | 
					a7ec57d4be | ||
| 
						 | 
					1ea5cc497f | ||
| 
						 | 
					b601cf6bc6 | ||
| 
						 | 
					5057caa7fc | ||
| 
						 | 
					95bef53d37 | ||
| 
						 | 
					ea019a057b | ||
| 
						 | 
					b860a317b9 | ||
| 
						 | 
					9591c903f7 | ||
| 
						 | 
					65fbb8bc60 | ||
| 
						 | 
					97428f2ba2 | ||
| 
						 | 
					9ab6a7b7ff | ||
| 
						 | 
					e2ad6fe3d8 | ||
| 
						 | 
					6781d08c9b | ||
| 
						 | 
					828f7946ea | ||
| 
						 | 
					23c663d5d4 | ||
| 
						 | 
					6a99789c92 | ||
| 
						 | 
					52e13164b4 | ||
| 
						 | 
					28f2582256 | ||
| 
						 | 
					652f6058d1 | ||
| 
						 | 
					717aab7c8b | ||
| 
						 | 
					5e799b5284 | ||
| 
						 | 
					9f36b25d4e | ||
| 
						 | 
					c8058e9636 | ||
| 
						 | 
					f2474c5c45 | ||
| 
						 | 
					7acc36d39d | ||
| 
						 | 
					b01db991a5 | ||
| 
						 | 
					8c849b9002 | ||
| 
						 | 
					6a0d4cb5a9 | ||
| 
						 | 
					72002ce70e | ||
| 
						 | 
					c67539cf5b | ||
| 
						 | 
					dcd3d2084d | ||
| 
						 | 
					585bb6dac8 | ||
| 
						 | 
					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 | ||
| 
						 | 
					53c231a7eb | ||
| 
						 | 
					d44ce82aa1 | ||
| 
						 | 
					a055de48e4 | ||
| 
						 | 
					37b8d665fe | ||
| 
						 | 
					dd7c8dabb1 | ||
| 
						 | 
					e41a9875e3 | ||
| 
						 | 
					c5c42c4338 | ||
| 
						 | 
					531428b8b0 | ||
| 
						 | 
					ea8068e001 | ||
| 
						 | 
					7842a55c81 | ||
| 
						 | 
					51d39862b1 | ||
| 
						 | 
					bfea6ca79b | ||
| 
						 | 
					6297395018 | ||
| 
						 | 
					a5b49dbfa6 | ||
| 
						 | 
					7c0d777173 | ||
| 
						 | 
					74878276fc | ||
| 
						 | 
					226e3b1dad | ||
| 
						 | 
					7752794fc5 | ||
| 
						 | 
					b3094d6a53 | ||
| 
						 | 
					e3640e710f | ||
| 
						 | 
					2ef64b55c5 | ||
| 
						 | 
					7f6672bb37 | ||
| 
						 | 
					68a3b31628 | ||
| 
						 | 
					1b35855e68 | ||
| 
						 | 
					1e1837000d | ||
| 
						 | 
					e2d5257632 | ||
| 
						 | 
					387c75793b | ||
| 
						 | 
					4f3a74d08a | ||
| 
						 | 
					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 | ||
| 
						 | 
					fdbc59a159 | ||
| 
						 | 
					0db37bb55c | ||
| 
						 | 
					8027facb39 | ||
| 
						 | 
					2ff2750628 | ||
| 
						 | 
					16c2dc2aaf | ||
| 
						 | 
					eae5c17b87 | ||
| 
						 | 
					a59cde91ad | ||
| 
						 | 
					1f243ae37e | ||
| 
						 | 
					603f82977e | ||
| 
						 | 
					2d70422a6f | ||
| 
						 | 
					e2c8b21195 | ||
| 
						 | 
					7adaeacd0b | ||
| 
						 | 
					dc2279b74f | ||
| 
						 | 
					75275c4e93 | ||
| 
						 | 
					65d3dc9cb8 | ||
| 
						 | 
					66aa02fc34 | ||
| 
						 | 
					be6b4ee47f | ||
| 
						 | 
					90f909d2ea | ||
| 
						 | 
					3aaa92fdff | ||
| 
						 | 
					5efd076c08 | ||
| 
						 | 
					09fd505f08 | ||
| 
						 | 
					042ccde441 | ||
| 
						 | 
					442030b6ca | ||
| 
						 | 
					1df9ae53f8 | ||
| 
						 | 
					5f535e9756 | ||
| 
						 | 
					70faeb2fa8 | ||
| 
						 | 
					469c0db981 | ||
| 
						 | 
					440e428aa4 | ||
| 
						 | 
					dde70c95a4 | ||
| 
						 | 
					09d1846261 | ||
| 
						 | 
					34d26a517d | ||
| 
						 | 
					d24b88271c | ||
| 
						 | 
					f22115792a | ||
| 
						 | 
					82a30558e1 | ||
| 
						 | 
					847fe5adca | ||
| 
						 | 
					775b51c6a1 | ||
| 
						 | 
					e0ad5a9009 | ||
| 
						 | 
					1bf01a9081 | ||
| 
						 | 
					6ae59bb43d | ||
| 
						 | 
					adf2a463fd | ||
| 
						 | 
					80aaf66963 | ||
| 
						 | 
					560251ab2a | ||
| 
						 | 
					864c5d8908 | ||
| 
						 | 
					742c21506c | ||
| 
						 | 
					a6faccb4d9 | ||
| 
						 | 
					69fd3e8937 | ||
| 
						 | 
					41233d7f25 | ||
| 
						 | 
					07286d1d76 | ||
| 
						 | 
					08148c5830 | ||
| 
						 | 
					969bdb06ce | ||
| 
						 | 
					b0bb692af4 | ||
| 
						 | 
					7bf6fd316f | ||
| 
						 | 
					c1f5e04d6c | ||
| 
						 | 
					5a67e72389 | ||
| 
						 | 
					91c9b11647 | ||
| 
						 | 
					929600d7f7 | ||
| 
						 | 
					163d0c55ab | ||
| 
						 | 
					327ccb241e | ||
| 
						 | 
					6b3c7b0854 | ||
| 
						 | 
					681dcb51da | ||
| 
						 | 
					576d5021fd | ||
| 
						 | 
					6cd76f00ac | ||
| 
						 | 
					6f63a62a8d | ||
| 
						 | 
					8867a0fcfb | ||
| 
						 | 
					bb2582717f | ||
| 
						 | 
					d62ef35860 | ||
| 
						 | 
					59c5956f93 | ||
| 
						 | 
					e4f055597c | ||
| 
						 | 
					4c49beb3c7 | ||
| 
						 | 
					8ff742d9ab | ||
| 
						 | 
					d63cd8b4cd | ||
| 
						 | 
					42b4a166ec | ||
| 
						 | 
					c27fd0f01a | ||
| 
						 | 
					dcb4a0a81e | ||
| 
						 | 
					17da9fddc3 | ||
| 
						 | 
					31aa3c55ca | ||
| 
						 | 
					eca3685ea0 | ||
| 
						 | 
					bd216c5c63 | ||
| 
						 | 
					31ff76427c | ||
| 
						 | 
					3f0503c296 | ||
| 
						 | 
					c18050bda0 | ||
| 
						 | 
					6542be5588 | ||
| 
						 | 
					9fb60b8015 | ||
| 
						 | 
					1177b856a0 | ||
| 
						 | 
					c0adaa8de8 | ||
| 
						 | 
					ae8700447e | ||
| 
						 | 
					d64a4ef2b4 | ||
| 
						 | 
					2229aa6ccc | ||
| 
						 | 
					872b468415 | ||
| 
						 | 
					9f022a7433 | ||
| 
						 | 
					85a958e300 | ||
| 
						 | 
					0ee56195ae | ||
| 
						 | 
					48f52db1d9 | ||
| 
						 | 
					d2c7afeef0 | ||
| 
						 | 
					644aec791e | ||
| 
						 | 
					b70a0325c5 | ||
| 
						 | 
					268387f829 | ||
| 
						 | 
					b975caef1e | ||
| 
						 | 
					54fe1c7d55 | ||
| 
						 | 
					89c1274d56 | ||
| 
						 | 
					f9ca3f1c27 | ||
| 
						 | 
					26dbc30279 | ||
| 
						 | 
					4bee316425 | ||
| 
						 | 
					1e22b1e959 | ||
| 
						 | 
					e077ad56bd | ||
| 
						 | 
					f1e00f8c8e | ||
| 
						 | 
					9a152e588e | ||
| 
						 | 
					16f42a3d03 | ||
| 
						 | 
					6c8d0f1852 | ||
| 
						 | 
					b59cf6572b | ||
| 
						 | 
					4fa11dfa68 | ||
| 
						 | 
					352bdd9fb5 | ||
| 
						 | 
					96ff9a162c | ||
| 
						 | 
					af15a4e710 | ||
| 
						 | 
					18426b71e4 | ||
| 
						 | 
					7063aa6009 | ||
| 
						 | 
					286ca07cc8 | ||
| 
						 | 
					58b6311821 | ||
| 
						 | 
					e553c0768e | ||
| 
						 | 
					62d4b29662 | ||
| 
						 | 
					0759140dc2 | ||
| 
						 | 
					a943bc6c80 | ||
| 
						 | 
					16bc60644d | ||
| 
						 | 
					347393b864 | ||
| 
						 | 
					bf6b11222a | ||
| 
						 | 
					823ae7d1aa | ||
| 
						 | 
					28031cfa3e | ||
| 
						 | 
					292c2d0c53 | ||
| 
						 | 
					869775ec7a | ||
| 
						 | 
					783b179af7 | ||
| 
						 | 
					28454ea4cd | ||
| 
						 | 
					9f4b666ef0 | ||
| 
						 | 
					80214640b1 | ||
| 
						 | 
					4310c14497 | ||
| 
						 | 
					aebb6d2fcc | ||
| 
						 | 
					af35c9258e | ||
| 
						 | 
					7a43231c43 | ||
| 
						 | 
					1bf55c130b | ||
| 
						 | 
					95a74a7f19 | ||
| 
						 | 
					b78b28ea0e | ||
| 
						 | 
					aed7b3fbb2 | ||
| 
						 | 
					1ade7bcb2d | ||
| 
						 | 
					21bbafb63d | ||
| 
						 | 
					1cfc6ac3c6 | ||
| 
						 | 
					c3aa834d80 | ||
| 
						 | 
					68d0d045c0 | ||
| 
						 | 
					72d6471ab8 | ||
| 
						 | 
					22aecdfc6f | ||
| 
						 | 
					ee0b6e835f | ||
| 
						 | 
					8292024306 | ||
| 
						 | 
					84cfcf2b4a | ||
| 
						 | 
					ea762b7295 | ||
| 
						 | 
					d51c0f13c0 | ||
| 
						 | 
					6ceb975a3a | ||
| 
						 | 
					9a40d6ef50 | ||
| 
						 | 
					32195f77d9 | ||
| 
						 | 
					578e5a0d7a | ||
| 
						 | 
					1242f43769 | ||
| 
						 | 
					3bb6430495 | ||
| 
						 | 
					aae633277f | ||
| 
						 | 
					996c50e8f2 | ||
| 
						 | 
					95c883ae9b | ||
| 
						 | 
					35a725fa1e | ||
| 
						 | 
					78c1adafcd | ||
| 
						 | 
					e15071228e | ||
| 
						 | 
					ac48ff1fd6 | ||
| 
						 | 
					428684bc1e | ||
| 
						 | 
					81b7653c9c | ||
| 
						 | 
					45736707bd | ||
| 
						 | 
					cdfbe5b523 | ||
| 
						 | 
					cdb9c59662 | ||
| 
						 | 
					9c30f4cc68 | ||
| 
						 | 
					acf3f6fb65 | ||
| 
						 | 
					23f99908db | ||
| 
						 | 
					a0046a2e55 | ||
| 
						 | 
					e30512931b | ||
| 
						 | 
					e207c6ad84 | ||
| 
						 | 
					9d7f76773d | ||
| 
						 | 
					5f2808ec2f | ||
| 
						 | 
					be91cfb261 | ||
| 
						 | 
					0eadda77b0 | ||
| 
						 | 
					b2388b6fe7 | ||
| 
						 | 
					1a763ae974 | ||
| 
						 | 
					38dfab11b4 | ||
| 
						 | 
					7c31592850 | ||
| 
						 | 
					57bee74225 | ||
| 
						 | 
					fa351cd37c | ||
| 
						 | 
					68e7e5a51c | ||
| 
						 | 
					4d31ad3bdc | ||
| 
						 | 
					f4f1164b94 | ||
| 
						 | 
					d4e0e1518a | ||
| 
						 | 
					bd0be41064 | ||
| 
						 | 
					4118a289a6 | ||
| 
						 | 
					1d5f8d5a52 | ||
| 
						 | 
					fd1dc24ac6 | ||
| 
						 | 
					be1e4c0a1d | ||
| 
						 | 
					c2028f7378 | ||
| 
						 | 
					4b0f203049 | ||
| 
						 | 
					23ff8178a0 | ||
| 
						 | 
					93cfee8026 | ||
| 
						 | 
					b6920025b2 | ||
| 
						 | 
					fb29ac27a2 | ||
| 
						 | 
					4c03cebef3 | ||
| 
						 | 
					244c4be8cc | ||
| 
						 | 
					9b28c732c6 | ||
| 
						 | 
					5dfb33ebee | ||
| 
						 | 
					2b30cde125 | ||
| 
						 | 
					f9b3e61c0f | ||
| 
						 | 
					83a92f03fc | ||
| 
						 | 
					d27291b997 | ||
| 
						 | 
					2c995cf145 | ||
| 
						 | 
					2822fa4a40 | ||
| 
						 | 
					ccf3da2a5a | ||
| 
						 | 
					5348b36a7c | ||
| 
						 | 
					947a6034e3 | ||
| 
						 | 
					65d08beaa4 | ||
| 
						 | 
					9770bc371b | ||
| 
						 | 
					22f9f75914 | ||
| 
						 | 
					54c9dd4173 | ||
| 
						 | 
					0fc267dfc7 | ||
| 
						 | 
					c5db457700 | ||
| 
						 | 
					15a7d2ef75 | ||
| 
						 | 
					071272a27f | ||
| 
						 | 
					655327a8b1 | ||
| 
						 | 
					15b87af8ed | ||
| 
						 | 
					a0b3d861fe | ||
| 
						 | 
					718c494013 | ||
| 
						 | 
					5c9755ecc1 | ||
| 
						 | 
					11e88019c2 | ||
| 
						 | 
					a783637a7a | ||
| 
						 | 
					7210ad7ed9 | ||
| 
						 | 
					1876c21e3e | ||
| 
						 | 
					6516a6ff7e | ||
| 
						 | 
					85195436c1 | ||
| 
						 | 
					c6512013bb | ||
| 
						 | 
					81a070d03d | ||
| 
						 | 
					0ef1d178d2 | ||
| 
						 | 
					762f1b1fc9 | ||
| 
						 | 
					7ad593d674 | ||
| 
						 | 
					13522c8f19 | ||
| 
						 | 
					d2938e82db | ||
| 
						 | 
					f95d4ca106 | ||
| 
						 | 
					486bafd009 | ||
| 
						 | 
					341c99b4fa | ||
| 
						 | 
					83095e8989 | ||
| 
						 | 
					71ba4bc31c | ||
| 
						 | 
					894ec07cc8 | ||
| 
						 | 
					59091100e4 | ||
| 
						 | 
					e5485ab650 | ||
| 
						 | 
					6c493d10d2 | ||
| 
						 | 
					840f599631 | ||
| 
						 | 
					5a76e61b1e | ||
| 
						 | 
					7b4366bfef | ||
| 
						 | 
					8dee5c5fe8 | ||
| 
						 | 
					b2e6d222cd | ||
| 
						 | 
					2712c44004 | ||
| 
						 | 
					82625a3080 | ||
| 
						 | 
					49f9ad66db | ||
| 
						 | 
					0dfab4d93c | ||
| 
						 | 
					5cd7f23065 | ||
| 
						 | 
					27453afa4e | ||
| 
						 | 
					369d175694 | ||
| 
						 | 
					fc465d6d93 | ||
| 
						 | 
					904a0b26ea | ||
| 
						 | 
					c13f132399 | ||
| 
						 | 
					db968bc6b0 | ||
| 
						 | 
					7abe8875bd | ||
| 
						 | 
					dc9f304d94 | ||
| 
						 | 
					1ca241615d | ||
| 
						 | 
					b8aa84002a | ||
| 
						 | 
					10cc0b1d5b | ||
| 
						 | 
					11d9c203c1 | ||
| 
						 | 
					c9ab454c3c | ||
| 
						 | 
					4a55692885 | ||
| 
						 | 
					88c129e705 | ||
| 
						 | 
					a09bd80636 | ||
| 
						 | 
					237ecb3adf | ||
| 
						 | 
					9d65b77f13 | ||
| 
						 | 
					97f2becc9e | ||
| 
						 | 
					80b48f01fb | ||
| 
						 | 
					f4160c363b | ||
| 
						 | 
					4fee9cc039 | ||
| 
						 | 
					36f47ade70 | ||
| 
						 | 
					8db6f3129c | ||
| 
						 | 
					75630a36f8 | ||
| 
						 | 
					642bc91a76 | ||
| 
						 | 
					d69926ee56 | ||
| 
						 | 
					4758403d44 | ||
| 
						 | 
					4b0ec5c28a | ||
| 
						 | 
					4b2a9e5e49 | ||
| 
						 | 
					1449c51d49 | ||
| 
						 | 
					a451705e0b | ||
| 
						 | 
					2e6db39173 | ||
| 
						 | 
					373f75253c | ||
| 
						 | 
					724842084e | ||
| 
						 | 
					8f3635b167 | ||
| 
						 | 
					11605a36f7 | ||
| 
						 | 
					533f81d625 | ||
| 
						 | 
					aacb9e44e8 | ||
| 
						 | 
					c6e3f1bca6 | ||
| 
						 | 
					a933d4aeb6 | ||
| 
						 | 
					d2be58ba31 | ||
| 
						 | 
					bbeb0461c4 | ||
| 
						 | 
					14fd08e225 | ||
| 
						 | 
					f99352f7e0 | ||
| 
						 | 
					b51cbc4207 | ||
| 
						 | 
					7a895adec9 | ||
| 
						 | 
					4fe0c95ccb | ||
| 
						 | 
					726b0e73d9 | ||
| 
						 | 
					88ccd60a08 | ||
| 
						 | 
					e6c16e9981 | ||
| 
						 | 
					1bd408937a | ||
| 
						 | 
					4d00dfd308 | ||
| 
						 | 
					75326d2271 | ||
| 
						 | 
					76fe2e4871 | ||
| 
						 | 
					f977e9da2b | ||
| 
						 | 
					16ae46e958 | ||
| 
						 | 
					73eea154d5 | ||
| 
						 | 
					0d36e66125 | ||
| 
						 | 
					970838ed09 | ||
| 
						 | 
					0a21816a5a | ||
| 
						 | 
					30a542e763 | ||
| 
						 | 
					ebe64e24f1 | ||
| 
						 | 
					caa5b20791 | ||
| 
						 | 
					e2ad9ed746 | ||
| 
						 | 
					32c0e7c2ae | ||
| 
						 | 
					6c564c7b7f | ||
| 
						 | 
					c81e3a3be4 | ||
| 
						 | 
					6b1b9ef7ec | ||
| 
						 | 
					c26a8b8718 | ||
| 
						 | 
					4a89a475bd | ||
| 
						 | 
					c53483a3b2 | ||
| 
						 | 
					fe24745815 | ||
| 
						 | 
					b5e75793e1 | ||
| 
						 | 
					734cc989de | ||
| 
						 | 
					2642750466 | ||
| 
						 | 
					ec9cc72320 | ||
| 
						 | 
					c97a9d83c6 | ||
| 
						 | 
					8cf15c7f5c | ||
| 
						 | 
					adc76ca1b8 | ||
| 
						 | 
					8f8892440c | ||
| 
						 | 
					570843150d | ||
| 
						 | 
					f3fc9e4142 | ||
| 
						 | 
					075fcb77a8 | ||
| 
						 | 
					e5899ff717 | ||
| 
						 | 
					2fb3970027 | ||
| 
						 | 
					1a4efa1b8c | ||
| 
						 | 
					f31c1480f3 | ||
| 
						 | 
					291d4be772 | ||
| 
						 | 
					52584ec2be | ||
| 
						 | 
					3bc08e5222 | ||
| 
						 | 
					672f8d1719 | ||
| 
						 | 
					420c8b49e2 | ||
| 
						 | 
					f921997ee6 | ||
| 
						 | 
					4e520d13dd | ||
| 
						 | 
					72f656ffef | ||
| 
						 | 
					b60239d5e5 | ||
| 
						 | 
					d02e280c3c | ||
| 
						 | 
					6535b0966e | ||
| 
						 | 
					82dbacbee5 | ||
| 
						 | 
					2432901974 | ||
| 
						 | 
					ebb5d58c14 | ||
| 
						 | 
					605e365405 | ||
| 
						 | 
					2617e5092b | ||
| 
						 | 
					d41ddf380c | ||
| 
						 | 
					a72c3ea9d7 | ||
| 
						 | 
					8be733efee | ||
| 
						 | 
					b9609286ea | ||
| 
						 | 
					2b186fdb0d | ||
| 
						 | 
					3012fee013 | ||
| 
						 | 
					01db114724 | ||
| 
						 | 
					e05688d639 | ||
| 
						 | 
					5ab995d8ca | ||
| 
						 | 
					4248741b11 | ||
| 
						 | 
					4b8ecc7634 | ||
| 
						 | 
					25d04c759c | ||
| 
						 | 
					b4ec84030e | ||
| 
						 | 
					925b030718 | ||
| 
						 | 
					9eba789c32 | ||
| 
						 | 
					3e6ae4afda | ||
| 
						 | 
					27abb38ecb | ||
| 
						 | 
					29e8761373 | ||
| 
						 | 
					a04299c59e | ||
| 
						 | 
					d7bf3c51d9 | ||
| 
						 | 
					1ce257c721 | ||
| 
						 | 
					8dd971b25e | ||
| 
						 | 
					31ddd3f668 | ||
| 
						 | 
					f35f6d2348 | ||
| 
						 | 
					02d34a0238 | ||
| 
						 | 
					3089ffa8e7 | ||
| 
						 | 
					15cb0e4ff3 | ||
| 
						 | 
					2f3c6d5b58 | ||
| 
						 | 
					ead5e8d855 | ||
| 
						 | 
					fd9a9ecc63 | ||
| 
						 | 
					bbb8ea7ec2 | ||
| 
						 | 
					667ed94e29 | ||
| 
						 | 
					928df2dcd1 | ||
| 
						 | 
					2decb8115c | ||
| 
						 | 
					9d26c16471 | ||
| 
						 | 
					5893506528 | ||
| 
						 | 
					df0d33c3cd | ||
| 
						 | 
					61ba2e0f35 | ||
| 
						 | 
					9fa1a334e6 | ||
| 
						 | 
					4a5365f6a0 | ||
| 
						 | 
					127efe9a52 | ||
| 
						 | 
					a23ebead68 | ||
| 
						 | 
					f39d459555 | ||
| 
						 | 
					6e7d25ed42 | ||
| 
						 | 
					ac0b095941 | ||
| 
						 | 
					cda9bad233 | ||
| 
						 | 
					41db8a1264 | ||
| 
						 | 
					e7e785fd60 | ||
| 
						 | 
					300d3a1f46 | ||
| 
						 | 
					356554c08d | ||
| 
						 | 
					ced28ad006 | 
							
								
								
									
										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}]
 | 
			
		||||
indent_style = space
 | 
			
		||||
indent_size = 2
 | 
			
		||||
quote_type = single
 | 
			
		||||
							
								
								
									
										8
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
# These are supported funding model platforms
 | 
			
		||||
 | 
			
		||||
github:
 | 
			
		||||
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.
 | 
			
		||||
    
 | 
			
		||||
							
								
								
									
										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="2.6.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.0.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}" \
 | 
			
		||||
            .
 | 
			
		||||
							
								
								
									
										178
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										178
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,178 @@
 | 
			
		||||
# 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:latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v2
 | 
			
		||||
      # Cache platformio intermediary files (like libraries etc)
 | 
			
		||||
      # Note: platformio platform versions should be cached via the esphome-lint image
 | 
			
		||||
      - name: Cache Platformio
 | 
			
		||||
        uses: actions/cache@v1
 | 
			
		||||
        with:
 | 
			
		||||
          path: .pio
 | 
			
		||||
          key: lint-cpp-pio-${{ hashFiles('platformio.ini') }}
 | 
			
		||||
          restore-keys: |
 | 
			
		||||
            lint-cpp-pio-
 | 
			
		||||
      # 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:latest
 | 
			
		||||
    # 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
 | 
			
		||||
      # Cache platformio intermediary files (like libraries etc)
 | 
			
		||||
      # Note: platformio platform versions should be cached via the esphome-lint image
 | 
			
		||||
      - name: Cache Platformio
 | 
			
		||||
        uses: actions/cache@v1
 | 
			
		||||
        with:
 | 
			
		||||
          path: .pio
 | 
			
		||||
          key: lint-cpp-pio-${{ hashFiles('platformio.ini') }}
 | 
			
		||||
          restore-keys: |
 | 
			
		||||
            lint-cpp-pio-
 | 
			
		||||
      # 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
 | 
			
		||||
    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 tests/${{ matrix.test }}.yaml compile
 | 
			
		||||
 | 
			
		||||
  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
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
        }
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										264
									
								
								.github/workflows/release-dev.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										264
									
								
								.github/workflows/release-dev.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,264 @@
 | 
			
		||||
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:latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v2
 | 
			
		||||
      # Cache platformio intermediary files (like libraries etc)
 | 
			
		||||
      # Note: platformio platform versions should be cached via the esphome-lint image
 | 
			
		||||
      - name: Cache Platformio
 | 
			
		||||
        uses: actions/cache@v1
 | 
			
		||||
        with:
 | 
			
		||||
          path: .pio
 | 
			
		||||
          key: lint-cpp-pio-${{ hashFiles('platformio.ini') }}
 | 
			
		||||
          restore-keys: |
 | 
			
		||||
            lint-cpp-pio-
 | 
			
		||||
      # 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:latest
 | 
			
		||||
    # 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
 | 
			
		||||
      # Cache platformio intermediary files (like libraries etc)
 | 
			
		||||
      # Note: platformio platform versions should be cached via the esphome-lint image
 | 
			
		||||
      - name: Cache Platformio
 | 
			
		||||
        uses: actions/cache@v1
 | 
			
		||||
        with:
 | 
			
		||||
          path: .pio
 | 
			
		||||
          key: lint-cpp-pio-${{ hashFiles('platformio.ini') }}
 | 
			
		||||
          restore-keys: |
 | 
			
		||||
            lint-cpp-pio-
 | 
			
		||||
      # 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
 | 
			
		||||
    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 tests/${{ matrix.test }}.yaml compile
 | 
			
		||||
 | 
			
		||||
  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="2.6.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.0.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
 | 
			
		||||
							
								
								
									
										327
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										327
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,327 @@
 | 
			
		||||
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:latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v2
 | 
			
		||||
      # Cache platformio intermediary files (like libraries etc)
 | 
			
		||||
      # Note: platformio platform versions should be cached via the esphome-lint image
 | 
			
		||||
      - name: Cache Platformio
 | 
			
		||||
        uses: actions/cache@v1
 | 
			
		||||
        with:
 | 
			
		||||
          path: .pio
 | 
			
		||||
          key: lint-cpp-pio-${{ hashFiles('platformio.ini') }}
 | 
			
		||||
          restore-keys: |
 | 
			
		||||
            lint-cpp-pio-
 | 
			
		||||
      # 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:latest
 | 
			
		||||
    # 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
 | 
			
		||||
      # Cache platformio intermediary files (like libraries etc)
 | 
			
		||||
      # Note: platformio platform versions should be cached via the esphome-lint image
 | 
			
		||||
      - name: Cache Platformio
 | 
			
		||||
        uses: actions/cache@v1
 | 
			
		||||
        with:
 | 
			
		||||
          path: .pio
 | 
			
		||||
          key: lint-cpp-pio-${{ hashFiles('platformio.ini') }}
 | 
			
		||||
          restore-keys: |
 | 
			
		||||
            lint-cpp-pio-
 | 
			
		||||
      # 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
 | 
			
		||||
    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 tests/${{ matrix.test }}.yaml compile
 | 
			
		||||
 | 
			
		||||
  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="2.6.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.0.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\"}}"
 | 
			
		||||
							
								
								
									
										10
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -10,6 +10,9 @@ __pycache__/
 | 
			
		||||
*.sublime-project
 | 
			
		||||
*.sublime-workspace
 | 
			
		||||
 | 
			
		||||
# Intellij Idea
 | 
			
		||||
.idea
 | 
			
		||||
 | 
			
		||||
# Hide some OS X stuff
 | 
			
		||||
.DS_Store
 | 
			
		||||
.AppleDouble
 | 
			
		||||
@@ -48,8 +51,10 @@ htmlcov/
 | 
			
		||||
.coverage
 | 
			
		||||
.coverage.*
 | 
			
		||||
.cache
 | 
			
		||||
.esphome
 | 
			
		||||
nosetests.xml
 | 
			
		||||
coverage.xml
 | 
			
		||||
cov.xml
 | 
			
		||||
*.cover
 | 
			
		||||
.hypothesis/
 | 
			
		||||
.pytest_cache/
 | 
			
		||||
@@ -75,7 +80,9 @@ venv.bak/
 | 
			
		||||
 | 
			
		||||
.pioenvs
 | 
			
		||||
.piolibdeps
 | 
			
		||||
.vscode
 | 
			
		||||
.pio
 | 
			
		||||
.vscode/
 | 
			
		||||
!.vscode/tasks.json
 | 
			
		||||
CMakeListsPrivate.txt
 | 
			
		||||
CMakeLists.txt
 | 
			
		||||
 | 
			
		||||
@@ -113,3 +120,4 @@ config/
 | 
			
		||||
tests/build/
 | 
			
		||||
tests/.esphome/
 | 
			
		||||
/.temp-clang-tidy.cpp
 | 
			
		||||
.pio/
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										346
									
								
								.gitlab-ci.yml
									
									
									
									
									
								
							
							
						
						
									
										346
									
								
								.gitlab-ci.yml
									
									
									
									
									
								
							@@ -1,346 +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: '1.5.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 hassioaddons/qemu-user-static:latest
 | 
			
		||||
    - 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
 | 
			
		||||
    - |
 | 
			
		||||
      if ! patch -R -p0 -s -f --dry-run <script/.neopixelbus.patch; then
 | 
			
		||||
        patch -p0 < script/.neopixelbus.patch
 | 
			
		||||
      fi
 | 
			
		||||
    - 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
 | 
			
		||||
  onOpen: open-preview
 | 
			
		||||
tasks:
 | 
			
		||||
- before: script/setup
 | 
			
		||||
- before: pyenv local $(pyenv version | grep '^3\.' | cut -d ' ' -f 1) && script/setup
 | 
			
		||||
  command: python -m esphome config dashboard
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										11
									
								
								.pre-commit-config.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								.pre-commit-config.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
# See https://pre-commit.com for more information
 | 
			
		||||
# See https://pre-commit.com/hooks.html for more hooks
 | 
			
		||||
repos:
 | 
			
		||||
-   repo: https://github.com/pre-commit/pre-commit-hooks
 | 
			
		||||
    rev: v2.4.0
 | 
			
		||||
    hooks:
 | 
			
		||||
    -   id: trailing-whitespace
 | 
			
		||||
    -   id: end-of-file-fixer
 | 
			
		||||
    -   id: check-yaml
 | 
			
		||||
    -   id: check-added-large-files
 | 
			
		||||
    -   id: flake8
 | 
			
		||||
							
								
								
									
										56
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										56
									
								
								.travis.yml
									
									
									
									
									
								
							@@ -1,56 +0,0 @@
 | 
			
		||||
sudo: false
 | 
			
		||||
language: python
 | 
			
		||||
install: script/setup
 | 
			
		||||
cache:
 | 
			
		||||
  directories:
 | 
			
		||||
    - "~/.platformio"
 | 
			
		||||
    - "$TRAVIS_BUILD_DIR/.piolibdeps"
 | 
			
		||||
    - "$TRAVIS_BUILD_DIR/tests/build/test1/.piolibdeps"
 | 
			
		||||
    - "$TRAVIS_BUILD_DIR/tests/build/test2/.piolibdeps"
 | 
			
		||||
    - "$TRAVIS_BUILD_DIR/tests/build/test3/.piolibdeps"
 | 
			
		||||
 | 
			
		||||
matrix:
 | 
			
		||||
  fast_finish: true
 | 
			
		||||
  include:
 | 
			
		||||
    - python: "2.7"
 | 
			
		||||
      env: TARGET=Lint2.7
 | 
			
		||||
      script:
 | 
			
		||||
        - script/ci-custom.py
 | 
			
		||||
        - flake8 esphome
 | 
			
		||||
        - pylint esphome
 | 
			
		||||
    - python: "3.5.3"
 | 
			
		||||
      env: TARGET=Lint3.5
 | 
			
		||||
      script:
 | 
			
		||||
        - script/ci-custom.py
 | 
			
		||||
        - flake8 esphome
 | 
			
		||||
        - pylint esphome
 | 
			
		||||
    - 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
 | 
			
		||||
        - |
 | 
			
		||||
          if ! patch -R -p0 -s -f --dry-run <script/.neopixelbus.patch; then
 | 
			
		||||
            patch -p0 < script/.neopixelbus.patch
 | 
			
		||||
          fi
 | 
			
		||||
        - 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 config dashboard",
 | 
			
		||||
            "problemMatcher": []
 | 
			
		||||
        }
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										103
									
								
								CODEOWNERS
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								CODEOWNERS
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,103 @@
 | 
			
		||||
# 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/animation/* @syndlex
 | 
			
		||||
esphome/components/api/* @OttoWinter
 | 
			
		||||
esphome/components/async_tcp/* @OttoWinter
 | 
			
		||||
esphome/components/atc_mithermometer/* @ahpohl
 | 
			
		||||
esphome/components/bang_bang/* @OttoWinter
 | 
			
		||||
esphome/components/binary_sensor/* @esphome/core
 | 
			
		||||
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/ct_clamp/* @jesserockz
 | 
			
		||||
esphome/components/debug/* @OttoWinter
 | 
			
		||||
esphome/components/dfplayer/* @glmnet
 | 
			
		||||
esphome/components/dht/* @OttoWinter
 | 
			
		||||
esphome/components/ds1307/* @badbadc0ffee
 | 
			
		||||
esphome/components/exposure_notifications/* @OttoWinter
 | 
			
		||||
esphome/components/ezo/* @ssieb
 | 
			
		||||
esphome/components/fastled_base/* @OttoWinter
 | 
			
		||||
esphome/components/globals/* @esphome/core
 | 
			
		||||
esphome/components/gpio/* @esphome/core
 | 
			
		||||
esphome/components/homeassistant/* @OttoWinter
 | 
			
		||||
esphome/components/i2c/* @esphome/core
 | 
			
		||||
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/mcp23s08/* @SenexCrenshaw
 | 
			
		||||
esphome/components/mcp23s17/* @SenexCrenshaw
 | 
			
		||||
esphome/components/mcp2515/* @danielschramm @mvturnho
 | 
			
		||||
esphome/components/mcp9808/* @k7hpn
 | 
			
		||||
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/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/sensor/* @esphome/core
 | 
			
		||||
esphome/components/shutdown/* @esphome/core
 | 
			
		||||
esphome/components/sim800l/* @glmnet
 | 
			
		||||
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/tcl112/* @glmnet
 | 
			
		||||
esphome/components/teleinfo/* @0hax
 | 
			
		||||
esphome/components/thermostat/* @kbx81
 | 
			
		||||
esphome/components/time/* @OttoWinter
 | 
			
		||||
esphome/components/tm1637/* @glmnet
 | 
			
		||||
esphome/components/tmp102/* @timsavage
 | 
			
		||||
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
 | 
			
		||||
@@ -1,5 +1,7 @@
 | 
			
		||||
include LICENSE
 | 
			
		||||
include README.md
 | 
			
		||||
include requirements.txt
 | 
			
		||||
include esphome/dashboard/templates/*.html
 | 
			
		||||
recursive-include esphome/dashboard/static *.ico *.js *.css
 | 
			
		||||
recursive-include esphome/dashboard/static *.ico *.js *.css *.woff* LICENSE
 | 
			
		||||
recursive-include esphome *.cpp *.h *.tcc
 | 
			
		||||
recursive-include esphome LICENSE.txt
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,28 @@
 | 
			
		||||
ARG BUILD_FROM=esphome/esphome-base-amd64:1.5.1
 | 
			
		||||
ARG BUILD_FROM=esphome/esphome-base-amd64:2.6.0
 | 
			
		||||
FROM ${BUILD_FROM}
 | 
			
		||||
 | 
			
		||||
COPY . .
 | 
			
		||||
RUN pip2 install --no-cache-dir -e .
 | 
			
		||||
# First install requirements to leverage caching when requirements don't change
 | 
			
		||||
COPY requirements.txt /
 | 
			
		||||
RUN pip3 install --no-cache-dir -r /requirements.txt
 | 
			
		||||
 | 
			
		||||
# Then copy esphome and install
 | 
			
		||||
COPY . .
 | 
			
		||||
RUN pip3 install --no-cache-dir -e .
 | 
			
		||||
 | 
			
		||||
# Settings for dashboard
 | 
			
		||||
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
 | 
			
		||||
# Set entrypoint to esphome so that the user doesn't have to type 'esphome'
 | 
			
		||||
# in every docker command twice
 | 
			
		||||
ENTRYPOINT ["esphome"]
 | 
			
		||||
# When no arguments given, start the dashboard in the workdir
 | 
			
		||||
CMD ["/config", "dashboard"]
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										13
									
								
								docker/Dockerfile.dev
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								docker/Dockerfile.dev
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
FROM esphome/esphome-base-amd64:2.6.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,12 +1,16 @@
 | 
			
		||||
ARG BUILD_FROM
 | 
			
		||||
FROM ${BUILD_FROM}
 | 
			
		||||
 | 
			
		||||
# First install requirements to leverage caching when requirements don't change
 | 
			
		||||
COPY requirements.txt /
 | 
			
		||||
RUN pip3 install --no-cache-dir -r /requirements.txt
 | 
			
		||||
 | 
			
		||||
# Copy root filesystem
 | 
			
		||||
COPY docker/rootfs/ /
 | 
			
		||||
COPY setup.py setup.cfg MANIFEST.in /opt/esphome/
 | 
			
		||||
COPY esphome /opt/esphome/esphome
 | 
			
		||||
 | 
			
		||||
RUN pip2 install --no-cache-dir -e /opt/esphome
 | 
			
		||||
# Then copy esphome and install
 | 
			
		||||
COPY . /opt/esphome/
 | 
			
		||||
RUN pip3 install --no-cache-dir -e /opt/esphome
 | 
			
		||||
 | 
			
		||||
# Build arguments
 | 
			
		||||
ARG BUILD_VERSION=dev
 | 
			
		||||
 
 | 
			
		||||
@@ -1,18 +1,7 @@
 | 
			
		||||
FROM esphome/esphome-base-amd64:1.5.1
 | 
			
		||||
FROM esphome/esphome-lint-base:2.6.0
 | 
			
		||||
 | 
			
		||||
RUN \
 | 
			
		||||
    apt-get update \
 | 
			
		||||
    && apt-get install -y --no-install-recommends \
 | 
			
		||||
        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 pip2 install -r /requirements_test.txt
 | 
			
		||||
COPY requirements.txt requirements_test.txt /
 | 
			
		||||
RUN pip3 install --no-cache-dir -r /requirements.txt -r /requirements_test.txt
 | 
			
		||||
 | 
			
		||||
VOLUME ["/esphome"]
 | 
			
		||||
WORKDIR /esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -1,21 +0,0 @@
 | 
			
		||||
FROM ubuntu:bionic
 | 
			
		||||
 | 
			
		||||
RUN apt-get update && apt-get install -y --no-install-recommends \
 | 
			
		||||
        python \
 | 
			
		||||
        python-pip \
 | 
			
		||||
        python-setuptools \
 | 
			
		||||
        python-pil \
 | 
			
		||||
        git \
 | 
			
		||||
    && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/*rm -rf /var/lib/apt/lists/* /tmp/* && \
 | 
			
		||||
    pip install --no-cache-dir platformio && \
 | 
			
		||||
    platformio settings set enable_telemetry No && \
 | 
			
		||||
    platformio settings set check_libraries_interval 1000000 && \
 | 
			
		||||
    platformio settings set check_platformio_interval 1000000 && \
 | 
			
		||||
    platformio settings set check_platforms_interval 1000000
 | 
			
		||||
 | 
			
		||||
COPY docker/platformio.ini /pio/platformio.ini
 | 
			
		||||
RUN platformio run -d /pio; rm -rf /pio
 | 
			
		||||
 | 
			
		||||
COPY requirements.txt /requirements.txt
 | 
			
		||||
 | 
			
		||||
RUN pip install --no-cache-dir -r /requirements.txt
 | 
			
		||||
@@ -1,12 +0,0 @@
 | 
			
		||||
; This file allows the docker build file to install the required platformio
 | 
			
		||||
; platforms
 | 
			
		||||
 | 
			
		||||
[env:espressif8266]
 | 
			
		||||
platform = espressif8266@1.8.0
 | 
			
		||||
board = nodemcuv2
 | 
			
		||||
framework = arduino
 | 
			
		||||
 | 
			
		||||
[env:espressif32]
 | 
			
		||||
platform = espressif32@1.5.0
 | 
			
		||||
board = nodemcu-32s
 | 
			
		||||
framework = arduino
 | 
			
		||||
							
								
								
									
										12
									
								
								docker/rootfs/etc/cont-init.d/30-esphome.sh
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										12
									
								
								docker/rootfs/etc/cont-init.d/30-esphome.sh
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							@@ -8,8 +8,16 @@ declare esphome_version
 | 
			
		||||
 | 
			
		||||
if bashio::config.has_value 'esphome_version'; then
 | 
			
		||||
    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})..."
 | 
			
		||||
    pip2 install -U --no-cache-dir "${full_url}" \
 | 
			
		||||
    pip3 install -U --no-cache-dir "${full_url}" \
 | 
			
		||||
      || bashio::exit.nok "Failed installing esphome pinned version."
 | 
			
		||||
fi
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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
									
								
							@@ -4,6 +4,11 @@ server {
 | 
			
		||||
    include /etc/nginx/includes/server_params.conf;
 | 
			
		||||
    include /etc/nginx/includes/proxy_params.conf;
 | 
			
		||||
    include /etc/nginx/includes/ssl_params.conf;
 | 
			
		||||
 | 
			
		||||
    ssl on;
 | 
			
		||||
    ssl_certificate /ssl/%%certfile%%;
 | 
			
		||||
    ssl_certificate_key /ssl/%%keyfile%%;
 | 
			
		||||
 | 
			
		||||
    # Clear Hass.io Ingress header
 | 
			
		||||
    proxy_set_header X-Hassio-Ingress "";
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,3 @@
 | 
			
		||||
from __future__ import print_function
 | 
			
		||||
 | 
			
		||||
import argparse
 | 
			
		||||
import functools
 | 
			
		||||
import logging
 | 
			
		||||
@@ -11,41 +9,30 @@ from esphome import const, writer, yaml_util
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
from esphome.config import iter_components, read_config, strip_default_ids
 | 
			
		||||
from esphome.const import CONF_BAUD_RATE, CONF_BROKER, CONF_LOGGER, CONF_OTA, \
 | 
			
		||||
    CONF_PASSWORD, CONF_PORT
 | 
			
		||||
    CONF_PASSWORD, CONF_PORT, CONF_ESPHOME, CONF_PLATFORMIO_OPTIONS
 | 
			
		||||
from esphome.core import CORE, EsphomeError, coroutine, coroutine_with_priority
 | 
			
		||||
from esphome.helpers import color, indent
 | 
			
		||||
from esphome.py_compat import IS_PY2, safe_input
 | 
			
		||||
from esphome.util import run_external_command, run_external_process, safe_print
 | 
			
		||||
from esphome.util import run_external_command, run_external_process, safe_print, list_yaml_files, \
 | 
			
		||||
    get_serial_ports
 | 
			
		||||
 | 
			
		||||
_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():
 | 
			
		||||
        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):
 | 
			
		||||
    if not options:
 | 
			
		||||
        raise ValueError
 | 
			
		||||
        raise EsphomeError("Found no valid options for upload/logging, please make sure relevant "
 | 
			
		||||
                           "sections (ota, api, mqtt, ...) are in your configuration and/or the "
 | 
			
		||||
                           "device is plugged in.")
 | 
			
		||||
 | 
			
		||||
    if len(options) == 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):
 | 
			
		||||
        safe_print(u"  [{}] {}".format(i + 1, desc))
 | 
			
		||||
        safe_print(f"  [{i+1}] {desc}")
 | 
			
		||||
 | 
			
		||||
    while True:
 | 
			
		||||
        opt = safe_input('(number): ')
 | 
			
		||||
        opt = input('(number): ')
 | 
			
		||||
        if opt in options:
 | 
			
		||||
            opt = options.index(opt)
 | 
			
		||||
            break
 | 
			
		||||
@@ -55,20 +42,20 @@ def choose_prompt(options):
 | 
			
		||||
                raise ValueError
 | 
			
		||||
            break
 | 
			
		||||
        except ValueError:
 | 
			
		||||
            safe_print(color('red', u"Invalid option: '{}'".format(opt)))
 | 
			
		||||
            safe_print(color('red', f"Invalid option: '{opt}'"))
 | 
			
		||||
    return options[opt - 1][1]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def choose_upload_log_host(default, check_default, show_ota, show_mqtt, show_api):
 | 
			
		||||
    options = []
 | 
			
		||||
    for res, desc in get_serial_ports():
 | 
			
		||||
        options.append((u"{} ({})".format(res, desc), res))
 | 
			
		||||
    for port in get_serial_ports():
 | 
			
		||||
        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):
 | 
			
		||||
        options.append((u"Over The Air ({})".format(CORE.address), CORE.address))
 | 
			
		||||
        options.append((f"Over The Air ({CORE.address})", CORE.address))
 | 
			
		||||
        if default == 'OTA':
 | 
			
		||||
            return CORE.address
 | 
			
		||||
    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':
 | 
			
		||||
            return 'MQTT'
 | 
			
		||||
    if default is not None:
 | 
			
		||||
@@ -106,11 +93,7 @@ def run_miniterm(config, port):
 | 
			
		||||
            except serial.SerialException:
 | 
			
		||||
                _LOGGER.error("Serial port closed!")
 | 
			
		||||
                return
 | 
			
		||||
            if IS_PY2:
 | 
			
		||||
                line = raw.replace('\r', '').replace('\n', '')
 | 
			
		||||
            else:
 | 
			
		||||
                line = raw.replace(b'\r', b'').replace(b'\n', b'').decode('utf8',
 | 
			
		||||
                                                                          'backslashreplace')
 | 
			
		||||
            line = raw.replace(b'\r', b'').replace(b'\n', b'').decode('utf8', 'backslashreplace')
 | 
			
		||||
            time = datetime.now().time().strftime('[%H:%M:%S]')
 | 
			
		||||
            message = time + line
 | 
			
		||||
            safe_print(message)
 | 
			
		||||
@@ -125,11 +108,10 @@ def wrap_to_code(name, comp):
 | 
			
		||||
    @functools.wraps(comp.to_code)
 | 
			
		||||
    @coroutine_with_priority(coro.priority)
 | 
			
		||||
    def wrapped(conf):
 | 
			
		||||
        cg.add(cg.LineComment(u"{}:".format(name)))
 | 
			
		||||
        cg.add(cg.LineComment(f"{name}:"))
 | 
			
		||||
        if comp.config_schema is not None:
 | 
			
		||||
            conf_str = yaml_util.dump(conf)
 | 
			
		||||
            if IS_PY2:
 | 
			
		||||
                conf_str = conf_str.decode('utf-8')
 | 
			
		||||
            conf_str = conf_str.replace('//', '')
 | 
			
		||||
            cg.add(cg.LineComment(indent(conf_str)))
 | 
			
		||||
        yield coro(conf)
 | 
			
		||||
 | 
			
		||||
@@ -137,6 +119,11 @@ def wrap_to_code(name, comp):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def write_cpp(config):
 | 
			
		||||
    generate_cpp_contents(config)
 | 
			
		||||
    return write_cpp_file()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def generate_cpp_contents(config):
 | 
			
		||||
    _LOGGER.info("Generating C++ source...")
 | 
			
		||||
 | 
			
		||||
    for name, component, conf in iter_components(CORE.config):
 | 
			
		||||
@@ -146,6 +133,8 @@ def write_cpp(config):
 | 
			
		||||
 | 
			
		||||
    CORE.flush_tasks()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def write_cpp_file():
 | 
			
		||||
    writer.write_platformio_project()
 | 
			
		||||
 | 
			
		||||
    code_s = indent(CORE.cpp_main_section)
 | 
			
		||||
@@ -157,20 +146,32 @@ def compile_program(args, config):
 | 
			
		||||
    from esphome import platformio_api
 | 
			
		||||
 | 
			
		||||
    _LOGGER.info("Compiling app...")
 | 
			
		||||
    return platformio_api.run_compile(config, args.verbose)
 | 
			
		||||
    return platformio_api.run_compile(config, CORE.verbose)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def upload_using_esptool(config, port):
 | 
			
		||||
    path = CORE.firmware_bin
 | 
			
		||||
    cmd = ['esptool.py', '--before', 'default_reset', '--after', 'hard_reset',
 | 
			
		||||
           '--chip', 'esp8266', '--port', port, 'write_flash', '0x0', path]
 | 
			
		||||
    first_baudrate = config[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS].get('upload_speed', 460800)
 | 
			
		||||
 | 
			
		||||
    if os.environ.get('ESPHOME_USE_SUBPROCESS') is None:
 | 
			
		||||
        import esptool
 | 
			
		||||
        # pylint: disable=protected-access
 | 
			
		||||
        return run_external_command(esptool._main, *cmd)
 | 
			
		||||
    def run_esptool(baud_rate):
 | 
			
		||||
        cmd = ['esptool.py', '--before', 'default_reset', '--after', 'hard_reset',
 | 
			
		||||
               '--baud', str(baud_rate),
 | 
			
		||||
               '--chip', 'esp8266', '--port', port, 'write_flash', '0x0', path]
 | 
			
		||||
 | 
			
		||||
    return run_external_process(*cmd)
 | 
			
		||||
        if os.environ.get('ESPHOME_USE_SUBPROCESS') is None:
 | 
			
		||||
            import esptool
 | 
			
		||||
            # pylint: disable=protected-access
 | 
			
		||||
            return run_external_command(esptool._main, *cmd)
 | 
			
		||||
 | 
			
		||||
        return run_external_process(*cmd)
 | 
			
		||||
 | 
			
		||||
    rc = run_esptool(first_baudrate)
 | 
			
		||||
    if rc == 0 or first_baudrate == 115200:
 | 
			
		||||
        return rc
 | 
			
		||||
    # 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.",
 | 
			
		||||
                 first_baudrate)
 | 
			
		||||
    return run_esptool(115200)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def upload_program(config, args, host):
 | 
			
		||||
@@ -180,15 +181,18 @@ def upload_program(config, args, host):
 | 
			
		||||
 | 
			
		||||
        if CORE.is_esp8266:
 | 
			
		||||
            return upload_using_esptool(config, host)
 | 
			
		||||
        return platformio_api.run_upload(config, args.verbose, host)
 | 
			
		||||
        return platformio_api.run_upload(config, CORE.verbose, host)
 | 
			
		||||
 | 
			
		||||
    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]
 | 
			
		||||
    remote_port = ota_conf[CONF_PORT]
 | 
			
		||||
    password = ota_conf[CONF_PASSWORD]
 | 
			
		||||
    res = espota2.run_ota(host, remote_port, password, CORE.firmware_bin)
 | 
			
		||||
    return res
 | 
			
		||||
    return espota2.run_ota(host, remote_port, password, CORE.firmware_bin)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def show_logs(config, args, port):
 | 
			
		||||
@@ -218,18 +222,22 @@ def clean_mqtt(config, args):
 | 
			
		||||
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)
 | 
			
		||||
    colorfmt = f"%(log_color)s{fmt}%(reset)s"
 | 
			
		||||
    datefmt = '%H:%M:%S'
 | 
			
		||||
 | 
			
		||||
    logging.getLogger('urllib3').setLevel(logging.WARNING)
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        import colorama
 | 
			
		||||
        colorama.init(strip=True)
 | 
			
		||||
 | 
			
		||||
        from colorlog import ColoredFormatter
 | 
			
		||||
        logging.getLogger().handlers[0].setFormatter(ColoredFormatter(
 | 
			
		||||
            colorfmt,
 | 
			
		||||
@@ -250,12 +258,12 @@ def setup_log(debug=False, quiet=False):
 | 
			
		||||
def command_wizard(args):
 | 
			
		||||
    from esphome import wizard
 | 
			
		||||
 | 
			
		||||
    return wizard.wizard(args.configuration)
 | 
			
		||||
    return wizard.wizard(args.configuration[0])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def command_config(args, config):
 | 
			
		||||
    _LOGGER.info("Configuration is valid!")
 | 
			
		||||
    if not args.verbose:
 | 
			
		||||
    if not CORE.verbose:
 | 
			
		||||
        config = strip_default_ids(config)
 | 
			
		||||
    safe_print(yaml_util.dump(config))
 | 
			
		||||
    return 0
 | 
			
		||||
@@ -264,7 +272,7 @@ def command_config(args, config):
 | 
			
		||||
def command_vscode(args):
 | 
			
		||||
    from esphome import vscode
 | 
			
		||||
 | 
			
		||||
    CORE.config_path = args.configuration
 | 
			
		||||
    CORE.config_path = args.configuration[0]
 | 
			
		||||
    vscode.read_config(args)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -273,12 +281,12 @@ def command_compile(args, config):
 | 
			
		||||
    if exit_code != 0:
 | 
			
		||||
        return exit_code
 | 
			
		||||
    if args.only_generate:
 | 
			
		||||
        _LOGGER.info(u"Successfully generated source code.")
 | 
			
		||||
        _LOGGER.info("Successfully generated source code.")
 | 
			
		||||
        return 0
 | 
			
		||||
    exit_code = compile_program(args, config)
 | 
			
		||||
    if exit_code != 0:
 | 
			
		||||
        return exit_code
 | 
			
		||||
    _LOGGER.info(u"Successfully compiled program.")
 | 
			
		||||
    _LOGGER.info("Successfully compiled program.")
 | 
			
		||||
    return 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -288,7 +296,7 @@ def command_upload(args, config):
 | 
			
		||||
    exit_code = upload_program(config, args, port)
 | 
			
		||||
    if exit_code != 0:
 | 
			
		||||
        return exit_code
 | 
			
		||||
    _LOGGER.info(u"Successfully uploaded program.")
 | 
			
		||||
    _LOGGER.info("Successfully uploaded program.")
 | 
			
		||||
    return 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -305,13 +313,13 @@ def command_run(args, config):
 | 
			
		||||
    exit_code = compile_program(args, config)
 | 
			
		||||
    if exit_code != 0:
 | 
			
		||||
        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,
 | 
			
		||||
                                  show_ota=True, show_mqtt=False, show_api=True)
 | 
			
		||||
    exit_code = upload_program(config, args, port)
 | 
			
		||||
    if exit_code != 0:
 | 
			
		||||
        return exit_code
 | 
			
		||||
    _LOGGER.info(u"Successfully uploaded program.")
 | 
			
		||||
    _LOGGER.info("Successfully uploaded program.")
 | 
			
		||||
    if args.no_logs:
 | 
			
		||||
        return 0
 | 
			
		||||
    port = choose_upload_log_host(default=args.upload_port, check_default=port,
 | 
			
		||||
@@ -330,7 +338,7 @@ def command_mqtt_fingerprint(args, config):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def command_version(args):
 | 
			
		||||
    safe_print(u"Version: {}".format(const.__version__))
 | 
			
		||||
    safe_print(f"Version: {const.__version__}")
 | 
			
		||||
    return 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -350,11 +358,53 @@ def command_dashboard(args):
 | 
			
		||||
    return dashboard.start_web_server(args)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def command_update_all(args):
 | 
			
		||||
    import click
 | 
			
		||||
 | 
			
		||||
    success = {}
 | 
			
		||||
    files = list_yaml_files(args.configuration[0])
 | 
			
		||||
    twidth = 60
 | 
			
		||||
 | 
			
		||||
    def print_bar(middle_text):
 | 
			
		||||
        middle_text = f" {middle_text} "
 | 
			
		||||
        width = len(click.unstyle(middle_text))
 | 
			
		||||
        half_line = "=" * ((twidth - width) // 2)
 | 
			
		||||
        click.echo(f"{half_line}{middle_text}{half_line}")
 | 
			
		||||
 | 
			
		||||
    for f in files:
 | 
			
		||||
        print("Updating {}".format(color('cyan', f)))
 | 
			
		||||
        print('-' * twidth)
 | 
			
		||||
        print()
 | 
			
		||||
        rc = run_external_process('esphome', '--dashboard', f, 'run', '--no-logs', '--upload-port',
 | 
			
		||||
                                  'OTA')
 | 
			
		||||
        if rc == 0:
 | 
			
		||||
            print_bar("[{}] {}".format(color('bold_green', 'SUCCESS'), f))
 | 
			
		||||
            success[f] = True
 | 
			
		||||
        else:
 | 
			
		||||
            print_bar("[{}] {}".format(color('bold_red', 'ERROR'), f))
 | 
			
		||||
            success[f] = False
 | 
			
		||||
 | 
			
		||||
        print()
 | 
			
		||||
        print()
 | 
			
		||||
        print()
 | 
			
		||||
 | 
			
		||||
    print_bar('[{}]'.format(color('bold_white', 'SUMMARY')))
 | 
			
		||||
    failed = 0
 | 
			
		||||
    for f in files:
 | 
			
		||||
        if success[f]:
 | 
			
		||||
            print("  - {}: {}".format(f, color('green', 'SUCCESS')))
 | 
			
		||||
        else:
 | 
			
		||||
            print("  - {}: {}".format(f, color('bold_red', 'FAILED')))
 | 
			
		||||
            failed += 1
 | 
			
		||||
    return failed
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
PRE_CONFIG_ACTIONS = {
 | 
			
		||||
    'wizard': command_wizard,
 | 
			
		||||
    'version': command_version,
 | 
			
		||||
    'dashboard': command_dashboard,
 | 
			
		||||
    'vscode': command_vscode,
 | 
			
		||||
    'update-all': command_update_all,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
POST_CONFIG_ACTIONS = {
 | 
			
		||||
@@ -370,13 +420,15 @@ POST_CONFIG_ACTIONS = {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def parse_args(argv):
 | 
			
		||||
    parser = argparse.ArgumentParser(prog='esphome')
 | 
			
		||||
    parser = argparse.ArgumentParser(description=f'ESPHome v{const.__version__}')
 | 
			
		||||
    parser.add_argument('-v', '--verbose', help="Enable verbose esphome logs.",
 | 
			
		||||
                        action='store_true')
 | 
			
		||||
    parser.add_argument('-q', '--quiet', help="Disable all esphome logs.",
 | 
			
		||||
                        action='store_true')
 | 
			
		||||
    parser.add_argument('--dashboard', help=argparse.SUPPRESS, action='store_true')
 | 
			
		||||
    parser.add_argument('configuration', help='Your YAML configuration file.')
 | 
			
		||||
    parser.add_argument('-s', '--substitution', nargs=2, action='append',
 | 
			
		||||
                        help='Add a substitution', metavar=('key', 'value'))
 | 
			
		||||
    parser.add_argument('configuration', help='Your YAML configuration file.', nargs='*')
 | 
			
		||||
 | 
			
		||||
    subparsers = parser.add_subparsers(help='Commands', dest='command')
 | 
			
		||||
    subparsers.required = True
 | 
			
		||||
@@ -433,7 +485,11 @@ def parse_args(argv):
 | 
			
		||||
                                      help="Create a simple web server for a dashboard.")
 | 
			
		||||
    dashboard.add_argument("--port", help="The HTTP port to open connections on. Defaults to 6052.",
 | 
			
		||||
                           type=int, default=6052)
 | 
			
		||||
    dashboard.add_argument("--password", help="The optional password to require for all requests.",
 | 
			
		||||
    dashboard.add_argument("--username", help="The optional username to require "
 | 
			
		||||
                                              "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')
 | 
			
		||||
@@ -446,6 +502,8 @@ def parse_args(argv):
 | 
			
		||||
    vscode = subparsers.add_parser('vscode', help=argparse.SUPPRESS)
 | 
			
		||||
    vscode.add_argument('--ace', action='store_true')
 | 
			
		||||
 | 
			
		||||
    subparsers.add_parser('update-all', help=argparse.SUPPRESS)
 | 
			
		||||
 | 
			
		||||
    return parser.parse_args(argv[1:])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -454,6 +512,15 @@ def run_esphome(argv):
 | 
			
		||||
    CORE.dashboard = args.dashboard
 | 
			
		||||
 | 
			
		||||
    setup_log(args.verbose, args.quiet)
 | 
			
		||||
    if args.command != 'version' and not args.configuration:
 | 
			
		||||
        _LOGGER.error("Missing configuration parameter, see esphome --help.")
 | 
			
		||||
        return 1
 | 
			
		||||
 | 
			
		||||
    if sys.version_info < (3, 6, 0):
 | 
			
		||||
        _LOGGER.error("You're running ESPHome with Python <3.6. ESPHome is no longer compatible "
 | 
			
		||||
                      "with this Python version. Please reinstall ESPHome with Python 3.6+")
 | 
			
		||||
        return 1
 | 
			
		||||
 | 
			
		||||
    if args.command in PRE_CONFIG_ACTIONS:
 | 
			
		||||
        try:
 | 
			
		||||
            return PRE_CONFIG_ACTIONS[args.command](args)
 | 
			
		||||
@@ -461,21 +528,28 @@ def run_esphome(argv):
 | 
			
		||||
            _LOGGER.error(e)
 | 
			
		||||
            return 1
 | 
			
		||||
 | 
			
		||||
    CORE.config_path = args.configuration
 | 
			
		||||
    for conf_path in args.configuration:
 | 
			
		||||
        CORE.config_path = conf_path
 | 
			
		||||
        CORE.dashboard = args.dashboard
 | 
			
		||||
 | 
			
		||||
    config = read_config(args.verbose)
 | 
			
		||||
    if config is None:
 | 
			
		||||
        return 1
 | 
			
		||||
    CORE.config = config
 | 
			
		||||
        config = read_config(dict(args.substitution) if args.substitution else {})
 | 
			
		||||
        if config is None:
 | 
			
		||||
            return 1
 | 
			
		||||
        CORE.config = config
 | 
			
		||||
 | 
			
		||||
        if args.command not in POST_CONFIG_ACTIONS:
 | 
			
		||||
            safe_print(f"Unknown command {args.command}")
 | 
			
		||||
 | 
			
		||||
    if args.command in POST_CONFIG_ACTIONS:
 | 
			
		||||
        try:
 | 
			
		||||
            return POST_CONFIG_ACTIONS[args.command](args, config)
 | 
			
		||||
            rc = POST_CONFIG_ACTIONS[args.command](args, config)
 | 
			
		||||
        except EsphomeError as e:
 | 
			
		||||
            _LOGGER.error(e)
 | 
			
		||||
            return 1
 | 
			
		||||
    safe_print(u"Unknown command {}".format(args.command))
 | 
			
		||||
    return 1
 | 
			
		||||
        if rc != 0:
 | 
			
		||||
            return rc
 | 
			
		||||
 | 
			
		||||
        CORE.reset()
 | 
			
		||||
    return 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def main():
 | 
			
		||||
 
 | 
			
		||||
@@ -1,330 +0,0 @@
 | 
			
		||||
syntax = "proto3";
 | 
			
		||||
 | 
			
		||||
// The Home Assistant protocol is structured as a simple
 | 
			
		||||
// TCP socket with short binary messages encoded in the protocol buffers format
 | 
			
		||||
// First, a message in this protocol has a specific format:
 | 
			
		||||
//  * VarInt denoting the size of the message object. (type is not part of this)
 | 
			
		||||
//  * VarInt denoting the type of message.
 | 
			
		||||
//  * The message object encoded as a ProtoBuf message
 | 
			
		||||
 | 
			
		||||
// The connection is established in 4 steps:
 | 
			
		||||
//  * First, the client connects to the server and sends a "Hello Request" identifying itself
 | 
			
		||||
//  * The server responds with a "Hello Response" and selects the protocol version
 | 
			
		||||
//  * After receiving this message, the client attempts to authenticate itself using
 | 
			
		||||
//    the password and a "Connect Request"
 | 
			
		||||
//  * The server responds with a "Connect Response" and notifies of invalid password.
 | 
			
		||||
// If anything in this initial process fails, the connection must immediately closed
 | 
			
		||||
// by both sides and _no_ disconnection message is to be sent.
 | 
			
		||||
 | 
			
		||||
// Message sent at the beginning of each connection
 | 
			
		||||
// Can only be sent by the client and only at the beginning of the connection
 | 
			
		||||
message HelloRequest {
 | 
			
		||||
  // Description of client (like User Agent)
 | 
			
		||||
  // For example "Home Assistant"
 | 
			
		||||
  // Not strictly necessary to send but nice for debugging
 | 
			
		||||
  // purposes.
 | 
			
		||||
  string client_info = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Confirmation of successful connection request.
 | 
			
		||||
// Can only be sent by the server and only at the beginning of the connection
 | 
			
		||||
message HelloResponse {
 | 
			
		||||
  // The version of the API to use. The _client_ (for example Home Assistant) needs to check
 | 
			
		||||
  // for compatibility and if necessary adopt to an older API.
 | 
			
		||||
  // Major is for breaking changes in the base protocol - a mismatch will lead to immediate disconnect_client_
 | 
			
		||||
  // Minor is for breaking changes in individual messages - a mismatch will lead to a warning message
 | 
			
		||||
  uint32 api_version_major = 1;
 | 
			
		||||
  uint32 api_version_minor = 2;
 | 
			
		||||
 | 
			
		||||
  // A string identifying the server (ESP); like client info this may be empty
 | 
			
		||||
  // and only exists for debugging/logging purposes.
 | 
			
		||||
  // For example "ESPHome v1.10.0 on ESP8266"
 | 
			
		||||
  string server_info = 3;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Message sent at the beginning of each connection to authenticate the client
 | 
			
		||||
// Can only be sent by the client and only at the beginning of the connection
 | 
			
		||||
message ConnectRequest {
 | 
			
		||||
  // The password to log in with
 | 
			
		||||
  string password = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Confirmation of successful connection. After this the connection is available for all traffic.
 | 
			
		||||
// Can only be sent by the server and only at the beginning of the connection
 | 
			
		||||
message ConnectResponse {
 | 
			
		||||
  bool invalid_password = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Request to close the connection.
 | 
			
		||||
// Can be sent by both the client and server
 | 
			
		||||
message DisconnectRequest {
 | 
			
		||||
  // Do not close the connection before the acknowledgement arrives
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message DisconnectResponse {
 | 
			
		||||
  // Empty - Both parties are required to close the connection after this
 | 
			
		||||
  // message has been received.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message PingRequest {
 | 
			
		||||
  // Empty
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message PingResponse {
 | 
			
		||||
  // Empty
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message DeviceInfoRequest {
 | 
			
		||||
  // Empty
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message DeviceInfoResponse {
 | 
			
		||||
  bool uses_password = 1;
 | 
			
		||||
 | 
			
		||||
  // The name of the node, given by "App.set_name()"
 | 
			
		||||
  string name = 2;
 | 
			
		||||
 | 
			
		||||
  // The mac address of the device. For example "AC:BC:32:89:0E:A9"
 | 
			
		||||
  string mac_address = 3;
 | 
			
		||||
 | 
			
		||||
  // A string describing the ESPHome version. For example "1.10.0"
 | 
			
		||||
  string esphome_core_version = 4;
 | 
			
		||||
 | 
			
		||||
  // A string describing the date of compilation, this is generated by the compiler
 | 
			
		||||
  // and therefore may not be in the same format all the time.
 | 
			
		||||
  // If the user isn't using esphome, this will also not be set.
 | 
			
		||||
  string compilation_time = 5;
 | 
			
		||||
 | 
			
		||||
  // The model of the board. For example NodeMCU
 | 
			
		||||
  string model = 6;
 | 
			
		||||
 | 
			
		||||
  bool has_deep_sleep = 7;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message ListEntitiesRequest {
 | 
			
		||||
  // Empty
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message ListEntitiesBinarySensorResponse {
 | 
			
		||||
  string object_id = 1;
 | 
			
		||||
  fixed32 key = 2;
 | 
			
		||||
  string name = 3;
 | 
			
		||||
  string unique_id = 4;
 | 
			
		||||
 | 
			
		||||
  string device_class = 5;
 | 
			
		||||
  bool is_status_binary_sensor = 6;
 | 
			
		||||
}
 | 
			
		||||
message ListEntitiesCoverResponse {
 | 
			
		||||
  string object_id = 1;
 | 
			
		||||
  fixed32 key = 2;
 | 
			
		||||
  string name = 3;
 | 
			
		||||
  string unique_id = 4;
 | 
			
		||||
 | 
			
		||||
  bool is_optimistic = 5;
 | 
			
		||||
}
 | 
			
		||||
message ListEntitiesFanResponse {
 | 
			
		||||
  string object_id = 1;
 | 
			
		||||
  fixed32 key = 2;
 | 
			
		||||
  string name = 3;
 | 
			
		||||
  string unique_id = 4;
 | 
			
		||||
 | 
			
		||||
  bool supports_oscillation = 5;
 | 
			
		||||
  bool supports_speed = 6;
 | 
			
		||||
}
 | 
			
		||||
message ListEntitiesLightResponse {
 | 
			
		||||
  string object_id = 1;
 | 
			
		||||
  fixed32 key = 2;
 | 
			
		||||
  string name = 3;
 | 
			
		||||
  string unique_id = 4;
 | 
			
		||||
 | 
			
		||||
  bool supports_brightness = 5;
 | 
			
		||||
  bool supports_rgb = 6;
 | 
			
		||||
  bool supports_white_value = 7;
 | 
			
		||||
  bool supports_color_temperature = 8;
 | 
			
		||||
  float min_mireds = 9;
 | 
			
		||||
  float max_mireds = 10;
 | 
			
		||||
  repeated string effects = 11;
 | 
			
		||||
}
 | 
			
		||||
message ListEntitiesSensorResponse {
 | 
			
		||||
  string object_id = 1;
 | 
			
		||||
  fixed32 key = 2;
 | 
			
		||||
  string name = 3;
 | 
			
		||||
  string unique_id = 4;
 | 
			
		||||
 | 
			
		||||
  string icon = 5;
 | 
			
		||||
  string unit_of_measurement = 6;
 | 
			
		||||
  int32 accuracy_decimals = 7;
 | 
			
		||||
}
 | 
			
		||||
message ListEntitiesSwitchResponse {
 | 
			
		||||
  string object_id = 1;
 | 
			
		||||
  fixed32 key = 2;
 | 
			
		||||
  string name = 3;
 | 
			
		||||
  string unique_id = 4;
 | 
			
		||||
 | 
			
		||||
  string icon = 5;
 | 
			
		||||
  bool optimistic = 6;
 | 
			
		||||
}
 | 
			
		||||
message ListEntitiesTextSensorResponse {
 | 
			
		||||
  string object_id = 1;
 | 
			
		||||
  fixed32 key = 2;
 | 
			
		||||
  string name = 3;
 | 
			
		||||
  string unique_id = 4;
 | 
			
		||||
 | 
			
		||||
  string icon = 5;
 | 
			
		||||
}
 | 
			
		||||
message ListEntitiesDoneResponse {
 | 
			
		||||
  // Empty
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message SubscribeStatesRequest {
 | 
			
		||||
  // Empty
 | 
			
		||||
}
 | 
			
		||||
message BinarySensorStateResponse {
 | 
			
		||||
  fixed32 key = 1;
 | 
			
		||||
  bool state = 2;
 | 
			
		||||
}
 | 
			
		||||
message CoverStateResponse {
 | 
			
		||||
  fixed32 key = 1;
 | 
			
		||||
  enum CoverState {
 | 
			
		||||
    OPEN = 0;
 | 
			
		||||
    CLOSED = 1;
 | 
			
		||||
  }
 | 
			
		||||
  CoverState state = 2;
 | 
			
		||||
}
 | 
			
		||||
enum FanSpeed {
 | 
			
		||||
  LOW = 0;
 | 
			
		||||
  MEDIUM = 1;
 | 
			
		||||
  HIGH = 2;
 | 
			
		||||
}
 | 
			
		||||
message FanStateResponse {
 | 
			
		||||
  fixed32 key = 1;
 | 
			
		||||
  bool state = 2;
 | 
			
		||||
  bool oscillating = 3;
 | 
			
		||||
  FanSpeed speed = 4;
 | 
			
		||||
}
 | 
			
		||||
message LightStateResponse {
 | 
			
		||||
  fixed32 key = 1;
 | 
			
		||||
  bool state = 2;
 | 
			
		||||
  float brightness = 3;
 | 
			
		||||
  float red = 4;
 | 
			
		||||
  float green = 5;
 | 
			
		||||
  float blue = 6;
 | 
			
		||||
  float white = 7;
 | 
			
		||||
  float color_temperature = 8;
 | 
			
		||||
  string effect = 9;
 | 
			
		||||
}
 | 
			
		||||
message SensorStateResponse {
 | 
			
		||||
  fixed32 key = 1;
 | 
			
		||||
  float state = 2;
 | 
			
		||||
}
 | 
			
		||||
message SwitchStateResponse {
 | 
			
		||||
  fixed32 key = 1;
 | 
			
		||||
  bool state = 2;
 | 
			
		||||
}
 | 
			
		||||
message TextSensorStateResponse {
 | 
			
		||||
  fixed32 key = 1;
 | 
			
		||||
  string state = 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message CoverCommandRequest {
 | 
			
		||||
  fixed32 key = 1;
 | 
			
		||||
  enum CoverCommand {
 | 
			
		||||
    OPEN = 0;
 | 
			
		||||
    CLOSE = 1;
 | 
			
		||||
    STOP = 2;
 | 
			
		||||
  }
 | 
			
		||||
  bool has_state = 2;
 | 
			
		||||
  CoverCommand command = 3;
 | 
			
		||||
}
 | 
			
		||||
message FanCommandRequest {
 | 
			
		||||
  fixed32 key = 1;
 | 
			
		||||
  bool has_state = 2;
 | 
			
		||||
  bool state = 3;
 | 
			
		||||
  bool has_speed = 4;
 | 
			
		||||
  FanSpeed speed = 5;
 | 
			
		||||
  bool has_oscillating = 6;
 | 
			
		||||
  bool oscillating = 7;
 | 
			
		||||
}
 | 
			
		||||
message LightCommandRequest {
 | 
			
		||||
  fixed32 key = 1;
 | 
			
		||||
  bool has_state = 2;
 | 
			
		||||
  bool state = 3;
 | 
			
		||||
  bool has_brightness = 4;
 | 
			
		||||
  float brightness = 5;
 | 
			
		||||
  bool has_rgb = 6;
 | 
			
		||||
  float red = 7;
 | 
			
		||||
  float green = 8;
 | 
			
		||||
  float blue = 9;
 | 
			
		||||
  bool has_white = 10;
 | 
			
		||||
  float white = 11;
 | 
			
		||||
  bool has_color_temperature = 12;
 | 
			
		||||
  float color_temperature = 13;
 | 
			
		||||
  bool has_transition_length = 14;
 | 
			
		||||
  uint32 transition_length = 15;
 | 
			
		||||
  bool has_flash_length = 16;
 | 
			
		||||
  uint32 flash_length = 17;
 | 
			
		||||
  bool has_effect = 18;
 | 
			
		||||
  string effect = 19;
 | 
			
		||||
}
 | 
			
		||||
message SwitchCommandRequest {
 | 
			
		||||
  fixed32 key = 1;
 | 
			
		||||
  bool state = 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum LogLevel {
 | 
			
		||||
  NONE = 0;
 | 
			
		||||
  ERROR = 1;
 | 
			
		||||
  WARN = 2;
 | 
			
		||||
  INFO = 3;
 | 
			
		||||
  DEBUG = 4;
 | 
			
		||||
  VERBOSE = 5;
 | 
			
		||||
  VERY_VERBOSE = 6;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message SubscribeLogsRequest {
 | 
			
		||||
  LogLevel level = 1;
 | 
			
		||||
  bool dump_config = 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message SubscribeLogsResponse {
 | 
			
		||||
  LogLevel level = 1;
 | 
			
		||||
  string tag = 2;
 | 
			
		||||
  string message = 3;
 | 
			
		||||
  bool send_failed = 4;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message SubscribeServiceCallsRequest {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message ServiceCallResponse {
 | 
			
		||||
  string service = 1;
 | 
			
		||||
  map<string, string> data = 2;
 | 
			
		||||
  map<string, string> data_template = 3;
 | 
			
		||||
  map<string, string> variables = 4;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 1. Client sends SubscribeHomeAssistantStatesRequest
 | 
			
		||||
// 2. Server responds with zero or more SubscribeHomeAssistantStateResponse (async)
 | 
			
		||||
// 3. Client sends HomeAssistantStateResponse for state changes.
 | 
			
		||||
message SubscribeHomeAssistantStatesRequest {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message SubscribeHomeAssistantStateResponse {
 | 
			
		||||
  string entity_id = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message HomeAssistantStateResponse {
 | 
			
		||||
  string entity_id = 1;
 | 
			
		||||
  string state = 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message GetTimeRequest {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message GetTimeResponse {
 | 
			
		||||
  fixed32 epoch_seconds = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -14,7 +14,6 @@ import esphome.api.api_pb2 as pb
 | 
			
		||||
from esphome.const import CONF_PASSWORD, CONF_PORT
 | 
			
		||||
from esphome.core import EsphomeError
 | 
			
		||||
from esphome.helpers import resolve_ip_address, indent, color
 | 
			
		||||
from esphome.py_compat import text_type, IS_PY2, byte_to_bytes, char_to_byte, format_bytes
 | 
			
		||||
from esphome.util import safe_print
 | 
			
		||||
 | 
			
		||||
_LOGGER = logging.getLogger(__name__)
 | 
			
		||||
@@ -67,16 +66,16 @@ MESSAGE_TYPE_TO_PROTO = {
 | 
			
		||||
 | 
			
		||||
def _varuint_to_bytes(value):
 | 
			
		||||
    if value <= 0x7F:
 | 
			
		||||
        return byte_to_bytes(value)
 | 
			
		||||
        return bytes([value])
 | 
			
		||||
 | 
			
		||||
    ret = bytes()
 | 
			
		||||
    while value:
 | 
			
		||||
        temp = value & 0x7F
 | 
			
		||||
        value >>= 7
 | 
			
		||||
        if value:
 | 
			
		||||
            ret += byte_to_bytes(temp | 0x80)
 | 
			
		||||
            ret += bytes([temp | 0x80])
 | 
			
		||||
        else:
 | 
			
		||||
            ret += byte_to_bytes(temp)
 | 
			
		||||
            ret += bytes([temp])
 | 
			
		||||
 | 
			
		||||
    return ret
 | 
			
		||||
 | 
			
		||||
@@ -84,8 +83,7 @@ def _varuint_to_bytes(value):
 | 
			
		||||
def _bytes_to_varuint(value):
 | 
			
		||||
    result = 0
 | 
			
		||||
    bitpos = 0
 | 
			
		||||
    for c in value:
 | 
			
		||||
        val = char_to_byte(c)
 | 
			
		||||
    for val in value:
 | 
			
		||||
        result |= (val & 0x7F) << bitpos
 | 
			
		||||
        bitpos += 7
 | 
			
		||||
        if (val & 0x80) == 0:
 | 
			
		||||
@@ -108,7 +106,6 @@ class APIClient(threading.Thread):
 | 
			
		||||
        self._message_handlers = []
 | 
			
		||||
        self._keepalive = 5
 | 
			
		||||
        self._ping_timer = None
 | 
			
		||||
        self._refresh_ping()
 | 
			
		||||
 | 
			
		||||
        self.on_disconnect = None
 | 
			
		||||
        self.on_connect = None
 | 
			
		||||
@@ -132,8 +129,8 @@ class APIClient(threading.Thread):
 | 
			
		||||
            if self._connected:
 | 
			
		||||
                try:
 | 
			
		||||
                    self.ping()
 | 
			
		||||
                except APIConnectionError:
 | 
			
		||||
                    self._fatal_error()
 | 
			
		||||
                except APIConnectionError as err:
 | 
			
		||||
                    self._fatal_error(err)
 | 
			
		||||
                else:
 | 
			
		||||
                    self._refresh_ping()
 | 
			
		||||
 | 
			
		||||
@@ -175,7 +172,7 @@ class APIClient(threading.Thread):
 | 
			
		||||
            raise APIConnectionError("You need to call start() first!")
 | 
			
		||||
 | 
			
		||||
        if self._connected:
 | 
			
		||||
            raise APIConnectionError("Already connected!")
 | 
			
		||||
            self.disconnect(on_disconnect=False)
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            ip = resolve_ip_address(self._address)
 | 
			
		||||
@@ -184,7 +181,7 @@ class APIClient(threading.Thread):
 | 
			
		||||
                            self._address)
 | 
			
		||||
            _LOGGER.warning("(If this error persists, please set a static IP address: "
 | 
			
		||||
                            "https://esphome.io/components/wifi.html#manual-ips)")
 | 
			
		||||
            raise APIConnectionError(err)
 | 
			
		||||
            raise APIConnectionError(err) from err
 | 
			
		||||
 | 
			
		||||
        _LOGGER.info("Connecting to %s:%s (%s)", self._address, self._port, ip)
 | 
			
		||||
        self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 | 
			
		||||
@@ -192,30 +189,33 @@ class APIClient(threading.Thread):
 | 
			
		||||
        self._socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
 | 
			
		||||
        try:
 | 
			
		||||
            self._socket.connect((ip, self._port))
 | 
			
		||||
        except socket.error as err:
 | 
			
		||||
            self._fatal_error()
 | 
			
		||||
            raise APIConnectionError("Error connecting to {}: {}".format(ip, err))
 | 
			
		||||
        except OSError as err:
 | 
			
		||||
            err = APIConnectionError(f"Error connecting to {ip}: {err}")
 | 
			
		||||
            self._fatal_error(err)
 | 
			
		||||
            raise err
 | 
			
		||||
        self._socket.settimeout(0.1)
 | 
			
		||||
 | 
			
		||||
        self._socket_open_event.set()
 | 
			
		||||
 | 
			
		||||
        hello = pb.HelloRequest()
 | 
			
		||||
        hello.client_info = 'ESPHome v{}'.format(const.__version__)
 | 
			
		||||
        hello.client_info = f'ESPHome v{const.__version__}'
 | 
			
		||||
        try:
 | 
			
		||||
            resp = self._send_message_await_response(hello, pb.HelloResponse)
 | 
			
		||||
        except APIConnectionError as err:
 | 
			
		||||
            self._fatal_error()
 | 
			
		||||
            self._fatal_error(err)
 | 
			
		||||
            raise err
 | 
			
		||||
        _LOGGER.debug("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._refresh_ping()
 | 
			
		||||
        if self.on_connect is not None:
 | 
			
		||||
            self.on_connect()
 | 
			
		||||
 | 
			
		||||
    def _check_connected(self):
 | 
			
		||||
        if not self._connected:
 | 
			
		||||
            self._fatal_error()
 | 
			
		||||
            raise APIConnectionError("Must be connected!")
 | 
			
		||||
            err = APIConnectionError("Must be connected!")
 | 
			
		||||
            self._fatal_error(err)
 | 
			
		||||
            raise err
 | 
			
		||||
 | 
			
		||||
    def login(self):
 | 
			
		||||
        self._check_connected()
 | 
			
		||||
@@ -233,25 +233,26 @@ class APIClient(threading.Thread):
 | 
			
		||||
        if self.on_login is not None:
 | 
			
		||||
            self.on_login()
 | 
			
		||||
 | 
			
		||||
    def _fatal_error(self):
 | 
			
		||||
    def _fatal_error(self, err):
 | 
			
		||||
        was_connected = self._connected
 | 
			
		||||
 | 
			
		||||
        self._close_socket()
 | 
			
		||||
 | 
			
		||||
        if was_connected and self.on_disconnect is not None:
 | 
			
		||||
            self.on_disconnect()
 | 
			
		||||
            self.on_disconnect(err)
 | 
			
		||||
 | 
			
		||||
    def _write(self, data):  # type: (bytes) -> None
 | 
			
		||||
        if self._socket is None:
 | 
			
		||||
            raise APIConnectionError("Socket closed")
 | 
			
		||||
 | 
			
		||||
        _LOGGER.debug("Write: %s", format_bytes(data))
 | 
			
		||||
        # _LOGGER.debug("Write: %s", format_bytes(data))
 | 
			
		||||
        with self._socket_write_lock:
 | 
			
		||||
            try:
 | 
			
		||||
                self._socket.sendall(data)
 | 
			
		||||
            except socket.error as err:
 | 
			
		||||
                self._fatal_error()
 | 
			
		||||
                raise APIConnectionError("Error while writing data: {}".format(err))
 | 
			
		||||
            except OSError as err:
 | 
			
		||||
                err = APIConnectionError(f"Error while writing data: {err}")
 | 
			
		||||
                self._fatal_error(err)
 | 
			
		||||
                raise err
 | 
			
		||||
 | 
			
		||||
    def _send_message(self, msg):
 | 
			
		||||
        # type: (message.Message) -> None
 | 
			
		||||
@@ -262,18 +263,14 @@ class APIClient(threading.Thread):
 | 
			
		||||
            raise ValueError
 | 
			
		||||
 | 
			
		||||
        encoded = msg.SerializeToString()
 | 
			
		||||
        _LOGGER.debug("Sending %s:\n%s", type(msg), indent(text_type(msg)))
 | 
			
		||||
        if IS_PY2:
 | 
			
		||||
            req = chr(0x00)
 | 
			
		||||
        else:
 | 
			
		||||
            req = bytes([0])
 | 
			
		||||
        _LOGGER.debug("Sending %s:\n%s", type(msg), indent(str(msg)))
 | 
			
		||||
        req = bytes([0])
 | 
			
		||||
        req += _varuint_to_bytes(len(encoded))
 | 
			
		||||
        req += _varuint_to_bytes(message_type)
 | 
			
		||||
        req += encoded
 | 
			
		||||
        self._write(req)
 | 
			
		||||
        self._refresh_ping()
 | 
			
		||||
 | 
			
		||||
    def _send_message_await_response_complex(self, send_msg, do_append, do_stop, timeout=1):
 | 
			
		||||
    def _send_message_await_response_complex(self, send_msg, do_append, do_stop, timeout=5):
 | 
			
		||||
        event = threading.Event()
 | 
			
		||||
        responses = []
 | 
			
		||||
 | 
			
		||||
@@ -294,7 +291,7 @@ class APIClient(threading.Thread):
 | 
			
		||||
            raise APIConnectionError("Timeout while waiting for message response!")
 | 
			
		||||
        return responses
 | 
			
		||||
 | 
			
		||||
    def _send_message_await_response(self, send_msg, response_type, timeout=1):
 | 
			
		||||
    def _send_message_await_response(self, send_msg, response_type, timeout=5):
 | 
			
		||||
        def is_response(msg):
 | 
			
		||||
            return isinstance(msg, response_type)
 | 
			
		||||
 | 
			
		||||
@@ -309,7 +306,7 @@ class APIClient(threading.Thread):
 | 
			
		||||
        self._check_connected()
 | 
			
		||||
        return self._send_message_await_response(pb.PingRequest(), pb.PingResponse)
 | 
			
		||||
 | 
			
		||||
    def disconnect(self):
 | 
			
		||||
    def disconnect(self, on_disconnect=True):
 | 
			
		||||
        self._check_connected()
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
@@ -318,14 +315,14 @@ class APIClient(threading.Thread):
 | 
			
		||||
            pass
 | 
			
		||||
        self._close_socket()
 | 
			
		||||
 | 
			
		||||
        if self.on_disconnect is not None:
 | 
			
		||||
            self.on_disconnect()
 | 
			
		||||
        if self.on_disconnect is not None and on_disconnect:
 | 
			
		||||
            self.on_disconnect(None)
 | 
			
		||||
 | 
			
		||||
    def _check_authenticated(self):
 | 
			
		||||
        if not self._authenticated:
 | 
			
		||||
            raise APIConnectionError("Must login first!")
 | 
			
		||||
 | 
			
		||||
    def subscribe_logs(self, on_log, log_level=None, dump_config=False):
 | 
			
		||||
    def subscribe_logs(self, on_log, log_level=7, dump_config=False):
 | 
			
		||||
        self._check_authenticated()
 | 
			
		||||
 | 
			
		||||
        def on_msg(msg):
 | 
			
		||||
@@ -334,8 +331,7 @@ class APIClient(threading.Thread):
 | 
			
		||||
 | 
			
		||||
        self._message_handlers.append(on_msg)
 | 
			
		||||
        req = pb.SubscribeLogsRequest(dump_config=dump_config)
 | 
			
		||||
        if log_level is not None:
 | 
			
		||||
            req.level = log_level
 | 
			
		||||
        req.level = log_level
 | 
			
		||||
        self._send_message(req)
 | 
			
		||||
 | 
			
		||||
    def _recv(self, amount):
 | 
			
		||||
@@ -350,18 +346,18 @@ class APIClient(threading.Thread):
 | 
			
		||||
                raise APIConnectionError("No socket!")
 | 
			
		||||
            try:
 | 
			
		||||
                val = self._socket.recv(amount - len(ret))
 | 
			
		||||
            except AttributeError:
 | 
			
		||||
                raise APIConnectionError("Socket was closed")
 | 
			
		||||
            except AttributeError as err:
 | 
			
		||||
                raise APIConnectionError("Socket was closed") from err
 | 
			
		||||
            except socket.timeout:
 | 
			
		||||
                continue
 | 
			
		||||
            except socket.error as err:
 | 
			
		||||
                raise APIConnectionError("Error while receiving data: {}".format(err))
 | 
			
		||||
            except OSError as err:
 | 
			
		||||
                raise APIConnectionError(f"Error while receiving data: {err}") from err
 | 
			
		||||
            ret += val
 | 
			
		||||
        return ret
 | 
			
		||||
 | 
			
		||||
    def _recv_varint(self):
 | 
			
		||||
        raw = bytes()
 | 
			
		||||
        while not raw or char_to_byte(raw[-1]) & 0x80:
 | 
			
		||||
        while not raw or raw[-1] & 0x80:
 | 
			
		||||
            raw += self._recv(1)
 | 
			
		||||
        return _bytes_to_varuint(raw)
 | 
			
		||||
 | 
			
		||||
@@ -370,7 +366,7 @@ class APIClient(threading.Thread):
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        # Preamble
 | 
			
		||||
        if char_to_byte(self._recv(1)[0]) != 0x00:
 | 
			
		||||
        if self._recv(1)[0] != 0x00:
 | 
			
		||||
            raise APIConnectionError("Invalid preamble")
 | 
			
		||||
 | 
			
		||||
        length = self._recv_varint()
 | 
			
		||||
@@ -387,7 +383,6 @@ class APIClient(threading.Thread):
 | 
			
		||||
        for msg_handler in self._message_handlers[:]:
 | 
			
		||||
            msg_handler(msg)
 | 
			
		||||
        self._handle_internal_messages(msg)
 | 
			
		||||
        self._refresh_ping()
 | 
			
		||||
 | 
			
		||||
    def run(self):
 | 
			
		||||
        self._running_event.set()
 | 
			
		||||
@@ -399,7 +394,7 @@ class APIClient(threading.Thread):
 | 
			
		||||
                    break
 | 
			
		||||
                if self._connected:
 | 
			
		||||
                    _LOGGER.error("Error while reading incoming messages: %s", err)
 | 
			
		||||
                    self._fatal_error()
 | 
			
		||||
                    self._fatal_error(err)
 | 
			
		||||
        self._running_event.clear()
 | 
			
		||||
 | 
			
		||||
    def _handle_internal_messages(self, msg):
 | 
			
		||||
@@ -410,7 +405,7 @@ class APIClient(threading.Thread):
 | 
			
		||||
                self._socket = None
 | 
			
		||||
            self._connected = False
 | 
			
		||||
            if self.on_disconnect is not None:
 | 
			
		||||
                self.on_disconnect()
 | 
			
		||||
                self.on_disconnect(None)
 | 
			
		||||
        elif isinstance(msg, pb.PingRequest):
 | 
			
		||||
            self._send_message(pb.PingResponse())
 | 
			
		||||
        elif isinstance(msg, pb.GetTimeRequest):
 | 
			
		||||
@@ -431,12 +426,12 @@ def run_logs(config, address):
 | 
			
		||||
 | 
			
		||||
    has_connects = []
 | 
			
		||||
 | 
			
		||||
    def try_connect(tries=0, is_disconnect=True):
 | 
			
		||||
    def try_connect(err, tries=0):
 | 
			
		||||
        if stopping:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        if is_disconnect:
 | 
			
		||||
            _LOGGER.warning(u"Disconnected from API.")
 | 
			
		||||
        if err:
 | 
			
		||||
            _LOGGER.warning("Disconnected from API: %s", err)
 | 
			
		||||
 | 
			
		||||
        while retry_timer:
 | 
			
		||||
            retry_timer.pop(0).cancel()
 | 
			
		||||
@@ -445,27 +440,27 @@ def run_logs(config, address):
 | 
			
		||||
        try:
 | 
			
		||||
            cli.connect()
 | 
			
		||||
            cli.login()
 | 
			
		||||
        except APIConnectionError as err:  # noqa
 | 
			
		||||
            error = err
 | 
			
		||||
        except APIConnectionError as err2:  # noqa
 | 
			
		||||
            error = err2
 | 
			
		||||
 | 
			
		||||
        if error is None:
 | 
			
		||||
            _LOGGER.info("Successfully connected to %s", address)
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        wait_time = min(2**tries, 300)
 | 
			
		||||
        wait_time = int(min(1.5**min(tries, 100), 30))
 | 
			
		||||
        if not has_connects:
 | 
			
		||||
            _LOGGER.warning(u"Initial connection failed. The ESP might not be connected "
 | 
			
		||||
                            u"to WiFi yet (%s). Re-Trying in %s seconds",
 | 
			
		||||
            _LOGGER.warning("Initial connection failed. The ESP might not be connected "
 | 
			
		||||
                            "to WiFi yet (%s). Re-Trying in %s seconds",
 | 
			
		||||
                            error, wait_time)
 | 
			
		||||
        else:
 | 
			
		||||
            _LOGGER.warning(u"Couldn't connect to API (%s). Trying to reconnect in %s seconds",
 | 
			
		||||
            _LOGGER.warning("Couldn't connect to API (%s). Trying to reconnect in %s seconds",
 | 
			
		||||
                            error, wait_time)
 | 
			
		||||
        timer = threading.Timer(wait_time, functools.partial(try_connect, tries + 1, is_disconnect))
 | 
			
		||||
        timer = threading.Timer(wait_time, functools.partial(try_connect, None, tries + 1))
 | 
			
		||||
        timer.start()
 | 
			
		||||
        retry_timer.append(timer)
 | 
			
		||||
 | 
			
		||||
    def on_log(msg):
 | 
			
		||||
        time_ = datetime.now().time().strftime(u'[%H:%M:%S]')
 | 
			
		||||
        time_ = datetime.now().time().strftime('[%H:%M:%S]')
 | 
			
		||||
        text = msg.message
 | 
			
		||||
        if msg.send_failed:
 | 
			
		||||
            text = color('white', '(Message skipped because it was too big to fit in '
 | 
			
		||||
@@ -484,7 +479,7 @@ def run_logs(config, address):
 | 
			
		||||
    cli.start()
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        try_connect(is_disconnect=False)
 | 
			
		||||
        try_connect(None)
 | 
			
		||||
        while True:
 | 
			
		||||
            time.sleep(1)
 | 
			
		||||
    except KeyboardInterrupt:
 | 
			
		||||
 
 | 
			
		||||
@@ -7,13 +7,17 @@ from esphome.util import Registry
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def maybe_simple_id(*validators):
 | 
			
		||||
    return maybe_conf(CONF_ID, *validators)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def maybe_conf(conf, *validators):
 | 
			
		||||
    validator = cv.All(*validators)
 | 
			
		||||
 | 
			
		||||
    def validate(value):
 | 
			
		||||
        if isinstance(value, dict):
 | 
			
		||||
            return validator(value)
 | 
			
		||||
        with cv.remove_prepend_path([CONF_ID]):
 | 
			
		||||
            return validator({CONF_ID: value})
 | 
			
		||||
        with cv.remove_prepend_path([conf]):
 | 
			
		||||
            return validator({conf: value})
 | 
			
		||||
 | 
			
		||||
    return validate
 | 
			
		||||
 | 
			
		||||
@@ -55,7 +59,7 @@ UpdateComponentAction = cg.esphome_ns.class_('UpdateComponentAction', Action)
 | 
			
		||||
Automation = cg.esphome_ns.class_('Automation')
 | 
			
		||||
 | 
			
		||||
LambdaCondition = cg.esphome_ns.class_('LambdaCondition', Condition)
 | 
			
		||||
ForCondition = cg.esphome_ns.class_('ForCondition', Condition)
 | 
			
		||||
ForCondition = cg.esphome_ns.class_('ForCondition', Condition, cg.Component)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_automation(extra_schema=None, extra_validators=None, single=False):
 | 
			
		||||
@@ -79,9 +83,10 @@ def validate_automation(extra_schema=None, extra_validators=None, single=False):
 | 
			
		||||
                try:
 | 
			
		||||
                    return cv.Schema([schema])(value)
 | 
			
		||||
                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
 | 
			
		||||
                    if u'Unable to find action' in str(err):
 | 
			
		||||
                    if 'Unable to find action' in str(err):
 | 
			
		||||
                        raise err2
 | 
			
		||||
                    raise cv.MultipleInvalid([err, err2])
 | 
			
		||||
        elif isinstance(value, dict):
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,7 @@ from esphome.cpp_helpers import (  # noqa
 | 
			
		||||
    gpio_pin_expression, register_component, build_registry_entry,
 | 
			
		||||
    build_registry_list, extract_registry_entry_config, register_parented)
 | 
			
		||||
from esphome.cpp_types import (  # noqa
 | 
			
		||||
    global_ns, void, nullptr, float_, double, bool_, std_ns, std_string,
 | 
			
		||||
    global_ns, void, nullptr, float_, 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,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
							
								
								
									
										45
									
								
								esphome/components/ac_dimmer/output.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								esphome/components/ac_dimmer/output.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
			
		||||
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)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
 | 
			
		||||
    # override default min power to 10%
 | 
			
		||||
    if CONF_MIN_POWER not in config:
 | 
			
		||||
        config[CONF_MIN_POWER] = 0.1
 | 
			
		||||
    yield output.register_output(var, config)
 | 
			
		||||
 | 
			
		||||
    pin = yield cg.gpio_pin_expression(config[CONF_GATE_PIN])
 | 
			
		||||
    cg.add(var.set_gate_pin(pin))
 | 
			
		||||
    pin = yield 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]))
 | 
			
		||||
							
								
								
									
										24
									
								
								esphome/components/adalight/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								esphome/components/adalight/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
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)
 | 
			
		||||
})
 | 
			
		||||
def adalight_light_effect_to_code(config, effect_id):
 | 
			
		||||
    effect = cg.new_Pvariable(effect_id, config[CONF_NAME])
 | 
			
		||||
    yield uart.register_uart_device(effect, config)
 | 
			
		||||
 | 
			
		||||
    yield 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(light::ESPColor::BLACK);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AdalightLightEffect::apply(light::AddressableLight &it, const light::ESPColor ¤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(light::ESPColor(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 light::ESPColor ¤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']
 | 
			
		||||
 
 | 
			
		||||
@@ -58,7 +58,7 @@ void ADCSensor::update() {
 | 
			
		||||
}
 | 
			
		||||
float ADCSensor::sample() {
 | 
			
		||||
#ifdef ARDUINO_ARCH_ESP32
 | 
			
		||||
  float value_v = analogRead(this->pin_) / 4095.0f;
 | 
			
		||||
  float value_v = analogRead(this->pin_) / 4095.0f;  // NOLINT
 | 
			
		||||
  switch (this->attenuation_) {
 | 
			
		||||
    case ADC_0db:
 | 
			
		||||
      value_v *= 1.1;
 | 
			
		||||
@@ -80,7 +80,7 @@ float ADCSensor::sample() {
 | 
			
		||||
#ifdef USE_ADC_SENSOR_VCC
 | 
			
		||||
  return ESP.getVcc() / 1024.0f;
 | 
			
		||||
#else
 | 
			
		||||
  return analogRead(this->pin_) / 1024.0f;
 | 
			
		||||
  return analogRead(this->pin_) / 1024.0f;  // NOLINT
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										0
									
								
								esphome/components/ade7953/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								esphome/components/ade7953/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										54
									
								
								esphome/components/ade7953/ade7953.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								esphome/components/ade7953/ade7953.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,54 @@
 | 
			
		||||
#include "ade7953.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace ade7953 {
 | 
			
		||||
 | 
			
		||||
static const char *TAG = "ade7953";
 | 
			
		||||
 | 
			
		||||
void ADE7953::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "ADE7953:");
 | 
			
		||||
  if (this->has_irq_) {
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  IRQ Pin: GPIO%u", this->irq_pin_number_);
 | 
			
		||||
  }
 | 
			
		||||
  LOG_I2C_DEVICE(this);
 | 
			
		||||
  LOG_UPDATE_INTERVAL(this);
 | 
			
		||||
  LOG_SENSOR("  ", "Voltage Sensor", this->voltage_sensor_);
 | 
			
		||||
  LOG_SENSOR("  ", "Current A Sensor", this->current_a_sensor_);
 | 
			
		||||
  LOG_SENSOR("  ", "Current B Sensor", this->current_b_sensor_);
 | 
			
		||||
  LOG_SENSOR("  ", "Active Power A Sensor", this->active_power_a_sensor_);
 | 
			
		||||
  LOG_SENSOR("  ", "Active Power B Sensor", this->active_power_b_sensor_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define ADE_PUBLISH_(name, factor) \
 | 
			
		||||
  if (name && this->name##_sensor_) { \
 | 
			
		||||
    float value = *name / factor; \
 | 
			
		||||
    this->name##_sensor_->publish_state(value); \
 | 
			
		||||
  }
 | 
			
		||||
#define ADE_PUBLISH(name, factor) ADE_PUBLISH_(name, factor)
 | 
			
		||||
 | 
			
		||||
void ADE7953::update() {
 | 
			
		||||
  if (!this->is_setup_)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  auto active_power_a = this->ade_read_<int32_t>(0x0312);
 | 
			
		||||
  ADE_PUBLISH(active_power_a, 154.0f);
 | 
			
		||||
  auto active_power_b = this->ade_read_<int32_t>(0x0313);
 | 
			
		||||
  ADE_PUBLISH(active_power_b, 154.0f);
 | 
			
		||||
  auto current_a = this->ade_read_<uint32_t>(0x031A);
 | 
			
		||||
  ADE_PUBLISH(current_a, 100000.0f);
 | 
			
		||||
  auto current_b = this->ade_read_<uint32_t>(0x031B);
 | 
			
		||||
  ADE_PUBLISH(current_b, 100000.0f);
 | 
			
		||||
  auto voltage = this->ade_read_<uint32_t>(0x031C);
 | 
			
		||||
  ADE_PUBLISH(voltage, 26000.0f);
 | 
			
		||||
 | 
			
		||||
  //    auto apparent_power_a = this->ade_read_<int32_t>(0x0310);
 | 
			
		||||
  //    auto apparent_power_b = this->ade_read_<int32_t>(0x0311);
 | 
			
		||||
  //    auto reactive_power_a = this->ade_read_<int32_t>(0x0314);
 | 
			
		||||
  //    auto reactive_power_b = this->ade_read_<int32_t>(0x0315);
 | 
			
		||||
  //    auto power_factor_a = this->ade_read_<int16_t>(0x010A);
 | 
			
		||||
  //    auto power_factor_b = this->ade_read_<int16_t>(0x010B);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace ade7953
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										79
									
								
								esphome/components/ade7953/ade7953.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								esphome/components/ade7953/ade7953.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,79 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/components/i2c/i2c.h"
 | 
			
		||||
#include "esphome/components/sensor/sensor.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace ade7953 {
 | 
			
		||||
 | 
			
		||||
class ADE7953 : public i2c::I2CDevice, public PollingComponent {
 | 
			
		||||
 public:
 | 
			
		||||
  void set_irq_pin(uint8_t irq_pin) {
 | 
			
		||||
    has_irq_ = true;
 | 
			
		||||
    irq_pin_number_ = irq_pin;
 | 
			
		||||
  }
 | 
			
		||||
  void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; }
 | 
			
		||||
  void set_current_a_sensor(sensor::Sensor *current_a_sensor) { current_a_sensor_ = current_a_sensor; }
 | 
			
		||||
  void set_current_b_sensor(sensor::Sensor *current_b_sensor) { current_b_sensor_ = current_b_sensor; }
 | 
			
		||||
  void set_active_power_a_sensor(sensor::Sensor *active_power_a_sensor) {
 | 
			
		||||
    active_power_a_sensor_ = active_power_a_sensor;
 | 
			
		||||
  }
 | 
			
		||||
  void set_active_power_b_sensor(sensor::Sensor *active_power_b_sensor) {
 | 
			
		||||
    active_power_b_sensor_ = active_power_b_sensor;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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->ade_write_<uint8_t>(0x0010, 0x04);
 | 
			
		||||
      this->ade_write_<uint8_t>(0x00FE, 0xAD);
 | 
			
		||||
      this->ade_write_<uint16_t>(0x0120, 0x0030);
 | 
			
		||||
      this->is_setup_ = true;
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
 | 
			
		||||
  void update() override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  template<typename T> bool ade_write_(uint16_t reg, T value) {
 | 
			
		||||
    std::vector<uint8_t> data;
 | 
			
		||||
    data.push_back(reg >> 8);
 | 
			
		||||
    data.push_back(reg >> 0);
 | 
			
		||||
    for (int i = sizeof(T) - 1; i >= 0; i--)
 | 
			
		||||
      data.push_back(value >> (i * 8));
 | 
			
		||||
    return this->write_bytes_raw(data);
 | 
			
		||||
  }
 | 
			
		||||
  template<typename T> optional<T> ade_read_(uint16_t reg) {
 | 
			
		||||
    uint8_t hi = reg >> 8;
 | 
			
		||||
    uint8_t lo = reg >> 0;
 | 
			
		||||
    if (!this->write_bytes_raw({hi, lo}))
 | 
			
		||||
      return {};
 | 
			
		||||
    auto ret = this->read_bytes_raw<sizeof(T)>();
 | 
			
		||||
    if (!ret.has_value())
 | 
			
		||||
      return {};
 | 
			
		||||
    T result = 0;
 | 
			
		||||
    for (int i = 0, j = sizeof(T) - 1; i < sizeof(T); i++, j--)
 | 
			
		||||
      result |= T((*ret)[i]) << (j * 8);
 | 
			
		||||
    return result;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  bool has_irq_ = false;
 | 
			
		||||
  uint8_t irq_pin_number_;
 | 
			
		||||
  GPIOPin *irq_pin_{nullptr};
 | 
			
		||||
  bool is_setup_{false};
 | 
			
		||||
  sensor::Sensor *voltage_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *current_a_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *current_b_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *active_power_a_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *active_power_b_sensor_{nullptr};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace ade7953
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										44
									
								
								esphome/components/ade7953/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								esphome/components/ade7953/sensor.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import sensor, i2c
 | 
			
		||||
from esphome import pins
 | 
			
		||||
from esphome.const import CONF_ID, CONF_VOLTAGE, \
 | 
			
		||||
    UNIT_VOLT, ICON_FLASH, UNIT_AMPERE, UNIT_WATT
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ['i2c']
 | 
			
		||||
 | 
			
		||||
ade7953_ns = cg.esphome_ns.namespace('ade7953')
 | 
			
		||||
ADE7953 = ade7953_ns.class_('ADE7953', cg.PollingComponent, i2c.I2CDevice)
 | 
			
		||||
 | 
			
		||||
CONF_IRQ_PIN = 'irq_pin'
 | 
			
		||||
CONF_CURRENT_A = 'current_a'
 | 
			
		||||
CONF_CURRENT_B = 'current_b'
 | 
			
		||||
CONF_ACTIVE_POWER_A = 'active_power_a'
 | 
			
		||||
CONF_ACTIVE_POWER_B = 'active_power_b'
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.Schema({
 | 
			
		||||
    cv.GenerateID(): cv.declare_id(ADE7953),
 | 
			
		||||
    cv.Optional(CONF_IRQ_PIN): pins.input_pin,
 | 
			
		||||
    cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 1),
 | 
			
		||||
    cv.Optional(CONF_CURRENT_A): sensor.sensor_schema(UNIT_AMPERE, ICON_FLASH, 2),
 | 
			
		||||
    cv.Optional(CONF_CURRENT_B): sensor.sensor_schema(UNIT_AMPERE, ICON_FLASH, 2),
 | 
			
		||||
    cv.Optional(CONF_ACTIVE_POWER_A): sensor.sensor_schema(UNIT_WATT, ICON_FLASH, 1),
 | 
			
		||||
    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))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    yield i2c.register_i2c_device(var, config)
 | 
			
		||||
 | 
			
		||||
    if CONF_IRQ_PIN in config:
 | 
			
		||||
        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:
 | 
			
		||||
            continue
 | 
			
		||||
        conf = config[key]
 | 
			
		||||
        sens = yield sensor.new_sensor(conf)
 | 
			
		||||
        cg.add(getattr(var, f'set_{key}_sensor')(sens))
 | 
			
		||||
@@ -10,8 +10,10 @@ MULTI_CONF = True
 | 
			
		||||
ads1115_ns = cg.esphome_ns.namespace('ads1115')
 | 
			
		||||
ADS1115Component = ads1115_ns.class_('ADS1115Component', cg.Component, i2c.I2CDevice)
 | 
			
		||||
 | 
			
		||||
CONF_CONTINUOUS_MODE = 'continuous_mode'
 | 
			
		||||
CONFIG_SCHEMA = cv.Schema({
 | 
			
		||||
    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))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -19,3 +21,5 @@ def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    yield i2c.register_i2c_device(var, config)
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_continuous_mode(config[CONF_CONTINUOUS_MODE]))
 | 
			
		||||
 
 | 
			
		||||
@@ -29,9 +29,15 @@ void ADS1115Component::setup() {
 | 
			
		||||
  //        0bxxxx000xxxxxxxxx
 | 
			
		||||
  config |= ADS1115_GAIN_6P144 << 9;
 | 
			
		||||
 | 
			
		||||
  // Set singleshot mode
 | 
			
		||||
  //        0bxxxxxxx1xxxxxxxx
 | 
			
		||||
  config |= 0b0000000100000000;
 | 
			
		||||
  if (this->continuous_mode_) {
 | 
			
		||||
    // Set continuous mode
 | 
			
		||||
    //        0bxxxxxxx0xxxxxxxx
 | 
			
		||||
    config |= 0b0000000000000000;
 | 
			
		||||
  } else {
 | 
			
		||||
    // Set singleshot mode
 | 
			
		||||
    //        0bxxxxxxx1xxxxxxxx
 | 
			
		||||
    config |= 0b0000000100000000;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Set data rate - 860 samples per second (we're in singleshot mode)
 | 
			
		||||
  //        0bxxxxxxxx100xxxxx
 | 
			
		||||
@@ -57,6 +63,8 @@ void ADS1115Component::setup() {
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->prev_config_ = config;
 | 
			
		||||
 | 
			
		||||
  for (auto *sensor : this->sensors_) {
 | 
			
		||||
    this->set_interval(sensor->get_name(), sensor->update_interval(),
 | 
			
		||||
                       [this, sensor] { this->request_measurement(sensor); });
 | 
			
		||||
@@ -75,13 +83,8 @@ void ADS1115Component::dump_config() {
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "    Gain: %u", sensor->get_gain());
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
float ADS1115Component::get_setup_priority() const { return setup_priority::DATA; }
 | 
			
		||||
float ADS1115Component::request_measurement(ADS1115Sensor *sensor) {
 | 
			
		||||
  uint16_t config;
 | 
			
		||||
  if (!this->read_byte_16(ADS1115_REGISTER_CONFIG, &config)) {
 | 
			
		||||
    this->status_set_warning();
 | 
			
		||||
    return NAN;
 | 
			
		||||
  }
 | 
			
		||||
  uint16_t config = this->prev_config_;
 | 
			
		||||
  // Multiplexer
 | 
			
		||||
  //        0bxBBBxxxxxxxxxxxx
 | 
			
		||||
  config &= 0b1000111111111111;
 | 
			
		||||
@@ -91,25 +94,31 @@ float ADS1115Component::request_measurement(ADS1115Sensor *sensor) {
 | 
			
		||||
  //        0bxxxxBBBxxxxxxxxx
 | 
			
		||||
  config &= 0b1111000111111111;
 | 
			
		||||
  config |= (sensor->get_gain() & 0b111) << 9;
 | 
			
		||||
  // Start conversion
 | 
			
		||||
  config |= 0b1000000000000000;
 | 
			
		||||
 | 
			
		||||
  if (!this->write_byte_16(ADS1115_REGISTER_CONFIG, config)) {
 | 
			
		||||
    this->status_set_warning();
 | 
			
		||||
    return NAN;
 | 
			
		||||
  if (!this->continuous_mode_) {
 | 
			
		||||
    // Start conversion
 | 
			
		||||
    config |= 0b1000000000000000;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // about 1.6 ms with 860 samples per second
 | 
			
		||||
  delay(2);
 | 
			
		||||
 | 
			
		||||
  uint32_t start = millis();
 | 
			
		||||
  while (this->read_byte_16(ADS1115_REGISTER_CONFIG, &config) && (config >> 15) == 0) {
 | 
			
		||||
    if (millis() - start > 100) {
 | 
			
		||||
      ESP_LOGW(TAG, "Reading ADS1115 timed out");
 | 
			
		||||
  if (!this->continuous_mode_ || this->prev_config_ != config) {
 | 
			
		||||
    if (!this->write_byte_16(ADS1115_REGISTER_CONFIG, config)) {
 | 
			
		||||
      this->status_set_warning();
 | 
			
		||||
      return NAN;
 | 
			
		||||
    }
 | 
			
		||||
    yield();
 | 
			
		||||
    this->prev_config_ = config;
 | 
			
		||||
 | 
			
		||||
    // about 1.6 ms with 860 samples per second
 | 
			
		||||
    delay(2);
 | 
			
		||||
 | 
			
		||||
    uint32_t start = millis();
 | 
			
		||||
    while (this->read_byte_16(ADS1115_REGISTER_CONFIG, &config) && (config >> 15) == 0) {
 | 
			
		||||
      if (millis() - start > 100) {
 | 
			
		||||
        ESP_LOGW(TAG, "Reading ADS1115 timed out");
 | 
			
		||||
        this->status_set_warning();
 | 
			
		||||
        return NAN;
 | 
			
		||||
      }
 | 
			
		||||
      yield();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint16_t raw_conversion;
 | 
			
		||||
@@ -144,13 +153,9 @@ float ADS1115Component::request_measurement(ADS1115Sensor *sensor) {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this->status_clear_warning();
 | 
			
		||||
  return millivolts / 1e4f;
 | 
			
		||||
  return millivolts / 1e3f;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t ADS1115Sensor::get_multiplexer() const { return this->multiplexer_; }
 | 
			
		||||
void ADS1115Sensor::set_multiplexer(ADS1115Multiplexer multiplexer) { this->multiplexer_ = multiplexer; }
 | 
			
		||||
uint8_t ADS1115Sensor::get_gain() const { return this->gain_; }
 | 
			
		||||
void ADS1115Sensor::set_gain(ADS1115Gain gain) { this->gain_ = gain; }
 | 
			
		||||
float ADS1115Sensor::sample() { return this->parent_->request_measurement(this); }
 | 
			
		||||
void ADS1115Sensor::update() {
 | 
			
		||||
  float v = this->parent_->request_measurement(this);
 | 
			
		||||
 
 | 
			
		||||
@@ -37,13 +37,16 @@ class ADS1115Component : public Component, public i2c::I2CDevice {
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
  /// HARDWARE_LATE setup priority
 | 
			
		||||
  float get_setup_priority() const override;
 | 
			
		||||
  float get_setup_priority() const override { return setup_priority::DATA; }
 | 
			
		||||
  void set_continuous_mode(bool continuous_mode) { continuous_mode_ = continuous_mode; }
 | 
			
		||||
 | 
			
		||||
  /// Helper method to request a measurement from a sensor.
 | 
			
		||||
  float request_measurement(ADS1115Sensor *sensor);
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  std::vector<ADS1115Sensor *> sensors_;
 | 
			
		||||
  uint16_t prev_config_{0};
 | 
			
		||||
  bool continuous_mode_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Internal holder class that is in instance of Sensor so that the hub can create individual sensors.
 | 
			
		||||
@@ -51,12 +54,12 @@ class ADS1115Sensor : public sensor::Sensor, public PollingComponent, public vol
 | 
			
		||||
 public:
 | 
			
		||||
  ADS1115Sensor(ADS1115Component *parent) : parent_(parent) {}
 | 
			
		||||
  void update() override;
 | 
			
		||||
  void set_multiplexer(ADS1115Multiplexer multiplexer);
 | 
			
		||||
  void set_gain(ADS1115Gain gain);
 | 
			
		||||
  void set_multiplexer(ADS1115Multiplexer multiplexer) { multiplexer_ = multiplexer; }
 | 
			
		||||
  void set_gain(ADS1115Gain gain) { gain_ = gain; }
 | 
			
		||||
 | 
			
		||||
  float sample() override;
 | 
			
		||||
  uint8_t get_multiplexer() const;
 | 
			
		||||
  uint8_t get_gain() const;
 | 
			
		||||
  uint8_t get_multiplexer() const { return multiplexer_; }
 | 
			
		||||
  uint8_t get_gain() const { return gain_; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  ADS1115Component *parent_;
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,6 @@ import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import sensor, voltage_sampler
 | 
			
		||||
from esphome.const import CONF_GAIN, CONF_MULTIPLEXER, ICON_FLASH, UNIT_VOLT, CONF_ID
 | 
			
		||||
from esphome.py_compat import string_types
 | 
			
		||||
from . import ads1115_ns, ADS1115Component
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ['ads1115']
 | 
			
		||||
@@ -32,9 +31,9 @@ GAIN = {
 | 
			
		||||
 | 
			
		||||
def validate_gain(value):
 | 
			
		||||
    if isinstance(value, float):
 | 
			
		||||
        value = u'{:0.03f}'.format(value)
 | 
			
		||||
    elif not isinstance(value, string_types):
 | 
			
		||||
        raise cv.Invalid('invalid gain "{}"'.format(value))
 | 
			
		||||
        value = f'{value:0.03f}'
 | 
			
		||||
    elif not isinstance(value, str):
 | 
			
		||||
        raise cv.Invalid(f'invalid gain "{value}"')
 | 
			
		||||
 | 
			
		||||
    return cv.enum(GAIN)(value)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										0
									
								
								esphome/components/aht10/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								esphome/components/aht10/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										127
									
								
								esphome/components/aht10/aht10.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								esphome/components/aht10/aht10.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,127 @@
 | 
			
		||||
// 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());
 | 
			
		||||
    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
 | 
			
		||||
							
								
								
									
										30
									
								
								esphome/components/aht10/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								esphome/components/aht10/sensor.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
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, \
 | 
			
		||||
    UNIT_CELSIUS, ICON_THERMOMETER, ICON_WATER_PERCENT, 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_THERMOMETER, 2),
 | 
			
		||||
    cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 2),
 | 
			
		||||
}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x38))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    yield i2c.register_i2c_device(var, config)
 | 
			
		||||
 | 
			
		||||
    if CONF_TEMPERATURE in config:
 | 
			
		||||
        sens = yield sensor.new_sensor(config[CONF_TEMPERATURE])
 | 
			
		||||
        cg.add(var.set_temperature_sensor(sens))
 | 
			
		||||
 | 
			
		||||
    if CONF_HUMIDITY in config:
 | 
			
		||||
        sens = yield sensor.new_sensor(config[CONF_HUMIDITY])
 | 
			
		||||
        cg.add(var.set_humidity_sensor(sens))
 | 
			
		||||
							
								
								
									
										94
									
								
								esphome/components/animation/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								esphome/components/animation/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,94 @@
 | 
			
		||||
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']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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,44 +3,42 @@ import esphome.config_validation as cv
 | 
			
		||||
from esphome import automation
 | 
			
		||||
from esphome.automation import Condition
 | 
			
		||||
from esphome.const import CONF_DATA, CONF_DATA_TEMPLATE, CONF_ID, CONF_PASSWORD, CONF_PORT, \
 | 
			
		||||
    CONF_REBOOT_TIMEOUT, CONF_SERVICE, CONF_VARIABLES, CONF_SERVICES, CONF_TRIGGER_ID
 | 
			
		||||
from esphome.core import CORE, coroutine_with_priority
 | 
			
		||||
    CONF_REBOOT_TIMEOUT, CONF_SERVICE, CONF_VARIABLES, CONF_SERVICES, CONF_TRIGGER_ID, CONF_EVENT, \
 | 
			
		||||
    CONF_TAG
 | 
			
		||||
from esphome.core import coroutine_with_priority
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ['network']
 | 
			
		||||
AUTO_LOAD = ['async_tcp']
 | 
			
		||||
CODEOWNERS = ['@OttoWinter']
 | 
			
		||||
 | 
			
		||||
api_ns = cg.esphome_ns.namespace('api')
 | 
			
		||||
APIServer = api_ns.class_('APIServer', cg.Component, cg.Controller)
 | 
			
		||||
HomeAssistantServiceCallAction = api_ns.class_('HomeAssistantServiceCallAction', automation.Action)
 | 
			
		||||
KeyValuePair = api_ns.class_('KeyValuePair')
 | 
			
		||||
TemplatableKeyValuePair = api_ns.class_('TemplatableKeyValuePair')
 | 
			
		||||
APIConnectedCondition = api_ns.class_('APIConnectedCondition', Condition)
 | 
			
		||||
 | 
			
		||||
UserService = api_ns.class_('UserService', automation.Trigger)
 | 
			
		||||
ServiceTypeArgument = api_ns.class_('ServiceTypeArgument')
 | 
			
		||||
ServiceArgType = api_ns.enum('ServiceArgType')
 | 
			
		||||
SERVICE_ARG_TYPES = {
 | 
			
		||||
    'bool': ServiceArgType.SERVICE_ARG_TYPE_BOOL,
 | 
			
		||||
    'int': ServiceArgType.SERVICE_ARG_TYPE_INT,
 | 
			
		||||
    'float': ServiceArgType.SERVICE_ARG_TYPE_FLOAT,
 | 
			
		||||
    'string': ServiceArgType.SERVICE_ARG_TYPE_STRING,
 | 
			
		||||
}
 | 
			
		||||
UserServiceTrigger = api_ns.class_('UserServiceTrigger', automation.Trigger)
 | 
			
		||||
ListEntitiesServicesArgument = api_ns.class_('ListEntitiesServicesArgument')
 | 
			
		||||
SERVICE_ARG_NATIVE_TYPES = {
 | 
			
		||||
    'bool': bool,
 | 
			
		||||
    'int': cg.int32,
 | 
			
		||||
    'float': float,
 | 
			
		||||
    'string': cg.std_string,
 | 
			
		||||
    'bool[]': cg.std_vector.template(bool),
 | 
			
		||||
    'int[]': cg.std_vector.template(cg.int32),
 | 
			
		||||
    'float[]': cg.std_vector.template(float),
 | 
			
		||||
    'string[]': cg.std_vector.template(cg.std_string),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.Schema({
 | 
			
		||||
    cv.GenerateID(): cv.declare_id(APIServer),
 | 
			
		||||
    cv.Optional(CONF_PORT, default=6053): cv.port,
 | 
			
		||||
    cv.Optional(CONF_PASSWORD, default=''): cv.string_strict,
 | 
			
		||||
    cv.Optional(CONF_REBOOT_TIMEOUT, default='5min'): cv.positive_time_period_milliseconds,
 | 
			
		||||
    cv.Optional(CONF_REBOOT_TIMEOUT, default='15min'): cv.positive_time_period_milliseconds,
 | 
			
		||||
    cv.Optional(CONF_SERVICES): automation.validate_automation({
 | 
			
		||||
        cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(UserService),
 | 
			
		||||
        cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(UserServiceTrigger),
 | 
			
		||||
        cv.Required(CONF_SERVICE): cv.valid_name,
 | 
			
		||||
        cv.Optional(CONF_VARIABLES, default={}): cv.Schema({
 | 
			
		||||
            cv.validate_id_name: cv.one_of(*SERVICE_ARG_TYPES, lower=True),
 | 
			
		||||
            cv.validate_id_name: cv.one_of(*SERVICE_ARG_NATIVE_TYPES, lower=True),
 | 
			
		||||
        }),
 | 
			
		||||
    }),
 | 
			
		||||
}).extend(cv.COMPONENT_SCHEMA)
 | 
			
		||||
@@ -58,37 +56,30 @@ def to_code(config):
 | 
			
		||||
    for conf in config.get(CONF_SERVICES, []):
 | 
			
		||||
        template_args = []
 | 
			
		||||
        func_args = []
 | 
			
		||||
        service_type_args = []
 | 
			
		||||
        service_arg_names = []
 | 
			
		||||
        for name, var_ in conf[CONF_VARIABLES].items():
 | 
			
		||||
            native = SERVICE_ARG_NATIVE_TYPES[var_]
 | 
			
		||||
            template_args.append(native)
 | 
			
		||||
            func_args.append((native, name))
 | 
			
		||||
            service_type_args.append(ServiceTypeArgument(name, SERVICE_ARG_TYPES[var_]))
 | 
			
		||||
            service_arg_names.append(name)
 | 
			
		||||
        templ = cg.TemplateArguments(*template_args)
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], templ,
 | 
			
		||||
                                   conf[CONF_SERVICE], service_type_args)
 | 
			
		||||
                                   conf[CONF_SERVICE], service_arg_names)
 | 
			
		||||
        cg.add(var.register_user_service(trigger))
 | 
			
		||||
        yield automation.build_automation(trigger, func_args, conf)
 | 
			
		||||
 | 
			
		||||
    cg.add_define('USE_API')
 | 
			
		||||
    if CORE.is_esp32:
 | 
			
		||||
        cg.add_library('AsyncTCP', '1.0.3')
 | 
			
		||||
    elif CORE.is_esp8266:
 | 
			
		||||
        cg.add_library('ESPAsyncTCP', '1.2.0')
 | 
			
		||||
    cg.add_global(api_ns.using)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
KEY_VALUE_SCHEMA = cv.Schema({cv.string: cv.templatable(cv.string_strict)})
 | 
			
		||||
 | 
			
		||||
HOMEASSISTANT_SERVICE_ACTION_SCHEMA = cv.Schema({
 | 
			
		||||
    cv.GenerateID(): cv.use_id(APIServer),
 | 
			
		||||
    cv.Required(CONF_SERVICE): cv.string,
 | 
			
		||||
    cv.Optional(CONF_DATA): cv.Schema({
 | 
			
		||||
        cv.string: cv.string,
 | 
			
		||||
    }),
 | 
			
		||||
    cv.Optional(CONF_DATA_TEMPLATE): cv.Schema({
 | 
			
		||||
        cv.string: cv.string,
 | 
			
		||||
    }),
 | 
			
		||||
    cv.Optional(CONF_VARIABLES): cv.Schema({
 | 
			
		||||
        cv.string: cv.returning_lambda,
 | 
			
		||||
    }),
 | 
			
		||||
    cv.Required(CONF_SERVICE): cv.templatable(cv.string),
 | 
			
		||||
    cv.Optional(CONF_DATA, default={}): KEY_VALUE_SCHEMA,
 | 
			
		||||
    cv.Optional(CONF_DATA_TEMPLATE, default={}): KEY_VALUE_SCHEMA,
 | 
			
		||||
    cv.Optional(CONF_VARIABLES, default={}): cv.Schema({cv.string: cv.returning_lambda}),
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -96,20 +87,71 @@ HOMEASSISTANT_SERVICE_ACTION_SCHEMA = cv.Schema({
 | 
			
		||||
                            HOMEASSISTANT_SERVICE_ACTION_SCHEMA)
 | 
			
		||||
def homeassistant_service_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    serv = yield cg.get_variable(config[CONF_ID])
 | 
			
		||||
    var = cg.new_Pvariable(action_id, template_arg, serv)
 | 
			
		||||
    cg.add(var.set_service(config[CONF_SERVICE]))
 | 
			
		||||
    if CONF_DATA in config:
 | 
			
		||||
        datas = [KeyValuePair(k, v) for k, v in config[CONF_DATA].items()]
 | 
			
		||||
        cg.add(var.set_data(datas))
 | 
			
		||||
    if CONF_DATA_TEMPLATE in config:
 | 
			
		||||
        datas = [KeyValuePair(k, v) for k, v in config[CONF_DATA_TEMPLATE].items()]
 | 
			
		||||
        cg.add(var.set_data_template(datas))
 | 
			
		||||
    if CONF_VARIABLES in config:
 | 
			
		||||
        datas = []
 | 
			
		||||
        for key, value in config[CONF_VARIABLES].items():
 | 
			
		||||
            value_ = yield cg.process_lambda(value, [])
 | 
			
		||||
            datas.append(TemplatableKeyValuePair(key, value_))
 | 
			
		||||
        cg.add(var.set_variables(datas))
 | 
			
		||||
    var = cg.new_Pvariable(action_id, template_arg, serv, False)
 | 
			
		||||
    templ = yield cg.templatable(config[CONF_SERVICE], args, None)
 | 
			
		||||
    cg.add(var.set_service(templ))
 | 
			
		||||
    for key, value in config[CONF_DATA].items():
 | 
			
		||||
        templ = yield cg.templatable(value, args, None)
 | 
			
		||||
        cg.add(var.add_data(key, templ))
 | 
			
		||||
    for key, value in config[CONF_DATA_TEMPLATE].items():
 | 
			
		||||
        templ = yield cg.templatable(value, args, None)
 | 
			
		||||
        cg.add(var.add_data_template(key, templ))
 | 
			
		||||
    for key, value in config[CONF_VARIABLES].items():
 | 
			
		||||
        templ = yield cg.templatable(value, args, None)
 | 
			
		||||
        cg.add(var.add_variable(key, templ))
 | 
			
		||||
    yield var
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_homeassistant_event(value):
 | 
			
		||||
    value = cv.string(value)
 | 
			
		||||
    if not value.startswith('esphome.'):
 | 
			
		||||
        raise cv.Invalid("ESPHome can only generate Home Assistant events that begin with "
 | 
			
		||||
                         "esphome. For example 'esphome.xyz'")
 | 
			
		||||
    return value
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
HOMEASSISTANT_EVENT_ACTION_SCHEMA = cv.Schema({
 | 
			
		||||
    cv.GenerateID(): cv.use_id(APIServer),
 | 
			
		||||
    cv.Required(CONF_EVENT): validate_homeassistant_event,
 | 
			
		||||
    cv.Optional(CONF_DATA, 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,
 | 
			
		||||
                            HOMEASSISTANT_EVENT_ACTION_SCHEMA)
 | 
			
		||||
def homeassistant_event_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    serv = yield cg.get_variable(config[CONF_ID])
 | 
			
		||||
    var = cg.new_Pvariable(action_id, template_arg, serv, True)
 | 
			
		||||
    templ = yield cg.templatable(config[CONF_EVENT], args, None)
 | 
			
		||||
    cg.add(var.set_service(templ))
 | 
			
		||||
    for key, value in config[CONF_DATA].items():
 | 
			
		||||
        templ = yield cg.templatable(value, args, None)
 | 
			
		||||
        cg.add(var.add_data(key, templ))
 | 
			
		||||
    for key, value in config[CONF_DATA_TEMPLATE].items():
 | 
			
		||||
        templ = yield cg.templatable(value, args, None)
 | 
			
		||||
        cg.add(var.add_data_template(key, templ))
 | 
			
		||||
    for key, value in config[CONF_VARIABLES].items():
 | 
			
		||||
        templ = yield cg.templatable(value, args, None)
 | 
			
		||||
        cg.add(var.add_variable(key, templ))
 | 
			
		||||
    yield var
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
HOMEASSISTANT_TAG_SCANNED_ACTION_SCHEMA = cv.maybe_simple_value({
 | 
			
		||||
    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)
 | 
			
		||||
def homeassistant_tag_scanned_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    serv = yield 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 = yield cg.templatable(config[CONF_TAG], args, cg.std_string)
 | 
			
		||||
    cg.add(var.add_data('tag_id', templ))
 | 
			
		||||
    yield var
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,45 @@
 | 
			
		||||
syntax = "proto3";
 | 
			
		||||
 | 
			
		||||
import "api_options.proto";
 | 
			
		||||
 | 
			
		||||
service APIConnection {
 | 
			
		||||
  rpc hello (HelloRequest) returns (HelloResponse) {
 | 
			
		||||
    option (needs_setup_connection) = false;
 | 
			
		||||
    option (needs_authentication) = false;
 | 
			
		||||
  }
 | 
			
		||||
  rpc connect (ConnectRequest) returns (ConnectResponse) {
 | 
			
		||||
    option (needs_setup_connection) = false;
 | 
			
		||||
    option (needs_authentication) = false;
 | 
			
		||||
  }
 | 
			
		||||
  rpc disconnect (DisconnectRequest) returns (DisconnectResponse) {
 | 
			
		||||
    option (needs_setup_connection) = false;
 | 
			
		||||
    option (needs_authentication) = false;
 | 
			
		||||
  }
 | 
			
		||||
  rpc ping (PingRequest) returns (PingResponse) {
 | 
			
		||||
    option (needs_setup_connection) = false;
 | 
			
		||||
    option (needs_authentication) = false;
 | 
			
		||||
  }
 | 
			
		||||
  rpc device_info (DeviceInfoRequest) returns (DeviceInfoResponse) {
 | 
			
		||||
    option (needs_authentication) = false;
 | 
			
		||||
  }
 | 
			
		||||
  rpc list_entities (ListEntitiesRequest) returns (void) {}
 | 
			
		||||
  rpc subscribe_states (SubscribeStatesRequest) returns (void) {}
 | 
			
		||||
  rpc subscribe_logs (SubscribeLogsRequest) returns (void) {}
 | 
			
		||||
  rpc subscribe_homeassistant_services (SubscribeHomeassistantServicesRequest) returns (void) {}
 | 
			
		||||
  rpc subscribe_home_assistant_states (SubscribeHomeAssistantStatesRequest) returns (void) {}
 | 
			
		||||
  rpc get_time (GetTimeRequest) returns (GetTimeResponse) {
 | 
			
		||||
    option (needs_authentication) = false;
 | 
			
		||||
  }
 | 
			
		||||
  rpc execute_service (ExecuteServiceRequest) returns (void) {}
 | 
			
		||||
 | 
			
		||||
  rpc cover_command (CoverCommandRequest) returns (void) {}
 | 
			
		||||
  rpc fan_command (FanCommandRequest) returns (void) {}
 | 
			
		||||
  rpc light_command (LightCommandRequest) returns (void) {}
 | 
			
		||||
  rpc switch_command (SwitchCommandRequest) returns (void) {}
 | 
			
		||||
  rpc camera_image (CameraImageRequest) returns (void) {}
 | 
			
		||||
  rpc climate_command (ClimateCommandRequest) returns (void) {}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// ==================== BASE PACKETS ====================
 | 
			
		||||
 | 
			
		||||
@@ -21,8 +61,11 @@ syntax = "proto3";
 | 
			
		||||
 | 
			
		||||
// Message sent at the beginning of each connection
 | 
			
		||||
// Can only be sent by the client and only at the beginning of the connection
 | 
			
		||||
// ID: 1
 | 
			
		||||
message HelloRequest {
 | 
			
		||||
  option (id) = 1;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
  option (no_delay) = true;
 | 
			
		||||
 | 
			
		||||
  // Description of client (like User Agent)
 | 
			
		||||
  // For example "Home Assistant"
 | 
			
		||||
  // Not strictly necessary to send but nice for debugging
 | 
			
		||||
@@ -32,8 +75,11 @@ message HelloRequest {
 | 
			
		||||
 | 
			
		||||
// Confirmation of successful connection request.
 | 
			
		||||
// Can only be sent by the server and only at the beginning of the connection
 | 
			
		||||
// ID: 2
 | 
			
		||||
message HelloResponse {
 | 
			
		||||
  option (id) = 2;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (no_delay) = true;
 | 
			
		||||
 | 
			
		||||
  // The version of the API to use. The _client_ (for example Home Assistant) needs to check
 | 
			
		||||
  // for compatibility and if necessary adopt to an older API.
 | 
			
		||||
  // Major is for breaking changes in the base protocol - a mismatch will lead to immediate disconnect_client_
 | 
			
		||||
@@ -49,49 +95,66 @@ message HelloResponse {
 | 
			
		||||
 | 
			
		||||
// Message sent at the beginning of each connection to authenticate the client
 | 
			
		||||
// Can only be sent by the client and only at the beginning of the connection
 | 
			
		||||
// ID: 3
 | 
			
		||||
message ConnectRequest {
 | 
			
		||||
  option (id) = 3;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
  option (no_delay) = true;
 | 
			
		||||
 | 
			
		||||
  // The password to log in with
 | 
			
		||||
  string password = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Confirmation of successful connection. After this the connection is available for all traffic.
 | 
			
		||||
// Can only be sent by the server and only at the beginning of the connection
 | 
			
		||||
// ID: 4
 | 
			
		||||
message ConnectResponse {
 | 
			
		||||
  option (id) = 4;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (no_delay) = true;
 | 
			
		||||
 | 
			
		||||
  bool invalid_password = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Request to close the connection.
 | 
			
		||||
// Can be sent by both the client and server
 | 
			
		||||
// ID: 5
 | 
			
		||||
message DisconnectRequest {
 | 
			
		||||
  option (id) = 5;
 | 
			
		||||
  option (source) = SOURCE_BOTH;
 | 
			
		||||
  option (no_delay) = true;
 | 
			
		||||
 | 
			
		||||
  // Do not close the connection before the acknowledgement arrives
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ID: 6
 | 
			
		||||
message DisconnectResponse {
 | 
			
		||||
  option (id) = 6;
 | 
			
		||||
  option (source) = SOURCE_BOTH;
 | 
			
		||||
  option (no_delay) = true;
 | 
			
		||||
 | 
			
		||||
  // Empty - Both parties are required to close the connection after this
 | 
			
		||||
  // message has been received.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ID: 7
 | 
			
		||||
message PingRequest {
 | 
			
		||||
  option (id) = 7;
 | 
			
		||||
  option (source) = SOURCE_BOTH;
 | 
			
		||||
  // Empty
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ID: 8
 | 
			
		||||
message PingResponse {
 | 
			
		||||
  option (id) = 8;
 | 
			
		||||
  option (source) = SOURCE_BOTH;
 | 
			
		||||
  // Empty
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ID: 9
 | 
			
		||||
message DeviceInfoRequest {
 | 
			
		||||
  option (id) = 9;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
  // Empty
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ID: 10
 | 
			
		||||
message DeviceInfoResponse {
 | 
			
		||||
  option (id) = 10;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
 | 
			
		||||
  bool uses_password = 1;
 | 
			
		||||
 | 
			
		||||
  // The name of the node, given by "App.set_name()"
 | 
			
		||||
@@ -101,7 +164,7 @@ message DeviceInfoResponse {
 | 
			
		||||
  string mac_address = 3;
 | 
			
		||||
 | 
			
		||||
  // A string describing the ESPHome version. For example "1.10.0"
 | 
			
		||||
  string esphome_core_version = 4;
 | 
			
		||||
  string esphome_version = 4;
 | 
			
		||||
 | 
			
		||||
  // A string describing the date of compilation, this is generated by the compiler
 | 
			
		||||
  // and therefore may not be in the same format all the time.
 | 
			
		||||
@@ -114,22 +177,29 @@ message DeviceInfoResponse {
 | 
			
		||||
  bool has_deep_sleep = 7;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ID: 11
 | 
			
		||||
message ListEntitiesRequest {
 | 
			
		||||
  option (id) = 11;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
  // Empty
 | 
			
		||||
}
 | 
			
		||||
// ID: 19
 | 
			
		||||
message ListEntitiesDoneResponse {
 | 
			
		||||
  option (id) = 19;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (no_delay) = true;
 | 
			
		||||
  // Empty
 | 
			
		||||
}
 | 
			
		||||
// ID: 20
 | 
			
		||||
message SubscribeStatesRequest {
 | 
			
		||||
  option (id) = 20;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
  // Empty
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ==================== BINARY SENSOR ====================
 | 
			
		||||
// ID: 12
 | 
			
		||||
message ListEntitiesBinarySensorResponse {
 | 
			
		||||
  option (id) = 12;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_BINARY_SENSOR";
 | 
			
		||||
 | 
			
		||||
  string object_id = 1;
 | 
			
		||||
  fixed32 key = 2;
 | 
			
		||||
  string name = 3;
 | 
			
		||||
@@ -138,15 +208,25 @@ message ListEntitiesBinarySensorResponse {
 | 
			
		||||
  string device_class = 5;
 | 
			
		||||
  bool is_status_binary_sensor = 6;
 | 
			
		||||
}
 | 
			
		||||
// ID: 21
 | 
			
		||||
message BinarySensorStateResponse {
 | 
			
		||||
  option (id) = 21;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_BINARY_SENSOR";
 | 
			
		||||
  option (no_delay) = true;
 | 
			
		||||
 | 
			
		||||
  fixed32 key = 1;
 | 
			
		||||
  bool state = 2;
 | 
			
		||||
  // If the binary sensor does not have a valid state yet.
 | 
			
		||||
  // Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
 | 
			
		||||
  bool missing_state = 3;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ==================== COVER ====================
 | 
			
		||||
// ID: 13
 | 
			
		||||
message ListEntitiesCoverResponse {
 | 
			
		||||
  option (id) = 13;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_COVER";
 | 
			
		||||
 | 
			
		||||
  string object_id = 1;
 | 
			
		||||
  fixed32 key = 2;
 | 
			
		||||
  string name = 3;
 | 
			
		||||
@@ -157,38 +237,47 @@ message ListEntitiesCoverResponse {
 | 
			
		||||
  bool supports_tilt = 7;
 | 
			
		||||
  string device_class = 8;
 | 
			
		||||
}
 | 
			
		||||
// ID: 22
 | 
			
		||||
message CoverStateResponse {
 | 
			
		||||
  fixed32 key = 1;
 | 
			
		||||
 | 
			
		||||
enum LegacyCoverState {
 | 
			
		||||
  LEGACY_COVER_STATE_OPEN = 0;
 | 
			
		||||
  LEGACY_COVER_STATE_CLOSED = 1;
 | 
			
		||||
}
 | 
			
		||||
enum CoverOperation {
 | 
			
		||||
  COVER_OPERATION_IDLE = 0;
 | 
			
		||||
  COVER_OPERATION_IS_OPENING = 1;
 | 
			
		||||
  COVER_OPERATION_IS_CLOSING = 2;
 | 
			
		||||
}
 | 
			
		||||
message CoverStateResponse {
 | 
			
		||||
  option (id) = 22;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_COVER";
 | 
			
		||||
  option (no_delay) = true;
 | 
			
		||||
 | 
			
		||||
  fixed32 key = 1;
 | 
			
		||||
  // legacy: state has been removed in 1.13
 | 
			
		||||
  // clients/servers must still send/accept it until the next protocol change
 | 
			
		||||
  enum LegacyCoverState {
 | 
			
		||||
    OPEN = 0;
 | 
			
		||||
    CLOSED = 1;
 | 
			
		||||
  }
 | 
			
		||||
  LegacyCoverState legacy_state = 2;
 | 
			
		||||
 | 
			
		||||
  float position = 3;
 | 
			
		||||
  float tilt = 4;
 | 
			
		||||
  enum CoverOperation {
 | 
			
		||||
    IDLE = 0;
 | 
			
		||||
    IS_OPENING = 1;
 | 
			
		||||
    IS_CLOSING = 2;
 | 
			
		||||
  }
 | 
			
		||||
  CoverOperation current_operation = 5;
 | 
			
		||||
}
 | 
			
		||||
// ID: 30
 | 
			
		||||
 | 
			
		||||
enum LegacyCoverCommand {
 | 
			
		||||
  LEGACY_COVER_COMMAND_OPEN = 0;
 | 
			
		||||
  LEGACY_COVER_COMMAND_CLOSE = 1;
 | 
			
		||||
  LEGACY_COVER_COMMAND_STOP = 2;
 | 
			
		||||
}
 | 
			
		||||
message CoverCommandRequest {
 | 
			
		||||
  option (id) = 30;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
  option (ifdef) = "USE_COVER";
 | 
			
		||||
  option (no_delay) = true;
 | 
			
		||||
 | 
			
		||||
  fixed32 key = 1;
 | 
			
		||||
 | 
			
		||||
  // legacy: command has been removed in 1.13
 | 
			
		||||
  // clients/servers must still send/accept it until the next protocol change
 | 
			
		||||
  enum LegacyCoverCommand {
 | 
			
		||||
    OPEN = 0;
 | 
			
		||||
    CLOSE = 1;
 | 
			
		||||
    STOP = 2;
 | 
			
		||||
  }
 | 
			
		||||
  bool has_legacy_command = 2;
 | 
			
		||||
  LegacyCoverCommand legacy_command = 3;
 | 
			
		||||
 | 
			
		||||
@@ -200,8 +289,11 @@ message CoverCommandRequest {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ==================== FAN ====================
 | 
			
		||||
// ID: 14
 | 
			
		||||
message ListEntitiesFanResponse {
 | 
			
		||||
  option (id) = 14;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_FAN";
 | 
			
		||||
 | 
			
		||||
  string object_id = 1;
 | 
			
		||||
  fixed32 key = 2;
 | 
			
		||||
  string name = 3;
 | 
			
		||||
@@ -209,21 +301,35 @@ message ListEntitiesFanResponse {
 | 
			
		||||
 | 
			
		||||
  bool supports_oscillation = 5;
 | 
			
		||||
  bool supports_speed = 6;
 | 
			
		||||
  bool supports_direction = 7;
 | 
			
		||||
}
 | 
			
		||||
enum FanSpeed {
 | 
			
		||||
  LOW = 0;
 | 
			
		||||
  MEDIUM = 1;
 | 
			
		||||
  HIGH = 2;
 | 
			
		||||
  FAN_SPEED_LOW = 0;
 | 
			
		||||
  FAN_SPEED_MEDIUM = 1;
 | 
			
		||||
  FAN_SPEED_HIGH = 2;
 | 
			
		||||
}
 | 
			
		||||
enum FanDirection {
 | 
			
		||||
  FAN_DIRECTION_FORWARD = 0;
 | 
			
		||||
  FAN_DIRECTION_REVERSE = 1;
 | 
			
		||||
}
 | 
			
		||||
// ID: 23
 | 
			
		||||
message FanStateResponse {
 | 
			
		||||
  option (id) = 23;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_FAN";
 | 
			
		||||
  option (no_delay) = true;
 | 
			
		||||
 | 
			
		||||
  fixed32 key = 1;
 | 
			
		||||
  bool state = 2;
 | 
			
		||||
  bool oscillating = 3;
 | 
			
		||||
  FanSpeed speed = 4;
 | 
			
		||||
  FanDirection direction = 5;
 | 
			
		||||
}
 | 
			
		||||
// ID: 31
 | 
			
		||||
message FanCommandRequest {
 | 
			
		||||
  option (id) = 31;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
  option (ifdef) = "USE_FAN";
 | 
			
		||||
  option (no_delay) = true;
 | 
			
		||||
 | 
			
		||||
  fixed32 key = 1;
 | 
			
		||||
  bool has_state = 2;
 | 
			
		||||
  bool state = 3;
 | 
			
		||||
@@ -231,11 +337,16 @@ message FanCommandRequest {
 | 
			
		||||
  FanSpeed speed = 5;
 | 
			
		||||
  bool has_oscillating = 6;
 | 
			
		||||
  bool oscillating = 7;
 | 
			
		||||
  bool has_direction = 8;
 | 
			
		||||
  FanDirection direction = 9;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ==================== LIGHT ====================
 | 
			
		||||
// ID: 15
 | 
			
		||||
message ListEntitiesLightResponse {
 | 
			
		||||
  option (id) = 15;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_LIGHT";
 | 
			
		||||
 | 
			
		||||
  string object_id = 1;
 | 
			
		||||
  fixed32 key = 2;
 | 
			
		||||
  string name = 3;
 | 
			
		||||
@@ -249,8 +360,12 @@ message ListEntitiesLightResponse {
 | 
			
		||||
  float max_mireds = 10;
 | 
			
		||||
  repeated string effects = 11;
 | 
			
		||||
}
 | 
			
		||||
// ID: 24
 | 
			
		||||
message LightStateResponse {
 | 
			
		||||
  option (id) = 24;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_LIGHT";
 | 
			
		||||
  option (no_delay) = true;
 | 
			
		||||
 | 
			
		||||
  fixed32 key = 1;
 | 
			
		||||
  bool state = 2;
 | 
			
		||||
  float brightness = 3;
 | 
			
		||||
@@ -261,8 +376,12 @@ message LightStateResponse {
 | 
			
		||||
  float color_temperature = 8;
 | 
			
		||||
  string effect = 9;
 | 
			
		||||
}
 | 
			
		||||
// ID: 32
 | 
			
		||||
message LightCommandRequest {
 | 
			
		||||
  option (id) = 32;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
  option (ifdef) = "USE_LIGHT";
 | 
			
		||||
  option (no_delay) = true;
 | 
			
		||||
 | 
			
		||||
  fixed32 key = 1;
 | 
			
		||||
  bool has_state = 2;
 | 
			
		||||
  bool state = 3;
 | 
			
		||||
@@ -285,8 +404,11 @@ message LightCommandRequest {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ==================== SENSOR ====================
 | 
			
		||||
// ID: 16
 | 
			
		||||
message ListEntitiesSensorResponse {
 | 
			
		||||
  option (id) = 16;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_SENSOR";
 | 
			
		||||
 | 
			
		||||
  string object_id = 1;
 | 
			
		||||
  fixed32 key = 2;
 | 
			
		||||
  string name = 3;
 | 
			
		||||
@@ -295,16 +417,27 @@ message ListEntitiesSensorResponse {
 | 
			
		||||
  string icon = 5;
 | 
			
		||||
  string unit_of_measurement = 6;
 | 
			
		||||
  int32 accuracy_decimals = 7;
 | 
			
		||||
  bool force_update = 8;
 | 
			
		||||
}
 | 
			
		||||
// ID: 25
 | 
			
		||||
message SensorStateResponse {
 | 
			
		||||
  option (id) = 25;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_SENSOR";
 | 
			
		||||
  option (no_delay) = true;
 | 
			
		||||
 | 
			
		||||
  fixed32 key = 1;
 | 
			
		||||
  float state = 2;
 | 
			
		||||
  // If the sensor does not have a valid state yet.
 | 
			
		||||
  // Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
 | 
			
		||||
  bool missing_state = 3;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ==================== SWITCH ====================
 | 
			
		||||
// ID: 17
 | 
			
		||||
message ListEntitiesSwitchResponse {
 | 
			
		||||
  option (id) = 17;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_SWITCH";
 | 
			
		||||
 | 
			
		||||
  string object_id = 1;
 | 
			
		||||
  fixed32 key = 2;
 | 
			
		||||
  string name = 3;
 | 
			
		||||
@@ -313,20 +446,31 @@ message ListEntitiesSwitchResponse {
 | 
			
		||||
  string icon = 5;
 | 
			
		||||
  bool assumed_state = 6;
 | 
			
		||||
}
 | 
			
		||||
// ID: 26
 | 
			
		||||
message SwitchStateResponse {
 | 
			
		||||
  option (id) = 26;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_SWITCH";
 | 
			
		||||
  option (no_delay) = true;
 | 
			
		||||
 | 
			
		||||
  fixed32 key = 1;
 | 
			
		||||
  bool state = 2;
 | 
			
		||||
}
 | 
			
		||||
// ID: 33
 | 
			
		||||
message SwitchCommandRequest {
 | 
			
		||||
  option (id) = 33;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
  option (ifdef) = "USE_SWITCH";
 | 
			
		||||
  option (no_delay) = true;
 | 
			
		||||
 | 
			
		||||
  fixed32 key = 1;
 | 
			
		||||
  bool state = 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ==================== TEXT SENSOR ====================
 | 
			
		||||
// ID: 18
 | 
			
		||||
message ListEntitiesTextSensorResponse {
 | 
			
		||||
  option (id) = 18;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_TEXT_SENSOR";
 | 
			
		||||
 | 
			
		||||
  string object_id = 1;
 | 
			
		||||
  fixed32 key = 2;
 | 
			
		||||
  string name = 3;
 | 
			
		||||
@@ -334,29 +478,41 @@ message ListEntitiesTextSensorResponse {
 | 
			
		||||
 | 
			
		||||
  string icon = 5;
 | 
			
		||||
}
 | 
			
		||||
// ID: 27
 | 
			
		||||
message TextSensorStateResponse {
 | 
			
		||||
  option (id) = 27;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_TEXT_SENSOR";
 | 
			
		||||
  option (no_delay) = true;
 | 
			
		||||
 | 
			
		||||
  fixed32 key = 1;
 | 
			
		||||
  string state = 2;
 | 
			
		||||
  // If the text sensor does not have a valid state yet.
 | 
			
		||||
  // Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
 | 
			
		||||
  bool missing_state = 3;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ==================== SUBSCRIBE LOGS ====================
 | 
			
		||||
enum LogLevel {
 | 
			
		||||
  NONE = 0;
 | 
			
		||||
  ERROR = 1;
 | 
			
		||||
  WARN = 2;
 | 
			
		||||
  INFO = 3;
 | 
			
		||||
  DEBUG = 4;
 | 
			
		||||
  VERBOSE = 5;
 | 
			
		||||
  VERY_VERBOSE = 6;
 | 
			
		||||
  LOG_LEVEL_NONE = 0;
 | 
			
		||||
  LOG_LEVEL_ERROR = 1;
 | 
			
		||||
  LOG_LEVEL_WARN = 2;
 | 
			
		||||
  LOG_LEVEL_INFO = 3;
 | 
			
		||||
  LOG_LEVEL_DEBUG = 4;
 | 
			
		||||
  LOG_LEVEL_VERBOSE = 5;
 | 
			
		||||
  LOG_LEVEL_VERY_VERBOSE = 6;
 | 
			
		||||
}
 | 
			
		||||
// ID: 28
 | 
			
		||||
message SubscribeLogsRequest {
 | 
			
		||||
  option (id) = 28;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
  LogLevel level = 1;
 | 
			
		||||
  bool dump_config = 2;
 | 
			
		||||
}
 | 
			
		||||
// ID: 29
 | 
			
		||||
message SubscribeLogsResponse {
 | 
			
		||||
  option (id) = 29;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (log) = false;
 | 
			
		||||
  option (no_delay) = false;
 | 
			
		||||
 | 
			
		||||
  LogLevel level = 1;
 | 
			
		||||
  string tag = 2;
 | 
			
		||||
  string message = 3;
 | 
			
		||||
@@ -364,109 +520,181 @@ message SubscribeLogsResponse {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ==================== HOMEASSISTANT.SERVICE ====================
 | 
			
		||||
// ID: 34
 | 
			
		||||
message SubscribeServiceCallsRequest {
 | 
			
		||||
 | 
			
		||||
message SubscribeHomeassistantServicesRequest {
 | 
			
		||||
  option (id) = 34;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ID: 35
 | 
			
		||||
message ServiceCallResponse {
 | 
			
		||||
message HomeassistantServiceMap {
 | 
			
		||||
  string key = 1;
 | 
			
		||||
  string value = 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message HomeassistantServiceResponse {
 | 
			
		||||
  option (id) = 35;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (no_delay) = true;
 | 
			
		||||
 | 
			
		||||
  string service = 1;
 | 
			
		||||
  map<string, string> data = 2;
 | 
			
		||||
  map<string, string> data_template = 3;
 | 
			
		||||
  map<string, string> variables = 4;
 | 
			
		||||
  repeated HomeassistantServiceMap data = 2;
 | 
			
		||||
  repeated HomeassistantServiceMap data_template = 3;
 | 
			
		||||
  repeated HomeassistantServiceMap variables = 4;
 | 
			
		||||
  bool is_event = 5;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ==================== IMPORT HOME ASSISTANT STATES ====================
 | 
			
		||||
// 1. Client sends SubscribeHomeAssistantStatesRequest
 | 
			
		||||
// 2. Server responds with zero or more SubscribeHomeAssistantStateResponse (async)
 | 
			
		||||
// 3. Client sends HomeAssistantStateResponse for state changes.
 | 
			
		||||
// ID: 38
 | 
			
		||||
message SubscribeHomeAssistantStatesRequest {
 | 
			
		||||
 | 
			
		||||
  option (id) = 38;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ID: 39
 | 
			
		||||
message SubscribeHomeAssistantStateResponse {
 | 
			
		||||
  option (id) = 39;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  string entity_id = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ID: 40
 | 
			
		||||
message HomeAssistantStateResponse {
 | 
			
		||||
  option (id) = 40;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
  option (no_delay) = true;
 | 
			
		||||
 | 
			
		||||
  string entity_id = 1;
 | 
			
		||||
  string state = 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ==================== IMPORT TIME ====================
 | 
			
		||||
// ID: 36
 | 
			
		||||
message GetTimeRequest {
 | 
			
		||||
 | 
			
		||||
  option (id) = 36;
 | 
			
		||||
  option (source) = SOURCE_BOTH;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ID: 37
 | 
			
		||||
message GetTimeResponse {
 | 
			
		||||
  option (id) = 37;
 | 
			
		||||
  option (source) = SOURCE_BOTH;
 | 
			
		||||
  option (no_delay) = true;
 | 
			
		||||
 | 
			
		||||
  fixed32 epoch_seconds = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ==================== USER-DEFINES SERVICES ====================
 | 
			
		||||
enum ServiceArgType {
 | 
			
		||||
  SERVICE_ARG_TYPE_BOOL = 0;
 | 
			
		||||
  SERVICE_ARG_TYPE_INT = 1;
 | 
			
		||||
  SERVICE_ARG_TYPE_FLOAT = 2;
 | 
			
		||||
  SERVICE_ARG_TYPE_STRING = 3;
 | 
			
		||||
  SERVICE_ARG_TYPE_BOOL_ARRAY = 4;
 | 
			
		||||
  SERVICE_ARG_TYPE_INT_ARRAY = 5;
 | 
			
		||||
  SERVICE_ARG_TYPE_FLOAT_ARRAY = 6;
 | 
			
		||||
  SERVICE_ARG_TYPE_STRING_ARRAY = 7;
 | 
			
		||||
}
 | 
			
		||||
message ListEntitiesServicesArgument {
 | 
			
		||||
  string name = 1;
 | 
			
		||||
  enum Type {
 | 
			
		||||
    BOOL = 0;
 | 
			
		||||
    INT = 1;
 | 
			
		||||
    FLOAT = 2;
 | 
			
		||||
    STRING = 3;
 | 
			
		||||
  }
 | 
			
		||||
  Type type = 2;
 | 
			
		||||
  ServiceArgType type = 2;
 | 
			
		||||
}
 | 
			
		||||
// ID: 41
 | 
			
		||||
message ListEntitiesServicesResponse {
 | 
			
		||||
  option (id) = 41;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
 | 
			
		||||
  string name = 1;
 | 
			
		||||
  fixed32 key = 2;
 | 
			
		||||
  repeated ListEntitiesServicesArgument args = 3;
 | 
			
		||||
}
 | 
			
		||||
message ExecuteServiceArgument {
 | 
			
		||||
  bool bool_ = 1;
 | 
			
		||||
  int32 int_ = 2;
 | 
			
		||||
  int32 legacy_int = 2;
 | 
			
		||||
  float float_ = 3;
 | 
			
		||||
  string string_ = 4;
 | 
			
		||||
  // ESPHome 1.14 (api v1.3) make int a signed value
 | 
			
		||||
  sint32 int_ = 5;
 | 
			
		||||
  repeated bool bool_array = 6 [packed=false];
 | 
			
		||||
  repeated sint32 int_array = 7 [packed=false];
 | 
			
		||||
  repeated float float_array = 8 [packed=false];
 | 
			
		||||
  repeated string string_array = 9;
 | 
			
		||||
}
 | 
			
		||||
// ID: 42
 | 
			
		||||
message ExecuteServiceRequest {
 | 
			
		||||
  option (id) = 42;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
  option (no_delay) = true;
 | 
			
		||||
 | 
			
		||||
  fixed32 key = 1;
 | 
			
		||||
  repeated ExecuteServiceArgument args = 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ==================== CAMERA ====================
 | 
			
		||||
// ID: 43
 | 
			
		||||
message ListEntitiesCameraResponse {
 | 
			
		||||
  option (id) = 43;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_ESP32_CAMERA";
 | 
			
		||||
 | 
			
		||||
  string object_id = 1;
 | 
			
		||||
  fixed32 key = 2;
 | 
			
		||||
  string name = 3;
 | 
			
		||||
  string unique_id = 4;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ID: 44
 | 
			
		||||
message CameraImageResponse {
 | 
			
		||||
  option (id) = 44;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_ESP32_CAMERA";
 | 
			
		||||
 | 
			
		||||
  fixed32 key = 1;
 | 
			
		||||
  bytes data = 2;
 | 
			
		||||
  bool done = 3;
 | 
			
		||||
}
 | 
			
		||||
// ID: 45
 | 
			
		||||
message CameraImageRequest {
 | 
			
		||||
  option (id) = 45;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
  option (ifdef) = "USE_ESP32_CAMERA";
 | 
			
		||||
  option (no_delay) = true;
 | 
			
		||||
 | 
			
		||||
  bool single = 1;
 | 
			
		||||
  bool stream = 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ==================== CLIMATE ====================
 | 
			
		||||
enum ClimateMode {
 | 
			
		||||
  OFF = 0;
 | 
			
		||||
  AUTO = 1;
 | 
			
		||||
  COOL = 2;
 | 
			
		||||
  HEAT = 3;
 | 
			
		||||
  CLIMATE_MODE_OFF = 0;
 | 
			
		||||
  CLIMATE_MODE_AUTO = 1;
 | 
			
		||||
  CLIMATE_MODE_COOL = 2;
 | 
			
		||||
  CLIMATE_MODE_HEAT = 3;
 | 
			
		||||
  CLIMATE_MODE_FAN_ONLY = 4;
 | 
			
		||||
  CLIMATE_MODE_DRY = 5;
 | 
			
		||||
}
 | 
			
		||||
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 {
 | 
			
		||||
  CLIMATE_ACTION_OFF = 0;
 | 
			
		||||
  // values same as mode for readability
 | 
			
		||||
  CLIMATE_ACTION_COOLING = 2;
 | 
			
		||||
  CLIMATE_ACTION_HEATING = 3;
 | 
			
		||||
  CLIMATE_ACTION_IDLE = 4;
 | 
			
		||||
  CLIMATE_ACTION_DRYING = 5;
 | 
			
		||||
  CLIMATE_ACTION_FAN = 6;
 | 
			
		||||
}
 | 
			
		||||
// ID: 46
 | 
			
		||||
message ListEntitiesClimateResponse {
 | 
			
		||||
  option (id) = 46;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_CLIMATE";
 | 
			
		||||
 | 
			
		||||
  string object_id = 1;
 | 
			
		||||
  fixed32 key = 2;
 | 
			
		||||
  string name = 3;
 | 
			
		||||
@@ -479,9 +707,16 @@ message ListEntitiesClimateResponse {
 | 
			
		||||
  float visual_max_temperature = 9;
 | 
			
		||||
  float visual_temperature_step = 10;
 | 
			
		||||
  bool supports_away = 11;
 | 
			
		||||
  bool supports_action = 12;
 | 
			
		||||
  repeated ClimateFanMode supported_fan_modes = 13;
 | 
			
		||||
  repeated ClimateSwingMode supported_swing_modes = 14;
 | 
			
		||||
}
 | 
			
		||||
// ID: 47
 | 
			
		||||
message ClimateStateResponse {
 | 
			
		||||
  option (id) = 47;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_CLIMATE";
 | 
			
		||||
  option (no_delay) = true;
 | 
			
		||||
 | 
			
		||||
  fixed32 key = 1;
 | 
			
		||||
  ClimateMode mode = 2;
 | 
			
		||||
  float current_temperature = 3;
 | 
			
		||||
@@ -489,9 +724,16 @@ message ClimateStateResponse {
 | 
			
		||||
  float target_temperature_low = 5;
 | 
			
		||||
  float target_temperature_high = 6;
 | 
			
		||||
  bool away = 7;
 | 
			
		||||
  ClimateAction action = 8;
 | 
			
		||||
  ClimateFanMode fan_mode = 9;
 | 
			
		||||
  ClimateSwingMode swing_mode = 10;
 | 
			
		||||
}
 | 
			
		||||
// ID: 48
 | 
			
		||||
message ClimateCommandRequest {
 | 
			
		||||
  option (id) = 48;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
  option (ifdef) = "USE_CLIMATE";
 | 
			
		||||
  option (no_delay) = true;
 | 
			
		||||
 | 
			
		||||
  fixed32 key = 1;
 | 
			
		||||
  bool has_mode = 2;
 | 
			
		||||
  ClimateMode mode = 3;
 | 
			
		||||
@@ -503,4 +745,8 @@ message ClimateCommandRequest {
 | 
			
		||||
  float target_temperature_high = 9;
 | 
			
		||||
  bool has_away = 10;
 | 
			
		||||
  bool away = 11;
 | 
			
		||||
  bool has_fan_mode = 12;
 | 
			
		||||
  ClimateFanMode fan_mode = 13;
 | 
			
		||||
  bool has_swing_mode = 14;
 | 
			
		||||
  ClimateSwingMode swing_mode = 15;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										701
									
								
								esphome/components/api/api_connection.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										701
									
								
								esphome/components/api/api_connection.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,701 @@
 | 
			
		||||
#include "api_connection.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/core/util.h"
 | 
			
		||||
#include "esphome/core/version.h"
 | 
			
		||||
 | 
			
		||||
#ifdef USE_DEEP_SLEEP
 | 
			
		||||
#include "esphome/components/deep_sleep/deep_sleep_component.h"
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_HOMEASSISTANT_TIME
 | 
			
		||||
#include "esphome/components/homeassistant/time/homeassistant_time.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
static const char *TAG = "api.connection";
 | 
			
		||||
 | 
			
		||||
APIConnection::APIConnection(AsyncClient *client, APIServer *parent)
 | 
			
		||||
    : client_(client), parent_(parent), initial_state_iterator_(parent, this), list_entities_iterator_(parent, this) {
 | 
			
		||||
  this->client_->onError([](void *s, AsyncClient *c, int8_t error) { ((APIConnection *) s)->on_error_(error); }, this);
 | 
			
		||||
  this->client_->onDisconnect([](void *s, AsyncClient *c) { ((APIConnection *) s)->on_disconnect_(); }, this);
 | 
			
		||||
  this->client_->onTimeout([](void *s, AsyncClient *c, uint32_t time) { ((APIConnection *) s)->on_timeout_(time); },
 | 
			
		||||
                           this);
 | 
			
		||||
  this->client_->onData([](void *s, AsyncClient *c, void *buf,
 | 
			
		||||
                           size_t len) { ((APIConnection *) s)->on_data_(reinterpret_cast<uint8_t *>(buf), len); },
 | 
			
		||||
                        this);
 | 
			
		||||
 | 
			
		||||
  this->send_buffer_.reserve(64);
 | 
			
		||||
  this->recv_buffer_.reserve(32);
 | 
			
		||||
  this->client_info_ = this->client_->remoteIP().toString().c_str();
 | 
			
		||||
  this->last_traffic_ = millis();
 | 
			
		||||
}
 | 
			
		||||
APIConnection::~APIConnection() { delete this->client_; }
 | 
			
		||||
void APIConnection::on_error_(int8_t error) { this->remove_ = true; }
 | 
			
		||||
void APIConnection::on_disconnect_() { this->remove_ = true; }
 | 
			
		||||
void APIConnection::on_timeout_(uint32_t time) { this->on_fatal_error(); }
 | 
			
		||||
void APIConnection::on_data_(uint8_t *buf, size_t len) {
 | 
			
		||||
  if (len == 0 || buf == nullptr)
 | 
			
		||||
    return;
 | 
			
		||||
  this->recv_buffer_.insert(this->recv_buffer_.end(), buf, buf + len);
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::parse_recv_buffer_() {
 | 
			
		||||
  if (this->recv_buffer_.empty() || this->remove_)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  while (!this->recv_buffer_.empty()) {
 | 
			
		||||
    if (this->recv_buffer_[0] != 0x00) {
 | 
			
		||||
      ESP_LOGW(TAG, "Invalid preamble from %s", this->client_info_.c_str());
 | 
			
		||||
      this->on_fatal_error();
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    uint32_t i = 1;
 | 
			
		||||
    const uint32_t size = this->recv_buffer_.size();
 | 
			
		||||
    uint32_t consumed;
 | 
			
		||||
    auto msg_size_varint = ProtoVarInt::parse(&this->recv_buffer_[i], size - i, &consumed);
 | 
			
		||||
    if (!msg_size_varint.has_value())
 | 
			
		||||
      // not enough data there yet
 | 
			
		||||
      return;
 | 
			
		||||
    i += consumed;
 | 
			
		||||
    uint32_t msg_size = msg_size_varint->as_uint32();
 | 
			
		||||
 | 
			
		||||
    auto msg_type_varint = ProtoVarInt::parse(&this->recv_buffer_[i], size - i, &consumed);
 | 
			
		||||
    if (!msg_type_varint.has_value())
 | 
			
		||||
      // not enough data there yet
 | 
			
		||||
      return;
 | 
			
		||||
    i += consumed;
 | 
			
		||||
    uint32_t msg_type = msg_type_varint->as_uint32();
 | 
			
		||||
 | 
			
		||||
    if (size - i < msg_size)
 | 
			
		||||
      // message body not fully received
 | 
			
		||||
      return;
 | 
			
		||||
 | 
			
		||||
    uint8_t *msg = &this->recv_buffer_[i];
 | 
			
		||||
    this->read_message(msg_size, msg_type, msg);
 | 
			
		||||
    if (this->remove_)
 | 
			
		||||
      return;
 | 
			
		||||
    // pop front
 | 
			
		||||
    uint32_t total = i + msg_size;
 | 
			
		||||
    this->recv_buffer_.erase(this->recv_buffer_.begin(), this->recv_buffer_.begin() + total);
 | 
			
		||||
    this->last_traffic_ = millis();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void APIConnection::disconnect_client() {
 | 
			
		||||
  this->client_->close();
 | 
			
		||||
  this->remove_ = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void APIConnection::loop() {
 | 
			
		||||
  if (this->remove_)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  if (this->next_close_) {
 | 
			
		||||
    this->disconnect_client();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!network_is_connected()) {
 | 
			
		||||
    // when network is disconnected force disconnect immediately
 | 
			
		||||
    // don't wait for timeout
 | 
			
		||||
    this->on_fatal_error();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (this->client_->disconnected()) {
 | 
			
		||||
    // failsafe for disconnect logic
 | 
			
		||||
    this->on_disconnect_();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->parse_recv_buffer_();
 | 
			
		||||
 | 
			
		||||
  this->list_entities_iterator_.advance();
 | 
			
		||||
  this->initial_state_iterator_.advance();
 | 
			
		||||
 | 
			
		||||
  const uint32_t keepalive = 60000;
 | 
			
		||||
  if (this->sent_ping_) {
 | 
			
		||||
    // Disconnect if not responded within 2.5*keepalive
 | 
			
		||||
    if (millis() - this->last_traffic_ > (keepalive * 5) / 2) {
 | 
			
		||||
      ESP_LOGW(TAG, "'%s' didn't respond to ping request in time. Disconnecting...", this->client_info_.c_str());
 | 
			
		||||
      this->disconnect_client();
 | 
			
		||||
    }
 | 
			
		||||
  } else if (millis() - this->last_traffic_ > keepalive) {
 | 
			
		||||
    this->sent_ping_ = true;
 | 
			
		||||
    this->send_ping_request(PingRequest());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
  if (this->image_reader_.available()) {
 | 
			
		||||
    uint32_t space = this->client_->space();
 | 
			
		||||
    // reserve 15 bytes for metadata, and at least 64 bytes of data
 | 
			
		||||
    if (space >= 15 + 64) {
 | 
			
		||||
      uint32_t to_send = std::min(space - 15, this->image_reader_.available());
 | 
			
		||||
      auto buffer = this->create_buffer();
 | 
			
		||||
      // fixed32 key = 1;
 | 
			
		||||
      buffer.encode_fixed32(1, esp32_camera::global_esp32_camera->get_object_id_hash());
 | 
			
		||||
      // bytes data = 2;
 | 
			
		||||
      buffer.encode_bytes(2, this->image_reader_.peek_data_buffer(), to_send);
 | 
			
		||||
      // bool done = 3;
 | 
			
		||||
      bool done = this->image_reader_.available() == to_send;
 | 
			
		||||
      buffer.encode_bool(3, done);
 | 
			
		||||
      bool success = this->send_buffer(buffer, 44);
 | 
			
		||||
 | 
			
		||||
      if (success) {
 | 
			
		||||
        this->image_reader_.consume_data(to_send);
 | 
			
		||||
      }
 | 
			
		||||
      if (success && done) {
 | 
			
		||||
        this->image_reader_.return_image();
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string get_default_unique_id(const std::string &component_type, Nameable *nameable) {
 | 
			
		||||
  return App.get_name() + component_type + nameable->get_object_id();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
bool APIConnection::send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor, bool state) {
 | 
			
		||||
  if (!this->state_subscription_)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  BinarySensorStateResponse resp;
 | 
			
		||||
  resp.key = binary_sensor->get_object_id_hash();
 | 
			
		||||
  resp.state = state;
 | 
			
		||||
  resp.missing_state = !binary_sensor->has_state();
 | 
			
		||||
  return this->send_binary_sensor_state_response(resp);
 | 
			
		||||
}
 | 
			
		||||
bool APIConnection::send_binary_sensor_info(binary_sensor::BinarySensor *binary_sensor) {
 | 
			
		||||
  ListEntitiesBinarySensorResponse msg;
 | 
			
		||||
  msg.object_id = binary_sensor->get_object_id();
 | 
			
		||||
  msg.key = binary_sensor->get_object_id_hash();
 | 
			
		||||
  msg.name = binary_sensor->get_name();
 | 
			
		||||
  msg.unique_id = get_default_unique_id("binary_sensor", binary_sensor);
 | 
			
		||||
  msg.device_class = binary_sensor->get_device_class();
 | 
			
		||||
  msg.is_status_binary_sensor = binary_sensor->is_status_binary_sensor();
 | 
			
		||||
  return this->send_list_entities_binary_sensor_response(msg);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_COVER
 | 
			
		||||
bool APIConnection::send_cover_state(cover::Cover *cover) {
 | 
			
		||||
  if (!this->state_subscription_)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  auto traits = cover->get_traits();
 | 
			
		||||
  CoverStateResponse resp{};
 | 
			
		||||
  resp.key = cover->get_object_id_hash();
 | 
			
		||||
  resp.legacy_state =
 | 
			
		||||
      (cover->position == cover::COVER_OPEN) ? enums::LEGACY_COVER_STATE_OPEN : enums::LEGACY_COVER_STATE_CLOSED;
 | 
			
		||||
  resp.position = cover->position;
 | 
			
		||||
  if (traits.get_supports_tilt())
 | 
			
		||||
    resp.tilt = cover->tilt;
 | 
			
		||||
  resp.current_operation = static_cast<enums::CoverOperation>(cover->current_operation);
 | 
			
		||||
  return this->send_cover_state_response(resp);
 | 
			
		||||
}
 | 
			
		||||
bool APIConnection::send_cover_info(cover::Cover *cover) {
 | 
			
		||||
  auto traits = cover->get_traits();
 | 
			
		||||
  ListEntitiesCoverResponse msg;
 | 
			
		||||
  msg.key = cover->get_object_id_hash();
 | 
			
		||||
  msg.object_id = cover->get_object_id();
 | 
			
		||||
  msg.name = cover->get_name();
 | 
			
		||||
  msg.unique_id = get_default_unique_id("cover", cover);
 | 
			
		||||
  msg.assumed_state = traits.get_is_assumed_state();
 | 
			
		||||
  msg.supports_position = traits.get_supports_position();
 | 
			
		||||
  msg.supports_tilt = traits.get_supports_tilt();
 | 
			
		||||
  msg.device_class = cover->get_device_class();
 | 
			
		||||
  return this->send_list_entities_cover_response(msg);
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::cover_command(const CoverCommandRequest &msg) {
 | 
			
		||||
  cover::Cover *cover = App.get_cover_by_key(msg.key);
 | 
			
		||||
  if (cover == nullptr)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  auto call = cover->make_call();
 | 
			
		||||
  if (msg.has_legacy_command) {
 | 
			
		||||
    switch (msg.legacy_command) {
 | 
			
		||||
      case enums::LEGACY_COVER_COMMAND_OPEN:
 | 
			
		||||
        call.set_command_open();
 | 
			
		||||
        break;
 | 
			
		||||
      case enums::LEGACY_COVER_COMMAND_CLOSE:
 | 
			
		||||
        call.set_command_close();
 | 
			
		||||
        break;
 | 
			
		||||
      case enums::LEGACY_COVER_COMMAND_STOP:
 | 
			
		||||
        call.set_command_stop();
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if (msg.has_position)
 | 
			
		||||
    call.set_position(msg.position);
 | 
			
		||||
  if (msg.has_tilt)
 | 
			
		||||
    call.set_tilt(msg.tilt);
 | 
			
		||||
  if (msg.stop)
 | 
			
		||||
    call.set_command_stop();
 | 
			
		||||
  call.perform();
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_FAN
 | 
			
		||||
bool APIConnection::send_fan_state(fan::FanState *fan) {
 | 
			
		||||
  if (!this->state_subscription_)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  auto traits = fan->get_traits();
 | 
			
		||||
  FanStateResponse resp{};
 | 
			
		||||
  resp.key = fan->get_object_id_hash();
 | 
			
		||||
  resp.state = fan->state;
 | 
			
		||||
  if (traits.supports_oscillation())
 | 
			
		||||
    resp.oscillating = fan->oscillating;
 | 
			
		||||
  if (traits.supports_speed())
 | 
			
		||||
    resp.speed = static_cast<enums::FanSpeed>(fan->speed);
 | 
			
		||||
  if (traits.supports_direction())
 | 
			
		||||
    resp.direction = static_cast<enums::FanDirection>(fan->direction);
 | 
			
		||||
  return this->send_fan_state_response(resp);
 | 
			
		||||
}
 | 
			
		||||
bool APIConnection::send_fan_info(fan::FanState *fan) {
 | 
			
		||||
  auto traits = fan->get_traits();
 | 
			
		||||
  ListEntitiesFanResponse msg;
 | 
			
		||||
  msg.key = fan->get_object_id_hash();
 | 
			
		||||
  msg.object_id = fan->get_object_id();
 | 
			
		||||
  msg.name = fan->get_name();
 | 
			
		||||
  msg.unique_id = get_default_unique_id("fan", fan);
 | 
			
		||||
  msg.supports_oscillation = traits.supports_oscillation();
 | 
			
		||||
  msg.supports_speed = traits.supports_speed();
 | 
			
		||||
  msg.supports_direction = traits.supports_direction();
 | 
			
		||||
  return this->send_list_entities_fan_response(msg);
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::fan_command(const FanCommandRequest &msg) {
 | 
			
		||||
  fan::FanState *fan = App.get_fan_by_key(msg.key);
 | 
			
		||||
  if (fan == nullptr)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  auto call = fan->make_call();
 | 
			
		||||
  if (msg.has_state)
 | 
			
		||||
    call.set_state(msg.state);
 | 
			
		||||
  if (msg.has_oscillating)
 | 
			
		||||
    call.set_oscillating(msg.oscillating);
 | 
			
		||||
  if (msg.has_speed)
 | 
			
		||||
    call.set_speed(static_cast<fan::FanSpeed>(msg.speed));
 | 
			
		||||
  if (msg.has_direction)
 | 
			
		||||
    call.set_direction(static_cast<fan::FanDirection>(msg.direction));
 | 
			
		||||
  call.perform();
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_LIGHT
 | 
			
		||||
bool APIConnection::send_light_state(light::LightState *light) {
 | 
			
		||||
  if (!this->state_subscription_)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  auto traits = light->get_traits();
 | 
			
		||||
  auto values = light->remote_values;
 | 
			
		||||
  LightStateResponse resp{};
 | 
			
		||||
 | 
			
		||||
  resp.key = light->get_object_id_hash();
 | 
			
		||||
  resp.state = values.is_on();
 | 
			
		||||
  if (traits.get_supports_brightness())
 | 
			
		||||
    resp.brightness = values.get_brightness();
 | 
			
		||||
  if (traits.get_supports_rgb()) {
 | 
			
		||||
    resp.red = values.get_red();
 | 
			
		||||
    resp.green = values.get_green();
 | 
			
		||||
    resp.blue = values.get_blue();
 | 
			
		||||
  }
 | 
			
		||||
  if (traits.get_supports_rgb_white_value())
 | 
			
		||||
    resp.white = values.get_white();
 | 
			
		||||
  if (traits.get_supports_color_temperature())
 | 
			
		||||
    resp.color_temperature = values.get_color_temperature();
 | 
			
		||||
  if (light->supports_effects())
 | 
			
		||||
    resp.effect = light->get_effect_name();
 | 
			
		||||
  return this->send_light_state_response(resp);
 | 
			
		||||
}
 | 
			
		||||
bool APIConnection::send_light_info(light::LightState *light) {
 | 
			
		||||
  auto traits = light->get_traits();
 | 
			
		||||
  ListEntitiesLightResponse msg;
 | 
			
		||||
  msg.key = light->get_object_id_hash();
 | 
			
		||||
  msg.object_id = light->get_object_id();
 | 
			
		||||
  msg.name = light->get_name();
 | 
			
		||||
  msg.unique_id = get_default_unique_id("light", light);
 | 
			
		||||
  msg.supports_brightness = traits.get_supports_brightness();
 | 
			
		||||
  msg.supports_rgb = traits.get_supports_rgb();
 | 
			
		||||
  msg.supports_white_value = traits.get_supports_rgb_white_value();
 | 
			
		||||
  msg.supports_color_temperature = traits.get_supports_color_temperature();
 | 
			
		||||
  if (msg.supports_color_temperature) {
 | 
			
		||||
    msg.min_mireds = traits.get_min_mireds();
 | 
			
		||||
    msg.max_mireds = traits.get_max_mireds();
 | 
			
		||||
  }
 | 
			
		||||
  if (light->supports_effects()) {
 | 
			
		||||
    msg.effects.emplace_back("None");
 | 
			
		||||
    for (auto *effect : light->get_effects())
 | 
			
		||||
      msg.effects.push_back(effect->get_name());
 | 
			
		||||
  }
 | 
			
		||||
  return this->send_list_entities_light_response(msg);
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::light_command(const LightCommandRequest &msg) {
 | 
			
		||||
  light::LightState *light = App.get_light_by_key(msg.key);
 | 
			
		||||
  if (light == nullptr)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  auto call = light->make_call();
 | 
			
		||||
  if (msg.has_state)
 | 
			
		||||
    call.set_state(msg.state);
 | 
			
		||||
  if (msg.has_brightness)
 | 
			
		||||
    call.set_brightness(msg.brightness);
 | 
			
		||||
  if (msg.has_rgb) {
 | 
			
		||||
    call.set_red(msg.red);
 | 
			
		||||
    call.set_green(msg.green);
 | 
			
		||||
    call.set_blue(msg.blue);
 | 
			
		||||
  }
 | 
			
		||||
  if (msg.has_white)
 | 
			
		||||
    call.set_white(msg.white);
 | 
			
		||||
  if (msg.has_color_temperature)
 | 
			
		||||
    call.set_color_temperature(msg.color_temperature);
 | 
			
		||||
  if (msg.has_transition_length)
 | 
			
		||||
    call.set_transition_length(msg.transition_length);
 | 
			
		||||
  if (msg.has_flash_length)
 | 
			
		||||
    call.set_flash_length(msg.flash_length);
 | 
			
		||||
  if (msg.has_effect)
 | 
			
		||||
    call.set_effect(msg.effect);
 | 
			
		||||
  call.perform();
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
bool APIConnection::send_sensor_state(sensor::Sensor *sensor, float state) {
 | 
			
		||||
  if (!this->state_subscription_)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  SensorStateResponse resp{};
 | 
			
		||||
  resp.key = sensor->get_object_id_hash();
 | 
			
		||||
  resp.state = state;
 | 
			
		||||
  resp.missing_state = !sensor->has_state();
 | 
			
		||||
  return this->send_sensor_state_response(resp);
 | 
			
		||||
}
 | 
			
		||||
bool APIConnection::send_sensor_info(sensor::Sensor *sensor) {
 | 
			
		||||
  ListEntitiesSensorResponse msg;
 | 
			
		||||
  msg.key = sensor->get_object_id_hash();
 | 
			
		||||
  msg.object_id = sensor->get_object_id();
 | 
			
		||||
  msg.name = sensor->get_name();
 | 
			
		||||
  msg.unique_id = sensor->unique_id();
 | 
			
		||||
  if (msg.unique_id.empty())
 | 
			
		||||
    msg.unique_id = get_default_unique_id("sensor", sensor);
 | 
			
		||||
  msg.icon = sensor->get_icon();
 | 
			
		||||
  msg.unit_of_measurement = sensor->get_unit_of_measurement();
 | 
			
		||||
  msg.accuracy_decimals = sensor->get_accuracy_decimals();
 | 
			
		||||
  msg.force_update = sensor->get_force_update();
 | 
			
		||||
  return this->send_list_entities_sensor_response(msg);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
bool APIConnection::send_switch_state(switch_::Switch *a_switch, bool state) {
 | 
			
		||||
  if (!this->state_subscription_)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  SwitchStateResponse resp{};
 | 
			
		||||
  resp.key = a_switch->get_object_id_hash();
 | 
			
		||||
  resp.state = state;
 | 
			
		||||
  return this->send_switch_state_response(resp);
 | 
			
		||||
}
 | 
			
		||||
bool APIConnection::send_switch_info(switch_::Switch *a_switch) {
 | 
			
		||||
  ListEntitiesSwitchResponse msg;
 | 
			
		||||
  msg.key = a_switch->get_object_id_hash();
 | 
			
		||||
  msg.object_id = a_switch->get_object_id();
 | 
			
		||||
  msg.name = a_switch->get_name();
 | 
			
		||||
  msg.unique_id = get_default_unique_id("switch", a_switch);
 | 
			
		||||
  msg.icon = a_switch->get_icon();
 | 
			
		||||
  msg.assumed_state = a_switch->assumed_state();
 | 
			
		||||
  return this->send_list_entities_switch_response(msg);
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::switch_command(const SwitchCommandRequest &msg) {
 | 
			
		||||
  switch_::Switch *a_switch = App.get_switch_by_key(msg.key);
 | 
			
		||||
  if (a_switch == nullptr)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  if (msg.state)
 | 
			
		||||
    a_switch->turn_on();
 | 
			
		||||
  else
 | 
			
		||||
    a_switch->turn_off();
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_TEXT_SENSOR
 | 
			
		||||
bool APIConnection::send_text_sensor_state(text_sensor::TextSensor *text_sensor, std::string state) {
 | 
			
		||||
  if (!this->state_subscription_)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  TextSensorStateResponse resp{};
 | 
			
		||||
  resp.key = text_sensor->get_object_id_hash();
 | 
			
		||||
  resp.state = std::move(state);
 | 
			
		||||
  resp.missing_state = !text_sensor->has_state();
 | 
			
		||||
  return this->send_text_sensor_state_response(resp);
 | 
			
		||||
}
 | 
			
		||||
bool APIConnection::send_text_sensor_info(text_sensor::TextSensor *text_sensor) {
 | 
			
		||||
  ListEntitiesTextSensorResponse msg;
 | 
			
		||||
  msg.key = text_sensor->get_object_id_hash();
 | 
			
		||||
  msg.object_id = text_sensor->get_object_id();
 | 
			
		||||
  msg.name = text_sensor->get_name();
 | 
			
		||||
  msg.unique_id = text_sensor->unique_id();
 | 
			
		||||
  if (msg.unique_id.empty())
 | 
			
		||||
    msg.unique_id = get_default_unique_id("text_sensor", text_sensor);
 | 
			
		||||
  msg.icon = text_sensor->get_icon();
 | 
			
		||||
  return this->send_list_entities_text_sensor_response(msg);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_CLIMATE
 | 
			
		||||
bool APIConnection::send_climate_state(climate::Climate *climate) {
 | 
			
		||||
  if (!this->state_subscription_)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  auto traits = climate->get_traits();
 | 
			
		||||
  ClimateStateResponse resp{};
 | 
			
		||||
  resp.key = climate->get_object_id_hash();
 | 
			
		||||
  resp.mode = static_cast<enums::ClimateMode>(climate->mode);
 | 
			
		||||
  resp.action = static_cast<enums::ClimateAction>(climate->action);
 | 
			
		||||
  if (traits.get_supports_current_temperature())
 | 
			
		||||
    resp.current_temperature = climate->current_temperature;
 | 
			
		||||
  if (traits.get_supports_two_point_target_temperature()) {
 | 
			
		||||
    resp.target_temperature_low = climate->target_temperature_low;
 | 
			
		||||
    resp.target_temperature_high = climate->target_temperature_high;
 | 
			
		||||
  } else {
 | 
			
		||||
    resp.target_temperature = climate->target_temperature;
 | 
			
		||||
  }
 | 
			
		||||
  if (traits.get_supports_away())
 | 
			
		||||
    resp.away = climate->away;
 | 
			
		||||
  if (traits.get_supports_fan_modes())
 | 
			
		||||
    resp.fan_mode = static_cast<enums::ClimateFanMode>(climate->fan_mode);
 | 
			
		||||
  if (traits.get_supports_swing_modes())
 | 
			
		||||
    resp.swing_mode = static_cast<enums::ClimateSwingMode>(climate->swing_mode);
 | 
			
		||||
  return this->send_climate_state_response(resp);
 | 
			
		||||
}
 | 
			
		||||
bool APIConnection::send_climate_info(climate::Climate *climate) {
 | 
			
		||||
  auto traits = climate->get_traits();
 | 
			
		||||
  ListEntitiesClimateResponse msg;
 | 
			
		||||
  msg.key = climate->get_object_id_hash();
 | 
			
		||||
  msg.object_id = climate->get_object_id();
 | 
			
		||||
  msg.name = climate->get_name();
 | 
			
		||||
  msg.unique_id = get_default_unique_id("climate", climate);
 | 
			
		||||
  msg.supports_current_temperature = traits.get_supports_current_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,
 | 
			
		||||
                    climate::CLIMATE_MODE_HEAT, climate::CLIMATE_MODE_DRY, climate::CLIMATE_MODE_FAN_ONLY}) {
 | 
			
		||||
    if (traits.supports_mode(mode))
 | 
			
		||||
      msg.supported_modes.push_back(static_cast<enums::ClimateMode>(mode));
 | 
			
		||||
  }
 | 
			
		||||
  msg.visual_min_temperature = traits.get_visual_min_temperature();
 | 
			
		||||
  msg.visual_max_temperature = traits.get_visual_max_temperature();
 | 
			
		||||
  msg.visual_temperature_step = traits.get_visual_temperature_step();
 | 
			
		||||
  msg.supports_away = traits.get_supports_away();
 | 
			
		||||
  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 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);
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::climate_command(const ClimateCommandRequest &msg) {
 | 
			
		||||
  climate::Climate *climate = App.get_climate_by_key(msg.key);
 | 
			
		||||
  if (climate == nullptr)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  auto call = climate->make_call();
 | 
			
		||||
  if (msg.has_mode)
 | 
			
		||||
    call.set_mode(static_cast<climate::ClimateMode>(msg.mode));
 | 
			
		||||
  if (msg.has_target_temperature)
 | 
			
		||||
    call.set_target_temperature(msg.target_temperature);
 | 
			
		||||
  if (msg.has_target_temperature_low)
 | 
			
		||||
    call.set_target_temperature_low(msg.target_temperature_low);
 | 
			
		||||
  if (msg.has_target_temperature_high)
 | 
			
		||||
    call.set_target_temperature_high(msg.target_temperature_high);
 | 
			
		||||
  if (msg.has_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_swing_mode)
 | 
			
		||||
    call.set_swing_mode(static_cast<climate::ClimateSwingMode>(msg.swing_mode));
 | 
			
		||||
  call.perform();
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
void APIConnection::send_camera_state(std::shared_ptr<esp32_camera::CameraImage> image) {
 | 
			
		||||
  if (!this->state_subscription_)
 | 
			
		||||
    return;
 | 
			
		||||
  if (this->image_reader_.available())
 | 
			
		||||
    return;
 | 
			
		||||
  this->image_reader_.set_image(image);
 | 
			
		||||
}
 | 
			
		||||
bool APIConnection::send_camera_info(esp32_camera::ESP32Camera *camera) {
 | 
			
		||||
  ListEntitiesCameraResponse msg;
 | 
			
		||||
  msg.key = camera->get_object_id_hash();
 | 
			
		||||
  msg.object_id = camera->get_object_id();
 | 
			
		||||
  msg.name = camera->get_name();
 | 
			
		||||
  msg.unique_id = get_default_unique_id("camera", camera);
 | 
			
		||||
  return this->send_list_entities_camera_response(msg);
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::camera_image(const CameraImageRequest &msg) {
 | 
			
		||||
  if (esp32_camera::global_esp32_camera == nullptr)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  if (msg.single)
 | 
			
		||||
    esp32_camera::global_esp32_camera->request_image();
 | 
			
		||||
  if (msg.stream)
 | 
			
		||||
    esp32_camera::global_esp32_camera->request_stream();
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_HOMEASSISTANT_TIME
 | 
			
		||||
void APIConnection::on_get_time_response(const GetTimeResponse &value) {
 | 
			
		||||
  if (homeassistant::global_homeassistant_time != nullptr)
 | 
			
		||||
    homeassistant::global_homeassistant_time->set_epoch_time(value.epoch_seconds);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
bool APIConnection::send_log_message(int level, const char *tag, const char *line) {
 | 
			
		||||
  if (this->log_subscription_ < level)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  // Send raw so that we don't copy too much
 | 
			
		||||
  auto buffer = this->create_buffer();
 | 
			
		||||
  // LogLevel level = 1;
 | 
			
		||||
  buffer.encode_uint32(1, static_cast<uint32_t>(level));
 | 
			
		||||
  // string tag = 2;
 | 
			
		||||
  // buffer.encode_string(2, tag, strlen(tag));
 | 
			
		||||
  // string message = 3;
 | 
			
		||||
  buffer.encode_string(3, line, strlen(line));
 | 
			
		||||
  // SubscribeLogsResponse - 29
 | 
			
		||||
  bool success = this->send_buffer(buffer, 29);
 | 
			
		||||
  if (!success) {
 | 
			
		||||
    buffer = this->create_buffer();
 | 
			
		||||
    // bool send_failed = 4;
 | 
			
		||||
    buffer.encode_bool(4, true);
 | 
			
		||||
    return this->send_buffer(buffer, 29);
 | 
			
		||||
  } else {
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
HelloResponse APIConnection::hello(const HelloRequest &msg) {
 | 
			
		||||
  this->client_info_ = msg.client_info + " (" + this->client_->remoteIP().toString().c_str();
 | 
			
		||||
  this->client_info_ += ")";
 | 
			
		||||
  ESP_LOGV(TAG, "Hello from client: '%s'", this->client_info_.c_str());
 | 
			
		||||
 | 
			
		||||
  HelloResponse resp;
 | 
			
		||||
  resp.api_version_major = 1;
 | 
			
		||||
  resp.api_version_minor = 3;
 | 
			
		||||
  resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")";
 | 
			
		||||
  this->connection_state_ = ConnectionState::CONNECTED;
 | 
			
		||||
  return resp;
 | 
			
		||||
}
 | 
			
		||||
ConnectResponse APIConnection::connect(const ConnectRequest &msg) {
 | 
			
		||||
  bool correct = this->parent_->check_password(msg.password);
 | 
			
		||||
 | 
			
		||||
  ConnectResponse resp;
 | 
			
		||||
  // bool invalid_password = 1;
 | 
			
		||||
  resp.invalid_password = !correct;
 | 
			
		||||
  if (correct) {
 | 
			
		||||
    ESP_LOGD(TAG, "Client '%s' connected successfully!", this->client_info_.c_str());
 | 
			
		||||
    this->connection_state_ = ConnectionState::AUTHENTICATED;
 | 
			
		||||
 | 
			
		||||
#ifdef USE_HOMEASSISTANT_TIME
 | 
			
		||||
    if (homeassistant::global_homeassistant_time != nullptr) {
 | 
			
		||||
      this->send_time_request();
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
  }
 | 
			
		||||
  return resp;
 | 
			
		||||
}
 | 
			
		||||
DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
 | 
			
		||||
  DeviceInfoResponse resp{};
 | 
			
		||||
  resp.uses_password = this->parent_->uses_password();
 | 
			
		||||
  resp.name = App.get_name();
 | 
			
		||||
  resp.mac_address = get_mac_address_pretty();
 | 
			
		||||
  resp.esphome_version = ESPHOME_VERSION;
 | 
			
		||||
  resp.compilation_time = App.get_compilation_time();
 | 
			
		||||
#ifdef ARDUINO_BOARD
 | 
			
		||||
  resp.model = ARDUINO_BOARD;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_DEEP_SLEEP
 | 
			
		||||
  resp.has_deep_sleep = deep_sleep::global_has_deep_sleep;
 | 
			
		||||
#endif
 | 
			
		||||
  return resp;
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::on_home_assistant_state_response(const HomeAssistantStateResponse &msg) {
 | 
			
		||||
  for (auto &it : this->parent_->get_state_subs())
 | 
			
		||||
    if (it.entity_id == msg.entity_id)
 | 
			
		||||
      it.callback(msg.state);
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::execute_service(const ExecuteServiceRequest &msg) {
 | 
			
		||||
  bool found = false;
 | 
			
		||||
  for (auto *service : this->parent_->get_user_services()) {
 | 
			
		||||
    if (service->execute_service(msg)) {
 | 
			
		||||
      found = true;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if (!found) {
 | 
			
		||||
    ESP_LOGV(TAG, "Could not find matching service!");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) {
 | 
			
		||||
  for (auto &it : this->parent_->get_state_subs()) {
 | 
			
		||||
    SubscribeHomeAssistantStateResponse resp;
 | 
			
		||||
    resp.entity_id = it.entity_id;
 | 
			
		||||
    if (!this->send_subscribe_home_assistant_state_response(resp)) {
 | 
			
		||||
      this->on_fatal_error();
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) {
 | 
			
		||||
  if (this->remove_)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  std::vector<uint8_t> header;
 | 
			
		||||
  header.push_back(0x00);
 | 
			
		||||
  ProtoVarInt(buffer.get_buffer()->size()).encode(header);
 | 
			
		||||
  ProtoVarInt(message_type).encode(header);
 | 
			
		||||
 | 
			
		||||
  size_t needed_space = buffer.get_buffer()->size() + header.size();
 | 
			
		||||
 | 
			
		||||
  if (needed_space > this->client_->space()) {
 | 
			
		||||
    delay(0);
 | 
			
		||||
    if (needed_space > this->client_->space()) {
 | 
			
		||||
      // SubscribeLogsResponse
 | 
			
		||||
      if (message_type != 29) {
 | 
			
		||||
        ESP_LOGV(TAG, "Cannot send message because of TCP buffer space");
 | 
			
		||||
      }
 | 
			
		||||
      delay(0);
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this->client_->add(reinterpret_cast<char *>(header.data()), header.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();
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::on_unauthenticated_access() {
 | 
			
		||||
  ESP_LOGD(TAG, "'%s' tried to access without authentication.", this->client_info_.c_str());
 | 
			
		||||
  this->on_fatal_error();
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::on_no_setup_connection() {
 | 
			
		||||
  ESP_LOGD(TAG, "'%s' tried to access without full connection.", this->client_info_.c_str());
 | 
			
		||||
  this->on_fatal_error();
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::on_fatal_error() {
 | 
			
		||||
  ESP_LOGV(TAG, "Error: Disconnecting %s", this->client_info_.c_str());
 | 
			
		||||
  this->client_->close();
 | 
			
		||||
  this->remove_ = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										172
									
								
								esphome/components/api/api_connection.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										172
									
								
								esphome/components/api/api_connection.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,172 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/application.h"
 | 
			
		||||
#include "api_pb2.h"
 | 
			
		||||
#include "api_pb2_service.h"
 | 
			
		||||
#include "api_server.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
class APIConnection : public APIServerConnection {
 | 
			
		||||
 public:
 | 
			
		||||
  APIConnection(AsyncClient *client, APIServer *parent);
 | 
			
		||||
  virtual ~APIConnection();
 | 
			
		||||
 | 
			
		||||
  void disconnect_client();
 | 
			
		||||
  void loop();
 | 
			
		||||
 | 
			
		||||
  bool send_list_info_done() {
 | 
			
		||||
    ListEntitiesDoneResponse resp;
 | 
			
		||||
    return this->send_list_entities_done_response(resp);
 | 
			
		||||
  }
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
  bool send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor, bool state);
 | 
			
		||||
  bool send_binary_sensor_info(binary_sensor::BinarySensor *binary_sensor);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_COVER
 | 
			
		||||
  bool send_cover_state(cover::Cover *cover);
 | 
			
		||||
  bool send_cover_info(cover::Cover *cover);
 | 
			
		||||
  void cover_command(const CoverCommandRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_FAN
 | 
			
		||||
  bool send_fan_state(fan::FanState *fan);
 | 
			
		||||
  bool send_fan_info(fan::FanState *fan);
 | 
			
		||||
  void fan_command(const FanCommandRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LIGHT
 | 
			
		||||
  bool send_light_state(light::LightState *light);
 | 
			
		||||
  bool send_light_info(light::LightState *light);
 | 
			
		||||
  void light_command(const LightCommandRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
  bool send_sensor_state(sensor::Sensor *sensor, float state);
 | 
			
		||||
  bool send_sensor_info(sensor::Sensor *sensor);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
  bool send_switch_state(switch_::Switch *a_switch, bool state);
 | 
			
		||||
  bool send_switch_info(switch_::Switch *a_switch);
 | 
			
		||||
  void switch_command(const SwitchCommandRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_TEXT_SENSOR
 | 
			
		||||
  bool send_text_sensor_state(text_sensor::TextSensor *text_sensor, std::string state);
 | 
			
		||||
  bool send_text_sensor_info(text_sensor::TextSensor *text_sensor);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
  void send_camera_state(std::shared_ptr<esp32_camera::CameraImage> image);
 | 
			
		||||
  bool send_camera_info(esp32_camera::ESP32Camera *camera);
 | 
			
		||||
  void camera_image(const CameraImageRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_CLIMATE
 | 
			
		||||
  bool send_climate_state(climate::Climate *climate);
 | 
			
		||||
  bool send_climate_info(climate::Climate *climate);
 | 
			
		||||
  void climate_command(const ClimateCommandRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
  bool send_log_message(int level, const char *tag, const char *line);
 | 
			
		||||
  void send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
 | 
			
		||||
    if (!this->service_call_subscription_)
 | 
			
		||||
      return;
 | 
			
		||||
    this->send_homeassistant_service_response(call);
 | 
			
		||||
  }
 | 
			
		||||
#ifdef USE_HOMEASSISTANT_TIME
 | 
			
		||||
  void send_time_request() {
 | 
			
		||||
    GetTimeRequest req;
 | 
			
		||||
    this->send_get_time_request(req);
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  void on_disconnect_response(const DisconnectResponse &value) override {
 | 
			
		||||
    // we initiated disconnect_client
 | 
			
		||||
    this->next_close_ = true;
 | 
			
		||||
  }
 | 
			
		||||
  void on_ping_response(const PingResponse &value) override {
 | 
			
		||||
    // we initiated ping
 | 
			
		||||
    this->sent_ping_ = false;
 | 
			
		||||
  }
 | 
			
		||||
  void on_home_assistant_state_response(const HomeAssistantStateResponse &msg) override;
 | 
			
		||||
#ifdef USE_HOMEASSISTANT_TIME
 | 
			
		||||
  void on_get_time_response(const GetTimeResponse &value) override;
 | 
			
		||||
#endif
 | 
			
		||||
  HelloResponse hello(const HelloRequest &msg) override;
 | 
			
		||||
  ConnectResponse connect(const ConnectRequest &msg) override;
 | 
			
		||||
  DisconnectResponse disconnect(const DisconnectRequest &msg) override {
 | 
			
		||||
    // remote initiated disconnect_client
 | 
			
		||||
    this->next_close_ = true;
 | 
			
		||||
    DisconnectResponse resp;
 | 
			
		||||
    return resp;
 | 
			
		||||
  }
 | 
			
		||||
  PingResponse ping(const PingRequest &msg) override { return {}; }
 | 
			
		||||
  DeviceInfoResponse device_info(const DeviceInfoRequest &msg) override;
 | 
			
		||||
  void list_entities(const ListEntitiesRequest &msg) override { this->list_entities_iterator_.begin(); }
 | 
			
		||||
  void subscribe_states(const SubscribeStatesRequest &msg) override {
 | 
			
		||||
    this->state_subscription_ = true;
 | 
			
		||||
    this->initial_state_iterator_.begin();
 | 
			
		||||
  }
 | 
			
		||||
  void subscribe_logs(const SubscribeLogsRequest &msg) override {
 | 
			
		||||
    this->log_subscription_ = msg.level;
 | 
			
		||||
    if (msg.dump_config)
 | 
			
		||||
      App.schedule_dump_config();
 | 
			
		||||
  }
 | 
			
		||||
  void subscribe_homeassistant_services(const SubscribeHomeassistantServicesRequest &msg) override {
 | 
			
		||||
    this->service_call_subscription_ = true;
 | 
			
		||||
  }
 | 
			
		||||
  void subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) override;
 | 
			
		||||
  GetTimeResponse get_time(const GetTimeRequest &msg) override {
 | 
			
		||||
    // TODO
 | 
			
		||||
    return {};
 | 
			
		||||
  }
 | 
			
		||||
  void execute_service(const ExecuteServiceRequest &msg) override;
 | 
			
		||||
  bool is_authenticated() override { return this->connection_state_ == ConnectionState::AUTHENTICATED; }
 | 
			
		||||
  bool is_connection_setup() override {
 | 
			
		||||
    return this->connection_state_ == ConnectionState ::CONNECTED || this->is_authenticated();
 | 
			
		||||
  }
 | 
			
		||||
  void on_fatal_error() override;
 | 
			
		||||
  void on_unauthenticated_access() override;
 | 
			
		||||
  void on_no_setup_connection() override;
 | 
			
		||||
  ProtoWriteBuffer create_buffer() override {
 | 
			
		||||
    this->send_buffer_.clear();
 | 
			
		||||
    return {&this->send_buffer_};
 | 
			
		||||
  }
 | 
			
		||||
  bool send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  friend APIServer;
 | 
			
		||||
 | 
			
		||||
  void on_error_(int8_t error);
 | 
			
		||||
  void on_disconnect_();
 | 
			
		||||
  void on_timeout_(uint32_t time);
 | 
			
		||||
  void on_data_(uint8_t *buf, size_t len);
 | 
			
		||||
  void parse_recv_buffer_();
 | 
			
		||||
 | 
			
		||||
  enum class ConnectionState {
 | 
			
		||||
    WAITING_FOR_HELLO,
 | 
			
		||||
    CONNECTED,
 | 
			
		||||
    AUTHENTICATED,
 | 
			
		||||
  } connection_state_{ConnectionState::WAITING_FOR_HELLO};
 | 
			
		||||
 | 
			
		||||
  bool remove_{false};
 | 
			
		||||
 | 
			
		||||
  std::vector<uint8_t> send_buffer_;
 | 
			
		||||
  std::vector<uint8_t> recv_buffer_;
 | 
			
		||||
 | 
			
		||||
  std::string client_info_;
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
  esp32_camera::CameraImageReader image_reader_;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  bool state_subscription_{false};
 | 
			
		||||
  int log_subscription_{ESPHOME_LOG_LEVEL_NONE};
 | 
			
		||||
  uint32_t last_traffic_;
 | 
			
		||||
  bool sent_ping_{false};
 | 
			
		||||
  bool service_call_subscription_{false};
 | 
			
		||||
  bool current_nodelay_{false};
 | 
			
		||||
  bool next_close_{false};
 | 
			
		||||
  AsyncClient *client_;
 | 
			
		||||
  APIServer *parent_;
 | 
			
		||||
  InitialStateIterator initial_state_iterator_;
 | 
			
		||||
  ListEntitiesIterator list_entities_iterator_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -1,79 +0,0 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "util.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
enum class APIMessageType {
 | 
			
		||||
  HELLO_REQUEST = 1,
 | 
			
		||||
  HELLO_RESPONSE = 2,
 | 
			
		||||
  CONNECT_REQUEST = 3,
 | 
			
		||||
  CONNECT_RESPONSE = 4,
 | 
			
		||||
  DISCONNECT_REQUEST = 5,
 | 
			
		||||
  DISCONNECT_RESPONSE = 6,
 | 
			
		||||
  PING_REQUEST = 7,
 | 
			
		||||
  PING_RESPONSE = 8,
 | 
			
		||||
  DEVICE_INFO_REQUEST = 9,
 | 
			
		||||
  DEVICE_INFO_RESPONSE = 10,
 | 
			
		||||
 | 
			
		||||
  LIST_ENTITIES_REQUEST = 11,
 | 
			
		||||
  LIST_ENTITIES_BINARY_SENSOR_RESPONSE = 12,
 | 
			
		||||
  LIST_ENTITIES_COVER_RESPONSE = 13,
 | 
			
		||||
  LIST_ENTITIES_FAN_RESPONSE = 14,
 | 
			
		||||
  LIST_ENTITIES_LIGHT_RESPONSE = 15,
 | 
			
		||||
  LIST_ENTITIES_SENSOR_RESPONSE = 16,
 | 
			
		||||
  LIST_ENTITIES_SWITCH_RESPONSE = 17,
 | 
			
		||||
  LIST_ENTITIES_TEXT_SENSOR_RESPONSE = 18,
 | 
			
		||||
  LIST_ENTITIES_SERVICE_RESPONSE = 41,
 | 
			
		||||
  LIST_ENTITIES_CAMERA_RESPONSE = 43,
 | 
			
		||||
  LIST_ENTITIES_CLIMATE_RESPONSE = 46,
 | 
			
		||||
  LIST_ENTITIES_DONE_RESPONSE = 19,
 | 
			
		||||
 | 
			
		||||
  SUBSCRIBE_STATES_REQUEST = 20,
 | 
			
		||||
  BINARY_SENSOR_STATE_RESPONSE = 21,
 | 
			
		||||
  COVER_STATE_RESPONSE = 22,
 | 
			
		||||
  FAN_STATE_RESPONSE = 23,
 | 
			
		||||
  LIGHT_STATE_RESPONSE = 24,
 | 
			
		||||
  SENSOR_STATE_RESPONSE = 25,
 | 
			
		||||
  SWITCH_STATE_RESPONSE = 26,
 | 
			
		||||
  TEXT_SENSOR_STATE_RESPONSE = 27,
 | 
			
		||||
  CAMERA_IMAGE_RESPONSE = 44,
 | 
			
		||||
  CLIMATE_STATE_RESPONSE = 47,
 | 
			
		||||
 | 
			
		||||
  SUBSCRIBE_LOGS_REQUEST = 28,
 | 
			
		||||
  SUBSCRIBE_LOGS_RESPONSE = 29,
 | 
			
		||||
 | 
			
		||||
  COVER_COMMAND_REQUEST = 30,
 | 
			
		||||
  FAN_COMMAND_REQUEST = 31,
 | 
			
		||||
  LIGHT_COMMAND_REQUEST = 32,
 | 
			
		||||
  SWITCH_COMMAND_REQUEST = 33,
 | 
			
		||||
  CAMERA_IMAGE_REQUEST = 45,
 | 
			
		||||
  CLIMATE_COMMAND_REQUEST = 48,
 | 
			
		||||
 | 
			
		||||
  SUBSCRIBE_SERVICE_CALLS_REQUEST = 34,
 | 
			
		||||
  SERVICE_CALL_RESPONSE = 35,
 | 
			
		||||
  GET_TIME_REQUEST = 36,
 | 
			
		||||
  GET_TIME_RESPONSE = 37,
 | 
			
		||||
 | 
			
		||||
  SUBSCRIBE_HOME_ASSISTANT_STATES_REQUEST = 38,
 | 
			
		||||
  SUBSCRIBE_HOME_ASSISTANT_STATE_RESPONSE = 39,
 | 
			
		||||
  HOME_ASSISTANT_STATE_RESPONSE = 40,
 | 
			
		||||
 | 
			
		||||
  EXECUTE_SERVICE_REQUEST = 42,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class APIMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  void decode(const uint8_t *buffer, size_t length);
 | 
			
		||||
  virtual bool decode_varint(uint32_t field_id, uint32_t value);
 | 
			
		||||
  virtual bool decode_length_delimited(uint32_t field_id, const uint8_t *value, size_t len);
 | 
			
		||||
  virtual bool decode_32bit(uint32_t field_id, uint32_t value);
 | 
			
		||||
  virtual APIMessageType message_type() const = 0;
 | 
			
		||||
 | 
			
		||||
  virtual void encode(APIBuffer &buffer);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										24
									
								
								esphome/components/api/api_options.proto
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								esphome/components/api/api_options.proto
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
syntax = "proto2";
 | 
			
		||||
import "google/protobuf/descriptor.proto";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
enum APISourceType {
 | 
			
		||||
    SOURCE_BOTH = 0;
 | 
			
		||||
    SOURCE_SERVER = 1;
 | 
			
		||||
    SOURCE_CLIENT = 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message void {}
 | 
			
		||||
 | 
			
		||||
extend google.protobuf.MethodOptions {
 | 
			
		||||
    optional bool needs_setup_connection = 1038 [default=true];
 | 
			
		||||
    optional bool needs_authentication = 1039 [default=true];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extend google.protobuf.MessageOptions {
 | 
			
		||||
    optional uint32 id = 1036 [default=0];
 | 
			
		||||
    optional APISourceType source = 1037 [default=SOURCE_BOTH];
 | 
			
		||||
    optional string ifdef = 1038;
 | 
			
		||||
    optional bool log = 1039 [default=true];
 | 
			
		||||
    optional bool no_delay = 1040 [default=false];
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										2973
									
								
								esphome/components/api/api_pb2.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2973
									
								
								esphome/components/api/api_pb2.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										745
									
								
								esphome/components/api/api_pb2.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										745
									
								
								esphome/components/api/api_pb2.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,745 @@
 | 
			
		||||
// This file was automatically generated with a tool.
 | 
			
		||||
// See scripts/api_protobuf/api_protobuf.py
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "proto.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
namespace enums {
 | 
			
		||||
 | 
			
		||||
enum LegacyCoverState : uint32_t {
 | 
			
		||||
  LEGACY_COVER_STATE_OPEN = 0,
 | 
			
		||||
  LEGACY_COVER_STATE_CLOSED = 1,
 | 
			
		||||
};
 | 
			
		||||
enum CoverOperation : uint32_t {
 | 
			
		||||
  COVER_OPERATION_IDLE = 0,
 | 
			
		||||
  COVER_OPERATION_IS_OPENING = 1,
 | 
			
		||||
  COVER_OPERATION_IS_CLOSING = 2,
 | 
			
		||||
};
 | 
			
		||||
enum LegacyCoverCommand : uint32_t {
 | 
			
		||||
  LEGACY_COVER_COMMAND_OPEN = 0,
 | 
			
		||||
  LEGACY_COVER_COMMAND_CLOSE = 1,
 | 
			
		||||
  LEGACY_COVER_COMMAND_STOP = 2,
 | 
			
		||||
};
 | 
			
		||||
enum FanSpeed : uint32_t {
 | 
			
		||||
  FAN_SPEED_LOW = 0,
 | 
			
		||||
  FAN_SPEED_MEDIUM = 1,
 | 
			
		||||
  FAN_SPEED_HIGH = 2,
 | 
			
		||||
};
 | 
			
		||||
enum FanDirection : uint32_t {
 | 
			
		||||
  FAN_DIRECTION_FORWARD = 0,
 | 
			
		||||
  FAN_DIRECTION_REVERSE = 1,
 | 
			
		||||
};
 | 
			
		||||
enum LogLevel : uint32_t {
 | 
			
		||||
  LOG_LEVEL_NONE = 0,
 | 
			
		||||
  LOG_LEVEL_ERROR = 1,
 | 
			
		||||
  LOG_LEVEL_WARN = 2,
 | 
			
		||||
  LOG_LEVEL_INFO = 3,
 | 
			
		||||
  LOG_LEVEL_DEBUG = 4,
 | 
			
		||||
  LOG_LEVEL_VERBOSE = 5,
 | 
			
		||||
  LOG_LEVEL_VERY_VERBOSE = 6,
 | 
			
		||||
};
 | 
			
		||||
enum ServiceArgType : uint32_t {
 | 
			
		||||
  SERVICE_ARG_TYPE_BOOL = 0,
 | 
			
		||||
  SERVICE_ARG_TYPE_INT = 1,
 | 
			
		||||
  SERVICE_ARG_TYPE_FLOAT = 2,
 | 
			
		||||
  SERVICE_ARG_TYPE_STRING = 3,
 | 
			
		||||
  SERVICE_ARG_TYPE_BOOL_ARRAY = 4,
 | 
			
		||||
  SERVICE_ARG_TYPE_INT_ARRAY = 5,
 | 
			
		||||
  SERVICE_ARG_TYPE_FLOAT_ARRAY = 6,
 | 
			
		||||
  SERVICE_ARG_TYPE_STRING_ARRAY = 7,
 | 
			
		||||
};
 | 
			
		||||
enum ClimateMode : uint32_t {
 | 
			
		||||
  CLIMATE_MODE_OFF = 0,
 | 
			
		||||
  CLIMATE_MODE_AUTO = 1,
 | 
			
		||||
  CLIMATE_MODE_COOL = 2,
 | 
			
		||||
  CLIMATE_MODE_HEAT = 3,
 | 
			
		||||
  CLIMATE_MODE_FAN_ONLY = 4,
 | 
			
		||||
  CLIMATE_MODE_DRY = 5,
 | 
			
		||||
};
 | 
			
		||||
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 {
 | 
			
		||||
  CLIMATE_ACTION_OFF = 0,
 | 
			
		||||
  CLIMATE_ACTION_COOLING = 2,
 | 
			
		||||
  CLIMATE_ACTION_HEATING = 3,
 | 
			
		||||
  CLIMATE_ACTION_IDLE = 4,
 | 
			
		||||
  CLIMATE_ACTION_DRYING = 5,
 | 
			
		||||
  CLIMATE_ACTION_FAN = 6,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace enums
 | 
			
		||||
 | 
			
		||||
class HelloRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string client_info{};  // NOLINT
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
};
 | 
			
		||||
class HelloResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint32_t api_version_major{0};  // NOLINT
 | 
			
		||||
  uint32_t api_version_minor{0};  // NOLINT
 | 
			
		||||
  std::string server_info{};      // NOLINT
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class ConnectRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string password{};  // NOLINT
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
};
 | 
			
		||||
class ConnectResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  bool invalid_password{false};  // NOLINT
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class DisconnectRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
};
 | 
			
		||||
class DisconnectResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
};
 | 
			
		||||
class PingRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
};
 | 
			
		||||
class PingResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
};
 | 
			
		||||
class DeviceInfoRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
};
 | 
			
		||||
class DeviceInfoResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  bool uses_password{false};       // NOLINT
 | 
			
		||||
  std::string name{};              // NOLINT
 | 
			
		||||
  std::string mac_address{};       // NOLINT
 | 
			
		||||
  std::string esphome_version{};   // NOLINT
 | 
			
		||||
  std::string compilation_time{};  // NOLINT
 | 
			
		||||
  std::string model{};             // NOLINT
 | 
			
		||||
  bool has_deep_sleep{false};      // NOLINT
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class ListEntitiesRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
};
 | 
			
		||||
class ListEntitiesDoneResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
};
 | 
			
		||||
class SubscribeStatesRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
};
 | 
			
		||||
class ListEntitiesBinarySensorResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string object_id{};              // NOLINT
 | 
			
		||||
  uint32_t key{0};                      // NOLINT
 | 
			
		||||
  std::string name{};                   // NOLINT
 | 
			
		||||
  std::string unique_id{};              // NOLINT
 | 
			
		||||
  std::string device_class{};           // NOLINT
 | 
			
		||||
  bool is_status_binary_sensor{false};  // NOLINT
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class BinarySensorStateResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint32_t key{0};            // NOLINT
 | 
			
		||||
  bool state{false};          // NOLINT
 | 
			
		||||
  bool missing_state{false};  // NOLINT
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class ListEntitiesCoverResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string object_id{};        // NOLINT
 | 
			
		||||
  uint32_t key{0};                // NOLINT
 | 
			
		||||
  std::string name{};             // NOLINT
 | 
			
		||||
  std::string unique_id{};        // NOLINT
 | 
			
		||||
  bool assumed_state{false};      // NOLINT
 | 
			
		||||
  bool supports_position{false};  // NOLINT
 | 
			
		||||
  bool supports_tilt{false};      // NOLINT
 | 
			
		||||
  std::string device_class{};     // NOLINT
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class CoverStateResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint32_t key{0};                            // NOLINT
 | 
			
		||||
  enums::LegacyCoverState legacy_state{};     // NOLINT
 | 
			
		||||
  float position{0.0f};                       // NOLINT
 | 
			
		||||
  float tilt{0.0f};                           // NOLINT
 | 
			
		||||
  enums::CoverOperation current_operation{};  // NOLINT
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class CoverCommandRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint32_t key{0};                             // NOLINT
 | 
			
		||||
  bool has_legacy_command{false};              // NOLINT
 | 
			
		||||
  enums::LegacyCoverCommand legacy_command{};  // NOLINT
 | 
			
		||||
  bool has_position{false};                    // NOLINT
 | 
			
		||||
  float position{0.0f};                        // NOLINT
 | 
			
		||||
  bool has_tilt{false};                        // NOLINT
 | 
			
		||||
  float tilt{0.0f};                            // NOLINT
 | 
			
		||||
  bool stop{false};                            // NOLINT
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class ListEntitiesFanResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string object_id{};           // NOLINT
 | 
			
		||||
  uint32_t key{0};                   // NOLINT
 | 
			
		||||
  std::string name{};                // NOLINT
 | 
			
		||||
  std::string unique_id{};           // NOLINT
 | 
			
		||||
  bool supports_oscillation{false};  // NOLINT
 | 
			
		||||
  bool supports_speed{false};        // NOLINT
 | 
			
		||||
  bool supports_direction{false};    // NOLINT
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class FanStateResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint32_t key{0};                  // NOLINT
 | 
			
		||||
  bool state{false};                // NOLINT
 | 
			
		||||
  bool oscillating{false};          // NOLINT
 | 
			
		||||
  enums::FanSpeed speed{};          // NOLINT
 | 
			
		||||
  enums::FanDirection direction{};  // NOLINT
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class FanCommandRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint32_t key{0};                  // NOLINT
 | 
			
		||||
  bool has_state{false};            // NOLINT
 | 
			
		||||
  bool state{false};                // NOLINT
 | 
			
		||||
  bool has_speed{false};            // NOLINT
 | 
			
		||||
  enums::FanSpeed speed{};          // NOLINT
 | 
			
		||||
  bool has_oscillating{false};      // NOLINT
 | 
			
		||||
  bool oscillating{false};          // NOLINT
 | 
			
		||||
  bool has_direction{false};        // NOLINT
 | 
			
		||||
  enums::FanDirection direction{};  // NOLINT
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class ListEntitiesLightResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string object_id{};                 // NOLINT
 | 
			
		||||
  uint32_t key{0};                         // NOLINT
 | 
			
		||||
  std::string name{};                      // NOLINT
 | 
			
		||||
  std::string unique_id{};                 // NOLINT
 | 
			
		||||
  bool supports_brightness{false};         // NOLINT
 | 
			
		||||
  bool supports_rgb{false};                // NOLINT
 | 
			
		||||
  bool supports_white_value{false};        // NOLINT
 | 
			
		||||
  bool supports_color_temperature{false};  // NOLINT
 | 
			
		||||
  float min_mireds{0.0f};                  // NOLINT
 | 
			
		||||
  float max_mireds{0.0f};                  // NOLINT
 | 
			
		||||
  std::vector<std::string> effects{};      // NOLINT
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class LightStateResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint32_t key{0};                // NOLINT
 | 
			
		||||
  bool state{false};              // NOLINT
 | 
			
		||||
  float brightness{0.0f};         // NOLINT
 | 
			
		||||
  float red{0.0f};                // NOLINT
 | 
			
		||||
  float green{0.0f};              // NOLINT
 | 
			
		||||
  float blue{0.0f};               // NOLINT
 | 
			
		||||
  float white{0.0f};              // NOLINT
 | 
			
		||||
  float color_temperature{0.0f};  // NOLINT
 | 
			
		||||
  std::string effect{};           // NOLINT
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class LightCommandRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint32_t key{0};                    // NOLINT
 | 
			
		||||
  bool has_state{false};              // NOLINT
 | 
			
		||||
  bool state{false};                  // NOLINT
 | 
			
		||||
  bool has_brightness{false};         // NOLINT
 | 
			
		||||
  float brightness{0.0f};             // NOLINT
 | 
			
		||||
  bool has_rgb{false};                // NOLINT
 | 
			
		||||
  float red{0.0f};                    // NOLINT
 | 
			
		||||
  float green{0.0f};                  // NOLINT
 | 
			
		||||
  float blue{0.0f};                   // NOLINT
 | 
			
		||||
  bool has_white{false};              // NOLINT
 | 
			
		||||
  float white{0.0f};                  // NOLINT
 | 
			
		||||
  bool has_color_temperature{false};  // NOLINT
 | 
			
		||||
  float color_temperature{0.0f};      // NOLINT
 | 
			
		||||
  bool has_transition_length{false};  // NOLINT
 | 
			
		||||
  uint32_t transition_length{0};      // NOLINT
 | 
			
		||||
  bool has_flash_length{false};       // NOLINT
 | 
			
		||||
  uint32_t flash_length{0};           // NOLINT
 | 
			
		||||
  bool has_effect{false};             // NOLINT
 | 
			
		||||
  std::string effect{};               // NOLINT
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class ListEntitiesSensorResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string object_id{};            // NOLINT
 | 
			
		||||
  uint32_t key{0};                    // NOLINT
 | 
			
		||||
  std::string name{};                 // NOLINT
 | 
			
		||||
  std::string unique_id{};            // NOLINT
 | 
			
		||||
  std::string icon{};                 // NOLINT
 | 
			
		||||
  std::string unit_of_measurement{};  // NOLINT
 | 
			
		||||
  int32_t accuracy_decimals{0};       // NOLINT
 | 
			
		||||
  bool force_update{false};           // NOLINT
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class SensorStateResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint32_t key{0};            // NOLINT
 | 
			
		||||
  float state{0.0f};          // NOLINT
 | 
			
		||||
  bool missing_state{false};  // NOLINT
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class ListEntitiesSwitchResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string object_id{};    // NOLINT
 | 
			
		||||
  uint32_t key{0};            // NOLINT
 | 
			
		||||
  std::string name{};         // NOLINT
 | 
			
		||||
  std::string unique_id{};    // NOLINT
 | 
			
		||||
  std::string icon{};         // NOLINT
 | 
			
		||||
  bool assumed_state{false};  // NOLINT
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class SwitchStateResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint32_t key{0};    // NOLINT
 | 
			
		||||
  bool state{false};  // NOLINT
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class SwitchCommandRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint32_t key{0};    // NOLINT
 | 
			
		||||
  bool state{false};  // NOLINT
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class ListEntitiesTextSensorResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string object_id{};  // NOLINT
 | 
			
		||||
  uint32_t key{0};          // NOLINT
 | 
			
		||||
  std::string name{};       // NOLINT
 | 
			
		||||
  std::string unique_id{};  // NOLINT
 | 
			
		||||
  std::string icon{};       // NOLINT
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
};
 | 
			
		||||
class TextSensorStateResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint32_t key{0};            // NOLINT
 | 
			
		||||
  std::string state{};        // NOLINT
 | 
			
		||||
  bool missing_state{false};  // NOLINT
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class SubscribeLogsRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  enums::LogLevel level{};  // NOLINT
 | 
			
		||||
  bool dump_config{false};  // NOLINT
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class SubscribeLogsResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  enums::LogLevel level{};  // NOLINT
 | 
			
		||||
  std::string tag{};        // NOLINT
 | 
			
		||||
  std::string message{};    // NOLINT
 | 
			
		||||
  bool send_failed{false};  // NOLINT
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class SubscribeHomeassistantServicesRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
};
 | 
			
		||||
class HomeassistantServiceMap : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string key{};    // NOLINT
 | 
			
		||||
  std::string value{};  // NOLINT
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
};
 | 
			
		||||
class HomeassistantServiceResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string service{};                                 // NOLINT
 | 
			
		||||
  std::vector<HomeassistantServiceMap> data{};           // NOLINT
 | 
			
		||||
  std::vector<HomeassistantServiceMap> data_template{};  // NOLINT
 | 
			
		||||
  std::vector<HomeassistantServiceMap> variables{};      // NOLINT
 | 
			
		||||
  bool is_event{false};                                  // NOLINT
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class SubscribeHomeAssistantStatesRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
};
 | 
			
		||||
class SubscribeHomeAssistantStateResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string entity_id{};  // NOLINT
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
};
 | 
			
		||||
class HomeAssistantStateResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string entity_id{};  // NOLINT
 | 
			
		||||
  std::string state{};      // NOLINT
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
};
 | 
			
		||||
class GetTimeRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
};
 | 
			
		||||
class GetTimeResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint32_t epoch_seconds{0};  // NOLINT
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
 | 
			
		||||
};
 | 
			
		||||
class ListEntitiesServicesArgument : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string name{};            // NOLINT
 | 
			
		||||
  enums::ServiceArgType type{};  // NOLINT
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class ListEntitiesServicesResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string name{};                                // NOLINT
 | 
			
		||||
  uint32_t key{0};                                   // NOLINT
 | 
			
		||||
  std::vector<ListEntitiesServicesArgument> args{};  // NOLINT
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
};
 | 
			
		||||
class ExecuteServiceArgument : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  bool bool_{false};                        // NOLINT
 | 
			
		||||
  int32_t legacy_int{0};                    // NOLINT
 | 
			
		||||
  float float_{0.0f};                       // NOLINT
 | 
			
		||||
  std::string string_{};                    // NOLINT
 | 
			
		||||
  int32_t int_{0};                          // NOLINT
 | 
			
		||||
  std::vector<bool> bool_array{};           // NOLINT
 | 
			
		||||
  std::vector<int32_t> int_array{};         // NOLINT
 | 
			
		||||
  std::vector<float> float_array{};         // NOLINT
 | 
			
		||||
  std::vector<std::string> string_array{};  // NOLINT
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class ExecuteServiceRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint32_t key{0};                             // NOLINT
 | 
			
		||||
  std::vector<ExecuteServiceArgument> args{};  // NOLINT
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
};
 | 
			
		||||
class ListEntitiesCameraResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string object_id{};  // NOLINT
 | 
			
		||||
  uint32_t key{0};          // NOLINT
 | 
			
		||||
  std::string name{};       // NOLINT
 | 
			
		||||
  std::string unique_id{};  // NOLINT
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
};
 | 
			
		||||
class CameraImageResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint32_t key{0};     // NOLINT
 | 
			
		||||
  std::string data{};  // NOLINT
 | 
			
		||||
  bool done{false};    // NOLINT
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class CameraImageRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  bool single{false};  // NOLINT
 | 
			
		||||
  bool stream{false};  // NOLINT
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class ListEntitiesClimateResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string object_id{};                                       // NOLINT
 | 
			
		||||
  uint32_t key{0};                                               // NOLINT
 | 
			
		||||
  std::string name{};                                            // NOLINT
 | 
			
		||||
  std::string unique_id{};                                       // NOLINT
 | 
			
		||||
  bool supports_current_temperature{false};                      // NOLINT
 | 
			
		||||
  bool supports_two_point_target_temperature{false};             // NOLINT
 | 
			
		||||
  std::vector<enums::ClimateMode> supported_modes{};             // NOLINT
 | 
			
		||||
  float visual_min_temperature{0.0f};                            // NOLINT
 | 
			
		||||
  float visual_max_temperature{0.0f};                            // NOLINT
 | 
			
		||||
  float visual_temperature_step{0.0f};                           // NOLINT
 | 
			
		||||
  bool supports_away{false};                                     // NOLINT
 | 
			
		||||
  bool supports_action{false};                                   // NOLINT
 | 
			
		||||
  std::vector<enums::ClimateFanMode> supported_fan_modes{};      // NOLINT
 | 
			
		||||
  std::vector<enums::ClimateSwingMode> supported_swing_modes{};  // NOLINT
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class ClimateStateResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint32_t key{0};                       // NOLINT
 | 
			
		||||
  enums::ClimateMode mode{};             // NOLINT
 | 
			
		||||
  float current_temperature{0.0f};       // NOLINT
 | 
			
		||||
  float target_temperature{0.0f};        // NOLINT
 | 
			
		||||
  float target_temperature_low{0.0f};    // NOLINT
 | 
			
		||||
  float target_temperature_high{0.0f};   // NOLINT
 | 
			
		||||
  bool away{false};                      // NOLINT
 | 
			
		||||
  enums::ClimateAction action{};         // NOLINT
 | 
			
		||||
  enums::ClimateFanMode fan_mode{};      // NOLINT
 | 
			
		||||
  enums::ClimateSwingMode swing_mode{};  // NOLINT
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class ClimateCommandRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint32_t key{0};                          // NOLINT
 | 
			
		||||
  bool has_mode{false};                     // NOLINT
 | 
			
		||||
  enums::ClimateMode mode{};                // NOLINT
 | 
			
		||||
  bool has_target_temperature{false};       // NOLINT
 | 
			
		||||
  float target_temperature{0.0f};           // NOLINT
 | 
			
		||||
  bool has_target_temperature_low{false};   // NOLINT
 | 
			
		||||
  float target_temperature_low{0.0f};       // NOLINT
 | 
			
		||||
  bool has_target_temperature_high{false};  // NOLINT
 | 
			
		||||
  float target_temperature_high{0.0f};      // NOLINT
 | 
			
		||||
  bool has_away{false};                     // NOLINT
 | 
			
		||||
  bool away{false};                         // NOLINT
 | 
			
		||||
  bool has_fan_mode{false};                 // NOLINT
 | 
			
		||||
  enums::ClimateFanMode fan_mode{};         // NOLINT
 | 
			
		||||
  bool has_swing_mode{false};               // NOLINT
 | 
			
		||||
  enums::ClimateSwingMode swing_mode{};     // NOLINT
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										552
									
								
								esphome/components/api/api_pb2_service.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										552
									
								
								esphome/components/api/api_pb2_service.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,552 @@
 | 
			
		||||
// This file was automatically generated with a tool.
 | 
			
		||||
// See scripts/api_protobuf/api_protobuf.py
 | 
			
		||||
#include "api_pb2_service.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
static const char *TAG = "api.service";
 | 
			
		||||
 | 
			
		||||
bool APIServerConnectionBase::send_hello_response(const HelloResponse &msg) {
 | 
			
		||||
  ESP_LOGVV(TAG, "send_hello_response: %s", msg.dump().c_str());
 | 
			
		||||
  return this->send_message_<HelloResponse>(msg, 2);
 | 
			
		||||
}
 | 
			
		||||
bool APIServerConnectionBase::send_connect_response(const ConnectResponse &msg) {
 | 
			
		||||
  ESP_LOGVV(TAG, "send_connect_response: %s", msg.dump().c_str());
 | 
			
		||||
  return this->send_message_<ConnectResponse>(msg, 4);
 | 
			
		||||
}
 | 
			
		||||
bool APIServerConnectionBase::send_disconnect_request(const DisconnectRequest &msg) {
 | 
			
		||||
  ESP_LOGVV(TAG, "send_disconnect_request: %s", msg.dump().c_str());
 | 
			
		||||
  return this->send_message_<DisconnectRequest>(msg, 5);
 | 
			
		||||
}
 | 
			
		||||
bool APIServerConnectionBase::send_disconnect_response(const DisconnectResponse &msg) {
 | 
			
		||||
  ESP_LOGVV(TAG, "send_disconnect_response: %s", msg.dump().c_str());
 | 
			
		||||
  return this->send_message_<DisconnectResponse>(msg, 6);
 | 
			
		||||
}
 | 
			
		||||
bool APIServerConnectionBase::send_ping_request(const PingRequest &msg) {
 | 
			
		||||
  ESP_LOGVV(TAG, "send_ping_request: %s", msg.dump().c_str());
 | 
			
		||||
  return this->send_message_<PingRequest>(msg, 7);
 | 
			
		||||
}
 | 
			
		||||
bool APIServerConnectionBase::send_ping_response(const PingResponse &msg) {
 | 
			
		||||
  ESP_LOGVV(TAG, "send_ping_response: %s", msg.dump().c_str());
 | 
			
		||||
  return this->send_message_<PingResponse>(msg, 8);
 | 
			
		||||
}
 | 
			
		||||
bool APIServerConnectionBase::send_device_info_response(const DeviceInfoResponse &msg) {
 | 
			
		||||
  ESP_LOGVV(TAG, "send_device_info_response: %s", msg.dump().c_str());
 | 
			
		||||
  return this->send_message_<DeviceInfoResponse>(msg, 10);
 | 
			
		||||
}
 | 
			
		||||
bool APIServerConnectionBase::send_list_entities_done_response(const ListEntitiesDoneResponse &msg) {
 | 
			
		||||
  ESP_LOGVV(TAG, "send_list_entities_done_response: %s", msg.dump().c_str());
 | 
			
		||||
  return this->send_message_<ListEntitiesDoneResponse>(msg, 19);
 | 
			
		||||
}
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
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());
 | 
			
		||||
  return this->send_message_<ListEntitiesBinarySensorResponse>(msg, 12);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
bool APIServerConnectionBase::send_binary_sensor_state_response(const BinarySensorStateResponse &msg) {
 | 
			
		||||
  ESP_LOGVV(TAG, "send_binary_sensor_state_response: %s", msg.dump().c_str());
 | 
			
		||||
  return this->send_message_<BinarySensorStateResponse>(msg, 21);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_COVER
 | 
			
		||||
bool APIServerConnectionBase::send_list_entities_cover_response(const ListEntitiesCoverResponse &msg) {
 | 
			
		||||
  ESP_LOGVV(TAG, "send_list_entities_cover_response: %s", msg.dump().c_str());
 | 
			
		||||
  return this->send_message_<ListEntitiesCoverResponse>(msg, 13);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_COVER
 | 
			
		||||
bool APIServerConnectionBase::send_cover_state_response(const CoverStateResponse &msg) {
 | 
			
		||||
  ESP_LOGVV(TAG, "send_cover_state_response: %s", msg.dump().c_str());
 | 
			
		||||
  return this->send_message_<CoverStateResponse>(msg, 22);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_COVER
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_FAN
 | 
			
		||||
bool APIServerConnectionBase::send_list_entities_fan_response(const ListEntitiesFanResponse &msg) {
 | 
			
		||||
  ESP_LOGVV(TAG, "send_list_entities_fan_response: %s", msg.dump().c_str());
 | 
			
		||||
  return this->send_message_<ListEntitiesFanResponse>(msg, 14);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_FAN
 | 
			
		||||
bool APIServerConnectionBase::send_fan_state_response(const FanStateResponse &msg) {
 | 
			
		||||
  ESP_LOGVV(TAG, "send_fan_state_response: %s", msg.dump().c_str());
 | 
			
		||||
  return this->send_message_<FanStateResponse>(msg, 23);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_FAN
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LIGHT
 | 
			
		||||
bool APIServerConnectionBase::send_list_entities_light_response(const ListEntitiesLightResponse &msg) {
 | 
			
		||||
  ESP_LOGVV(TAG, "send_list_entities_light_response: %s", msg.dump().c_str());
 | 
			
		||||
  return this->send_message_<ListEntitiesLightResponse>(msg, 15);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LIGHT
 | 
			
		||||
bool APIServerConnectionBase::send_light_state_response(const LightStateResponse &msg) {
 | 
			
		||||
  ESP_LOGVV(TAG, "send_light_state_response: %s", msg.dump().c_str());
 | 
			
		||||
  return this->send_message_<LightStateResponse>(msg, 24);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LIGHT
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
bool APIServerConnectionBase::send_list_entities_sensor_response(const ListEntitiesSensorResponse &msg) {
 | 
			
		||||
  ESP_LOGVV(TAG, "send_list_entities_sensor_response: %s", msg.dump().c_str());
 | 
			
		||||
  return this->send_message_<ListEntitiesSensorResponse>(msg, 16);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
bool APIServerConnectionBase::send_sensor_state_response(const SensorStateResponse &msg) {
 | 
			
		||||
  ESP_LOGVV(TAG, "send_sensor_state_response: %s", msg.dump().c_str());
 | 
			
		||||
  return this->send_message_<SensorStateResponse>(msg, 25);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
bool APIServerConnectionBase::send_list_entities_switch_response(const ListEntitiesSwitchResponse &msg) {
 | 
			
		||||
  ESP_LOGVV(TAG, "send_list_entities_switch_response: %s", msg.dump().c_str());
 | 
			
		||||
  return this->send_message_<ListEntitiesSwitchResponse>(msg, 17);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
bool APIServerConnectionBase::send_switch_state_response(const SwitchStateResponse &msg) {
 | 
			
		||||
  ESP_LOGVV(TAG, "send_switch_state_response: %s", msg.dump().c_str());
 | 
			
		||||
  return this->send_message_<SwitchStateResponse>(msg, 26);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_TEXT_SENSOR
 | 
			
		||||
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());
 | 
			
		||||
  return this->send_message_<ListEntitiesTextSensorResponse>(msg, 18);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_TEXT_SENSOR
 | 
			
		||||
bool APIServerConnectionBase::send_text_sensor_state_response(const TextSensorStateResponse &msg) {
 | 
			
		||||
  ESP_LOGVV(TAG, "send_text_sensor_state_response: %s", msg.dump().c_str());
 | 
			
		||||
  return this->send_message_<TextSensorStateResponse>(msg, 27);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
bool APIServerConnectionBase::send_subscribe_logs_response(const SubscribeLogsResponse &msg) {
 | 
			
		||||
  return this->send_message_<SubscribeLogsResponse>(msg, 29);
 | 
			
		||||
}
 | 
			
		||||
bool APIServerConnectionBase::send_homeassistant_service_response(const HomeassistantServiceResponse &msg) {
 | 
			
		||||
  ESP_LOGVV(TAG, "send_homeassistant_service_response: %s", msg.dump().c_str());
 | 
			
		||||
  return this->send_message_<HomeassistantServiceResponse>(msg, 35);
 | 
			
		||||
}
 | 
			
		||||
bool APIServerConnectionBase::send_subscribe_home_assistant_state_response(
 | 
			
		||||
    const SubscribeHomeAssistantStateResponse &msg) {
 | 
			
		||||
  ESP_LOGVV(TAG, "send_subscribe_home_assistant_state_response: %s", msg.dump().c_str());
 | 
			
		||||
  return this->send_message_<SubscribeHomeAssistantStateResponse>(msg, 39);
 | 
			
		||||
}
 | 
			
		||||
bool APIServerConnectionBase::send_get_time_request(const GetTimeRequest &msg) {
 | 
			
		||||
  ESP_LOGVV(TAG, "send_get_time_request: %s", msg.dump().c_str());
 | 
			
		||||
  return this->send_message_<GetTimeRequest>(msg, 36);
 | 
			
		||||
}
 | 
			
		||||
bool APIServerConnectionBase::send_get_time_response(const GetTimeResponse &msg) {
 | 
			
		||||
  ESP_LOGVV(TAG, "send_get_time_response: %s", msg.dump().c_str());
 | 
			
		||||
  return this->send_message_<GetTimeResponse>(msg, 37);
 | 
			
		||||
}
 | 
			
		||||
bool APIServerConnectionBase::send_list_entities_services_response(const ListEntitiesServicesResponse &msg) {
 | 
			
		||||
  ESP_LOGVV(TAG, "send_list_entities_services_response: %s", msg.dump().c_str());
 | 
			
		||||
  return this->send_message_<ListEntitiesServicesResponse>(msg, 41);
 | 
			
		||||
}
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
bool APIServerConnectionBase::send_list_entities_camera_response(const ListEntitiesCameraResponse &msg) {
 | 
			
		||||
  ESP_LOGVV(TAG, "send_list_entities_camera_response: %s", msg.dump().c_str());
 | 
			
		||||
  return this->send_message_<ListEntitiesCameraResponse>(msg, 43);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
bool APIServerConnectionBase::send_camera_image_response(const CameraImageResponse &msg) {
 | 
			
		||||
  ESP_LOGVV(TAG, "send_camera_image_response: %s", msg.dump().c_str());
 | 
			
		||||
  return this->send_message_<CameraImageResponse>(msg, 44);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_CLIMATE
 | 
			
		||||
bool APIServerConnectionBase::send_list_entities_climate_response(const ListEntitiesClimateResponse &msg) {
 | 
			
		||||
  ESP_LOGVV(TAG, "send_list_entities_climate_response: %s", msg.dump().c_str());
 | 
			
		||||
  return this->send_message_<ListEntitiesClimateResponse>(msg, 46);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_CLIMATE
 | 
			
		||||
bool APIServerConnectionBase::send_climate_state_response(const ClimateStateResponse &msg) {
 | 
			
		||||
  ESP_LOGVV(TAG, "send_climate_state_response: %s", msg.dump().c_str());
 | 
			
		||||
  return this->send_message_<ClimateStateResponse>(msg, 47);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_CLIMATE
 | 
			
		||||
#endif
 | 
			
		||||
bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {
 | 
			
		||||
  switch (msg_type) {
 | 
			
		||||
    case 1: {
 | 
			
		||||
      HelloRequest msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
      ESP_LOGVV(TAG, "on_hello_request: %s", msg.dump().c_str());
 | 
			
		||||
      this->on_hello_request(msg);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 3: {
 | 
			
		||||
      ConnectRequest msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
      ESP_LOGVV(TAG, "on_connect_request: %s", msg.dump().c_str());
 | 
			
		||||
      this->on_connect_request(msg);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 5: {
 | 
			
		||||
      DisconnectRequest msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
      ESP_LOGVV(TAG, "on_disconnect_request: %s", msg.dump().c_str());
 | 
			
		||||
      this->on_disconnect_request(msg);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 6: {
 | 
			
		||||
      DisconnectResponse msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
      ESP_LOGVV(TAG, "on_disconnect_response: %s", msg.dump().c_str());
 | 
			
		||||
      this->on_disconnect_response(msg);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 7: {
 | 
			
		||||
      PingRequest msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
      ESP_LOGVV(TAG, "on_ping_request: %s", msg.dump().c_str());
 | 
			
		||||
      this->on_ping_request(msg);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 8: {
 | 
			
		||||
      PingResponse msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
      ESP_LOGVV(TAG, "on_ping_response: %s", msg.dump().c_str());
 | 
			
		||||
      this->on_ping_response(msg);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 9: {
 | 
			
		||||
      DeviceInfoRequest msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
      ESP_LOGVV(TAG, "on_device_info_request: %s", msg.dump().c_str());
 | 
			
		||||
      this->on_device_info_request(msg);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 11: {
 | 
			
		||||
      ListEntitiesRequest msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
      ESP_LOGVV(TAG, "on_list_entities_request: %s", msg.dump().c_str());
 | 
			
		||||
      this->on_list_entities_request(msg);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 20: {
 | 
			
		||||
      SubscribeStatesRequest msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
      ESP_LOGVV(TAG, "on_subscribe_states_request: %s", msg.dump().c_str());
 | 
			
		||||
      this->on_subscribe_states_request(msg);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 28: {
 | 
			
		||||
      SubscribeLogsRequest msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
      ESP_LOGVV(TAG, "on_subscribe_logs_request: %s", msg.dump().c_str());
 | 
			
		||||
      this->on_subscribe_logs_request(msg);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 30: {
 | 
			
		||||
#ifdef USE_COVER
 | 
			
		||||
      CoverCommandRequest msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
      ESP_LOGVV(TAG, "on_cover_command_request: %s", msg.dump().c_str());
 | 
			
		||||
      this->on_cover_command_request(msg);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 31: {
 | 
			
		||||
#ifdef USE_FAN
 | 
			
		||||
      FanCommandRequest msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
      ESP_LOGVV(TAG, "on_fan_command_request: %s", msg.dump().c_str());
 | 
			
		||||
      this->on_fan_command_request(msg);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 32: {
 | 
			
		||||
#ifdef USE_LIGHT
 | 
			
		||||
      LightCommandRequest msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
      ESP_LOGVV(TAG, "on_light_command_request: %s", msg.dump().c_str());
 | 
			
		||||
      this->on_light_command_request(msg);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 33: {
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
      SwitchCommandRequest msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
      ESP_LOGVV(TAG, "on_switch_command_request: %s", msg.dump().c_str());
 | 
			
		||||
      this->on_switch_command_request(msg);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 34: {
 | 
			
		||||
      SubscribeHomeassistantServicesRequest msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
      ESP_LOGVV(TAG, "on_subscribe_homeassistant_services_request: %s", msg.dump().c_str());
 | 
			
		||||
      this->on_subscribe_homeassistant_services_request(msg);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 36: {
 | 
			
		||||
      GetTimeRequest msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
      ESP_LOGVV(TAG, "on_get_time_request: %s", msg.dump().c_str());
 | 
			
		||||
      this->on_get_time_request(msg);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 37: {
 | 
			
		||||
      GetTimeResponse msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
      ESP_LOGVV(TAG, "on_get_time_response: %s", msg.dump().c_str());
 | 
			
		||||
      this->on_get_time_response(msg);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 38: {
 | 
			
		||||
      SubscribeHomeAssistantStatesRequest msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
      ESP_LOGVV(TAG, "on_subscribe_home_assistant_states_request: %s", msg.dump().c_str());
 | 
			
		||||
      this->on_subscribe_home_assistant_states_request(msg);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 40: {
 | 
			
		||||
      HomeAssistantStateResponse msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
      ESP_LOGVV(TAG, "on_home_assistant_state_response: %s", msg.dump().c_str());
 | 
			
		||||
      this->on_home_assistant_state_response(msg);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 42: {
 | 
			
		||||
      ExecuteServiceRequest msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
      ESP_LOGVV(TAG, "on_execute_service_request: %s", msg.dump().c_str());
 | 
			
		||||
      this->on_execute_service_request(msg);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 45: {
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
      CameraImageRequest msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
      ESP_LOGVV(TAG, "on_camera_image_request: %s", msg.dump().c_str());
 | 
			
		||||
      this->on_camera_image_request(msg);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 48: {
 | 
			
		||||
#ifdef USE_CLIMATE
 | 
			
		||||
      ClimateCommandRequest msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
      ESP_LOGVV(TAG, "on_climate_command_request: %s", msg.dump().c_str());
 | 
			
		||||
      this->on_climate_command_request(msg);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void APIServerConnection::on_hello_request(const HelloRequest &msg) {
 | 
			
		||||
  HelloResponse ret = this->hello(msg);
 | 
			
		||||
  if (!this->send_hello_response(ret)) {
 | 
			
		||||
    this->on_fatal_error();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void APIServerConnection::on_connect_request(const ConnectRequest &msg) {
 | 
			
		||||
  ConnectResponse ret = this->connect(msg);
 | 
			
		||||
  if (!this->send_connect_response(ret)) {
 | 
			
		||||
    this->on_fatal_error();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void APIServerConnection::on_disconnect_request(const DisconnectRequest &msg) {
 | 
			
		||||
  DisconnectResponse ret = this->disconnect(msg);
 | 
			
		||||
  if (!this->send_disconnect_response(ret)) {
 | 
			
		||||
    this->on_fatal_error();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void APIServerConnection::on_ping_request(const PingRequest &msg) {
 | 
			
		||||
  PingResponse ret = this->ping(msg);
 | 
			
		||||
  if (!this->send_ping_response(ret)) {
 | 
			
		||||
    this->on_fatal_error();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void APIServerConnection::on_device_info_request(const DeviceInfoRequest &msg) {
 | 
			
		||||
  if (!this->is_connection_setup()) {
 | 
			
		||||
    this->on_no_setup_connection();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  DeviceInfoResponse ret = this->device_info(msg);
 | 
			
		||||
  if (!this->send_device_info_response(ret)) {
 | 
			
		||||
    this->on_fatal_error();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void APIServerConnection::on_list_entities_request(const ListEntitiesRequest &msg) {
 | 
			
		||||
  if (!this->is_connection_setup()) {
 | 
			
		||||
    this->on_no_setup_connection();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (!this->is_authenticated()) {
 | 
			
		||||
    this->on_unauthenticated_access();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->list_entities(msg);
 | 
			
		||||
}
 | 
			
		||||
void APIServerConnection::on_subscribe_states_request(const SubscribeStatesRequest &msg) {
 | 
			
		||||
  if (!this->is_connection_setup()) {
 | 
			
		||||
    this->on_no_setup_connection();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (!this->is_authenticated()) {
 | 
			
		||||
    this->on_unauthenticated_access();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->subscribe_states(msg);
 | 
			
		||||
}
 | 
			
		||||
void APIServerConnection::on_subscribe_logs_request(const SubscribeLogsRequest &msg) {
 | 
			
		||||
  if (!this->is_connection_setup()) {
 | 
			
		||||
    this->on_no_setup_connection();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (!this->is_authenticated()) {
 | 
			
		||||
    this->on_unauthenticated_access();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->subscribe_logs(msg);
 | 
			
		||||
}
 | 
			
		||||
void APIServerConnection::on_subscribe_homeassistant_services_request(
 | 
			
		||||
    const SubscribeHomeassistantServicesRequest &msg) {
 | 
			
		||||
  if (!this->is_connection_setup()) {
 | 
			
		||||
    this->on_no_setup_connection();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (!this->is_authenticated()) {
 | 
			
		||||
    this->on_unauthenticated_access();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->subscribe_homeassistant_services(msg);
 | 
			
		||||
}
 | 
			
		||||
void APIServerConnection::on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &msg) {
 | 
			
		||||
  if (!this->is_connection_setup()) {
 | 
			
		||||
    this->on_no_setup_connection();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (!this->is_authenticated()) {
 | 
			
		||||
    this->on_unauthenticated_access();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->subscribe_home_assistant_states(msg);
 | 
			
		||||
}
 | 
			
		||||
void APIServerConnection::on_get_time_request(const GetTimeRequest &msg) {
 | 
			
		||||
  if (!this->is_connection_setup()) {
 | 
			
		||||
    this->on_no_setup_connection();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  GetTimeResponse ret = this->get_time(msg);
 | 
			
		||||
  if (!this->send_get_time_response(ret)) {
 | 
			
		||||
    this->on_fatal_error();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void APIServerConnection::on_execute_service_request(const ExecuteServiceRequest &msg) {
 | 
			
		||||
  if (!this->is_connection_setup()) {
 | 
			
		||||
    this->on_no_setup_connection();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (!this->is_authenticated()) {
 | 
			
		||||
    this->on_unauthenticated_access();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->execute_service(msg);
 | 
			
		||||
}
 | 
			
		||||
#ifdef USE_COVER
 | 
			
		||||
void APIServerConnection::on_cover_command_request(const CoverCommandRequest &msg) {
 | 
			
		||||
  if (!this->is_connection_setup()) {
 | 
			
		||||
    this->on_no_setup_connection();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (!this->is_authenticated()) {
 | 
			
		||||
    this->on_unauthenticated_access();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->cover_command(msg);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_FAN
 | 
			
		||||
void APIServerConnection::on_fan_command_request(const FanCommandRequest &msg) {
 | 
			
		||||
  if (!this->is_connection_setup()) {
 | 
			
		||||
    this->on_no_setup_connection();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (!this->is_authenticated()) {
 | 
			
		||||
    this->on_unauthenticated_access();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->fan_command(msg);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LIGHT
 | 
			
		||||
void APIServerConnection::on_light_command_request(const LightCommandRequest &msg) {
 | 
			
		||||
  if (!this->is_connection_setup()) {
 | 
			
		||||
    this->on_no_setup_connection();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (!this->is_authenticated()) {
 | 
			
		||||
    this->on_unauthenticated_access();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->light_command(msg);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
void APIServerConnection::on_switch_command_request(const SwitchCommandRequest &msg) {
 | 
			
		||||
  if (!this->is_connection_setup()) {
 | 
			
		||||
    this->on_no_setup_connection();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (!this->is_authenticated()) {
 | 
			
		||||
    this->on_unauthenticated_access();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->switch_command(msg);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
void APIServerConnection::on_camera_image_request(const CameraImageRequest &msg) {
 | 
			
		||||
  if (!this->is_connection_setup()) {
 | 
			
		||||
    this->on_no_setup_connection();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (!this->is_authenticated()) {
 | 
			
		||||
    this->on_unauthenticated_access();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->camera_image(msg);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_CLIMATE
 | 
			
		||||
void APIServerConnection::on_climate_command_request(const ClimateCommandRequest &msg) {
 | 
			
		||||
  if (!this->is_connection_setup()) {
 | 
			
		||||
    this->on_no_setup_connection();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (!this->is_authenticated()) {
 | 
			
		||||
    this->on_unauthenticated_access();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->climate_command(msg);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										185
									
								
								esphome/components/api/api_pb2_service.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										185
									
								
								esphome/components/api/api_pb2_service.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,185 @@
 | 
			
		||||
// This file was automatically generated with a tool.
 | 
			
		||||
// See scripts/api_protobuf/api_protobuf.py
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "api_pb2.h"
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
class APIServerConnectionBase : public ProtoService {
 | 
			
		||||
 public:
 | 
			
		||||
  virtual void on_hello_request(const HelloRequest &value){};
 | 
			
		||||
  bool send_hello_response(const HelloResponse &msg);
 | 
			
		||||
  virtual void on_connect_request(const ConnectRequest &value){};
 | 
			
		||||
  bool send_connect_response(const ConnectResponse &msg);
 | 
			
		||||
  bool send_disconnect_request(const DisconnectRequest &msg);
 | 
			
		||||
  virtual void on_disconnect_request(const DisconnectRequest &value){};
 | 
			
		||||
  bool send_disconnect_response(const DisconnectResponse &msg);
 | 
			
		||||
  virtual void on_disconnect_response(const DisconnectResponse &value){};
 | 
			
		||||
  bool send_ping_request(const PingRequest &msg);
 | 
			
		||||
  virtual void on_ping_request(const PingRequest &value){};
 | 
			
		||||
  bool send_ping_response(const PingResponse &msg);
 | 
			
		||||
  virtual void on_ping_response(const PingResponse &value){};
 | 
			
		||||
  virtual void on_device_info_request(const DeviceInfoRequest &value){};
 | 
			
		||||
  bool send_device_info_response(const DeviceInfoResponse &msg);
 | 
			
		||||
  virtual void on_list_entities_request(const ListEntitiesRequest &value){};
 | 
			
		||||
  bool send_list_entities_done_response(const ListEntitiesDoneResponse &msg);
 | 
			
		||||
  virtual void on_subscribe_states_request(const SubscribeStatesRequest &value){};
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
  bool send_list_entities_binary_sensor_response(const ListEntitiesBinarySensorResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
  bool send_binary_sensor_state_response(const BinarySensorStateResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_COVER
 | 
			
		||||
  bool send_list_entities_cover_response(const ListEntitiesCoverResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_COVER
 | 
			
		||||
  bool send_cover_state_response(const CoverStateResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_COVER
 | 
			
		||||
  virtual void on_cover_command_request(const CoverCommandRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_FAN
 | 
			
		||||
  bool send_list_entities_fan_response(const ListEntitiesFanResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_FAN
 | 
			
		||||
  bool send_fan_state_response(const FanStateResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_FAN
 | 
			
		||||
  virtual void on_fan_command_request(const FanCommandRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LIGHT
 | 
			
		||||
  bool send_list_entities_light_response(const ListEntitiesLightResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LIGHT
 | 
			
		||||
  bool send_light_state_response(const LightStateResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LIGHT
 | 
			
		||||
  virtual void on_light_command_request(const LightCommandRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
  bool send_list_entities_sensor_response(const ListEntitiesSensorResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
  bool send_sensor_state_response(const SensorStateResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
  bool send_list_entities_switch_response(const ListEntitiesSwitchResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
  bool send_switch_state_response(const SwitchStateResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
  virtual void on_switch_command_request(const SwitchCommandRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_TEXT_SENSOR
 | 
			
		||||
  bool send_list_entities_text_sensor_response(const ListEntitiesTextSensorResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_TEXT_SENSOR
 | 
			
		||||
  bool send_text_sensor_state_response(const TextSensorStateResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
  virtual void on_subscribe_logs_request(const SubscribeLogsRequest &value){};
 | 
			
		||||
  bool send_subscribe_logs_response(const SubscribeLogsResponse &msg);
 | 
			
		||||
  virtual void on_subscribe_homeassistant_services_request(const SubscribeHomeassistantServicesRequest &value){};
 | 
			
		||||
  bool send_homeassistant_service_response(const HomeassistantServiceResponse &msg);
 | 
			
		||||
  virtual void on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &value){};
 | 
			
		||||
  bool send_subscribe_home_assistant_state_response(const SubscribeHomeAssistantStateResponse &msg);
 | 
			
		||||
  virtual void on_home_assistant_state_response(const HomeAssistantStateResponse &value){};
 | 
			
		||||
  bool send_get_time_request(const GetTimeRequest &msg);
 | 
			
		||||
  virtual void on_get_time_request(const GetTimeRequest &value){};
 | 
			
		||||
  bool send_get_time_response(const GetTimeResponse &msg);
 | 
			
		||||
  virtual void on_get_time_response(const GetTimeResponse &value){};
 | 
			
		||||
  bool send_list_entities_services_response(const ListEntitiesServicesResponse &msg);
 | 
			
		||||
  virtual void on_execute_service_request(const ExecuteServiceRequest &value){};
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
  bool send_list_entities_camera_response(const ListEntitiesCameraResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
  bool send_camera_image_response(const CameraImageResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
  virtual void on_camera_image_request(const CameraImageRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_CLIMATE
 | 
			
		||||
  bool send_list_entities_climate_response(const ListEntitiesClimateResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_CLIMATE
 | 
			
		||||
  bool send_climate_state_response(const ClimateStateResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_CLIMATE
 | 
			
		||||
  virtual void on_climate_command_request(const ClimateCommandRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
 protected:
 | 
			
		||||
  bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class APIServerConnection : public APIServerConnectionBase {
 | 
			
		||||
 public:
 | 
			
		||||
  virtual HelloResponse hello(const HelloRequest &msg) = 0;
 | 
			
		||||
  virtual ConnectResponse connect(const ConnectRequest &msg) = 0;
 | 
			
		||||
  virtual DisconnectResponse disconnect(const DisconnectRequest &msg) = 0;
 | 
			
		||||
  virtual PingResponse ping(const PingRequest &msg) = 0;
 | 
			
		||||
  virtual DeviceInfoResponse device_info(const DeviceInfoRequest &msg) = 0;
 | 
			
		||||
  virtual void list_entities(const ListEntitiesRequest &msg) = 0;
 | 
			
		||||
  virtual void subscribe_states(const SubscribeStatesRequest &msg) = 0;
 | 
			
		||||
  virtual void subscribe_logs(const SubscribeLogsRequest &msg) = 0;
 | 
			
		||||
  virtual void subscribe_homeassistant_services(const SubscribeHomeassistantServicesRequest &msg) = 0;
 | 
			
		||||
  virtual void subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) = 0;
 | 
			
		||||
  virtual GetTimeResponse get_time(const GetTimeRequest &msg) = 0;
 | 
			
		||||
  virtual void execute_service(const ExecuteServiceRequest &msg) = 0;
 | 
			
		||||
#ifdef USE_COVER
 | 
			
		||||
  virtual void cover_command(const CoverCommandRequest &msg) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_FAN
 | 
			
		||||
  virtual void fan_command(const FanCommandRequest &msg) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LIGHT
 | 
			
		||||
  virtual void light_command(const LightCommandRequest &msg) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
  virtual void switch_command(const SwitchCommandRequest &msg) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
  virtual void camera_image(const CameraImageRequest &msg) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_CLIMATE
 | 
			
		||||
  virtual void climate_command(const ClimateCommandRequest &msg) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
 protected:
 | 
			
		||||
  void on_hello_request(const HelloRequest &msg) override;
 | 
			
		||||
  void on_connect_request(const ConnectRequest &msg) override;
 | 
			
		||||
  void on_disconnect_request(const DisconnectRequest &msg) override;
 | 
			
		||||
  void on_ping_request(const PingRequest &msg) override;
 | 
			
		||||
  void on_device_info_request(const DeviceInfoRequest &msg) override;
 | 
			
		||||
  void on_list_entities_request(const ListEntitiesRequest &msg) override;
 | 
			
		||||
  void on_subscribe_states_request(const SubscribeStatesRequest &msg) override;
 | 
			
		||||
  void on_subscribe_logs_request(const SubscribeLogsRequest &msg) override;
 | 
			
		||||
  void on_subscribe_homeassistant_services_request(const SubscribeHomeassistantServicesRequest &msg) override;
 | 
			
		||||
  void on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &msg) override;
 | 
			
		||||
  void on_get_time_request(const GetTimeRequest &msg) override;
 | 
			
		||||
  void on_execute_service_request(const ExecuteServiceRequest &msg) override;
 | 
			
		||||
#ifdef USE_COVER
 | 
			
		||||
  void on_cover_command_request(const CoverCommandRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_FAN
 | 
			
		||||
  void on_fan_command_request(const FanCommandRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LIGHT
 | 
			
		||||
  void on_light_command_request(const LightCommandRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
  void on_switch_command_request(const SwitchCommandRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
  void on_camera_image_request(const CameraImageRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_CLIMATE
 | 
			
		||||
  void on_climate_command_request(const ClimateCommandRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -1,18 +1,11 @@
 | 
			
		||||
#include <utility>
 | 
			
		||||
 | 
			
		||||
#include "api_server.h"
 | 
			
		||||
#include "basic_messages.h"
 | 
			
		||||
#include "api_connection.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/core/application.h"
 | 
			
		||||
#include "esphome/core/util.h"
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#include "esphome/core/version.h"
 | 
			
		||||
 | 
			
		||||
#ifdef USE_DEEP_SLEEP
 | 
			
		||||
#include "esphome/components/deep_sleep/deep_sleep_component.h"
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_HOMEASSISTANT_TIME
 | 
			
		||||
#include "esphome/components/homeassistant/time/homeassistant_time.h"
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LOGGER
 | 
			
		||||
#include "esphome/components/logger/logger.h"
 | 
			
		||||
#endif
 | 
			
		||||
@@ -209,9 +202,9 @@ void APIServer::set_port(uint16_t port) { this->port_ = port; }
 | 
			
		||||
APIServer *global_api_server = nullptr;
 | 
			
		||||
 | 
			
		||||
void APIServer::set_password(const std::string &password) { this->password_ = password; }
 | 
			
		||||
void APIServer::send_service_call(ServiceCallResponse &call) {
 | 
			
		||||
void APIServer::send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
 | 
			
		||||
  for (auto *client : this->clients_) {
 | 
			
		||||
    client->send_service_call(call);
 | 
			
		||||
    client->send_homeassistant_service_call(call);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
APIServer::APIServer() { global_api_server = this; }
 | 
			
		||||
@@ -237,965 +230,10 @@ void APIServer::request_time() {
 | 
			
		||||
bool APIServer::is_connected() const { return !this->clients_.empty(); }
 | 
			
		||||
void APIServer::on_shutdown() {
 | 
			
		||||
  for (auto *c : this->clients_) {
 | 
			
		||||
    c->send_disconnect_request();
 | 
			
		||||
    c->send_disconnect_request(DisconnectRequest());
 | 
			
		||||
  }
 | 
			
		||||
  delay(10);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// APIConnection
 | 
			
		||||
APIConnection::APIConnection(AsyncClient *client, APIServer *parent)
 | 
			
		||||
    : client_(client), parent_(parent), initial_state_iterator_(parent, this), list_entities_iterator_(parent, this) {
 | 
			
		||||
  this->client_->onError([](void *s, AsyncClient *c, int8_t error) { ((APIConnection *) s)->on_error_(error); }, this);
 | 
			
		||||
  this->client_->onDisconnect([](void *s, AsyncClient *c) { ((APIConnection *) s)->on_disconnect_(); }, this);
 | 
			
		||||
  this->client_->onTimeout([](void *s, AsyncClient *c, uint32_t time) { ((APIConnection *) s)->on_timeout_(time); },
 | 
			
		||||
                           this);
 | 
			
		||||
  this->client_->onData([](void *s, AsyncClient *c, void *buf,
 | 
			
		||||
                           size_t len) { ((APIConnection *) s)->on_data_(reinterpret_cast<uint8_t *>(buf), len); },
 | 
			
		||||
                        this);
 | 
			
		||||
 | 
			
		||||
  this->send_buffer_.reserve(64);
 | 
			
		||||
  this->recv_buffer_.reserve(32);
 | 
			
		||||
  this->client_info_ = this->client_->remoteIP().toString().c_str();
 | 
			
		||||
  this->last_traffic_ = millis();
 | 
			
		||||
}
 | 
			
		||||
APIConnection::~APIConnection() { delete this->client_; }
 | 
			
		||||
void APIConnection::on_error_(int8_t error) {
 | 
			
		||||
  // disconnect will also be called, nothing to do here
 | 
			
		||||
  this->remove_ = true;
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::on_disconnect_() {
 | 
			
		||||
  // delete self, generally unsafe but not in this case.
 | 
			
		||||
  this->remove_ = true;
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::on_timeout_(uint32_t time) { this->disconnect_client(); }
 | 
			
		||||
void APIConnection::on_data_(uint8_t *buf, size_t len) {
 | 
			
		||||
  if (len == 0 || buf == nullptr)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  this->recv_buffer_.insert(this->recv_buffer_.end(), buf, buf + len);
 | 
			
		||||
  // TODO: On ESP32, use queue to notify main thread of new data
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::parse_recv_buffer_() {
 | 
			
		||||
  if (this->recv_buffer_.empty() || this->remove_)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  while (!this->recv_buffer_.empty()) {
 | 
			
		||||
    if (this->recv_buffer_[0] != 0x00) {
 | 
			
		||||
      ESP_LOGW(TAG, "Invalid preamble from %s", this->client_info_.c_str());
 | 
			
		||||
      this->fatal_error_();
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    uint32_t i = 1;
 | 
			
		||||
    const uint32_t size = this->recv_buffer_.size();
 | 
			
		||||
    uint32_t msg_size = 0;
 | 
			
		||||
    while (i < size) {
 | 
			
		||||
      const uint8_t dat = this->recv_buffer_[i];
 | 
			
		||||
      msg_size |= (dat & 0x7F);
 | 
			
		||||
      // consume
 | 
			
		||||
      i += 1;
 | 
			
		||||
      if ((dat & 0x80) == 0x00) {
 | 
			
		||||
        break;
 | 
			
		||||
      } else {
 | 
			
		||||
        msg_size <<= 7;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    if (i == size)
 | 
			
		||||
      // not enough data there yet
 | 
			
		||||
      return;
 | 
			
		||||
 | 
			
		||||
    uint32_t msg_type = 0;
 | 
			
		||||
    bool msg_type_done = false;
 | 
			
		||||
    while (i < size) {
 | 
			
		||||
      const uint8_t dat = this->recv_buffer_[i];
 | 
			
		||||
      msg_type |= (dat & 0x7F);
 | 
			
		||||
      // consume
 | 
			
		||||
      i += 1;
 | 
			
		||||
      if ((dat & 0x80) == 0x00) {
 | 
			
		||||
        msg_type_done = true;
 | 
			
		||||
        break;
 | 
			
		||||
      } else {
 | 
			
		||||
        msg_type <<= 7;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    if (!msg_type_done)
 | 
			
		||||
      // not enough data there yet
 | 
			
		||||
      return;
 | 
			
		||||
 | 
			
		||||
    if (size - i < msg_size)
 | 
			
		||||
      // message body not fully received
 | 
			
		||||
      return;
 | 
			
		||||
 | 
			
		||||
    // ESP_LOGVV(TAG, "RECV Message: Size=%u Type=%u", msg_size, msg_type);
 | 
			
		||||
 | 
			
		||||
    if (!this->valid_rx_message_type_(msg_type)) {
 | 
			
		||||
      ESP_LOGE(TAG, "Not a valid message type: %u", msg_type);
 | 
			
		||||
      this->fatal_error_();
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    uint8_t *msg = &this->recv_buffer_[i];
 | 
			
		||||
    this->read_message_(msg_size, msg_type, msg);
 | 
			
		||||
    if (this->remove_)
 | 
			
		||||
      return;
 | 
			
		||||
    // pop front
 | 
			
		||||
    uint32_t total = i + msg_size;
 | 
			
		||||
    this->recv_buffer_.erase(this->recv_buffer_.begin(), this->recv_buffer_.begin() + total);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::read_message_(uint32_t size, uint32_t type, uint8_t *msg) {
 | 
			
		||||
  this->last_traffic_ = millis();
 | 
			
		||||
 | 
			
		||||
  switch (static_cast<APIMessageType>(type)) {
 | 
			
		||||
    case APIMessageType::HELLO_REQUEST: {
 | 
			
		||||
      HelloRequest req;
 | 
			
		||||
      req.decode(msg, size);
 | 
			
		||||
      this->on_hello_request_(req);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case APIMessageType::HELLO_RESPONSE: {
 | 
			
		||||
      // Invalid
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case APIMessageType::CONNECT_REQUEST: {
 | 
			
		||||
      ConnectRequest req;
 | 
			
		||||
      req.decode(msg, size);
 | 
			
		||||
      this->on_connect_request_(req);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case APIMessageType::CONNECT_RESPONSE:
 | 
			
		||||
      // Invalid
 | 
			
		||||
      break;
 | 
			
		||||
    case APIMessageType::DISCONNECT_REQUEST: {
 | 
			
		||||
      DisconnectRequest req;
 | 
			
		||||
      req.decode(msg, size);
 | 
			
		||||
      this->on_disconnect_request_(req);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case APIMessageType::DISCONNECT_RESPONSE: {
 | 
			
		||||
      DisconnectResponse req;
 | 
			
		||||
      req.decode(msg, size);
 | 
			
		||||
      this->on_disconnect_response_(req);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case APIMessageType::PING_REQUEST: {
 | 
			
		||||
      PingRequest req;
 | 
			
		||||
      req.decode(msg, size);
 | 
			
		||||
      this->on_ping_request_(req);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case APIMessageType::PING_RESPONSE: {
 | 
			
		||||
      PingResponse req;
 | 
			
		||||
      req.decode(msg, size);
 | 
			
		||||
      this->on_ping_response_(req);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case APIMessageType::DEVICE_INFO_REQUEST: {
 | 
			
		||||
      DeviceInfoRequest req;
 | 
			
		||||
      req.decode(msg, size);
 | 
			
		||||
      this->on_device_info_request_(req);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case APIMessageType::DEVICE_INFO_RESPONSE: {
 | 
			
		||||
      // Invalid
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case APIMessageType::LIST_ENTITIES_REQUEST: {
 | 
			
		||||
      ListEntitiesRequest req;
 | 
			
		||||
      req.decode(msg, size);
 | 
			
		||||
      this->on_list_entities_request_(req);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case APIMessageType::LIST_ENTITIES_BINARY_SENSOR_RESPONSE:
 | 
			
		||||
    case APIMessageType::LIST_ENTITIES_COVER_RESPONSE:
 | 
			
		||||
    case APIMessageType::LIST_ENTITIES_FAN_RESPONSE:
 | 
			
		||||
    case APIMessageType::LIST_ENTITIES_LIGHT_RESPONSE:
 | 
			
		||||
    case APIMessageType::LIST_ENTITIES_SENSOR_RESPONSE:
 | 
			
		||||
    case APIMessageType::LIST_ENTITIES_SWITCH_RESPONSE:
 | 
			
		||||
    case APIMessageType::LIST_ENTITIES_TEXT_SENSOR_RESPONSE:
 | 
			
		||||
    case APIMessageType::LIST_ENTITIES_SERVICE_RESPONSE:
 | 
			
		||||
    case APIMessageType::LIST_ENTITIES_CAMERA_RESPONSE:
 | 
			
		||||
    case APIMessageType::LIST_ENTITIES_CLIMATE_RESPONSE:
 | 
			
		||||
    case APIMessageType::LIST_ENTITIES_DONE_RESPONSE:
 | 
			
		||||
      // Invalid
 | 
			
		||||
      break;
 | 
			
		||||
    case APIMessageType::SUBSCRIBE_STATES_REQUEST: {
 | 
			
		||||
      SubscribeStatesRequest req;
 | 
			
		||||
      req.decode(msg, size);
 | 
			
		||||
      this->on_subscribe_states_request_(req);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case APIMessageType::BINARY_SENSOR_STATE_RESPONSE:
 | 
			
		||||
    case APIMessageType::COVER_STATE_RESPONSE:
 | 
			
		||||
    case APIMessageType::FAN_STATE_RESPONSE:
 | 
			
		||||
    case APIMessageType::LIGHT_STATE_RESPONSE:
 | 
			
		||||
    case APIMessageType::SENSOR_STATE_RESPONSE:
 | 
			
		||||
    case APIMessageType::SWITCH_STATE_RESPONSE:
 | 
			
		||||
    case APIMessageType::TEXT_SENSOR_STATE_RESPONSE:
 | 
			
		||||
    case APIMessageType::CAMERA_IMAGE_RESPONSE:
 | 
			
		||||
    case APIMessageType::CLIMATE_STATE_RESPONSE:
 | 
			
		||||
      // Invalid
 | 
			
		||||
      break;
 | 
			
		||||
    case APIMessageType::SUBSCRIBE_LOGS_REQUEST: {
 | 
			
		||||
      SubscribeLogsRequest req;
 | 
			
		||||
      req.decode(msg, size);
 | 
			
		||||
      this->on_subscribe_logs_request_(req);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case APIMessageType ::SUBSCRIBE_LOGS_RESPONSE:
 | 
			
		||||
      // Invalid
 | 
			
		||||
      break;
 | 
			
		||||
    case APIMessageType::COVER_COMMAND_REQUEST: {
 | 
			
		||||
#ifdef USE_COVER
 | 
			
		||||
      CoverCommandRequest req;
 | 
			
		||||
      req.decode(msg, size);
 | 
			
		||||
      this->on_cover_command_request_(req);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case APIMessageType::FAN_COMMAND_REQUEST: {
 | 
			
		||||
#ifdef USE_FAN
 | 
			
		||||
      FanCommandRequest req;
 | 
			
		||||
      req.decode(msg, size);
 | 
			
		||||
      this->on_fan_command_request_(req);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case APIMessageType::LIGHT_COMMAND_REQUEST: {
 | 
			
		||||
#ifdef USE_LIGHT
 | 
			
		||||
      LightCommandRequest req;
 | 
			
		||||
      req.decode(msg, size);
 | 
			
		||||
      this->on_light_command_request_(req);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case APIMessageType::SWITCH_COMMAND_REQUEST: {
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
      SwitchCommandRequest req;
 | 
			
		||||
      req.decode(msg, size);
 | 
			
		||||
      this->on_switch_command_request_(req);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case APIMessageType::CLIMATE_COMMAND_REQUEST: {
 | 
			
		||||
#ifdef USE_CLIMATE
 | 
			
		||||
      ClimateCommandRequest req;
 | 
			
		||||
      req.decode(msg, size);
 | 
			
		||||
      this->on_climate_command_request_(req);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case APIMessageType::SUBSCRIBE_SERVICE_CALLS_REQUEST: {
 | 
			
		||||
      SubscribeServiceCallsRequest req;
 | 
			
		||||
      req.decode(msg, size);
 | 
			
		||||
      this->on_subscribe_service_calls_request_(req);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case APIMessageType::SERVICE_CALL_RESPONSE:
 | 
			
		||||
      // Invalid
 | 
			
		||||
      break;
 | 
			
		||||
    case APIMessageType::GET_TIME_REQUEST:
 | 
			
		||||
      // Invalid
 | 
			
		||||
      break;
 | 
			
		||||
    case APIMessageType::GET_TIME_RESPONSE: {
 | 
			
		||||
#ifdef USE_HOMEASSISTANT_TIME
 | 
			
		||||
      homeassistant::GetTimeResponse req;
 | 
			
		||||
      req.decode(msg, size);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case APIMessageType::SUBSCRIBE_HOME_ASSISTANT_STATES_REQUEST: {
 | 
			
		||||
      SubscribeHomeAssistantStatesRequest req;
 | 
			
		||||
      req.decode(msg, size);
 | 
			
		||||
      this->on_subscribe_home_assistant_states_request_(req);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case APIMessageType::SUBSCRIBE_HOME_ASSISTANT_STATE_RESPONSE:
 | 
			
		||||
      // Invalid
 | 
			
		||||
      break;
 | 
			
		||||
    case APIMessageType::HOME_ASSISTANT_STATE_RESPONSE: {
 | 
			
		||||
      HomeAssistantStateResponse req;
 | 
			
		||||
      req.decode(msg, size);
 | 
			
		||||
      this->on_home_assistant_state_response_(req);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case APIMessageType::EXECUTE_SERVICE_REQUEST: {
 | 
			
		||||
      ExecuteServiceRequest req;
 | 
			
		||||
      req.decode(msg, size);
 | 
			
		||||
      this->on_execute_service_(req);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case APIMessageType::CAMERA_IMAGE_REQUEST: {
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
      CameraImageRequest req;
 | 
			
		||||
      req.decode(msg, size);
 | 
			
		||||
      this->on_camera_image_request_(req);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::on_hello_request_(const HelloRequest &req) {
 | 
			
		||||
  ESP_LOGVV(TAG, "on_hello_request_(client_info='%s')", req.get_client_info().c_str());
 | 
			
		||||
  this->client_info_ = req.get_client_info() + " (" + this->client_->remoteIP().toString().c_str();
 | 
			
		||||
  this->client_info_ += ")";
 | 
			
		||||
  ESP_LOGV(TAG, "Hello from client: '%s'", this->client_info_.c_str());
 | 
			
		||||
 | 
			
		||||
  auto buffer = this->get_buffer();
 | 
			
		||||
  // uint32 api_version_major = 1; -> 1
 | 
			
		||||
  buffer.encode_uint32(1, 1);
 | 
			
		||||
  // uint32 api_version_minor = 2; -> 1
 | 
			
		||||
  buffer.encode_uint32(2, 1);
 | 
			
		||||
 | 
			
		||||
  // string server_info = 3;
 | 
			
		||||
  buffer.encode_string(3, App.get_name() + " (esphome v" ESPHOME_VERSION ")");
 | 
			
		||||
  bool success = this->send_buffer(APIMessageType::HELLO_RESPONSE);
 | 
			
		||||
  if (!success) {
 | 
			
		||||
    this->fatal_error_();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this->connection_state_ = ConnectionState::WAITING_FOR_CONNECT;
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::on_connect_request_(const ConnectRequest &req) {
 | 
			
		||||
  ESP_LOGVV(TAG, "on_connect_request_(password='%s')", req.get_password().c_str());
 | 
			
		||||
  bool correct = this->parent_->check_password(req.get_password());
 | 
			
		||||
  auto buffer = this->get_buffer();
 | 
			
		||||
  // bool invalid_password = 1;
 | 
			
		||||
  buffer.encode_bool(1, !correct);
 | 
			
		||||
  bool success = this->send_buffer(APIMessageType::CONNECT_RESPONSE);
 | 
			
		||||
  if (!success) {
 | 
			
		||||
    this->fatal_error_();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (correct) {
 | 
			
		||||
    ESP_LOGD(TAG, "Client '%s' connected successfully!", this->client_info_.c_str());
 | 
			
		||||
    this->connection_state_ = ConnectionState::CONNECTED;
 | 
			
		||||
 | 
			
		||||
#ifdef USE_HOMEASSISTANT_TIME
 | 
			
		||||
    if (homeassistant::global_homeassistant_time != nullptr) {
 | 
			
		||||
      this->send_time_request();
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::on_disconnect_request_(const DisconnectRequest &req) {
 | 
			
		||||
  ESP_LOGVV(TAG, "on_disconnect_request_");
 | 
			
		||||
  // remote initiated disconnect_client
 | 
			
		||||
  if (!this->send_empty_message(APIMessageType::DISCONNECT_RESPONSE)) {
 | 
			
		||||
    this->fatal_error_();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->disconnect_client();
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::on_disconnect_response_(const DisconnectResponse &req) {
 | 
			
		||||
  ESP_LOGVV(TAG, "on_disconnect_response_");
 | 
			
		||||
  // we initiated disconnect_client
 | 
			
		||||
  this->disconnect_client();
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::on_ping_request_(const PingRequest &req) {
 | 
			
		||||
  ESP_LOGVV(TAG, "on_ping_request_");
 | 
			
		||||
  PingResponse resp;
 | 
			
		||||
  this->send_message(resp);
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::on_ping_response_(const PingResponse &req) {
 | 
			
		||||
  ESP_LOGVV(TAG, "on_ping_response_");
 | 
			
		||||
  // we initiated ping
 | 
			
		||||
  this->sent_ping_ = false;
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::on_device_info_request_(const DeviceInfoRequest &req) {
 | 
			
		||||
  ESP_LOGVV(TAG, "on_device_info_request_");
 | 
			
		||||
  auto buffer = this->get_buffer();
 | 
			
		||||
  // bool uses_password = 1;
 | 
			
		||||
  buffer.encode_bool(1, this->parent_->uses_password());
 | 
			
		||||
  // string name = 2;
 | 
			
		||||
  buffer.encode_string(2, App.get_name());
 | 
			
		||||
  // string mac_address = 3;
 | 
			
		||||
  buffer.encode_string(3, get_mac_address_pretty());
 | 
			
		||||
  // string esphome_version = 4;
 | 
			
		||||
  buffer.encode_string(4, ESPHOME_VERSION);
 | 
			
		||||
  // string compilation_time = 5;
 | 
			
		||||
  buffer.encode_string(5, App.get_compilation_time());
 | 
			
		||||
#ifdef ARDUINO_BOARD
 | 
			
		||||
  // string model = 6;
 | 
			
		||||
  buffer.encode_string(6, ARDUINO_BOARD);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_DEEP_SLEEP
 | 
			
		||||
  // bool has_deep_sleep = 7;
 | 
			
		||||
  buffer.encode_bool(7, deep_sleep::global_has_deep_sleep);
 | 
			
		||||
#endif
 | 
			
		||||
  this->send_buffer(APIMessageType::DEVICE_INFO_RESPONSE);
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::on_list_entities_request_(const ListEntitiesRequest &req) {
 | 
			
		||||
  ESP_LOGVV(TAG, "on_list_entities_request_");
 | 
			
		||||
  this->list_entities_iterator_.begin();
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::on_subscribe_states_request_(const SubscribeStatesRequest &req) {
 | 
			
		||||
  ESP_LOGVV(TAG, "on_subscribe_states_request_");
 | 
			
		||||
  this->state_subscription_ = true;
 | 
			
		||||
  this->initial_state_iterator_.begin();
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::on_subscribe_logs_request_(const SubscribeLogsRequest &req) {
 | 
			
		||||
  ESP_LOGVV(TAG, "on_subscribe_logs_request_");
 | 
			
		||||
  this->log_subscription_ = req.get_level();
 | 
			
		||||
  if (req.get_dump_config()) {
 | 
			
		||||
    App.schedule_dump_config();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void APIConnection::fatal_error_() {
 | 
			
		||||
  this->client_->close();
 | 
			
		||||
  this->remove_ = true;
 | 
			
		||||
}
 | 
			
		||||
bool APIConnection::valid_rx_message_type_(uint32_t type) {
 | 
			
		||||
  switch (static_cast<APIMessageType>(type)) {
 | 
			
		||||
    case APIMessageType::HELLO_RESPONSE:
 | 
			
		||||
    case APIMessageType::CONNECT_RESPONSE:
 | 
			
		||||
      return false;
 | 
			
		||||
    case APIMessageType::HELLO_REQUEST:
 | 
			
		||||
      return this->connection_state_ == ConnectionState::WAITING_FOR_HELLO;
 | 
			
		||||
    case APIMessageType::CONNECT_REQUEST:
 | 
			
		||||
      return this->connection_state_ == ConnectionState::WAITING_FOR_CONNECT;
 | 
			
		||||
    case APIMessageType::PING_REQUEST:
 | 
			
		||||
    case APIMessageType::PING_RESPONSE:
 | 
			
		||||
    case APIMessageType::DISCONNECT_REQUEST:
 | 
			
		||||
    case APIMessageType::DISCONNECT_RESPONSE:
 | 
			
		||||
    case APIMessageType::DEVICE_INFO_REQUEST:
 | 
			
		||||
      if (this->connection_state_ == ConnectionState::WAITING_FOR_CONNECT)
 | 
			
		||||
        return true;
 | 
			
		||||
    default:
 | 
			
		||||
      return this->connection_state_ == ConnectionState::CONNECTED;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
bool APIConnection::send_message(APIMessage &msg) {
 | 
			
		||||
  this->send_buffer_.clear();
 | 
			
		||||
  APIBuffer buf(&this->send_buffer_);
 | 
			
		||||
  msg.encode(buf);
 | 
			
		||||
  return this->send_buffer(msg.message_type());
 | 
			
		||||
}
 | 
			
		||||
bool APIConnection::send_empty_message(APIMessageType type) {
 | 
			
		||||
  this->send_buffer_.clear();
 | 
			
		||||
  return this->send_buffer(type);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void APIConnection::disconnect_client() {
 | 
			
		||||
  this->client_->close();
 | 
			
		||||
  this->remove_ = true;
 | 
			
		||||
}
 | 
			
		||||
void encode_varint(uint8_t *dat, uint8_t *len, uint32_t value) {
 | 
			
		||||
  if (value <= 0x7F) {
 | 
			
		||||
    *dat = value;
 | 
			
		||||
    (*len)++;
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  while (value) {
 | 
			
		||||
    uint8_t temp = value & 0x7F;
 | 
			
		||||
    value >>= 7;
 | 
			
		||||
    if (value) {
 | 
			
		||||
      *dat = temp | 0x80;
 | 
			
		||||
    } else {
 | 
			
		||||
      *dat = temp;
 | 
			
		||||
    }
 | 
			
		||||
    dat++;
 | 
			
		||||
    (*len)++;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool APIConnection::send_buffer(APIMessageType type) {
 | 
			
		||||
  uint8_t header[20];
 | 
			
		||||
  header[0] = 0x00;
 | 
			
		||||
  uint8_t header_len = 1;
 | 
			
		||||
  encode_varint(header + header_len, &header_len, this->send_buffer_.size());
 | 
			
		||||
  encode_varint(header + header_len, &header_len, static_cast<uint32_t>(type));
 | 
			
		||||
 | 
			
		||||
  size_t needed_space = this->send_buffer_.size() + header_len;
 | 
			
		||||
 | 
			
		||||
  if (needed_space > this->client_->space()) {
 | 
			
		||||
    delay(5);
 | 
			
		||||
    if (needed_space > this->client_->space()) {
 | 
			
		||||
      if (type != APIMessageType::SUBSCRIBE_LOGS_RESPONSE) {
 | 
			
		||||
        ESP_LOGV(TAG, "Cannot send message because of TCP buffer space");
 | 
			
		||||
      }
 | 
			
		||||
      delay(5);
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  //  char buffer[512];
 | 
			
		||||
  //  uint32_t offset = 0;
 | 
			
		||||
  //  for (int j = 0; j < header_len; j++) {
 | 
			
		||||
  //    offset += snprintf(buffer + offset, 512 - offset, "0x%02X ", header[j]);
 | 
			
		||||
  //  }
 | 
			
		||||
  //  offset += snprintf(buffer + offset, 512 - offset, "| ");
 | 
			
		||||
  //  for (auto &it : this->send_buffer_) {
 | 
			
		||||
  //    int i = snprintf(buffer + offset, 512 - offset, "0x%02X ", it);
 | 
			
		||||
  //    if (i <= 0)
 | 
			
		||||
  //      break;
 | 
			
		||||
  //    offset += i;
 | 
			
		||||
  //  }
 | 
			
		||||
  //  ESP_LOGVV(TAG, "SEND %s", buffer);
 | 
			
		||||
 | 
			
		||||
  this->client_->add(reinterpret_cast<char *>(header), header_len);
 | 
			
		||||
  this->client_->add(reinterpret_cast<char *>(this->send_buffer_.data()), this->send_buffer_.size());
 | 
			
		||||
  return this->client_->send();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void APIConnection::loop() {
 | 
			
		||||
  if (!network_is_connected()) {
 | 
			
		||||
    // when network is disconnected force disconnect immediately
 | 
			
		||||
    // don't wait for timeout
 | 
			
		||||
    this->fatal_error_();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (this->client_->disconnected()) {
 | 
			
		||||
    // failsafe for disconnect logic
 | 
			
		||||
    this->on_disconnect_();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->parse_recv_buffer_();
 | 
			
		||||
 | 
			
		||||
  this->list_entities_iterator_.advance();
 | 
			
		||||
  this->initial_state_iterator_.advance();
 | 
			
		||||
 | 
			
		||||
  const uint32_t keepalive = 60000;
 | 
			
		||||
  if (this->sent_ping_) {
 | 
			
		||||
    if (millis() - this->last_traffic_ > (keepalive * 3) / 2) {
 | 
			
		||||
      ESP_LOGW(TAG, "'%s' didn't respond to ping request in time. Disconnecting...", this->client_info_.c_str());
 | 
			
		||||
      this->disconnect_client();
 | 
			
		||||
    }
 | 
			
		||||
  } else if (millis() - this->last_traffic_ > keepalive) {
 | 
			
		||||
    this->sent_ping_ = true;
 | 
			
		||||
    this->send_ping_request();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
  if (this->image_reader_.available()) {
 | 
			
		||||
    uint32_t space = this->client_->space();
 | 
			
		||||
    // reserve 15 bytes for metadata, and at least 64 bytes of data
 | 
			
		||||
    if (space >= 15 + 64) {
 | 
			
		||||
      uint32_t to_send = std::min(space - 15, this->image_reader_.available());
 | 
			
		||||
      auto buffer = this->get_buffer();
 | 
			
		||||
      // fixed32 key = 1;
 | 
			
		||||
      buffer.encode_fixed32(1, esp32_camera::global_esp32_camera->get_object_id_hash());
 | 
			
		||||
      // bytes data = 2;
 | 
			
		||||
      buffer.encode_bytes(2, this->image_reader_.peek_data_buffer(), to_send);
 | 
			
		||||
      // bool done = 3;
 | 
			
		||||
      bool done = this->image_reader_.available() == to_send;
 | 
			
		||||
      buffer.encode_bool(3, done);
 | 
			
		||||
      bool success = this->send_buffer(APIMessageType::CAMERA_IMAGE_RESPONSE);
 | 
			
		||||
      if (success) {
 | 
			
		||||
        this->image_reader_.consume_data(to_send);
 | 
			
		||||
      }
 | 
			
		||||
      if (success && done) {
 | 
			
		||||
        this->image_reader_.return_image();
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
bool APIConnection::send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor, bool state) {
 | 
			
		||||
  if (!this->state_subscription_)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  auto buffer = this->get_buffer();
 | 
			
		||||
  // fixed32 key = 1;
 | 
			
		||||
  buffer.encode_fixed32(1, binary_sensor->get_object_id_hash());
 | 
			
		||||
  // bool state = 2;
 | 
			
		||||
  buffer.encode_bool(2, state);
 | 
			
		||||
  return this->send_buffer(APIMessageType::BINARY_SENSOR_STATE_RESPONSE);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_COVER
 | 
			
		||||
bool APIConnection::send_cover_state(cover::Cover *cover) {
 | 
			
		||||
  if (!this->state_subscription_)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  auto buffer = this->get_buffer();
 | 
			
		||||
  auto traits = cover->get_traits();
 | 
			
		||||
  // fixed32 key = 1;
 | 
			
		||||
  buffer.encode_fixed32(1, cover->get_object_id_hash());
 | 
			
		||||
  // enum LegacyCoverState {
 | 
			
		||||
  //   OPEN = 0;
 | 
			
		||||
  //   CLOSED = 1;
 | 
			
		||||
  // }
 | 
			
		||||
  // LegacyCoverState legacy_state = 2;
 | 
			
		||||
  uint32_t state = (cover->position == cover::COVER_OPEN) ? 0 : 1;
 | 
			
		||||
  buffer.encode_uint32(2, state);
 | 
			
		||||
  // float position = 3;
 | 
			
		||||
  buffer.encode_float(3, cover->position);
 | 
			
		||||
  if (traits.get_supports_tilt()) {
 | 
			
		||||
    // float tilt = 4;
 | 
			
		||||
    buffer.encode_float(4, cover->tilt);
 | 
			
		||||
  }
 | 
			
		||||
  // enum CoverCurrentOperation {
 | 
			
		||||
  //   IDLE = 0;
 | 
			
		||||
  //   IS_OPENING = 1;
 | 
			
		||||
  //   IS_CLOSING = 2;
 | 
			
		||||
  // }
 | 
			
		||||
  // CoverCurrentOperation current_operation = 5;
 | 
			
		||||
  buffer.encode_uint32(5, cover->current_operation);
 | 
			
		||||
  return this->send_buffer(APIMessageType::COVER_STATE_RESPONSE);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_FAN
 | 
			
		||||
bool APIConnection::send_fan_state(fan::FanState *fan) {
 | 
			
		||||
  if (!this->state_subscription_)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  auto buffer = this->get_buffer();
 | 
			
		||||
  // fixed32 key = 1;
 | 
			
		||||
  buffer.encode_fixed32(1, fan->get_object_id_hash());
 | 
			
		||||
  // bool state = 2;
 | 
			
		||||
  buffer.encode_bool(2, fan->state);
 | 
			
		||||
  // bool oscillating = 3;
 | 
			
		||||
  if (fan->get_traits().supports_oscillation()) {
 | 
			
		||||
    buffer.encode_bool(3, fan->oscillating);
 | 
			
		||||
  }
 | 
			
		||||
  // enum FanSpeed {
 | 
			
		||||
  //   LOW = 0;
 | 
			
		||||
  //   MEDIUM = 1;
 | 
			
		||||
  //   HIGH = 2;
 | 
			
		||||
  // }
 | 
			
		||||
  // FanSpeed speed = 4;
 | 
			
		||||
  if (fan->get_traits().supports_speed()) {
 | 
			
		||||
    buffer.encode_uint32(4, fan->speed);
 | 
			
		||||
  }
 | 
			
		||||
  return this->send_buffer(APIMessageType::FAN_STATE_RESPONSE);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_LIGHT
 | 
			
		||||
bool APIConnection::send_light_state(light::LightState *light) {
 | 
			
		||||
  if (!this->state_subscription_)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  auto buffer = this->get_buffer();
 | 
			
		||||
  auto traits = light->get_traits();
 | 
			
		||||
  auto values = light->remote_values;
 | 
			
		||||
 | 
			
		||||
  // fixed32 key = 1;
 | 
			
		||||
  buffer.encode_fixed32(1, light->get_object_id_hash());
 | 
			
		||||
  // bool state = 2;
 | 
			
		||||
  buffer.encode_bool(2, values.get_state() != 0.0f);
 | 
			
		||||
  // float brightness = 3;
 | 
			
		||||
  if (traits.get_supports_brightness()) {
 | 
			
		||||
    buffer.encode_float(3, values.get_brightness());
 | 
			
		||||
  }
 | 
			
		||||
  if (traits.get_supports_rgb()) {
 | 
			
		||||
    // float red = 4;
 | 
			
		||||
    buffer.encode_float(4, values.get_red());
 | 
			
		||||
    // float green = 5;
 | 
			
		||||
    buffer.encode_float(5, values.get_green());
 | 
			
		||||
    // float blue = 6;
 | 
			
		||||
    buffer.encode_float(6, values.get_blue());
 | 
			
		||||
  }
 | 
			
		||||
  // float white = 7;
 | 
			
		||||
  if (traits.get_supports_rgb_white_value()) {
 | 
			
		||||
    buffer.encode_float(7, values.get_white());
 | 
			
		||||
  }
 | 
			
		||||
  // float color_temperature = 8;
 | 
			
		||||
  if (traits.get_supports_color_temperature()) {
 | 
			
		||||
    buffer.encode_float(8, values.get_color_temperature());
 | 
			
		||||
  }
 | 
			
		||||
  // string effect = 9;
 | 
			
		||||
  if (light->supports_effects()) {
 | 
			
		||||
    buffer.encode_string(9, light->get_effect_name());
 | 
			
		||||
  }
 | 
			
		||||
  return this->send_buffer(APIMessageType::LIGHT_STATE_RESPONSE);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
bool APIConnection::send_sensor_state(sensor::Sensor *sensor, float state) {
 | 
			
		||||
  if (!this->state_subscription_)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  auto buffer = this->get_buffer();
 | 
			
		||||
  // fixed32 key = 1;
 | 
			
		||||
  buffer.encode_fixed32(1, sensor->get_object_id_hash());
 | 
			
		||||
  // float state = 2;
 | 
			
		||||
  buffer.encode_float(2, state);
 | 
			
		||||
  return this->send_buffer(APIMessageType::SENSOR_STATE_RESPONSE);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
bool APIConnection::send_switch_state(switch_::Switch *a_switch, bool state) {
 | 
			
		||||
  if (!this->state_subscription_)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  auto buffer = this->get_buffer();
 | 
			
		||||
  // fixed32 key = 1;
 | 
			
		||||
  buffer.encode_fixed32(1, a_switch->get_object_id_hash());
 | 
			
		||||
  // bool state = 2;
 | 
			
		||||
  buffer.encode_bool(2, state);
 | 
			
		||||
  return this->send_buffer(APIMessageType::SWITCH_STATE_RESPONSE);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_TEXT_SENSOR
 | 
			
		||||
bool APIConnection::send_text_sensor_state(text_sensor::TextSensor *text_sensor, std::string state) {
 | 
			
		||||
  if (!this->state_subscription_)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  auto buffer = this->get_buffer();
 | 
			
		||||
  // fixed32 key = 1;
 | 
			
		||||
  buffer.encode_fixed32(1, text_sensor->get_object_id_hash());
 | 
			
		||||
  // string state = 2;
 | 
			
		||||
  buffer.encode_string(2, state);
 | 
			
		||||
  return this->send_buffer(APIMessageType::TEXT_SENSOR_STATE_RESPONSE);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_CLIMATE
 | 
			
		||||
bool APIConnection::send_climate_state(climate::Climate *climate) {
 | 
			
		||||
  if (!this->state_subscription_)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  auto buffer = this->get_buffer();
 | 
			
		||||
  auto traits = climate->get_traits();
 | 
			
		||||
  // fixed32 key = 1;
 | 
			
		||||
  buffer.encode_fixed32(1, climate->get_object_id_hash());
 | 
			
		||||
  // ClimateMode mode = 2;
 | 
			
		||||
  buffer.encode_uint32(2, static_cast<uint32_t>(climate->mode));
 | 
			
		||||
  // float current_temperature = 3;
 | 
			
		||||
  if (traits.get_supports_current_temperature()) {
 | 
			
		||||
    buffer.encode_float(3, climate->current_temperature);
 | 
			
		||||
  }
 | 
			
		||||
  if (traits.get_supports_two_point_target_temperature()) {
 | 
			
		||||
    // float target_temperature_low = 5;
 | 
			
		||||
    buffer.encode_float(5, climate->target_temperature_low);
 | 
			
		||||
    // float target_temperature_high = 6;
 | 
			
		||||
    buffer.encode_float(6, climate->target_temperature_high);
 | 
			
		||||
  } else {
 | 
			
		||||
    // float target_temperature = 4;
 | 
			
		||||
    buffer.encode_float(4, climate->target_temperature);
 | 
			
		||||
  }
 | 
			
		||||
  // bool away = 7;
 | 
			
		||||
  if (traits.get_supports_away()) {
 | 
			
		||||
    buffer.encode_bool(7, climate->away);
 | 
			
		||||
  }
 | 
			
		||||
  return this->send_buffer(APIMessageType::CLIMATE_STATE_RESPONSE);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
bool APIConnection::send_log_message(int level, const char *tag, const char *line) {
 | 
			
		||||
  if (this->log_subscription_ < level)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  auto buffer = this->get_buffer();
 | 
			
		||||
  // LogLevel level = 1;
 | 
			
		||||
  buffer.encode_uint32(1, static_cast<uint32_t>(level));
 | 
			
		||||
  // string tag = 2;
 | 
			
		||||
  // buffer.encode_string(2, tag, strlen(tag));
 | 
			
		||||
  // string message = 3;
 | 
			
		||||
  buffer.encode_string(3, line, strlen(line));
 | 
			
		||||
  bool success = this->send_buffer(APIMessageType::SUBSCRIBE_LOGS_RESPONSE);
 | 
			
		||||
 | 
			
		||||
  if (!success) {
 | 
			
		||||
    buffer = this->get_buffer();
 | 
			
		||||
    // bool send_failed = 4;
 | 
			
		||||
    buffer.encode_bool(4, true);
 | 
			
		||||
    return this->send_buffer(APIMessageType::SUBSCRIBE_LOGS_RESPONSE);
 | 
			
		||||
  } else {
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
bool APIConnection::send_disconnect_request() {
 | 
			
		||||
  DisconnectRequest req;
 | 
			
		||||
  return this->send_message(req);
 | 
			
		||||
}
 | 
			
		||||
bool APIConnection::send_ping_request() {
 | 
			
		||||
  ESP_LOGVV(TAG, "Sending ping...");
 | 
			
		||||
  PingRequest req;
 | 
			
		||||
  return this->send_message(req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef USE_COVER
 | 
			
		||||
void APIConnection::on_cover_command_request_(const CoverCommandRequest &req) {
 | 
			
		||||
  ESP_LOGVV(TAG, "on_cover_command_request_");
 | 
			
		||||
  cover::Cover *cover = App.get_cover_by_key(req.get_key());
 | 
			
		||||
  if (cover == nullptr)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  auto call = cover->make_call();
 | 
			
		||||
  if (req.get_legacy_command().has_value()) {
 | 
			
		||||
    auto cmd = *req.get_legacy_command();
 | 
			
		||||
    switch (cmd) {
 | 
			
		||||
      case LEGACY_COVER_COMMAND_OPEN:
 | 
			
		||||
        call.set_command_open();
 | 
			
		||||
        break;
 | 
			
		||||
      case LEGACY_COVER_COMMAND_CLOSE:
 | 
			
		||||
        call.set_command_close();
 | 
			
		||||
        break;
 | 
			
		||||
      case LEGACY_COVER_COMMAND_STOP:
 | 
			
		||||
        call.set_command_stop();
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if (req.get_position().has_value()) {
 | 
			
		||||
    auto pos = *req.get_position();
 | 
			
		||||
    call.set_position(pos);
 | 
			
		||||
  }
 | 
			
		||||
  if (req.get_tilt().has_value()) {
 | 
			
		||||
    auto tilt = *req.get_tilt();
 | 
			
		||||
    call.set_tilt(tilt);
 | 
			
		||||
  }
 | 
			
		||||
  if (req.get_stop()) {
 | 
			
		||||
    call.set_command_stop();
 | 
			
		||||
  }
 | 
			
		||||
  call.perform();
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_FAN
 | 
			
		||||
void APIConnection::on_fan_command_request_(const FanCommandRequest &req) {
 | 
			
		||||
  ESP_LOGVV(TAG, "on_fan_command_request_");
 | 
			
		||||
  fan::FanState *fan = App.get_fan_by_key(req.get_key());
 | 
			
		||||
  if (fan == nullptr)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  auto call = fan->make_call();
 | 
			
		||||
  call.set_state(req.get_state());
 | 
			
		||||
  call.set_oscillating(req.get_oscillating());
 | 
			
		||||
  call.set_speed(req.get_speed());
 | 
			
		||||
  call.perform();
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_LIGHT
 | 
			
		||||
void APIConnection::on_light_command_request_(const LightCommandRequest &req) {
 | 
			
		||||
  ESP_LOGVV(TAG, "on_light_command_request_");
 | 
			
		||||
  light::LightState *light = App.get_light_by_key(req.get_key());
 | 
			
		||||
  if (light == nullptr)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  auto call = light->make_call();
 | 
			
		||||
  call.set_state(req.get_state());
 | 
			
		||||
  call.set_brightness(req.get_brightness());
 | 
			
		||||
  call.set_red(req.get_red());
 | 
			
		||||
  call.set_green(req.get_green());
 | 
			
		||||
  call.set_blue(req.get_blue());
 | 
			
		||||
  call.set_white(req.get_white());
 | 
			
		||||
  call.set_color_temperature(req.get_color_temperature());
 | 
			
		||||
  call.set_transition_length(req.get_transition_length());
 | 
			
		||||
  call.set_flash_length(req.get_flash_length());
 | 
			
		||||
  call.set_effect(req.get_effect());
 | 
			
		||||
  call.perform();
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
void APIConnection::on_switch_command_request_(const SwitchCommandRequest &req) {
 | 
			
		||||
  ESP_LOGVV(TAG, "on_switch_command_request_");
 | 
			
		||||
  switch_::Switch *a_switch = App.get_switch_by_key(req.get_key());
 | 
			
		||||
  if (a_switch == nullptr || a_switch->is_internal())
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  if (req.get_state()) {
 | 
			
		||||
    a_switch->turn_on();
 | 
			
		||||
  } else {
 | 
			
		||||
    a_switch->turn_off();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_CLIMATE
 | 
			
		||||
void APIConnection::on_climate_command_request_(const ClimateCommandRequest &req) {
 | 
			
		||||
  ESP_LOGVV(TAG, "on_climate_command_request_");
 | 
			
		||||
  climate::Climate *climate = App.get_climate_by_key(req.get_key());
 | 
			
		||||
  if (climate == nullptr)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  auto call = climate->make_call();
 | 
			
		||||
  if (req.get_mode().has_value())
 | 
			
		||||
    call.set_mode(*req.get_mode());
 | 
			
		||||
  if (req.get_target_temperature().has_value())
 | 
			
		||||
    call.set_target_temperature(*req.get_target_temperature());
 | 
			
		||||
  if (req.get_target_temperature_low().has_value())
 | 
			
		||||
    call.set_target_temperature_low(*req.get_target_temperature_low());
 | 
			
		||||
  if (req.get_target_temperature_high().has_value())
 | 
			
		||||
    call.set_target_temperature_high(*req.get_target_temperature_high());
 | 
			
		||||
  if (req.get_away().has_value())
 | 
			
		||||
    call.set_away(*req.get_away());
 | 
			
		||||
  call.perform();
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void APIConnection::on_subscribe_service_calls_request_(const SubscribeServiceCallsRequest &req) {
 | 
			
		||||
  this->service_call_subscription_ = true;
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::send_service_call(ServiceCallResponse &call) {
 | 
			
		||||
  if (!this->service_call_subscription_)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  this->send_message(call);
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::on_subscribe_home_assistant_states_request_(const SubscribeHomeAssistantStatesRequest &req) {
 | 
			
		||||
  for (auto &it : this->parent_->get_state_subs()) {
 | 
			
		||||
    auto buffer = this->get_buffer();
 | 
			
		||||
    // string entity_id = 1;
 | 
			
		||||
    buffer.encode_string(1, it.entity_id);
 | 
			
		||||
    this->send_buffer(APIMessageType::SUBSCRIBE_HOME_ASSISTANT_STATE_RESPONSE);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::on_home_assistant_state_response_(const HomeAssistantStateResponse &req) {
 | 
			
		||||
  for (auto &it : this->parent_->get_state_subs()) {
 | 
			
		||||
    if (it.entity_id == req.get_entity_id()) {
 | 
			
		||||
      it.callback(req.get_state());
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::on_execute_service_(const ExecuteServiceRequest &req) {
 | 
			
		||||
  ESP_LOGVV(TAG, "on_execute_service_");
 | 
			
		||||
  bool found = false;
 | 
			
		||||
  for (auto *service : this->parent_->get_user_services()) {
 | 
			
		||||
    if (service->execute_service(req)) {
 | 
			
		||||
      found = true;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if (!found) {
 | 
			
		||||
    ESP_LOGV(TAG, "Could not find matching service!");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
APIBuffer APIConnection::get_buffer() {
 | 
			
		||||
  this->send_buffer_.clear();
 | 
			
		||||
  return {&this->send_buffer_};
 | 
			
		||||
}
 | 
			
		||||
#ifdef USE_HOMEASSISTANT_TIME
 | 
			
		||||
void APIConnection::send_time_request() { this->send_empty_message(APIMessageType::GET_TIME_REQUEST); }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
void APIConnection::send_camera_state(std::shared_ptr<esp32_camera::CameraImage> image) {
 | 
			
		||||
  if (!this->state_subscription_)
 | 
			
		||||
    return;
 | 
			
		||||
  if (this->image_reader_.available())
 | 
			
		||||
    return;
 | 
			
		||||
  this->image_reader_.set_image(image);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
void APIConnection::on_camera_image_request_(const CameraImageRequest &req) {
 | 
			
		||||
  if (esp32_camera::global_esp32_camera == nullptr)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  ESP_LOGV(TAG, "on_camera_image_request_ stream=%s single=%s", YESNO(req.get_stream()), YESNO(req.get_single()));
 | 
			
		||||
  if (req.get_single()) {
 | 
			
		||||
    esp32_camera::global_esp32_camera->request_image();
 | 
			
		||||
  }
 | 
			
		||||
  if (req.get_stream()) {
 | 
			
		||||
    esp32_camera::global_esp32_camera->request_stream();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -4,14 +4,12 @@
 | 
			
		||||
#include "esphome/core/controller.h"
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "api_pb2.h"
 | 
			
		||||
#include "api_pb2_service.h"
 | 
			
		||||
#include "util.h"
 | 
			
		||||
#include "api_message.h"
 | 
			
		||||
#include "basic_messages.h"
 | 
			
		||||
#include "list_entities.h"
 | 
			
		||||
#include "subscribe_state.h"
 | 
			
		||||
#include "subscribe_logs.h"
 | 
			
		||||
#include "command_messages.h"
 | 
			
		||||
#include "service_call_message.h"
 | 
			
		||||
#include "homeassistant_service.h"
 | 
			
		||||
#include "user_services.h"
 | 
			
		||||
 | 
			
		||||
#ifdef ARDUINO_ARCH_ESP32
 | 
			
		||||
@@ -24,130 +22,6 @@
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
class APIServer;
 | 
			
		||||
 | 
			
		||||
class APIConnection {
 | 
			
		||||
 public:
 | 
			
		||||
  APIConnection(AsyncClient *client, APIServer *parent);
 | 
			
		||||
  ~APIConnection();
 | 
			
		||||
 | 
			
		||||
  void disconnect_client();
 | 
			
		||||
  APIBuffer get_buffer();
 | 
			
		||||
  bool send_buffer(APIMessageType type);
 | 
			
		||||
  bool send_message(APIMessage &msg);
 | 
			
		||||
  bool send_empty_message(APIMessageType type);
 | 
			
		||||
  void loop();
 | 
			
		||||
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
  bool send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor, bool state);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_COVER
 | 
			
		||||
  bool send_cover_state(cover::Cover *cover);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_FAN
 | 
			
		||||
  bool send_fan_state(fan::FanState *fan);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LIGHT
 | 
			
		||||
  bool send_light_state(light::LightState *light);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
  bool send_sensor_state(sensor::Sensor *sensor, float state);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
  bool send_switch_state(switch_::Switch *a_switch, bool state);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_TEXT_SENSOR
 | 
			
		||||
  bool send_text_sensor_state(text_sensor::TextSensor *text_sensor, std::string state);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
  void send_camera_state(std::shared_ptr<esp32_camera::CameraImage> image);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_CLIMATE
 | 
			
		||||
  bool send_climate_state(climate::Climate *climate);
 | 
			
		||||
#endif
 | 
			
		||||
  bool send_log_message(int level, const char *tag, const char *line);
 | 
			
		||||
  bool send_disconnect_request();
 | 
			
		||||
  bool send_ping_request();
 | 
			
		||||
  void send_service_call(ServiceCallResponse &call);
 | 
			
		||||
#ifdef USE_HOMEASSISTANT_TIME
 | 
			
		||||
  void send_time_request();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  friend APIServer;
 | 
			
		||||
 | 
			
		||||
  void on_error_(int8_t error);
 | 
			
		||||
  void on_disconnect_();
 | 
			
		||||
  void on_timeout_(uint32_t time);
 | 
			
		||||
  void on_data_(uint8_t *buf, size_t len);
 | 
			
		||||
  void fatal_error_();
 | 
			
		||||
  bool valid_rx_message_type_(uint32_t msg_type);
 | 
			
		||||
  void read_message_(uint32_t size, uint32_t type, uint8_t *msg);
 | 
			
		||||
  void parse_recv_buffer_();
 | 
			
		||||
 | 
			
		||||
  // request types
 | 
			
		||||
  void on_hello_request_(const HelloRequest &req);
 | 
			
		||||
  void on_connect_request_(const ConnectRequest &req);
 | 
			
		||||
  void on_disconnect_request_(const DisconnectRequest &req);
 | 
			
		||||
  void on_disconnect_response_(const DisconnectResponse &req);
 | 
			
		||||
  void on_ping_request_(const PingRequest &req);
 | 
			
		||||
  void on_ping_response_(const PingResponse &req);
 | 
			
		||||
  void on_device_info_request_(const DeviceInfoRequest &req);
 | 
			
		||||
  void on_list_entities_request_(const ListEntitiesRequest &req);
 | 
			
		||||
  void on_subscribe_states_request_(const SubscribeStatesRequest &req);
 | 
			
		||||
  void on_subscribe_logs_request_(const SubscribeLogsRequest &req);
 | 
			
		||||
#ifdef USE_COVER
 | 
			
		||||
  void on_cover_command_request_(const CoverCommandRequest &req);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_FAN
 | 
			
		||||
  void on_fan_command_request_(const FanCommandRequest &req);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LIGHT
 | 
			
		||||
  void on_light_command_request_(const LightCommandRequest &req);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
  void on_switch_command_request_(const SwitchCommandRequest &req);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_CLIMATE
 | 
			
		||||
  void on_climate_command_request_(const ClimateCommandRequest &req);
 | 
			
		||||
#endif
 | 
			
		||||
  void on_subscribe_service_calls_request_(const SubscribeServiceCallsRequest &req);
 | 
			
		||||
  void on_subscribe_home_assistant_states_request_(const SubscribeHomeAssistantStatesRequest &req);
 | 
			
		||||
  void on_home_assistant_state_response_(const HomeAssistantStateResponse &req);
 | 
			
		||||
  void on_execute_service_(const ExecuteServiceRequest &req);
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
  void on_camera_image_request_(const CameraImageRequest &req);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  enum class ConnectionState {
 | 
			
		||||
    WAITING_FOR_HELLO,
 | 
			
		||||
    WAITING_FOR_CONNECT,
 | 
			
		||||
    CONNECTED,
 | 
			
		||||
  } connection_state_{ConnectionState::WAITING_FOR_HELLO};
 | 
			
		||||
 | 
			
		||||
  bool remove_{false};
 | 
			
		||||
 | 
			
		||||
  std::vector<uint8_t> send_buffer_;
 | 
			
		||||
  std::vector<uint8_t> recv_buffer_;
 | 
			
		||||
 | 
			
		||||
  std::string client_info_;
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
  esp32_camera::CameraImageReader image_reader_;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  bool state_subscription_{false};
 | 
			
		||||
  int log_subscription_{ESPHOME_LOG_LEVEL_NONE};
 | 
			
		||||
  uint32_t last_traffic_;
 | 
			
		||||
  bool sent_ping_{false};
 | 
			
		||||
  bool service_call_subscription_{false};
 | 
			
		||||
  AsyncClient *client_;
 | 
			
		||||
  APIServer *parent_;
 | 
			
		||||
  InitialStateIterator initial_state_iterator_;
 | 
			
		||||
  ListEntitiesIterator list_entities_iterator_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<typename... Ts> class HomeAssistantServiceCallAction;
 | 
			
		||||
 | 
			
		||||
class APIServer : public Component, public Controller {
 | 
			
		||||
 public:
 | 
			
		||||
  APIServer();
 | 
			
		||||
@@ -187,7 +61,7 @@ class APIServer : public Component, public Controller {
 | 
			
		||||
#ifdef USE_CLIMATE
 | 
			
		||||
  void on_climate_update(climate::Climate *obj) override;
 | 
			
		||||
#endif
 | 
			
		||||
  void send_service_call(ServiceCallResponse &call);
 | 
			
		||||
  void send_homeassistant_service_call(const HomeassistantServiceResponse &call);
 | 
			
		||||
  void register_user_service(UserServiceDescriptor *descriptor) { this->user_services_.push_back(descriptor); }
 | 
			
		||||
#ifdef USE_HOMEASSISTANT_TIME
 | 
			
		||||
  void request_time();
 | 
			
		||||
@@ -217,22 +91,6 @@ class APIServer : public Component, public Controller {
 | 
			
		||||
 | 
			
		||||
extern APIServer *global_api_server;
 | 
			
		||||
 | 
			
		||||
template<typename... Ts> class HomeAssistantServiceCallAction : public Action<Ts...> {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit HomeAssistantServiceCallAction(APIServer *parent) : parent_(parent) {}
 | 
			
		||||
  void set_service(const std::string &service) { this->resp_.set_service(service); }
 | 
			
		||||
  void set_data(const std::vector<KeyValuePair> &data) { this->resp_.set_data(data); }
 | 
			
		||||
  void set_data_template(const std::vector<KeyValuePair> &data_template) {
 | 
			
		||||
    this->resp_.set_data_template(data_template);
 | 
			
		||||
  }
 | 
			
		||||
  void set_variables(const std::vector<TemplatableKeyValuePair> &variables) { this->resp_.set_variables(variables); }
 | 
			
		||||
  void play(Ts... x) override { this->parent_->send_service_call(this->resp_); }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  APIServer *parent_;
 | 
			
		||||
  ServiceCallResponse resp_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<typename... Ts> class APIConnectedCondition : public Condition<Ts...> {
 | 
			
		||||
 public:
 | 
			
		||||
  bool check(Ts... x) override { return global_api_server->is_connected(); }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,57 +0,0 @@
 | 
			
		||||
#include "basic_messages.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
// Hello
 | 
			
		||||
bool HelloRequest::decode_length_delimited(uint32_t field_id, const uint8_t *value, size_t len) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 1:  // string client_info = 1;
 | 
			
		||||
      this->client_info_ = as_string(value, len);
 | 
			
		||||
      return true;
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
const std::string &HelloRequest::get_client_info() const { return this->client_info_; }
 | 
			
		||||
void HelloRequest::set_client_info(const std::string &client_info) { this->client_info_ = client_info; }
 | 
			
		||||
APIMessageType HelloRequest::message_type() const { return APIMessageType::HELLO_REQUEST; }
 | 
			
		||||
 | 
			
		||||
// Connect
 | 
			
		||||
bool ConnectRequest::decode_length_delimited(uint32_t field_id, const uint8_t *value, size_t len) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 1:  // string password = 1;
 | 
			
		||||
      this->password_ = as_string(value, len);
 | 
			
		||||
      return true;
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
const std::string &ConnectRequest::get_password() const { return this->password_; }
 | 
			
		||||
void ConnectRequest::set_password(const std::string &password) { this->password_ = password; }
 | 
			
		||||
APIMessageType ConnectRequest::message_type() const { return APIMessageType::CONNECT_REQUEST; }
 | 
			
		||||
 | 
			
		||||
APIMessageType DeviceInfoRequest::message_type() const { return APIMessageType::DEVICE_INFO_REQUEST; }
 | 
			
		||||
APIMessageType DisconnectRequest::message_type() const { return APIMessageType::DISCONNECT_REQUEST; }
 | 
			
		||||
bool DisconnectRequest::decode_length_delimited(uint32_t field_id, const uint8_t *value, size_t len) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 1:  // string reason = 1;
 | 
			
		||||
      this->reason_ = as_string(value, len);
 | 
			
		||||
      return true;
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
const std::string &DisconnectRequest::get_reason() const { return this->reason_; }
 | 
			
		||||
void DisconnectRequest::set_reason(const std::string &reason) { this->reason_ = reason; }
 | 
			
		||||
void DisconnectRequest::encode(APIBuffer &buffer) {
 | 
			
		||||
  // string reason = 1;
 | 
			
		||||
  buffer.encode_string(1, this->reason_);
 | 
			
		||||
}
 | 
			
		||||
APIMessageType DisconnectResponse::message_type() const { return APIMessageType::DISCONNECT_RESPONSE; }
 | 
			
		||||
APIMessageType PingRequest::message_type() const { return APIMessageType::PING_REQUEST; }
 | 
			
		||||
APIMessageType PingResponse::message_type() const { return APIMessageType::PING_RESPONSE; }
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -1,63 +0,0 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "api_message.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
class HelloRequest : public APIMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  bool decode_length_delimited(uint32_t field_id, const uint8_t *value, size_t len) override;
 | 
			
		||||
  const std::string &get_client_info() const;
 | 
			
		||||
  void set_client_info(const std::string &client_info);
 | 
			
		||||
  APIMessageType message_type() const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  std::string client_info_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ConnectRequest : public APIMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  bool decode_length_delimited(uint32_t field_id, const uint8_t *value, size_t len) override;
 | 
			
		||||
  const std::string &get_password() const;
 | 
			
		||||
  void set_password(const std::string &password);
 | 
			
		||||
  APIMessageType message_type() const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  std::string password_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class DeviceInfoRequest : public APIMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  APIMessageType message_type() const override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class DisconnectRequest : public APIMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  bool decode_length_delimited(uint32_t field_id, const uint8_t *value, size_t len) override;
 | 
			
		||||
  void encode(APIBuffer &buffer) override;
 | 
			
		||||
  APIMessageType message_type() const override;
 | 
			
		||||
  const std::string &get_reason() const;
 | 
			
		||||
  void set_reason(const std::string &reason);
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  std::string reason_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class DisconnectResponse : public APIMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  APIMessageType message_type() const override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class PingRequest : public APIMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  APIMessageType message_type() const override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class PingResponse : public APIMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  APIMessageType message_type() const override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -1,417 +0,0 @@
 | 
			
		||||
#include "command_messages.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
#ifdef USE_COVER
 | 
			
		||||
bool CoverCommandRequest::decode_varint(uint32_t field_id, uint32_t value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 2:
 | 
			
		||||
      // bool has_legacy_command = 2;
 | 
			
		||||
      this->has_legacy_command_ = value;
 | 
			
		||||
      return true;
 | 
			
		||||
    case 3:
 | 
			
		||||
      // enum LegacyCoverCommand {
 | 
			
		||||
      //   OPEN = 0;
 | 
			
		||||
      //   CLOSE = 1;
 | 
			
		||||
      //   STOP = 2;
 | 
			
		||||
      // }
 | 
			
		||||
      // LegacyCoverCommand legacy_command_ = 3;
 | 
			
		||||
      this->legacy_command_ = static_cast<LegacyCoverCommand>(value);
 | 
			
		||||
      return true;
 | 
			
		||||
    case 4:
 | 
			
		||||
      // bool has_position = 4;
 | 
			
		||||
      this->has_position_ = value;
 | 
			
		||||
      return true;
 | 
			
		||||
    case 6:
 | 
			
		||||
      // bool has_tilt = 6;
 | 
			
		||||
      this->has_tilt_ = value;
 | 
			
		||||
      return true;
 | 
			
		||||
    case 8:
 | 
			
		||||
      // bool stop = 8;
 | 
			
		||||
      this->stop_ = value;
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
bool CoverCommandRequest::decode_32bit(uint32_t field_id, uint32_t value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 1:
 | 
			
		||||
      // fixed32 key = 1;
 | 
			
		||||
      this->key_ = value;
 | 
			
		||||
      return true;
 | 
			
		||||
    case 5:
 | 
			
		||||
      // float position = 5;
 | 
			
		||||
      this->position_ = as_float(value);
 | 
			
		||||
      return true;
 | 
			
		||||
    case 7:
 | 
			
		||||
      // float tilt = 7;
 | 
			
		||||
      this->tilt_ = as_float(value);
 | 
			
		||||
      return true;
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
APIMessageType CoverCommandRequest::message_type() const { return APIMessageType ::COVER_COMMAND_REQUEST; }
 | 
			
		||||
uint32_t CoverCommandRequest::get_key() const { return this->key_; }
 | 
			
		||||
optional<LegacyCoverCommand> CoverCommandRequest::get_legacy_command() const {
 | 
			
		||||
  if (!this->has_legacy_command_)
 | 
			
		||||
    return {};
 | 
			
		||||
  return this->legacy_command_;
 | 
			
		||||
}
 | 
			
		||||
optional<float> CoverCommandRequest::get_position() const {
 | 
			
		||||
  if (!this->has_position_)
 | 
			
		||||
    return {};
 | 
			
		||||
  return this->position_;
 | 
			
		||||
}
 | 
			
		||||
optional<float> CoverCommandRequest::get_tilt() const {
 | 
			
		||||
  if (!this->has_tilt_)
 | 
			
		||||
    return {};
 | 
			
		||||
  return this->tilt_;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_FAN
 | 
			
		||||
bool FanCommandRequest::decode_varint(uint32_t field_id, uint32_t value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 2:
 | 
			
		||||
      // bool has_state = 2;
 | 
			
		||||
      this->has_state_ = value;
 | 
			
		||||
      return true;
 | 
			
		||||
    case 3:
 | 
			
		||||
      // bool state = 3;
 | 
			
		||||
      this->state_ = value;
 | 
			
		||||
      return true;
 | 
			
		||||
    case 4:
 | 
			
		||||
      // bool has_speed = 4;
 | 
			
		||||
      this->has_speed_ = value;
 | 
			
		||||
      return true;
 | 
			
		||||
    case 5:
 | 
			
		||||
      // FanSpeed speed = 5;
 | 
			
		||||
      this->speed_ = static_cast<fan::FanSpeed>(value);
 | 
			
		||||
      return true;
 | 
			
		||||
    case 6:
 | 
			
		||||
      // bool has_oscillating = 6;
 | 
			
		||||
      this->has_oscillating_ = value;
 | 
			
		||||
      return true;
 | 
			
		||||
    case 7:
 | 
			
		||||
      // bool oscillating = 7;
 | 
			
		||||
      this->oscillating_ = value;
 | 
			
		||||
      return true;
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
bool FanCommandRequest::decode_32bit(uint32_t field_id, uint32_t value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 1:
 | 
			
		||||
      // fixed32 key = 1;
 | 
			
		||||
      this->key_ = value;
 | 
			
		||||
      return true;
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
APIMessageType FanCommandRequest::message_type() const { return APIMessageType::FAN_COMMAND_REQUEST; }
 | 
			
		||||
uint32_t FanCommandRequest::get_key() const { return this->key_; }
 | 
			
		||||
optional<bool> FanCommandRequest::get_state() const {
 | 
			
		||||
  if (!this->has_state_)
 | 
			
		||||
    return {};
 | 
			
		||||
  return this->state_;
 | 
			
		||||
}
 | 
			
		||||
optional<fan::FanSpeed> FanCommandRequest::get_speed() const {
 | 
			
		||||
  if (!this->has_speed_)
 | 
			
		||||
    return {};
 | 
			
		||||
  return this->speed_;
 | 
			
		||||
}
 | 
			
		||||
optional<bool> FanCommandRequest::get_oscillating() const {
 | 
			
		||||
  if (!this->has_oscillating_)
 | 
			
		||||
    return {};
 | 
			
		||||
  return this->oscillating_;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_LIGHT
 | 
			
		||||
bool LightCommandRequest::decode_varint(uint32_t field_id, uint32_t value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 2:
 | 
			
		||||
      // bool has_state = 2;
 | 
			
		||||
      this->has_state_ = value;
 | 
			
		||||
      return true;
 | 
			
		||||
    case 3:
 | 
			
		||||
      // bool state = 3;
 | 
			
		||||
      this->state_ = value;
 | 
			
		||||
      return true;
 | 
			
		||||
    case 4:
 | 
			
		||||
      // bool has_brightness = 4;
 | 
			
		||||
      this->has_brightness_ = value;
 | 
			
		||||
      return true;
 | 
			
		||||
    case 6:
 | 
			
		||||
      // bool has_rgb = 6;
 | 
			
		||||
      this->has_rgb_ = value;
 | 
			
		||||
      return true;
 | 
			
		||||
    case 10:
 | 
			
		||||
      // bool has_white = 10;
 | 
			
		||||
      this->has_white_ = value;
 | 
			
		||||
      return true;
 | 
			
		||||
    case 12:
 | 
			
		||||
      // bool has_color_temperature = 12;
 | 
			
		||||
      this->has_color_temperature_ = value;
 | 
			
		||||
      return true;
 | 
			
		||||
    case 14:
 | 
			
		||||
      // bool has_transition_length = 14;
 | 
			
		||||
      this->has_transition_length_ = value;
 | 
			
		||||
      return true;
 | 
			
		||||
    case 15:
 | 
			
		||||
      // uint32 transition_length = 15;
 | 
			
		||||
      this->transition_length_ = value;
 | 
			
		||||
      return true;
 | 
			
		||||
    case 16:
 | 
			
		||||
      // bool has_flash_length = 16;
 | 
			
		||||
      this->has_flash_length_ = value;
 | 
			
		||||
      return true;
 | 
			
		||||
    case 17:
 | 
			
		||||
      // uint32 flash_length = 17;
 | 
			
		||||
      this->flash_length_ = value;
 | 
			
		||||
      return true;
 | 
			
		||||
    case 18:
 | 
			
		||||
      // bool has_effect = 18;
 | 
			
		||||
      this->has_effect_ = value;
 | 
			
		||||
      return true;
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
bool LightCommandRequest::decode_length_delimited(uint32_t field_id, const uint8_t *value, size_t len) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 19:
 | 
			
		||||
      // string effect = 19;
 | 
			
		||||
      this->effect_ = as_string(value, len);
 | 
			
		||||
      return true;
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
bool LightCommandRequest::decode_32bit(uint32_t field_id, uint32_t value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 1:
 | 
			
		||||
      // fixed32 key = 1;
 | 
			
		||||
      this->key_ = value;
 | 
			
		||||
      return true;
 | 
			
		||||
    case 5:
 | 
			
		||||
      // float brightness = 5;
 | 
			
		||||
      this->brightness_ = as_float(value);
 | 
			
		||||
      return true;
 | 
			
		||||
    case 7:
 | 
			
		||||
      // float red = 7;
 | 
			
		||||
      this->red_ = as_float(value);
 | 
			
		||||
      return true;
 | 
			
		||||
    case 8:
 | 
			
		||||
      // float green = 8;
 | 
			
		||||
      this->green_ = as_float(value);
 | 
			
		||||
      return true;
 | 
			
		||||
    case 9:
 | 
			
		||||
      // float blue = 9;
 | 
			
		||||
      this->blue_ = as_float(value);
 | 
			
		||||
      return true;
 | 
			
		||||
    case 11:
 | 
			
		||||
      // float white = 11;
 | 
			
		||||
      this->white_ = as_float(value);
 | 
			
		||||
      return true;
 | 
			
		||||
    case 13:
 | 
			
		||||
      // float color_temperature = 13;
 | 
			
		||||
      this->color_temperature_ = as_float(value);
 | 
			
		||||
      return true;
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
APIMessageType LightCommandRequest::message_type() const { return APIMessageType::LIGHT_COMMAND_REQUEST; }
 | 
			
		||||
uint32_t LightCommandRequest::get_key() const { return this->key_; }
 | 
			
		||||
optional<bool> LightCommandRequest::get_state() const {
 | 
			
		||||
  if (!this->has_state_)
 | 
			
		||||
    return {};
 | 
			
		||||
  return this->state_;
 | 
			
		||||
}
 | 
			
		||||
optional<float> LightCommandRequest::get_brightness() const {
 | 
			
		||||
  if (!this->has_brightness_)
 | 
			
		||||
    return {};
 | 
			
		||||
  return this->brightness_;
 | 
			
		||||
}
 | 
			
		||||
optional<float> LightCommandRequest::get_red() const {
 | 
			
		||||
  if (!this->has_rgb_)
 | 
			
		||||
    return {};
 | 
			
		||||
  return this->red_;
 | 
			
		||||
}
 | 
			
		||||
optional<float> LightCommandRequest::get_green() const {
 | 
			
		||||
  if (!this->has_rgb_)
 | 
			
		||||
    return {};
 | 
			
		||||
  return this->green_;
 | 
			
		||||
}
 | 
			
		||||
optional<float> LightCommandRequest::get_blue() const {
 | 
			
		||||
  if (!this->has_rgb_)
 | 
			
		||||
    return {};
 | 
			
		||||
  return this->blue_;
 | 
			
		||||
}
 | 
			
		||||
optional<float> LightCommandRequest::get_white() const {
 | 
			
		||||
  if (!this->has_white_)
 | 
			
		||||
    return {};
 | 
			
		||||
  return this->white_;
 | 
			
		||||
}
 | 
			
		||||
optional<float> LightCommandRequest::get_color_temperature() const {
 | 
			
		||||
  if (!this->has_color_temperature_)
 | 
			
		||||
    return {};
 | 
			
		||||
  return this->color_temperature_;
 | 
			
		||||
}
 | 
			
		||||
optional<uint32_t> LightCommandRequest::get_transition_length() const {
 | 
			
		||||
  if (!this->has_transition_length_)
 | 
			
		||||
    return {};
 | 
			
		||||
  return this->transition_length_;
 | 
			
		||||
}
 | 
			
		||||
optional<uint32_t> LightCommandRequest::get_flash_length() const {
 | 
			
		||||
  if (!this->has_flash_length_)
 | 
			
		||||
    return {};
 | 
			
		||||
  return this->flash_length_;
 | 
			
		||||
}
 | 
			
		||||
optional<std::string> LightCommandRequest::get_effect() const {
 | 
			
		||||
  if (!this->has_effect_)
 | 
			
		||||
    return {};
 | 
			
		||||
  return this->effect_;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
bool SwitchCommandRequest::decode_varint(uint32_t field_id, uint32_t value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 2:
 | 
			
		||||
      // bool state = 2;
 | 
			
		||||
      this->state_ = value;
 | 
			
		||||
      return true;
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
bool SwitchCommandRequest::decode_32bit(uint32_t field_id, uint32_t value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 1:
 | 
			
		||||
      // fixed32 key = 1;
 | 
			
		||||
      this->key_ = value;
 | 
			
		||||
      return true;
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
APIMessageType SwitchCommandRequest::message_type() const { return APIMessageType::SWITCH_COMMAND_REQUEST; }
 | 
			
		||||
uint32_t SwitchCommandRequest::get_key() const { return this->key_; }
 | 
			
		||||
bool SwitchCommandRequest::get_state() const { return this->state_; }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
bool CameraImageRequest::get_single() const { return this->single_; }
 | 
			
		||||
bool CameraImageRequest::get_stream() const { return this->stream_; }
 | 
			
		||||
bool CameraImageRequest::decode_varint(uint32_t field_id, uint32_t value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 1:
 | 
			
		||||
      // bool single = 1;
 | 
			
		||||
      this->single_ = value;
 | 
			
		||||
      return true;
 | 
			
		||||
    case 2:
 | 
			
		||||
      // bool stream = 2;
 | 
			
		||||
      this->stream_ = value;
 | 
			
		||||
      return true;
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
APIMessageType CameraImageRequest::message_type() const { return APIMessageType::CAMERA_IMAGE_REQUEST; }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_CLIMATE
 | 
			
		||||
bool ClimateCommandRequest::decode_varint(uint32_t field_id, uint32_t value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 2:
 | 
			
		||||
      // bool has_mode = 2;
 | 
			
		||||
      this->has_mode_ = value;
 | 
			
		||||
      return true;
 | 
			
		||||
    case 3:
 | 
			
		||||
      // ClimateMode mode = 3;
 | 
			
		||||
      this->mode_ = static_cast<climate::ClimateMode>(value);
 | 
			
		||||
      return true;
 | 
			
		||||
    case 4:
 | 
			
		||||
      // bool has_target_temperature = 4;
 | 
			
		||||
      this->has_target_temperature_ = value;
 | 
			
		||||
      return true;
 | 
			
		||||
    case 6:
 | 
			
		||||
      // bool has_target_temperature_low = 6;
 | 
			
		||||
      this->has_target_temperature_low_ = value;
 | 
			
		||||
      return true;
 | 
			
		||||
    case 8:
 | 
			
		||||
      // bool has_target_temperature_high = 8;
 | 
			
		||||
      this->has_target_temperature_high_ = value;
 | 
			
		||||
      return true;
 | 
			
		||||
    case 10:
 | 
			
		||||
      // bool has_away = 10;
 | 
			
		||||
      this->has_away_ = value;
 | 
			
		||||
      return true;
 | 
			
		||||
    case 11:
 | 
			
		||||
      // bool away = 11;
 | 
			
		||||
      this->away_ = value;
 | 
			
		||||
      return true;
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
bool ClimateCommandRequest::decode_32bit(uint32_t field_id, uint32_t value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 1:
 | 
			
		||||
      // fixed32 key = 1;
 | 
			
		||||
      this->key_ = value;
 | 
			
		||||
      return true;
 | 
			
		||||
    case 5:
 | 
			
		||||
      // float target_temperature = 5;
 | 
			
		||||
      this->target_temperature_ = as_float(value);
 | 
			
		||||
      return true;
 | 
			
		||||
    case 7:
 | 
			
		||||
      // float target_temperature_low = 7;
 | 
			
		||||
      this->target_temperature_low_ = as_float(value);
 | 
			
		||||
      return true;
 | 
			
		||||
    case 9:
 | 
			
		||||
      // float target_temperature_high = 9;
 | 
			
		||||
      this->target_temperature_high_ = as_float(value);
 | 
			
		||||
      return true;
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
APIMessageType ClimateCommandRequest::message_type() const { return APIMessageType::CLIMATE_COMMAND_REQUEST; }
 | 
			
		||||
uint32_t ClimateCommandRequest::get_key() const { return this->key_; }
 | 
			
		||||
optional<climate::ClimateMode> ClimateCommandRequest::get_mode() const {
 | 
			
		||||
  if (!this->has_mode_)
 | 
			
		||||
    return {};
 | 
			
		||||
  return this->mode_;
 | 
			
		||||
}
 | 
			
		||||
optional<float> ClimateCommandRequest::get_target_temperature() const {
 | 
			
		||||
  if (!this->has_target_temperature_)
 | 
			
		||||
    return {};
 | 
			
		||||
  return this->target_temperature_;
 | 
			
		||||
}
 | 
			
		||||
optional<float> ClimateCommandRequest::get_target_temperature_low() const {
 | 
			
		||||
  if (!this->has_target_temperature_low_)
 | 
			
		||||
    return {};
 | 
			
		||||
  return this->target_temperature_low_;
 | 
			
		||||
}
 | 
			
		||||
optional<float> ClimateCommandRequest::get_target_temperature_high() const {
 | 
			
		||||
  if (!this->has_target_temperature_high_)
 | 
			
		||||
    return {};
 | 
			
		||||
  return this->target_temperature_high_;
 | 
			
		||||
}
 | 
			
		||||
optional<bool> ClimateCommandRequest::get_away() const {
 | 
			
		||||
  if (!this->has_away_)
 | 
			
		||||
    return {};
 | 
			
		||||
  return this->away_;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -1,162 +0,0 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#include "api_message.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
#ifdef USE_COVER
 | 
			
		||||
enum LegacyCoverCommand {
 | 
			
		||||
  LEGACY_COVER_COMMAND_OPEN = 0,
 | 
			
		||||
  LEGACY_COVER_COMMAND_CLOSE = 1,
 | 
			
		||||
  LEGACY_COVER_COMMAND_STOP = 2,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class CoverCommandRequest : public APIMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  bool decode_varint(uint32_t field_id, uint32_t value) override;
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, uint32_t value) override;
 | 
			
		||||
  APIMessageType message_type() const override;
 | 
			
		||||
  uint32_t get_key() const;
 | 
			
		||||
  optional<LegacyCoverCommand> get_legacy_command() const;
 | 
			
		||||
  optional<float> get_position() const;
 | 
			
		||||
  optional<float> get_tilt() const;
 | 
			
		||||
  bool get_stop() const { return this->stop_; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  uint32_t key_{0};
 | 
			
		||||
  bool has_legacy_command_{false};
 | 
			
		||||
  LegacyCoverCommand legacy_command_{LEGACY_COVER_COMMAND_OPEN};
 | 
			
		||||
  bool has_position_{false};
 | 
			
		||||
  float position_{0.0f};
 | 
			
		||||
  bool has_tilt_{false};
 | 
			
		||||
  float tilt_{0.0f};
 | 
			
		||||
  bool stop_{false};
 | 
			
		||||
};
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_FAN
 | 
			
		||||
class FanCommandRequest : public APIMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  bool decode_varint(uint32_t field_id, uint32_t value) override;
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, uint32_t value) override;
 | 
			
		||||
  APIMessageType message_type() const override;
 | 
			
		||||
  uint32_t get_key() const;
 | 
			
		||||
  optional<bool> get_state() const;
 | 
			
		||||
  optional<fan::FanSpeed> get_speed() const;
 | 
			
		||||
  optional<bool> get_oscillating() const;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  uint32_t key_{0};
 | 
			
		||||
  bool has_state_{false};
 | 
			
		||||
  bool state_{false};
 | 
			
		||||
  bool has_speed_{false};
 | 
			
		||||
  fan::FanSpeed speed_{fan::FAN_SPEED_LOW};
 | 
			
		||||
  bool has_oscillating_{false};
 | 
			
		||||
  bool oscillating_{false};
 | 
			
		||||
};
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_LIGHT
 | 
			
		||||
class LightCommandRequest : public APIMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  bool decode_varint(uint32_t field_id, uint32_t value) override;
 | 
			
		||||
  bool decode_length_delimited(uint32_t field_id, const uint8_t *value, size_t len) override;
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, uint32_t value) override;
 | 
			
		||||
  APIMessageType message_type() const override;
 | 
			
		||||
  uint32_t get_key() const;
 | 
			
		||||
  optional<bool> get_state() const;
 | 
			
		||||
  optional<float> get_brightness() const;
 | 
			
		||||
  optional<float> get_red() const;
 | 
			
		||||
  optional<float> get_green() const;
 | 
			
		||||
  optional<float> get_blue() const;
 | 
			
		||||
  optional<float> get_white() const;
 | 
			
		||||
  optional<float> get_color_temperature() const;
 | 
			
		||||
  optional<uint32_t> get_transition_length() const;
 | 
			
		||||
  optional<uint32_t> get_flash_length() const;
 | 
			
		||||
  optional<std::string> get_effect() const;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  uint32_t key_{0};
 | 
			
		||||
  bool has_state_{false};
 | 
			
		||||
  bool state_{false};
 | 
			
		||||
  bool has_brightness_{false};
 | 
			
		||||
  float brightness_{0.0f};
 | 
			
		||||
  bool has_rgb_{false};
 | 
			
		||||
  float red_{0.0f};
 | 
			
		||||
  float green_{0.0f};
 | 
			
		||||
  float blue_{0.0f};
 | 
			
		||||
  bool has_white_{false};
 | 
			
		||||
  float white_{0.0f};
 | 
			
		||||
  bool has_color_temperature_{false};
 | 
			
		||||
  float color_temperature_{0.0f};
 | 
			
		||||
  bool has_transition_length_{false};
 | 
			
		||||
  uint32_t transition_length_{0};
 | 
			
		||||
  bool has_flash_length_{false};
 | 
			
		||||
  uint32_t flash_length_{0};
 | 
			
		||||
  bool has_effect_{false};
 | 
			
		||||
  std::string effect_{};
 | 
			
		||||
};
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
class SwitchCommandRequest : public APIMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  bool decode_varint(uint32_t field_id, uint32_t value) override;
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, uint32_t value) override;
 | 
			
		||||
  APIMessageType message_type() const override;
 | 
			
		||||
  uint32_t get_key() const;
 | 
			
		||||
  bool get_state() const;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  uint32_t key_{0};
 | 
			
		||||
  bool state_{false};
 | 
			
		||||
};
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
class CameraImageRequest : public APIMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  bool decode_varint(uint32_t field_id, uint32_t value) override;
 | 
			
		||||
  bool get_single() const;
 | 
			
		||||
  bool get_stream() const;
 | 
			
		||||
  APIMessageType message_type() const override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool single_{false};
 | 
			
		||||
  bool stream_{false};
 | 
			
		||||
};
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_CLIMATE
 | 
			
		||||
class ClimateCommandRequest : public APIMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  bool decode_varint(uint32_t field_id, uint32_t value) override;
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, uint32_t value) override;
 | 
			
		||||
  APIMessageType message_type() const override;
 | 
			
		||||
  uint32_t get_key() const;
 | 
			
		||||
  optional<climate::ClimateMode> get_mode() const;
 | 
			
		||||
  optional<float> get_target_temperature() const;
 | 
			
		||||
  optional<float> get_target_temperature_low() const;
 | 
			
		||||
  optional<float> get_target_temperature_high() const;
 | 
			
		||||
  optional<bool> get_away() const;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  uint32_t key_{0};
 | 
			
		||||
  bool has_mode_{false};
 | 
			
		||||
  climate::ClimateMode mode_{climate::CLIMATE_MODE_OFF};
 | 
			
		||||
  bool has_target_temperature_{false};
 | 
			
		||||
  float target_temperature_{0.0f};
 | 
			
		||||
  bool has_target_temperature_low_{false};
 | 
			
		||||
  float target_temperature_low_{0.0f};
 | 
			
		||||
  bool has_target_temperature_high_{false};
 | 
			
		||||
  float target_temperature_high_{0.0f};
 | 
			
		||||
  bool has_away_{false};
 | 
			
		||||
  bool away_{false};
 | 
			
		||||
};
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										214
									
								
								esphome/components/api/custom_api_device.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										214
									
								
								esphome/components/api/custom_api_device.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,214 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <map>
 | 
			
		||||
#include "user_services.h"
 | 
			
		||||
#include "api_server.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
template<typename T, typename... Ts> class CustomAPIDeviceService : public UserServiceBase<Ts...> {
 | 
			
		||||
 public:
 | 
			
		||||
  CustomAPIDeviceService(const std::string &name, const std::array<std::string, sizeof...(Ts)> &arg_names, T *obj,
 | 
			
		||||
                         void (T::*callback)(Ts...))
 | 
			
		||||
      : UserServiceBase<Ts...>(name, arg_names), obj_(obj), callback_(callback) {}
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  void execute(Ts... x) override { (this->obj_->*this->callback_)(x...); }  // NOLINT
 | 
			
		||||
 | 
			
		||||
  T *obj_;
 | 
			
		||||
  void (T::*callback_)(Ts...);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class CustomAPIDevice {
 | 
			
		||||
 public:
 | 
			
		||||
  /// Return if a client (such as Home Assistant) is connected to the native API.
 | 
			
		||||
  bool is_connected() const { return global_api_server->is_connected(); }
 | 
			
		||||
 | 
			
		||||
  /** Register a custom native API service that will show up in Home Assistant.
 | 
			
		||||
   *
 | 
			
		||||
   * Usage:
 | 
			
		||||
   *
 | 
			
		||||
   * ```cpp
 | 
			
		||||
   * void setup() override {
 | 
			
		||||
   *   register_service(&CustomNativeAPI::on_start_washer_cycle, "start_washer_cycle",
 | 
			
		||||
   *                    {"cycle_length"});
 | 
			
		||||
   * }
 | 
			
		||||
   *
 | 
			
		||||
   * void on_start_washer_cycle(int cycle_length) {
 | 
			
		||||
   *   // Start washer cycle.
 | 
			
		||||
   * }
 | 
			
		||||
   * ```
 | 
			
		||||
   *
 | 
			
		||||
   * @tparam T The class type creating the service, automatically deduced from the function pointer.
 | 
			
		||||
   * @tparam Ts The argument types for the service, automatically deduced from the function arguments.
 | 
			
		||||
   * @param callback The member function to call when the service is triggered.
 | 
			
		||||
   * @param name The name of the service to register.
 | 
			
		||||
   * @param arg_names The name of the arguments for the service, must match the arguments of the function.
 | 
			
		||||
   */
 | 
			
		||||
  template<typename T, typename... Ts>
 | 
			
		||||
  void register_service(void (T::*callback)(Ts...), const std::string &name,
 | 
			
		||||
                        const std::array<std::string, sizeof...(Ts)> &arg_names) {
 | 
			
		||||
    auto *service = new CustomAPIDeviceService<T, Ts...>(name, arg_names, (T *) this, callback);
 | 
			
		||||
    global_api_server->register_user_service(service);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** Register a custom native API service that will show up in Home Assistant.
 | 
			
		||||
   *
 | 
			
		||||
   * Usage:
 | 
			
		||||
   *
 | 
			
		||||
   * ```cpp
 | 
			
		||||
   * void setup() override {
 | 
			
		||||
   *   register_service(&CustomNativeAPI::on_hello_world, "hello_world");
 | 
			
		||||
   * }
 | 
			
		||||
   *
 | 
			
		||||
   * void on_hello_world() {
 | 
			
		||||
   *   // Hello World service called.
 | 
			
		||||
   * }
 | 
			
		||||
   * ```
 | 
			
		||||
   *
 | 
			
		||||
   * @tparam T The class type creating the service, automatically deduced from the function pointer.
 | 
			
		||||
   * @param callback The member function to call when the service is triggered.
 | 
			
		||||
   * @param name The name of the arguments for the service, must match the arguments of the function.
 | 
			
		||||
   */
 | 
			
		||||
  template<typename T> void register_service(void (T::*callback)(), const std::string &name) {
 | 
			
		||||
    auto *service = new CustomAPIDeviceService<T>(name, {}, (T *) this, callback);
 | 
			
		||||
    global_api_server->register_user_service(service);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** Subscribe to the state of an entity from Home Assistant.
 | 
			
		||||
   *
 | 
			
		||||
   * Usage:
 | 
			
		||||
   *
 | 
			
		||||
   * ```cpp
 | 
			
		||||
   * void setup() override {
 | 
			
		||||
   *   subscribe_homeassistant_state(&CustomNativeAPI::on_state_changed, "sensor.weather_forecast");
 | 
			
		||||
   * }
 | 
			
		||||
   *
 | 
			
		||||
   * void on_state_changed(std::string state) {
 | 
			
		||||
   *   // State of sensor.weather_forecast is `state`
 | 
			
		||||
   * }
 | 
			
		||||
   * ```
 | 
			
		||||
   *
 | 
			
		||||
   * @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 entity_id The entity_id to track.
 | 
			
		||||
   */
 | 
			
		||||
  template<typename T>
 | 
			
		||||
  void subscribe_homeassistant_state(void (T::*callback)(std::string), const std::string &entity_id) {
 | 
			
		||||
    auto f = std::bind(callback, (T *) this, std::placeholders::_1);
 | 
			
		||||
    global_api_server->subscribe_home_assistant_state(entity_id, f);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** Subscribe to the state of an entity from Home Assistant.
 | 
			
		||||
   *
 | 
			
		||||
   * Usage:
 | 
			
		||||
   *
 | 
			
		||||
   * ```cpp
 | 
			
		||||
   * void setup() override {
 | 
			
		||||
   *   subscribe_homeassistant_state(&CustomNativeAPI::on_state_changed, "sensor.weather_forecast");
 | 
			
		||||
   * }
 | 
			
		||||
   *
 | 
			
		||||
   * void on_state_changed(std::string entity_id, std::string state) {
 | 
			
		||||
   *   // State of `entity_id` is `state`
 | 
			
		||||
   * }
 | 
			
		||||
   * ```
 | 
			
		||||
   *
 | 
			
		||||
   * @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 entity_id The entity_id to track.
 | 
			
		||||
   */
 | 
			
		||||
  template<typename T>
 | 
			
		||||
  void subscribe_homeassistant_state(void (T::*callback)(std::string, std::string), const std::string &entity_id) {
 | 
			
		||||
    auto f = std::bind(callback, (T *) this, entity_id, std::placeholders::_1);
 | 
			
		||||
    global_api_server->subscribe_home_assistant_state(entity_id, f);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** Call a Home Assistant service from ESPHome.
 | 
			
		||||
   *
 | 
			
		||||
   * Usage:
 | 
			
		||||
   *
 | 
			
		||||
   * ```cpp
 | 
			
		||||
   * call_homeassistant_service("homeassistant.restart");
 | 
			
		||||
   * ```
 | 
			
		||||
   *
 | 
			
		||||
   * @param service_name The service to call.
 | 
			
		||||
   */
 | 
			
		||||
  void call_homeassistant_service(const std::string &service_name) {
 | 
			
		||||
    HomeassistantServiceResponse resp;
 | 
			
		||||
    resp.service = service_name;
 | 
			
		||||
    global_api_server->send_homeassistant_service_call(resp);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** Call a Home Assistant service from ESPHome.
 | 
			
		||||
   *
 | 
			
		||||
   * Usage:
 | 
			
		||||
   *
 | 
			
		||||
   * ```cpp
 | 
			
		||||
   * call_homeassistant_service("light.turn_on", {
 | 
			
		||||
   *   {"entity_id", "light.my_light"},
 | 
			
		||||
   *   {"brightness", "127"},
 | 
			
		||||
   * });
 | 
			
		||||
   * ```
 | 
			
		||||
   *
 | 
			
		||||
   * @param service_name The service to call.
 | 
			
		||||
   * @param data The data for the service call, mapping from string to string.
 | 
			
		||||
   */
 | 
			
		||||
  void call_homeassistant_service(const std::string &service_name, const std::map<std::string, std::string> &data) {
 | 
			
		||||
    HomeassistantServiceResponse resp;
 | 
			
		||||
    resp.service = service_name;
 | 
			
		||||
    for (auto &it : data) {
 | 
			
		||||
      HomeassistantServiceMap kv;
 | 
			
		||||
      kv.key = it.first;
 | 
			
		||||
      kv.value = it.second;
 | 
			
		||||
      resp.data.push_back(kv);
 | 
			
		||||
    }
 | 
			
		||||
    global_api_server->send_homeassistant_service_call(resp);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** Fire an ESPHome event in Home Assistant.
 | 
			
		||||
   *
 | 
			
		||||
   * Usage:
 | 
			
		||||
   *
 | 
			
		||||
   * ```cpp
 | 
			
		||||
   * fire_homeassistant_event("esphome.something_happened");
 | 
			
		||||
   * ```
 | 
			
		||||
   *
 | 
			
		||||
   * @param event_name The event to fire.
 | 
			
		||||
   */
 | 
			
		||||
  void fire_homeassistant_event(const std::string &event_name) {
 | 
			
		||||
    HomeassistantServiceResponse resp;
 | 
			
		||||
    resp.service = event_name;
 | 
			
		||||
    resp.is_event = true;
 | 
			
		||||
    global_api_server->send_homeassistant_service_call(resp);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** Fire an ESPHome event in Home Assistant.
 | 
			
		||||
   *
 | 
			
		||||
   * Usage:
 | 
			
		||||
   *
 | 
			
		||||
   * ```cpp
 | 
			
		||||
   * fire_homeassistant_event("esphome.something_happened", {
 | 
			
		||||
   *   {"my_value", "500"},
 | 
			
		||||
   * });
 | 
			
		||||
   * ```
 | 
			
		||||
   *
 | 
			
		||||
   * @param event_name The event to fire.
 | 
			
		||||
   * @param data The data for the event, mapping from string to string.
 | 
			
		||||
   */
 | 
			
		||||
  void fire_homeassistant_event(const std::string &service_name, const std::map<std::string, std::string> &data) {
 | 
			
		||||
    HomeassistantServiceResponse resp;
 | 
			
		||||
    resp.service = service_name;
 | 
			
		||||
    resp.is_event = true;
 | 
			
		||||
    for (auto &it : data) {
 | 
			
		||||
      HomeassistantServiceMap kv;
 | 
			
		||||
      kv.key = it.first;
 | 
			
		||||
      kv.value = it.second;
 | 
			
		||||
      resp.data.push_back(kv);
 | 
			
		||||
    }
 | 
			
		||||
    global_api_server->send_homeassistant_service_call(resp);
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										67
									
								
								esphome/components/api/homeassistant_service.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								esphome/components/api/homeassistant_service.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,67 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
#include "esphome/core/automation.h"
 | 
			
		||||
#include "api_pb2.h"
 | 
			
		||||
#include "api_server.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
template<typename... Ts> class TemplatableKeyValuePair {
 | 
			
		||||
 public:
 | 
			
		||||
  template<typename T> TemplatableKeyValuePair(std::string key, T value) : key(std::move(key)), value(value) {}
 | 
			
		||||
  std::string key;
 | 
			
		||||
  TemplatableStringValue<Ts...> value;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<typename... Ts> class HomeAssistantServiceCallAction : public Action<Ts...> {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit HomeAssistantServiceCallAction(APIServer *parent, bool is_event) : parent_(parent), is_event_(is_event) {}
 | 
			
		||||
 | 
			
		||||
  TEMPLATABLE_STRING_VALUE(service);
 | 
			
		||||
  template<typename T> void add_data(std::string key, T value) {
 | 
			
		||||
    this->data_.push_back(TemplatableKeyValuePair<Ts...>(key, value));
 | 
			
		||||
  }
 | 
			
		||||
  template<typename T> void add_data_template(std::string key, T value) {
 | 
			
		||||
    this->data_template_.push_back(TemplatableKeyValuePair<Ts...>(key, value));
 | 
			
		||||
  }
 | 
			
		||||
  template<typename T> void add_variable(std::string key, T value) {
 | 
			
		||||
    this->variables_.push_back(TemplatableKeyValuePair<Ts...>(key, value));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void play(Ts... x) override {
 | 
			
		||||
    HomeassistantServiceResponse resp;
 | 
			
		||||
    resp.service = this->service_.value(x...);
 | 
			
		||||
    resp.is_event = this->is_event_;
 | 
			
		||||
    for (auto &it : this->data_) {
 | 
			
		||||
      HomeassistantServiceMap kv;
 | 
			
		||||
      kv.key = it.key;
 | 
			
		||||
      kv.value = it.value.value(x...);
 | 
			
		||||
      resp.data.push_back(kv);
 | 
			
		||||
    }
 | 
			
		||||
    for (auto &it : this->data_template_) {
 | 
			
		||||
      HomeassistantServiceMap kv;
 | 
			
		||||
      kv.key = it.key;
 | 
			
		||||
      kv.value = it.value.value(x...);
 | 
			
		||||
      resp.data_template.push_back(kv);
 | 
			
		||||
    }
 | 
			
		||||
    for (auto &it : this->variables_) {
 | 
			
		||||
      HomeassistantServiceMap kv;
 | 
			
		||||
      kv.key = it.key;
 | 
			
		||||
      kv.value = it.value.value(x...);
 | 
			
		||||
      resp.variables.push_back(kv);
 | 
			
		||||
    }
 | 
			
		||||
    this->parent_->send_homeassistant_service_call(resp);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  APIServer *parent_;
 | 
			
		||||
  bool is_event_;
 | 
			
		||||
  std::vector<TemplatableKeyValuePair<Ts...>> data_;
 | 
			
		||||
  std::vector<TemplatableKeyValuePair<Ts...>> data_template_;
 | 
			
		||||
  std::vector<TemplatableKeyValuePair<Ts...>> variables_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -2,189 +2,54 @@
 | 
			
		||||
#include "esphome/core/util.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/core/application.h"
 | 
			
		||||
#include "api_connection.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
std::string get_default_unique_id(const std::string &component_type, Nameable *nameable) {
 | 
			
		||||
  return App.get_name() + component_type + nameable->get_object_id();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
bool ListEntitiesIterator::on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) {
 | 
			
		||||
  auto buffer = this->client_->get_buffer();
 | 
			
		||||
  buffer.encode_nameable(binary_sensor);
 | 
			
		||||
  // string unique_id = 4;
 | 
			
		||||
  buffer.encode_string(4, get_default_unique_id("binary_sensor", binary_sensor));
 | 
			
		||||
  // string device_class = 5;
 | 
			
		||||
  buffer.encode_string(5, binary_sensor->get_device_class());
 | 
			
		||||
  // bool is_status_binary_sensor = 6;
 | 
			
		||||
  buffer.encode_bool(6, binary_sensor->is_status_binary_sensor());
 | 
			
		||||
  return this->client_->send_buffer(APIMessageType::LIST_ENTITIES_BINARY_SENSOR_RESPONSE);
 | 
			
		||||
  return this->client_->send_binary_sensor_info(binary_sensor);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_COVER
 | 
			
		||||
bool ListEntitiesIterator::on_cover(cover::Cover *cover) {
 | 
			
		||||
  auto buffer = this->client_->get_buffer();
 | 
			
		||||
  buffer.encode_nameable(cover);
 | 
			
		||||
  // string unique_id = 4;
 | 
			
		||||
  buffer.encode_string(4, get_default_unique_id("cover", cover));
 | 
			
		||||
  auto traits = cover->get_traits();
 | 
			
		||||
 | 
			
		||||
  // bool assumed_state = 5;
 | 
			
		||||
  buffer.encode_bool(5, traits.get_is_assumed_state());
 | 
			
		||||
  // bool supports_position = 6;
 | 
			
		||||
  buffer.encode_bool(6, traits.get_supports_position());
 | 
			
		||||
  // bool supports_tilt = 7;
 | 
			
		||||
  buffer.encode_bool(7, traits.get_supports_tilt());
 | 
			
		||||
  // string device_class = 8;
 | 
			
		||||
  buffer.encode_string(8, cover->get_device_class());
 | 
			
		||||
  return this->client_->send_buffer(APIMessageType::LIST_ENTITIES_COVER_RESPONSE);
 | 
			
		||||
}
 | 
			
		||||
bool ListEntitiesIterator::on_cover(cover::Cover *cover) { return this->client_->send_cover_info(cover); }
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_FAN
 | 
			
		||||
bool ListEntitiesIterator::on_fan(fan::FanState *fan) {
 | 
			
		||||
  auto buffer = this->client_->get_buffer();
 | 
			
		||||
  buffer.encode_nameable(fan);
 | 
			
		||||
  // string unique_id = 4;
 | 
			
		||||
  buffer.encode_string(4, get_default_unique_id("fan", fan));
 | 
			
		||||
  // bool supports_oscillation = 5;
 | 
			
		||||
  buffer.encode_bool(5, fan->get_traits().supports_oscillation());
 | 
			
		||||
  // bool supports_speed = 6;
 | 
			
		||||
  buffer.encode_bool(6, fan->get_traits().supports_speed());
 | 
			
		||||
  return this->client_->send_buffer(APIMessageType::LIST_ENTITIES_FAN_RESPONSE);
 | 
			
		||||
}
 | 
			
		||||
bool ListEntitiesIterator::on_fan(fan::FanState *fan) { return this->client_->send_fan_info(fan); }
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LIGHT
 | 
			
		||||
bool ListEntitiesIterator::on_light(light::LightState *light) {
 | 
			
		||||
  auto buffer = this->client_->get_buffer();
 | 
			
		||||
  buffer.encode_nameable(light);
 | 
			
		||||
  // string unique_id = 4;
 | 
			
		||||
  buffer.encode_string(4, get_default_unique_id("light", light));
 | 
			
		||||
  // bool supports_brightness = 5;
 | 
			
		||||
  auto traits = light->get_traits();
 | 
			
		||||
  buffer.encode_bool(5, traits.get_supports_brightness());
 | 
			
		||||
  // bool supports_rgb = 6;
 | 
			
		||||
  buffer.encode_bool(6, traits.get_supports_rgb());
 | 
			
		||||
  // bool supports_white_value = 7;
 | 
			
		||||
  buffer.encode_bool(7, traits.get_supports_rgb_white_value());
 | 
			
		||||
  // bool supports_color_temperature = 8;
 | 
			
		||||
  buffer.encode_bool(8, traits.get_supports_color_temperature());
 | 
			
		||||
  if (traits.get_supports_color_temperature()) {
 | 
			
		||||
    // float min_mireds = 9;
 | 
			
		||||
    buffer.encode_float(9, traits.get_min_mireds());
 | 
			
		||||
    // float max_mireds = 10;
 | 
			
		||||
    buffer.encode_float(10, traits.get_max_mireds());
 | 
			
		||||
  }
 | 
			
		||||
  // repeated string effects = 11;
 | 
			
		||||
  if (light->supports_effects()) {
 | 
			
		||||
    buffer.encode_string(11, "None");
 | 
			
		||||
    for (auto *effect : light->get_effects()) {
 | 
			
		||||
      buffer.encode_string(11, effect->get_name());
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return this->client_->send_buffer(APIMessageType::LIST_ENTITIES_LIGHT_RESPONSE);
 | 
			
		||||
}
 | 
			
		||||
bool ListEntitiesIterator::on_light(light::LightState *light) { return this->client_->send_light_info(light); }
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
bool ListEntitiesIterator::on_sensor(sensor::Sensor *sensor) {
 | 
			
		||||
  auto buffer = this->client_->get_buffer();
 | 
			
		||||
  buffer.encode_nameable(sensor);
 | 
			
		||||
  // string unique_id = 4;
 | 
			
		||||
  std::string unique_id = sensor->unique_id();
 | 
			
		||||
  if (unique_id.empty())
 | 
			
		||||
    unique_id = get_default_unique_id("sensor", sensor);
 | 
			
		||||
  buffer.encode_string(4, unique_id);
 | 
			
		||||
  // string icon = 5;
 | 
			
		||||
  buffer.encode_string(5, sensor->get_icon());
 | 
			
		||||
  // string unit_of_measurement = 6;
 | 
			
		||||
  buffer.encode_string(6, sensor->get_unit_of_measurement());
 | 
			
		||||
  // int32 accuracy_decimals = 7;
 | 
			
		||||
  buffer.encode_int32(7, sensor->get_accuracy_decimals());
 | 
			
		||||
  return this->client_->send_buffer(APIMessageType::LIST_ENTITIES_SENSOR_RESPONSE);
 | 
			
		||||
}
 | 
			
		||||
bool ListEntitiesIterator::on_sensor(sensor::Sensor *sensor) { return this->client_->send_sensor_info(sensor); }
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
bool ListEntitiesIterator::on_switch(switch_::Switch *a_switch) {
 | 
			
		||||
  auto buffer = this->client_->get_buffer();
 | 
			
		||||
  buffer.encode_nameable(a_switch);
 | 
			
		||||
  // string unique_id = 4;
 | 
			
		||||
  buffer.encode_string(4, get_default_unique_id("switch", a_switch));
 | 
			
		||||
  // string icon = 5;
 | 
			
		||||
  buffer.encode_string(5, a_switch->get_icon());
 | 
			
		||||
  // bool assumed_state = 6;
 | 
			
		||||
  buffer.encode_bool(6, a_switch->assumed_state());
 | 
			
		||||
  return this->client_->send_buffer(APIMessageType::LIST_ENTITIES_SWITCH_RESPONSE);
 | 
			
		||||
}
 | 
			
		||||
bool ListEntitiesIterator::on_switch(switch_::Switch *a_switch) { return this->client_->send_switch_info(a_switch); }
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_TEXT_SENSOR
 | 
			
		||||
bool ListEntitiesIterator::on_text_sensor(text_sensor::TextSensor *text_sensor) {
 | 
			
		||||
  auto buffer = this->client_->get_buffer();
 | 
			
		||||
  buffer.encode_nameable(text_sensor);
 | 
			
		||||
  // string unique_id = 4;
 | 
			
		||||
  std::string unique_id = text_sensor->unique_id();
 | 
			
		||||
  if (unique_id.empty())
 | 
			
		||||
    unique_id = get_default_unique_id("text_sensor", text_sensor);
 | 
			
		||||
  buffer.encode_string(4, unique_id);
 | 
			
		||||
  // string icon = 5;
 | 
			
		||||
  buffer.encode_string(5, text_sensor->get_icon());
 | 
			
		||||
  return this->client_->send_buffer(APIMessageType::LIST_ENTITIES_TEXT_SENSOR_RESPONSE);
 | 
			
		||||
  return this->client_->send_text_sensor_info(text_sensor);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
bool ListEntitiesIterator::on_end() {
 | 
			
		||||
  return this->client_->send_empty_message(APIMessageType::LIST_ENTITIES_DONE_RESPONSE);
 | 
			
		||||
}
 | 
			
		||||
bool ListEntitiesIterator::on_end() { return this->client_->send_list_info_done(); }
 | 
			
		||||
ListEntitiesIterator::ListEntitiesIterator(APIServer *server, APIConnection *client)
 | 
			
		||||
    : ComponentIterator(server), client_(client) {}
 | 
			
		||||
bool ListEntitiesIterator::on_service(UserServiceDescriptor *service) {
 | 
			
		||||
  auto buffer = this->client_->get_buffer();
 | 
			
		||||
  service->encode_list_service_response(buffer);
 | 
			
		||||
  return this->client_->send_buffer(APIMessageType::LIST_ENTITIES_SERVICE_RESPONSE);
 | 
			
		||||
  auto resp = service->encode_list_service_response();
 | 
			
		||||
  return this->client_->send_list_entities_services_response(resp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
bool ListEntitiesIterator::on_camera(esp32_camera::ESP32Camera *camera) {
 | 
			
		||||
  auto buffer = this->client_->get_buffer();
 | 
			
		||||
  buffer.encode_nameable(camera);
 | 
			
		||||
  // string unique_id = 4;
 | 
			
		||||
  buffer.encode_string(4, get_default_unique_id("camera", camera));
 | 
			
		||||
  return this->client_->send_buffer(APIMessageType::LIST_ENTITIES_CAMERA_RESPONSE);
 | 
			
		||||
  return this->client_->send_camera_info(camera);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_CLIMATE
 | 
			
		||||
bool ListEntitiesIterator::on_climate(climate::Climate *climate) {
 | 
			
		||||
  auto buffer = this->client_->get_buffer();
 | 
			
		||||
  buffer.encode_nameable(climate);
 | 
			
		||||
  // string unique_id = 4;
 | 
			
		||||
  buffer.encode_string(4, get_default_unique_id("climate", climate));
 | 
			
		||||
 | 
			
		||||
  auto traits = climate->get_traits();
 | 
			
		||||
  // bool supports_current_temperature = 5;
 | 
			
		||||
  buffer.encode_bool(5, traits.get_supports_current_temperature());
 | 
			
		||||
  // bool supports_two_point_target_temperature = 6;
 | 
			
		||||
  buffer.encode_bool(6, traits.get_supports_two_point_target_temperature());
 | 
			
		||||
  // repeated ClimateMode supported_modes = 7;
 | 
			
		||||
  for (auto mode : {climate::CLIMATE_MODE_AUTO, climate::CLIMATE_MODE_OFF, climate::CLIMATE_MODE_COOL,
 | 
			
		||||
                    climate::CLIMATE_MODE_HEAT}) {
 | 
			
		||||
    if (traits.supports_mode(mode))
 | 
			
		||||
      buffer.encode_uint32(7, mode, true);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // float visual_min_temperature = 8;
 | 
			
		||||
  buffer.encode_float(8, traits.get_visual_min_temperature());
 | 
			
		||||
  // float visual_max_temperature = 9;
 | 
			
		||||
  buffer.encode_float(9, traits.get_visual_max_temperature());
 | 
			
		||||
  // float visual_temperature_step = 10;
 | 
			
		||||
  buffer.encode_float(10, traits.get_visual_temperature_step());
 | 
			
		||||
  // bool supports_away = 11;
 | 
			
		||||
  buffer.encode_bool(11, traits.get_supports_away());
 | 
			
		||||
  return this->client_->send_buffer(APIMessageType::LIST_ENTITIES_CLIMATE_RESPONSE);
 | 
			
		||||
}
 | 
			
		||||
bool ListEntitiesIterator::on_climate(climate::Climate *climate) { return this->client_->send_climate_info(climate); }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
APIMessageType ListEntitiesRequest::message_type() const { return APIMessageType::LIST_ENTITIES_REQUEST; }
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -2,16 +2,11 @@
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#include "api_message.h"
 | 
			
		||||
#include "util.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
class ListEntitiesRequest : public APIMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  APIMessageType message_type() const override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class APIConnection;
 | 
			
		||||
 | 
			
		||||
class ListEntitiesIterator : public ComponentIterator {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,61 +1,59 @@
 | 
			
		||||
#include "api_message.h"
 | 
			
		||||
#include "proto.h"
 | 
			
		||||
#include "util.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
static const char *TAG = "api.message";
 | 
			
		||||
static const char *TAG = "api.proto";
 | 
			
		||||
 | 
			
		||||
bool APIMessage::decode_varint(uint32_t field_id, uint32_t value) { return false; }
 | 
			
		||||
bool APIMessage::decode_length_delimited(uint32_t field_id, const uint8_t *value, size_t len) { return false; }
 | 
			
		||||
bool APIMessage::decode_32bit(uint32_t field_id, uint32_t value) { return false; }
 | 
			
		||||
void APIMessage::encode(APIBuffer &buffer) {}
 | 
			
		||||
void APIMessage::decode(const uint8_t *buffer, size_t length) {
 | 
			
		||||
void ProtoMessage::decode(const uint8_t *buffer, size_t length) {
 | 
			
		||||
  uint32_t i = 0;
 | 
			
		||||
  bool error = false;
 | 
			
		||||
  while (i < length) {
 | 
			
		||||
    uint32_t consumed;
 | 
			
		||||
    auto res = proto_decode_varuint32(&buffer[i], length - i, &consumed);
 | 
			
		||||
    auto res = ProtoVarInt::parse(&buffer[i], length - i, &consumed);
 | 
			
		||||
    if (!res.has_value()) {
 | 
			
		||||
      ESP_LOGV(TAG, "Invalid field start at %u", i);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    uint32_t field_type = (*res) & 0b111;
 | 
			
		||||
    uint32_t field_id = (*res) >> 3;
 | 
			
		||||
    uint32_t field_type = (res->as_uint32()) & 0b111;
 | 
			
		||||
    uint32_t field_id = (res->as_uint32()) >> 3;
 | 
			
		||||
    i += consumed;
 | 
			
		||||
 | 
			
		||||
    switch (field_type) {
 | 
			
		||||
      case 0: {  // VarInt
 | 
			
		||||
        res = proto_decode_varuint32(&buffer[i], length - i, &consumed);
 | 
			
		||||
        res = ProtoVarInt::parse(&buffer[i], length - i, &consumed);
 | 
			
		||||
        if (!res.has_value()) {
 | 
			
		||||
          ESP_LOGV(TAG, "Invalid VarInt at %u", i);
 | 
			
		||||
          error = true;
 | 
			
		||||
          break;
 | 
			
		||||
        }
 | 
			
		||||
        if (!this->decode_varint(field_id, *res)) {
 | 
			
		||||
          ESP_LOGV(TAG, "Cannot decode VarInt field %u with value %u!", field_id, *res);
 | 
			
		||||
          ESP_LOGV(TAG, "Cannot decode VarInt field %u with value %u!", field_id, res->as_uint32());
 | 
			
		||||
        }
 | 
			
		||||
        i += consumed;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      case 2: {  // Length-delimited
 | 
			
		||||
        res = proto_decode_varuint32(&buffer[i], length - i, &consumed);
 | 
			
		||||
        res = ProtoVarInt::parse(&buffer[i], length - i, &consumed);
 | 
			
		||||
        if (!res.has_value()) {
 | 
			
		||||
          ESP_LOGV(TAG, "Invalid Length Delimited at %u", i);
 | 
			
		||||
          error = true;
 | 
			
		||||
          break;
 | 
			
		||||
        }
 | 
			
		||||
        uint32_t field_length = res->as_uint32();
 | 
			
		||||
        i += consumed;
 | 
			
		||||
        if (*res > length - i) {
 | 
			
		||||
        if (field_length > length - i) {
 | 
			
		||||
          ESP_LOGV(TAG, "Out-of-bounds Length Delimited at %u", i);
 | 
			
		||||
          error = true;
 | 
			
		||||
          break;
 | 
			
		||||
        }
 | 
			
		||||
        if (!this->decode_length_delimited(field_id, &buffer[i], *res)) {
 | 
			
		||||
        if (!this->decode_length(field_id, ProtoLengthDelimited(&buffer[i], field_length))) {
 | 
			
		||||
          ESP_LOGV(TAG, "Cannot decode Length Delimited field %u!", field_id);
 | 
			
		||||
        }
 | 
			
		||||
        i += *res;
 | 
			
		||||
        i += field_length;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      case 5: {  // 32-bit
 | 
			
		||||
@@ -64,9 +62,8 @@ void APIMessage::decode(const uint8_t *buffer, size_t length) {
 | 
			
		||||
          error = true;
 | 
			
		||||
          break;
 | 
			
		||||
        }
 | 
			
		||||
        uint32_t val = (uint32_t(buffer[i]) << 0) | (uint32_t(buffer[i + 1]) << 8) | (uint32_t(buffer[i + 2]) << 16) |
 | 
			
		||||
                       (uint32_t(buffer[i + 3]) << 24);
 | 
			
		||||
        if (!this->decode_32bit(field_id, val)) {
 | 
			
		||||
        uint32_t val = encode_uint32(buffer[i + 3], buffer[i + 2], buffer[i + 1], buffer[i]);
 | 
			
		||||
        if (!this->decode_32bit(field_id, Proto32Bit(val))) {
 | 
			
		||||
          ESP_LOGV(TAG, "Cannot decode 32-bit field %u with value %u!", field_id, val);
 | 
			
		||||
        }
 | 
			
		||||
        i += 4;
 | 
			
		||||
@@ -83,5 +80,11 @@ void APIMessage::decode(const uint8_t *buffer, size_t length) {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string ProtoMessage::dump() const {
 | 
			
		||||
  std::string out;
 | 
			
		||||
  this->dump_to(out);
 | 
			
		||||
  return out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										278
									
								
								esphome/components/api/proto.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										278
									
								
								esphome/components/api/proto.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,278 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
/// Representation of a VarInt - in ProtoBuf should be 64bit but we only use 32bit
 | 
			
		||||
class ProtoVarInt {
 | 
			
		||||
 public:
 | 
			
		||||
  ProtoVarInt() : value_(0) {}
 | 
			
		||||
  explicit ProtoVarInt(uint64_t value) : value_(value) {}
 | 
			
		||||
 | 
			
		||||
  static optional<ProtoVarInt> parse(const uint8_t *buffer, uint32_t len, uint32_t *consumed) {
 | 
			
		||||
    if (consumed != nullptr)
 | 
			
		||||
      *consumed = 0;
 | 
			
		||||
 | 
			
		||||
    if (len == 0)
 | 
			
		||||
      return {};
 | 
			
		||||
 | 
			
		||||
    uint64_t result = 0;
 | 
			
		||||
    uint8_t bitpos = 0;
 | 
			
		||||
 | 
			
		||||
    for (uint32_t i = 0; i < len; i++) {
 | 
			
		||||
      uint8_t val = buffer[i];
 | 
			
		||||
      result |= uint64_t(val & 0x7F) << uint64_t(bitpos);
 | 
			
		||||
      bitpos += 7;
 | 
			
		||||
      if ((val & 0x80) == 0) {
 | 
			
		||||
        if (consumed != nullptr)
 | 
			
		||||
          *consumed = i + 1;
 | 
			
		||||
        return ProtoVarInt(result);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {};
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint32_t as_uint32() const { return this->value_; }
 | 
			
		||||
  uint64_t as_uint64() const { return this->value_; }
 | 
			
		||||
  bool as_bool() const { return this->value_; }
 | 
			
		||||
  template<typename T> T as_enum() const { return static_cast<T>(this->as_uint32()); }
 | 
			
		||||
  int32_t as_int32() const {
 | 
			
		||||
    // Not ZigZag encoded
 | 
			
		||||
    return static_cast<int32_t>(this->as_int64());
 | 
			
		||||
  }
 | 
			
		||||
  int64_t as_int64() const {
 | 
			
		||||
    // Not ZigZag encoded
 | 
			
		||||
    return static_cast<int64_t>(this->value_);
 | 
			
		||||
  }
 | 
			
		||||
  int32_t as_sint32() const {
 | 
			
		||||
    // with ZigZag encoding
 | 
			
		||||
    if (this->value_ & 1)
 | 
			
		||||
      return static_cast<int32_t>(~(this->value_ >> 1));
 | 
			
		||||
    else
 | 
			
		||||
      return static_cast<int32_t>(this->value_ >> 1);
 | 
			
		||||
  }
 | 
			
		||||
  int64_t as_sint64() const {
 | 
			
		||||
    // with ZigZag encoding
 | 
			
		||||
    if (this->value_ & 1)
 | 
			
		||||
      return static_cast<int64_t>(~(this->value_ >> 1));
 | 
			
		||||
    else
 | 
			
		||||
      return static_cast<int64_t>(this->value_ >> 1);
 | 
			
		||||
  }
 | 
			
		||||
  void encode(std::vector<uint8_t> &out) {
 | 
			
		||||
    uint32_t val = this->value_;
 | 
			
		||||
    if (val <= 0x7F) {
 | 
			
		||||
      out.push_back(val);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    while (val) {
 | 
			
		||||
      uint8_t temp = val & 0x7F;
 | 
			
		||||
      val >>= 7;
 | 
			
		||||
      if (val) {
 | 
			
		||||
        out.push_back(temp | 0x80);
 | 
			
		||||
      } else {
 | 
			
		||||
        out.push_back(temp);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  uint64_t value_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ProtoLengthDelimited {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit ProtoLengthDelimited(const uint8_t *value, size_t length) : value_(value), length_(length) {}
 | 
			
		||||
  std::string as_string() const { return std::string(reinterpret_cast<const char *>(this->value_), this->length_); }
 | 
			
		||||
  template<class C> C as_message() const {
 | 
			
		||||
    auto msg = C();
 | 
			
		||||
    msg.decode(this->value_, this->length_);
 | 
			
		||||
    return msg;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  const uint8_t *const value_;
 | 
			
		||||
  const size_t length_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class Proto32Bit {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit Proto32Bit(uint32_t value) : value_(value) {}
 | 
			
		||||
  uint32_t as_fixed32() const { return this->value_; }
 | 
			
		||||
  int32_t as_sfixed32() const { return static_cast<int32_t>(this->value_); }
 | 
			
		||||
  float as_float() const {
 | 
			
		||||
    union {
 | 
			
		||||
      uint32_t raw;
 | 
			
		||||
      float value;
 | 
			
		||||
    } s{};
 | 
			
		||||
    s.raw = this->value_;
 | 
			
		||||
    return s.value;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  const uint32_t value_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class Proto64Bit {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit Proto64Bit(uint64_t value) : value_(value) {}
 | 
			
		||||
  uint64_t as_fixed64() const { return this->value_; }
 | 
			
		||||
  int64_t as_sfixed64() const { return static_cast<int64_t>(this->value_); }
 | 
			
		||||
  double as_double() const {
 | 
			
		||||
    union {
 | 
			
		||||
      uint64_t raw;
 | 
			
		||||
      double value;
 | 
			
		||||
    } s{};
 | 
			
		||||
    s.raw = this->value_;
 | 
			
		||||
    return s.value;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  const uint64_t value_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ProtoWriteBuffer {
 | 
			
		||||
 public:
 | 
			
		||||
  ProtoWriteBuffer(std::vector<uint8_t> *buffer) : buffer_(buffer) {}
 | 
			
		||||
  void write(uint8_t value) { this->buffer_->push_back(value); }
 | 
			
		||||
  void encode_varint_raw(ProtoVarInt value) { value.encode(*this->buffer_); }
 | 
			
		||||
  void encode_varint_raw(uint32_t value) { this->encode_varint_raw(ProtoVarInt(value)); }
 | 
			
		||||
  void encode_field_raw(uint32_t field_id, uint32_t type) {
 | 
			
		||||
    uint32_t val = (field_id << 3) | (type & 0b111);
 | 
			
		||||
    this->encode_varint_raw(val);
 | 
			
		||||
  }
 | 
			
		||||
  void encode_string(uint32_t field_id, const char *string, size_t len, bool force = false) {
 | 
			
		||||
    if (len == 0 && !force)
 | 
			
		||||
      return;
 | 
			
		||||
 | 
			
		||||
    this->encode_field_raw(field_id, 2);
 | 
			
		||||
    this->encode_varint_raw(len);
 | 
			
		||||
    auto *data = reinterpret_cast<const uint8_t *>(string);
 | 
			
		||||
    for (size_t i = 0; i < len; i++)
 | 
			
		||||
      this->write(data[i]);
 | 
			
		||||
  }
 | 
			
		||||
  void encode_string(uint32_t field_id, const std::string &value, bool force = false) {
 | 
			
		||||
    this->encode_string(field_id, value.data(), value.size());
 | 
			
		||||
  }
 | 
			
		||||
  void encode_bytes(uint32_t field_id, const uint8_t *data, size_t len, bool force = false) {
 | 
			
		||||
    this->encode_string(field_id, reinterpret_cast<const char *>(data), len, force);
 | 
			
		||||
  }
 | 
			
		||||
  void encode_uint32(uint32_t field_id, uint32_t value, bool force = false) {
 | 
			
		||||
    if (value == 0 && !force)
 | 
			
		||||
      return;
 | 
			
		||||
    this->encode_field_raw(field_id, 0);
 | 
			
		||||
    this->encode_varint_raw(value);
 | 
			
		||||
  }
 | 
			
		||||
  void encode_uint64(uint32_t field_id, uint64_t value, bool force = false) {
 | 
			
		||||
    if (value == 0 && !force)
 | 
			
		||||
      return;
 | 
			
		||||
    this->encode_field_raw(field_id, 0);
 | 
			
		||||
    this->encode_varint_raw(ProtoVarInt(value));
 | 
			
		||||
  }
 | 
			
		||||
  void encode_bool(uint32_t field_id, bool value, bool force = false) {
 | 
			
		||||
    if (!value && !force)
 | 
			
		||||
      return;
 | 
			
		||||
    this->encode_field_raw(field_id, 0);
 | 
			
		||||
    this->write(0x01);
 | 
			
		||||
  }
 | 
			
		||||
  void encode_fixed32(uint32_t field_id, uint32_t value, bool force = false) {
 | 
			
		||||
    if (value == 0 && !force)
 | 
			
		||||
      return;
 | 
			
		||||
 | 
			
		||||
    this->encode_field_raw(field_id, 5);
 | 
			
		||||
    this->write((value >> 0) & 0xFF);
 | 
			
		||||
    this->write((value >> 8) & 0xFF);
 | 
			
		||||
    this->write((value >> 16) & 0xFF);
 | 
			
		||||
    this->write((value >> 24) & 0xFF);
 | 
			
		||||
  }
 | 
			
		||||
  template<typename T> void encode_enum(uint32_t field_id, T value, bool force = false) {
 | 
			
		||||
    this->encode_uint32(field_id, static_cast<uint32_t>(value), force);
 | 
			
		||||
  }
 | 
			
		||||
  void encode_float(uint32_t field_id, float value, bool force = false) {
 | 
			
		||||
    if (value == 0.0f && !force)
 | 
			
		||||
      return;
 | 
			
		||||
 | 
			
		||||
    union {
 | 
			
		||||
      float value;
 | 
			
		||||
      uint32_t raw;
 | 
			
		||||
    } val{};
 | 
			
		||||
    val.value = value;
 | 
			
		||||
    this->encode_fixed32(field_id, val.raw);
 | 
			
		||||
  }
 | 
			
		||||
  void encode_int32(uint32_t field_id, int32_t value, bool force = false) {
 | 
			
		||||
    if (value < 0) {
 | 
			
		||||
      // negative int32 is always 10 byte long
 | 
			
		||||
      this->encode_int64(field_id, value, force);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    this->encode_uint32(field_id, static_cast<uint32_t>(value), force);
 | 
			
		||||
  }
 | 
			
		||||
  void encode_int64(uint32_t field_id, int64_t value, bool force = false) {
 | 
			
		||||
    this->encode_uint64(field_id, static_cast<uint64_t>(value), force);
 | 
			
		||||
  }
 | 
			
		||||
  void encode_sint32(uint32_t field_id, int32_t value, bool force = false) {
 | 
			
		||||
    uint32_t uvalue;
 | 
			
		||||
    if (value < 0)
 | 
			
		||||
      uvalue = ~(value << 1);
 | 
			
		||||
    else
 | 
			
		||||
      uvalue = value << 1;
 | 
			
		||||
    this->encode_uint32(field_id, uvalue, force);
 | 
			
		||||
  }
 | 
			
		||||
  template<class C> void encode_message(uint32_t field_id, const C &value, bool force = false) {
 | 
			
		||||
    this->encode_field_raw(field_id, 2);
 | 
			
		||||
    size_t begin = this->buffer_->size();
 | 
			
		||||
 | 
			
		||||
    value.encode(*this);
 | 
			
		||||
 | 
			
		||||
    const uint32_t nested_length = this->buffer_->size() - begin;
 | 
			
		||||
    // add size varint
 | 
			
		||||
    std::vector<uint8_t> var;
 | 
			
		||||
    ProtoVarInt(nested_length).encode(var);
 | 
			
		||||
    this->buffer_->insert(this->buffer_->begin() + begin, var.begin(), var.end());
 | 
			
		||||
  }
 | 
			
		||||
  std::vector<uint8_t> *get_buffer() const { return buffer_; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  std::vector<uint8_t> *buffer_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  virtual void encode(ProtoWriteBuffer buffer) const = 0;
 | 
			
		||||
  void decode(const uint8_t *buffer, size_t length);
 | 
			
		||||
  std::string dump() const;
 | 
			
		||||
  virtual void dump_to(std::string &out) const = 0;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  virtual bool decode_varint(uint32_t field_id, ProtoVarInt value) { return false; }
 | 
			
		||||
  virtual bool decode_length(uint32_t field_id, ProtoLengthDelimited value) { return false; }
 | 
			
		||||
  virtual bool decode_32bit(uint32_t field_id, Proto32Bit value) { return false; }
 | 
			
		||||
  virtual bool decode_64bit(uint32_t field_id, Proto64Bit value) { return false; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<typename T> const char *proto_enum_to_string(T value);
 | 
			
		||||
 | 
			
		||||
class ProtoService {
 | 
			
		||||
 public:
 | 
			
		||||
 protected:
 | 
			
		||||
  virtual bool is_authenticated() = 0;
 | 
			
		||||
  virtual bool is_connection_setup() = 0;
 | 
			
		||||
  virtual void on_fatal_error() = 0;
 | 
			
		||||
  virtual void on_unauthenticated_access() = 0;
 | 
			
		||||
  virtual void on_no_setup_connection() = 0;
 | 
			
		||||
  virtual ProtoWriteBuffer create_buffer() = 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;
 | 
			
		||||
 | 
			
		||||
  template<class C> bool send_message_(const C &msg, uint32_t message_type) {
 | 
			
		||||
    auto buffer = this->create_buffer();
 | 
			
		||||
    msg.encode(buffer);
 | 
			
		||||
    return this->send_buffer(buffer, message_type);
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -1,49 +0,0 @@
 | 
			
		||||
#include "service_call_message.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
APIMessageType SubscribeServiceCallsRequest::message_type() const {
 | 
			
		||||
  return APIMessageType::SUBSCRIBE_SERVICE_CALLS_REQUEST;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
APIMessageType ServiceCallResponse::message_type() const { return APIMessageType::SERVICE_CALL_RESPONSE; }
 | 
			
		||||
void ServiceCallResponse::encode(APIBuffer &buffer) {
 | 
			
		||||
  // string service = 1;
 | 
			
		||||
  buffer.encode_string(1, this->service_);
 | 
			
		||||
  // map<string, string> data = 2;
 | 
			
		||||
  for (auto &it : this->data_) {
 | 
			
		||||
    auto nested = buffer.begin_nested(2);
 | 
			
		||||
    buffer.encode_string(1, it.key);
 | 
			
		||||
    buffer.encode_string(2, it.value);
 | 
			
		||||
    buffer.end_nested(nested);
 | 
			
		||||
  }
 | 
			
		||||
  // map<string, string> data_template = 3;
 | 
			
		||||
  for (auto &it : this->data_template_) {
 | 
			
		||||
    auto nested = buffer.begin_nested(3);
 | 
			
		||||
    buffer.encode_string(1, it.key);
 | 
			
		||||
    buffer.encode_string(2, it.value);
 | 
			
		||||
    buffer.end_nested(nested);
 | 
			
		||||
  }
 | 
			
		||||
  // map<string, string> variables = 4;
 | 
			
		||||
  for (auto &it : this->variables_) {
 | 
			
		||||
    auto nested = buffer.begin_nested(4);
 | 
			
		||||
    buffer.encode_string(1, it.key);
 | 
			
		||||
    buffer.encode_string(2, it.value());
 | 
			
		||||
    buffer.end_nested(nested);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void ServiceCallResponse::set_service(const std::string &service) { this->service_ = service; }
 | 
			
		||||
void ServiceCallResponse::set_data(const std::vector<KeyValuePair> &data) { this->data_ = data; }
 | 
			
		||||
void ServiceCallResponse::set_data_template(const std::vector<KeyValuePair> &data_template) {
 | 
			
		||||
  this->data_template_ = data_template;
 | 
			
		||||
}
 | 
			
		||||
void ServiceCallResponse::set_variables(const std::vector<TemplatableKeyValuePair> &variables) {
 | 
			
		||||
  this->variables_ = variables;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
KeyValuePair::KeyValuePair(const std::string &key, const std::string &value) : key(key), value(value) {}
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -1,53 +0,0 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
#include "esphome/core/automation.h"
 | 
			
		||||
#include "api_message.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
class SubscribeServiceCallsRequest : public APIMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  APIMessageType message_type() const override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class KeyValuePair {
 | 
			
		||||
 public:
 | 
			
		||||
  KeyValuePair(const std::string &key, const std::string &value);
 | 
			
		||||
 | 
			
		||||
  std::string key;
 | 
			
		||||
  std::string value;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class TemplatableKeyValuePair {
 | 
			
		||||
 public:
 | 
			
		||||
  template<typename T> TemplatableKeyValuePair(std::string key, T func);
 | 
			
		||||
 | 
			
		||||
  std::string key;
 | 
			
		||||
  std::function<std::string()> value;
 | 
			
		||||
};
 | 
			
		||||
template<typename T> TemplatableKeyValuePair::TemplatableKeyValuePair(std::string key, T func) : key(key) {
 | 
			
		||||
  this->value = [func]() -> std::string { return to_string(func()); };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class ServiceCallResponse : public APIMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  APIMessageType message_type() const override;
 | 
			
		||||
 | 
			
		||||
  void encode(APIBuffer &buffer) override;
 | 
			
		||||
 | 
			
		||||
  void set_service(const std::string &service);
 | 
			
		||||
  void set_data(const std::vector<KeyValuePair> &data);
 | 
			
		||||
  void set_data_template(const std::vector<KeyValuePair> &data_template);
 | 
			
		||||
  void set_variables(const std::vector<TemplatableKeyValuePair> &variables);
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  std::string service_;
 | 
			
		||||
  std::vector<KeyValuePair> data_;
 | 
			
		||||
  std::vector<KeyValuePair> data_template_;
 | 
			
		||||
  std::vector<TemplatableKeyValuePair> variables_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -1,26 +0,0 @@
 | 
			
		||||
#include "subscribe_logs.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
APIMessageType SubscribeLogsRequest::message_type() const { return APIMessageType::SUBSCRIBE_LOGS_REQUEST; }
 | 
			
		||||
bool SubscribeLogsRequest::decode_varint(uint32_t field_id, uint32_t value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 1:  // LogLevel level = 1;
 | 
			
		||||
      this->level_ = value;
 | 
			
		||||
      return true;
 | 
			
		||||
    case 2:  // bool dump_config = 2;
 | 
			
		||||
      this->dump_config_ = value;
 | 
			
		||||
      return true;
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
uint32_t SubscribeLogsRequest::get_level() const { return this->level_; }
 | 
			
		||||
void SubscribeLogsRequest::set_level(uint32_t level) { this->level_ = level; }
 | 
			
		||||
bool SubscribeLogsRequest::get_dump_config() const { return this->dump_config_; }
 | 
			
		||||
void SubscribeLogsRequest::set_dump_config(bool dump_config) { this->dump_config_ = dump_config; }
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -1,24 +0,0 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "api_message.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
class SubscribeLogsRequest : public APIMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  bool decode_varint(uint32_t field_id, uint32_t value) override;
 | 
			
		||||
  APIMessageType message_type() const override;
 | 
			
		||||
  uint32_t get_level() const;
 | 
			
		||||
  void set_level(uint32_t level);
 | 
			
		||||
  bool get_dump_config() const;
 | 
			
		||||
  void set_dump_config(bool dump_config);
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  uint32_t level_{6};
 | 
			
		||||
  bool dump_config_{false};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
#include "subscribe_state.h"
 | 
			
		||||
#include "api_connection.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
@@ -6,9 +7,6 @@ namespace api {
 | 
			
		||||
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
bool InitialStateIterator::on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) {
 | 
			
		||||
  if (!binary_sensor->has_state())
 | 
			
		||||
    return true;
 | 
			
		||||
 | 
			
		||||
  return this->client_->send_binary_sensor_state(binary_sensor, binary_sensor->state);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
@@ -23,9 +21,6 @@ bool InitialStateIterator::on_light(light::LightState *light) { return this->cli
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
bool InitialStateIterator::on_sensor(sensor::Sensor *sensor) {
 | 
			
		||||
  if (!sensor->has_state())
 | 
			
		||||
    return true;
 | 
			
		||||
 | 
			
		||||
  return this->client_->send_sensor_state(sensor, sensor->state);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
@@ -36,9 +31,6 @@ bool InitialStateIterator::on_switch(switch_::Switch *a_switch) {
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_TEXT_SENSOR
 | 
			
		||||
bool InitialStateIterator::on_text_sensor(text_sensor::TextSensor *text_sensor) {
 | 
			
		||||
  if (!text_sensor->has_state())
 | 
			
		||||
    return true;
 | 
			
		||||
 | 
			
		||||
  return this->client_->send_text_sensor_state(text_sensor, text_sensor->state);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
@@ -48,30 +40,5 @@ bool InitialStateIterator::on_climate(climate::Climate *climate) { return this->
 | 
			
		||||
InitialStateIterator::InitialStateIterator(APIServer *server, APIConnection *client)
 | 
			
		||||
    : ComponentIterator(server), client_(client) {}
 | 
			
		||||
 | 
			
		||||
APIMessageType SubscribeStatesRequest::message_type() const { return APIMessageType::SUBSCRIBE_STATES_REQUEST; }
 | 
			
		||||
 | 
			
		||||
bool HomeAssistantStateResponse::decode_length_delimited(uint32_t field_id, const uint8_t *value, size_t len) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 1:
 | 
			
		||||
      // string entity_id = 1;
 | 
			
		||||
      this->entity_id_ = as_string(value, len);
 | 
			
		||||
      return true;
 | 
			
		||||
    case 2:
 | 
			
		||||
      // string state = 2;
 | 
			
		||||
      this->state_ = as_string(value, len);
 | 
			
		||||
      return true;
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
APIMessageType HomeAssistantStateResponse::message_type() const {
 | 
			
		||||
  return APIMessageType::HOME_ASSISTANT_STATE_RESPONSE;
 | 
			
		||||
}
 | 
			
		||||
const std::string &HomeAssistantStateResponse::get_entity_id() const { return this->entity_id_; }
 | 
			
		||||
const std::string &HomeAssistantStateResponse::get_state() const { return this->state_; }
 | 
			
		||||
APIMessageType SubscribeHomeAssistantStatesRequest::message_type() const {
 | 
			
		||||
  return APIMessageType::SUBSCRIBE_HOME_ASSISTANT_STATES_REQUEST;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -4,16 +4,10 @@
 | 
			
		||||
#include "esphome/core/controller.h"
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#include "util.h"
 | 
			
		||||
#include "api_message.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
class SubscribeStatesRequest : public APIMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  APIMessageType message_type() const override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class APIConnection;
 | 
			
		||||
 | 
			
		||||
class InitialStateIterator : public ComponentIterator {
 | 
			
		||||
@@ -47,23 +41,6 @@ class InitialStateIterator : public ComponentIterator {
 | 
			
		||||
  APIConnection *client_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class SubscribeHomeAssistantStatesRequest : public APIMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  APIMessageType message_type() const override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class HomeAssistantStateResponse : public APIMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  bool decode_length_delimited(uint32_t field_id, const uint8_t *value, size_t len) override;
 | 
			
		||||
  APIMessageType message_type() const override;
 | 
			
		||||
  const std::string &get_entity_id() const;
 | 
			
		||||
  const std::string &get_state() const;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  std::string entity_id_;
 | 
			
		||||
  std::string state_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -4,71 +4,39 @@
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
template<> bool ExecuteServiceArgument::get_value<bool>() { return this->value_bool_; }
 | 
			
		||||
template<> int ExecuteServiceArgument::get_value<int>() { return this->value_int_; }
 | 
			
		||||
template<> float ExecuteServiceArgument::get_value<float>() { return this->value_float_; }
 | 
			
		||||
template<> std::string ExecuteServiceArgument::get_value<std::string>() { return this->value_string_; }
 | 
			
		||||
 | 
			
		||||
APIMessageType ExecuteServiceArgument::message_type() const { return APIMessageType::EXECUTE_SERVICE_REQUEST; }
 | 
			
		||||
bool ExecuteServiceArgument::decode_varint(uint32_t field_id, uint32_t value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 1:  // bool bool_ = 1;
 | 
			
		||||
      this->value_bool_ = value;
 | 
			
		||||
      return true;
 | 
			
		||||
    case 2:  // int32 int_ = 2;
 | 
			
		||||
      this->value_int_ = value;
 | 
			
		||||
      return true;
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
template<> bool get_execute_arg_value<bool>(const ExecuteServiceArgument &arg) { return arg.bool_; }
 | 
			
		||||
template<> int get_execute_arg_value<int>(const ExecuteServiceArgument &arg) {
 | 
			
		||||
  if (arg.legacy_int != 0)
 | 
			
		||||
    return arg.legacy_int;
 | 
			
		||||
  return arg.int_;
 | 
			
		||||
}
 | 
			
		||||
bool ExecuteServiceArgument::decode_32bit(uint32_t field_id, uint32_t value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 3:  // float float_ = 3;
 | 
			
		||||
      this->value_float_ = as_float(value);
 | 
			
		||||
      return true;
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
template<> float get_execute_arg_value<float>(const ExecuteServiceArgument &arg) { return arg.float_; }
 | 
			
		||||
template<> std::string get_execute_arg_value<std::string>(const ExecuteServiceArgument &arg) { return arg.string_; }
 | 
			
		||||
template<> std::vector<bool> get_execute_arg_value<std::vector<bool>>(const ExecuteServiceArgument &arg) {
 | 
			
		||||
  return arg.bool_array;
 | 
			
		||||
}
 | 
			
		||||
bool ExecuteServiceArgument::decode_length_delimited(uint32_t field_id, const uint8_t *value, size_t len) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 4:  // string string_ = 4;
 | 
			
		||||
      this->value_string_ = as_string(value, len);
 | 
			
		||||
      return true;
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
template<> std::vector<int> get_execute_arg_value<std::vector<int>>(const ExecuteServiceArgument &arg) {
 | 
			
		||||
  return arg.int_array;
 | 
			
		||||
}
 | 
			
		||||
template<> std::vector<float> get_execute_arg_value<std::vector<float>>(const ExecuteServiceArgument &arg) {
 | 
			
		||||
  return arg.float_array;
 | 
			
		||||
}
 | 
			
		||||
template<> std::vector<std::string> get_execute_arg_value<std::vector<std::string>>(const ExecuteServiceArgument &arg) {
 | 
			
		||||
  return arg.string_array;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ExecuteServiceRequest::decode_32bit(uint32_t field_id, uint32_t value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 1:  // fixed32 key = 1;
 | 
			
		||||
      this->key_ = value;
 | 
			
		||||
      return true;
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
template<> enums::ServiceArgType to_service_arg_type<bool>() { return enums::SERVICE_ARG_TYPE_BOOL; }
 | 
			
		||||
template<> enums::ServiceArgType to_service_arg_type<int>() { return enums::SERVICE_ARG_TYPE_INT; }
 | 
			
		||||
template<> enums::ServiceArgType to_service_arg_type<float>() { return enums::SERVICE_ARG_TYPE_FLOAT; }
 | 
			
		||||
template<> enums::ServiceArgType to_service_arg_type<std::string>() { return enums::SERVICE_ARG_TYPE_STRING; }
 | 
			
		||||
template<> enums::ServiceArgType to_service_arg_type<std::vector<bool>>() { return enums::SERVICE_ARG_TYPE_BOOL_ARRAY; }
 | 
			
		||||
template<> enums::ServiceArgType to_service_arg_type<std::vector<int>>() { return enums::SERVICE_ARG_TYPE_INT_ARRAY; }
 | 
			
		||||
template<> enums::ServiceArgType to_service_arg_type<std::vector<float>>() {
 | 
			
		||||
  return enums::SERVICE_ARG_TYPE_FLOAT_ARRAY;
 | 
			
		||||
}
 | 
			
		||||
bool ExecuteServiceRequest::decode_length_delimited(uint32_t field_id, const uint8_t *value, size_t len) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 2: {  // repeated ExecuteServiceArgument args = 2;
 | 
			
		||||
      ExecuteServiceArgument arg;
 | 
			
		||||
      arg.decode(value, len);
 | 
			
		||||
      this->args_.push_back(arg);
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
template<> enums::ServiceArgType to_service_arg_type<std::vector<std::string>>() {
 | 
			
		||||
  return enums::SERVICE_ARG_TYPE_STRING_ARRAY;
 | 
			
		||||
}
 | 
			
		||||
APIMessageType ExecuteServiceRequest::message_type() const { return APIMessageType::EXECUTE_SERVICE_REQUEST; }
 | 
			
		||||
const std::vector<ExecuteServiceArgument> &ExecuteServiceRequest::get_args() const { return this->args_; }
 | 
			
		||||
uint32_t ExecuteServiceRequest::get_key() const { return this->key_; }
 | 
			
		||||
 | 
			
		||||
ServiceTypeArgument::ServiceTypeArgument(const std::string &name, ServiceArgType type) : name_(name), type_(type) {}
 | 
			
		||||
const std::string &ServiceTypeArgument::get_name() const { return this->name_; }
 | 
			
		||||
ServiceArgType ServiceTypeArgument::get_type() const { return this->type_; }
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -2,124 +2,71 @@
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/automation.h"
 | 
			
		||||
#include "api_message.h"
 | 
			
		||||
#include "api_pb2.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
enum ServiceArgType {
 | 
			
		||||
  SERVICE_ARG_TYPE_BOOL = 0,
 | 
			
		||||
  SERVICE_ARG_TYPE_INT = 1,
 | 
			
		||||
  SERVICE_ARG_TYPE_FLOAT = 2,
 | 
			
		||||
  SERVICE_ARG_TYPE_STRING = 3,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ServiceTypeArgument {
 | 
			
		||||
 public:
 | 
			
		||||
  ServiceTypeArgument(const std::string &name, ServiceArgType type);
 | 
			
		||||
  const std::string &get_name() const;
 | 
			
		||||
  ServiceArgType get_type() const;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  std::string name_;
 | 
			
		||||
  ServiceArgType type_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ExecuteServiceArgument : public APIMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  APIMessageType message_type() const override;
 | 
			
		||||
  template<typename T> T get_value();
 | 
			
		||||
 | 
			
		||||
  bool decode_varint(uint32_t field_id, uint32_t value) override;
 | 
			
		||||
  bool decode_length_delimited(uint32_t field_id, const uint8_t *value, size_t len) override;
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, uint32_t value) override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool value_bool_{false};
 | 
			
		||||
  int value_int_{0};
 | 
			
		||||
  float value_float_{0.0f};
 | 
			
		||||
  std::string value_string_{};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ExecuteServiceRequest : public APIMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  bool decode_length_delimited(uint32_t field_id, const uint8_t *value, size_t len) override;
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, uint32_t value) override;
 | 
			
		||||
  APIMessageType message_type() const override;
 | 
			
		||||
 | 
			
		||||
  uint32_t get_key() const;
 | 
			
		||||
  const std::vector<ExecuteServiceArgument> &get_args() const;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  uint32_t key_;
 | 
			
		||||
  std::vector<ExecuteServiceArgument> args_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class UserServiceDescriptor {
 | 
			
		||||
 public:
 | 
			
		||||
  virtual void encode_list_service_response(APIBuffer &buffer) = 0;
 | 
			
		||||
  virtual ListEntitiesServicesResponse encode_list_service_response() = 0;
 | 
			
		||||
 | 
			
		||||
  virtual bool execute_service(const ExecuteServiceRequest &req) = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<typename... Ts> class UserService : public UserServiceDescriptor, public Trigger<Ts...> {
 | 
			
		||||
template<typename T> T get_execute_arg_value(const ExecuteServiceArgument &arg);
 | 
			
		||||
 | 
			
		||||
template<typename T> enums::ServiceArgType to_service_arg_type();
 | 
			
		||||
 | 
			
		||||
template<typename... Ts> class UserServiceBase : public UserServiceDescriptor {
 | 
			
		||||
 public:
 | 
			
		||||
  UserService(const std::string &name, const std::array<ServiceTypeArgument, sizeof...(Ts)> &args);
 | 
			
		||||
  UserServiceBase(const std::string &name, const std::array<std::string, sizeof...(Ts)> &arg_names)
 | 
			
		||||
      : name_(name), arg_names_(arg_names) {
 | 
			
		||||
    this->key_ = fnv1_hash(this->name_);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void encode_list_service_response(APIBuffer &buffer) override;
 | 
			
		||||
  ListEntitiesServicesResponse encode_list_service_response() override {
 | 
			
		||||
    ListEntitiesServicesResponse msg;
 | 
			
		||||
    msg.name = this->name_;
 | 
			
		||||
    msg.key = this->key_;
 | 
			
		||||
    std::array<enums::ServiceArgType, sizeof...(Ts)> arg_types = {to_service_arg_type<Ts>()...};
 | 
			
		||||
    for (int i = 0; i < sizeof...(Ts); i++) {
 | 
			
		||||
      ListEntitiesServicesArgument arg;
 | 
			
		||||
      arg.type = arg_types[i];
 | 
			
		||||
      arg.name = this->arg_names_[i];
 | 
			
		||||
      msg.args.push_back(arg);
 | 
			
		||||
    }
 | 
			
		||||
    return msg;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  bool execute_service(const ExecuteServiceRequest &req) override;
 | 
			
		||||
  bool execute_service(const ExecuteServiceRequest &req) override {
 | 
			
		||||
    if (req.key != this->key_)
 | 
			
		||||
      return false;
 | 
			
		||||
    if (req.args.size() != this->arg_names_.size())
 | 
			
		||||
      return false;
 | 
			
		||||
    this->execute_(req.args, typename gens<sizeof...(Ts)>::type());
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  template<int... S> void execute_(std::vector<ExecuteServiceArgument> args, seq<S...>);
 | 
			
		||||
  virtual void execute(Ts... x) = 0;
 | 
			
		||||
  template<int... S> void execute_(std::vector<ExecuteServiceArgument> args, seq<S...>) {
 | 
			
		||||
    this->execute((get_execute_arg_value<Ts>(args[S]))...);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  std::string name_;
 | 
			
		||||
  uint32_t key_{0};
 | 
			
		||||
  std::array<ServiceTypeArgument, sizeof...(Ts)> args_;
 | 
			
		||||
  std::array<std::string, sizeof...(Ts)> arg_names_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<typename... Ts>
 | 
			
		||||
template<int... S>
 | 
			
		||||
void UserService<Ts...>::execute_(std::vector<ExecuteServiceArgument> args, seq<S...>) {
 | 
			
		||||
  this->trigger((args[S].get_value<Ts>())...);
 | 
			
		||||
}
 | 
			
		||||
template<typename... Ts> void UserService<Ts...>::encode_list_service_response(APIBuffer &buffer) {
 | 
			
		||||
  // string name = 1;
 | 
			
		||||
  buffer.encode_string(1, this->name_);
 | 
			
		||||
  // fixed32 key = 2;
 | 
			
		||||
  buffer.encode_fixed32(2, this->key_);
 | 
			
		||||
template<typename... Ts> class UserServiceTrigger : public UserServiceBase<Ts...>, public Trigger<Ts...> {
 | 
			
		||||
 public:
 | 
			
		||||
  UserServiceTrigger(const std::string &name, const std::array<std::string, sizeof...(Ts)> &arg_names)
 | 
			
		||||
      : UserServiceBase<Ts...>(name, arg_names) {}
 | 
			
		||||
 | 
			
		||||
  // repeated ListServicesArgument args = 3;
 | 
			
		||||
  for (auto &arg : this->args_) {
 | 
			
		||||
    auto nested = buffer.begin_nested(3);
 | 
			
		||||
    // string name = 1;
 | 
			
		||||
    buffer.encode_string(1, arg.get_name());
 | 
			
		||||
    // Type type = 2;
 | 
			
		||||
    buffer.encode_int32(2, arg.get_type());
 | 
			
		||||
    buffer.end_nested(nested);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
template<typename... Ts> bool UserService<Ts...>::execute_service(const ExecuteServiceRequest &req) {
 | 
			
		||||
  if (req.get_key() != this->key_)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  if (req.get_args().size() != this->args_.size()) {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this->execute_(req.get_args(), typename gens<sizeof...(Ts)>::type());
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
template<typename... Ts>
 | 
			
		||||
UserService<Ts...>::UserService(const std::string &name, const std::array<ServiceTypeArgument, sizeof...(Ts)> &args)
 | 
			
		||||
    : name_(name), args_(args) {
 | 
			
		||||
  this->key_ = fnv1_hash(this->name_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template<> bool ExecuteServiceArgument::get_value<bool>();
 | 
			
		||||
template<> int ExecuteServiceArgument::get_value<int>();
 | 
			
		||||
template<> float ExecuteServiceArgument::get_value<float>();
 | 
			
		||||
template<> std::string ExecuteServiceArgument::get_value<std::string>();
 | 
			
		||||
 protected:
 | 
			
		||||
  void execute(Ts... x) override { this->trigger(x...); }  // NOLINT
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -7,166 +7,6 @@
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
APIBuffer::APIBuffer(std::vector<uint8_t> *buffer) : buffer_(buffer) {}
 | 
			
		||||
size_t APIBuffer::get_length() const { return this->buffer_->size(); }
 | 
			
		||||
void APIBuffer::write(uint8_t value) { this->buffer_->push_back(value); }
 | 
			
		||||
void APIBuffer::encode_uint32(uint32_t field, uint32_t value, bool force) {
 | 
			
		||||
  if (value == 0 && !force)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  this->encode_field_raw(field, 0);
 | 
			
		||||
  this->encode_varint_raw(value);
 | 
			
		||||
}
 | 
			
		||||
void APIBuffer::encode_int32(uint32_t field, int32_t value, bool force) {
 | 
			
		||||
  this->encode_uint32(field, static_cast<uint32_t>(value), force);
 | 
			
		||||
}
 | 
			
		||||
void APIBuffer::encode_bool(uint32_t field, bool value, bool force) {
 | 
			
		||||
  if (!value && !force)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  this->encode_field_raw(field, 0);
 | 
			
		||||
  this->write(0x01);
 | 
			
		||||
}
 | 
			
		||||
void APIBuffer::encode_string(uint32_t field, const std::string &value) {
 | 
			
		||||
  this->encode_string(field, value.data(), value.size());
 | 
			
		||||
}
 | 
			
		||||
void APIBuffer::encode_bytes(uint32_t field, const uint8_t *data, size_t len) {
 | 
			
		||||
  this->encode_string(field, reinterpret_cast<const char *>(data), len);
 | 
			
		||||
}
 | 
			
		||||
void APIBuffer::encode_string(uint32_t field, const char *string, size_t len) {
 | 
			
		||||
  if (len == 0)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  this->encode_field_raw(field, 2);
 | 
			
		||||
  this->encode_varint_raw(len);
 | 
			
		||||
  const uint8_t *data = reinterpret_cast<const uint8_t *>(string);
 | 
			
		||||
  for (size_t i = 0; i < len; i++) {
 | 
			
		||||
    this->write(data[i]);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void APIBuffer::encode_fixed32(uint32_t field, uint32_t value, bool force) {
 | 
			
		||||
  if (value == 0 && !force)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  this->encode_field_raw(field, 5);
 | 
			
		||||
  this->write((value >> 0) & 0xFF);
 | 
			
		||||
  this->write((value >> 8) & 0xFF);
 | 
			
		||||
  this->write((value >> 16) & 0xFF);
 | 
			
		||||
  this->write((value >> 24) & 0xFF);
 | 
			
		||||
}
 | 
			
		||||
void APIBuffer::encode_float(uint32_t field, float value, bool force) {
 | 
			
		||||
  if (value == 0.0f && !force)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  union {
 | 
			
		||||
    float value_f;
 | 
			
		||||
    uint32_t value_raw;
 | 
			
		||||
  } val;
 | 
			
		||||
  val.value_f = value;
 | 
			
		||||
  this->encode_fixed32(field, val.value_raw);
 | 
			
		||||
}
 | 
			
		||||
void APIBuffer::encode_field_raw(uint32_t field, uint32_t type) {
 | 
			
		||||
  uint32_t val = (field << 3) | (type & 0b111);
 | 
			
		||||
  this->encode_varint_raw(val);
 | 
			
		||||
}
 | 
			
		||||
void APIBuffer::encode_varint_raw(uint32_t value) {
 | 
			
		||||
  if (value <= 0x7F) {
 | 
			
		||||
    this->write(value);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  while (value) {
 | 
			
		||||
    uint8_t temp = value & 0x7F;
 | 
			
		||||
    value >>= 7;
 | 
			
		||||
    if (value) {
 | 
			
		||||
      this->write(temp | 0x80);
 | 
			
		||||
    } else {
 | 
			
		||||
      this->write(temp);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void APIBuffer::encode_sint32(uint32_t field, int32_t value, bool force) {
 | 
			
		||||
  if (value < 0)
 | 
			
		||||
    this->encode_uint32(field, ~(uint32_t(value) << 1), force);
 | 
			
		||||
  else
 | 
			
		||||
    this->encode_uint32(field, uint32_t(value) << 1, force);
 | 
			
		||||
}
 | 
			
		||||
void APIBuffer::encode_nameable(Nameable *nameable) {
 | 
			
		||||
  // string object_id = 1;
 | 
			
		||||
  this->encode_string(1, nameable->get_object_id());
 | 
			
		||||
  // fixed32 key = 2;
 | 
			
		||||
  this->encode_fixed32(2, nameable->get_object_id_hash());
 | 
			
		||||
  // string name = 3;
 | 
			
		||||
  this->encode_string(3, nameable->get_name());
 | 
			
		||||
}
 | 
			
		||||
size_t APIBuffer::begin_nested(uint32_t field) {
 | 
			
		||||
  this->encode_field_raw(field, 2);
 | 
			
		||||
  return this->buffer_->size();
 | 
			
		||||
}
 | 
			
		||||
void APIBuffer::end_nested(size_t begin_index) {
 | 
			
		||||
  const uint32_t nested_length = this->buffer_->size() - begin_index;
 | 
			
		||||
  // add varint
 | 
			
		||||
  std::vector<uint8_t> var;
 | 
			
		||||
  uint32_t val = nested_length;
 | 
			
		||||
  if (val <= 0x7F) {
 | 
			
		||||
    var.push_back(val);
 | 
			
		||||
  } else {
 | 
			
		||||
    while (val) {
 | 
			
		||||
      uint8_t temp = val & 0x7F;
 | 
			
		||||
      val >>= 7;
 | 
			
		||||
      if (val) {
 | 
			
		||||
        var.push_back(temp | 0x80);
 | 
			
		||||
      } else {
 | 
			
		||||
        var.push_back(temp);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  this->buffer_->insert(this->buffer_->begin() + begin_index, var.begin(), var.end());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
optional<uint32_t> proto_decode_varuint32(const uint8_t *buf, size_t len, uint32_t *consumed) {
 | 
			
		||||
  if (len == 0)
 | 
			
		||||
    return {};
 | 
			
		||||
 | 
			
		||||
  uint32_t result = 0;
 | 
			
		||||
  uint8_t bitpos = 0;
 | 
			
		||||
 | 
			
		||||
  for (uint32_t i = 0; i < len; i++) {
 | 
			
		||||
    uint8_t val = buf[i];
 | 
			
		||||
    result |= uint32_t(val & 0x7F) << bitpos;
 | 
			
		||||
    bitpos += 7;
 | 
			
		||||
    if ((val & 0x80) == 0) {
 | 
			
		||||
      if (consumed != nullptr) {
 | 
			
		||||
        *consumed = i + 1;
 | 
			
		||||
      }
 | 
			
		||||
      return result;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string as_string(const uint8_t *value, size_t len) {
 | 
			
		||||
  return std::string(reinterpret_cast<const char *>(value), len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int32_t as_sint32(uint32_t val) {
 | 
			
		||||
  if (val & 1)
 | 
			
		||||
    return uint32_t(~(val >> 1));
 | 
			
		||||
  else
 | 
			
		||||
    return uint32_t(val >> 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float as_float(uint32_t val) {
 | 
			
		||||
  static_assert(sizeof(uint32_t) == sizeof(float), "float must be 32bit long");
 | 
			
		||||
  union {
 | 
			
		||||
    uint32_t raw;
 | 
			
		||||
    float value;
 | 
			
		||||
  } x;
 | 
			
		||||
  x.raw = val;
 | 
			
		||||
  return x.value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ComponentIterator::ComponentIterator(APIServer *server) : server_(server) {}
 | 
			
		||||
void ComponentIterator::begin() {
 | 
			
		||||
  this->state_ = IteratorState::BEGIN;
 | 
			
		||||
 
 | 
			
		||||
@@ -10,40 +10,6 @@
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
class APIBuffer {
 | 
			
		||||
 public:
 | 
			
		||||
  APIBuffer(std::vector<uint8_t> *buffer);
 | 
			
		||||
 | 
			
		||||
  size_t get_length() const;
 | 
			
		||||
  void write(uint8_t value);
 | 
			
		||||
 | 
			
		||||
  void encode_int32(uint32_t field, int32_t value, bool force = false);
 | 
			
		||||
  void encode_uint32(uint32_t field, uint32_t value, bool force = false);
 | 
			
		||||
  void encode_sint32(uint32_t field, int32_t value, bool force = false);
 | 
			
		||||
  void encode_bool(uint32_t field, bool value, bool force = false);
 | 
			
		||||
  void encode_string(uint32_t field, const std::string &value);
 | 
			
		||||
  void encode_string(uint32_t field, const char *string, size_t len);
 | 
			
		||||
  void encode_bytes(uint32_t field, const uint8_t *data, size_t len);
 | 
			
		||||
  void encode_fixed32(uint32_t field, uint32_t value, bool force = false);
 | 
			
		||||
  void encode_float(uint32_t field, float value, bool force = false);
 | 
			
		||||
  void encode_nameable(Nameable *nameable);
 | 
			
		||||
 | 
			
		||||
  size_t begin_nested(uint32_t field);
 | 
			
		||||
  void end_nested(size_t begin_index);
 | 
			
		||||
 | 
			
		||||
  void encode_field_raw(uint32_t field, uint32_t type);
 | 
			
		||||
  void encode_varint_raw(uint32_t value);
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  std::vector<uint8_t> *buffer_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
optional<uint32_t> proto_decode_varuint32(const uint8_t *buf, size_t len, uint32_t *consumed = nullptr);
 | 
			
		||||
 | 
			
		||||
std::string as_string(const uint8_t *value, size_t len);
 | 
			
		||||
int32_t as_sint32(uint32_t val);
 | 
			
		||||
float as_float(uint32_t val);
 | 
			
		||||
 | 
			
		||||
class APIServer;
 | 
			
		||||
class UserServiceDescriptor;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										46
									
								
								esphome/components/as3935/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								esphome/components/as3935/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome import pins
 | 
			
		||||
from esphome.const import CONF_INDOOR, CONF_WATCHDOG_THRESHOLD, \
 | 
			
		||||
    CONF_NOISE_LEVEL, CONF_SPIKE_REJECTION, CONF_LIGHTNING_THRESHOLD, \
 | 
			
		||||
    CONF_MASK_DISTURBER, CONF_DIV_RATIO, CONF_CAPACITANCE
 | 
			
		||||
from esphome.core import coroutine
 | 
			
		||||
 | 
			
		||||
AUTO_LOAD = ['sensor', 'binary_sensor']
 | 
			
		||||
MULTI_CONF = True
 | 
			
		||||
 | 
			
		||||
CONF_AS3935_ID = 'as3935_id'
 | 
			
		||||
 | 
			
		||||
as3935_ns = cg.esphome_ns.namespace('as3935')
 | 
			
		||||
AS3935 = as3935_ns.class_('AS3935Component', cg.Component)
 | 
			
		||||
 | 
			
		||||
CONF_IRQ_PIN = 'irq_pin'
 | 
			
		||||
AS3935_SCHEMA = cv.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_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_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_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
 | 
			
		||||
def setup_as3935(var, config):
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
 | 
			
		||||
    irq_pin = yield cg.gpio_pin_expression(config[CONF_IRQ_PIN])
 | 
			
		||||
    cg.add(var.set_irq_pin(irq_pin))
 | 
			
		||||
    cg.add(var.set_indoor(config[CONF_INDOOR]))
 | 
			
		||||
    cg.add(var.set_noise_level(config[CONF_NOISE_LEVEL]))
 | 
			
		||||
    cg.add(var.set_watchdog_threshold(config[CONF_WATCHDOG_THRESHOLD]))
 | 
			
		||||
    cg.add(var.set_spike_rejection(config[CONF_SPIKE_REJECTION]))
 | 
			
		||||
    cg.add(var.set_lightning_threshold(config[CONF_LIGHTNING_THRESHOLD]))
 | 
			
		||||
    cg.add(var.set_mask_disturber(config[CONF_MASK_DISTURBER]))
 | 
			
		||||
    cg.add(var.set_div_ratio(config[CONF_DIV_RATIO]))
 | 
			
		||||
    cg.add(var.set_capacitance(config[CONF_CAPACITANCE]))
 | 
			
		||||
							
								
								
									
										226
									
								
								esphome/components/as3935/as3935.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										226
									
								
								esphome/components/as3935/as3935.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,226 @@
 | 
			
		||||
#include "as3935.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace as3935 {
 | 
			
		||||
 | 
			
		||||
static const char *TAG = "as3935";
 | 
			
		||||
 | 
			
		||||
void AS3935Component::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up AS3935...");
 | 
			
		||||
 | 
			
		||||
  this->irq_pin_->setup();
 | 
			
		||||
  LOG_PIN("  IRQ Pin: ", this->irq_pin_);
 | 
			
		||||
 | 
			
		||||
  // Write properties to sensor
 | 
			
		||||
  this->write_indoor(this->indoor_);
 | 
			
		||||
  this->write_noise_level(this->noise_level_);
 | 
			
		||||
  this->write_watchdog_threshold(this->watchdog_threshold_);
 | 
			
		||||
  this->write_spike_rejection(this->spike_rejection_);
 | 
			
		||||
  this->write_lightning_threshold(this->lightning_threshold_);
 | 
			
		||||
  this->write_mask_disturber(this->mask_disturber_);
 | 
			
		||||
  this->write_div_ratio(this->div_ratio_);
 | 
			
		||||
  this->write_capacitance(this->capacitance_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AS3935Component::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "AS3935:");
 | 
			
		||||
  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; }
 | 
			
		||||
 | 
			
		||||
void AS3935Component::loop() {
 | 
			
		||||
  if (!this->irq_pin_->digital_read())
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  uint8_t int_value = this->read_interrupt_register_();
 | 
			
		||||
  if (int_value == NOISE_INT) {
 | 
			
		||||
    ESP_LOGI(TAG, "Noise was detected - try increasing the noise level value!");
 | 
			
		||||
  } else if (int_value == DISTURBER_INT) {
 | 
			
		||||
    ESP_LOGI(TAG, "Disturber was detected - try increasing the spike rejection value!");
 | 
			
		||||
  } else if (int_value == LIGHTNING_INT) {
 | 
			
		||||
    ESP_LOGI(TAG, "Lightning has been detected!");
 | 
			
		||||
    if (this->thunder_alert_binary_sensor_ != nullptr)
 | 
			
		||||
      this->thunder_alert_binary_sensor_->publish_state(true);
 | 
			
		||||
    uint8_t distance = this->get_distance_to_storm_();
 | 
			
		||||
    if (this->distance_sensor_ != nullptr)
 | 
			
		||||
      this->distance_sensor_->publish_state(distance);
 | 
			
		||||
    uint32_t energy = this->get_lightning_energy_();
 | 
			
		||||
    if (this->energy_sensor_ != nullptr)
 | 
			
		||||
      this->energy_sensor_->publish_state(energy);
 | 
			
		||||
  }
 | 
			
		||||
  this->thunder_alert_binary_sensor_->publish_state(false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AS3935Component::write_indoor(bool indoor) {
 | 
			
		||||
  ESP_LOGV(TAG, "Setting indoor to %d", indoor);
 | 
			
		||||
  if (indoor)
 | 
			
		||||
    this->write_register(AFE_GAIN, GAIN_MASK, INDOOR, 1);
 | 
			
		||||
  else
 | 
			
		||||
    this->write_register(AFE_GAIN, GAIN_MASK, OUTDOOR, 1);
 | 
			
		||||
}
 | 
			
		||||
// REG0x01, bits[3:0], manufacturer default: 0010 (2).
 | 
			
		||||
// This setting determines the threshold for events that trigger the
 | 
			
		||||
// IRQ Pin.
 | 
			
		||||
void AS3935Component::write_watchdog_threshold(uint8_t watchdog_threshold) {
 | 
			
		||||
  ESP_LOGV(TAG, "Setting watchdog sensitivity to %d", watchdog_threshold);
 | 
			
		||||
  if ((watchdog_threshold < 1) || (watchdog_threshold > 10))  // 10 is the max sensitivity setting
 | 
			
		||||
    return;
 | 
			
		||||
  this->write_register(THRESHOLD, THRESH_MASK, watchdog_threshold, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// REG0x01, bits [6:4], manufacturer default: 010 (2).
 | 
			
		||||
// The noise floor level is compared to a known reference voltage. If this
 | 
			
		||||
// level is exceeded the chip will issue an interrupt to the IRQ pin,
 | 
			
		||||
// broadcasting that it can not operate properly due to noise (INT_NH).
 | 
			
		||||
// Check datasheet for specific noise level tolerances when setting this register.
 | 
			
		||||
void AS3935Component::write_noise_level(uint8_t noise_level) {
 | 
			
		||||
  ESP_LOGV(TAG, "Setting noise level to %d", noise_level);
 | 
			
		||||
  if ((noise_level < 1) || (noise_level > 7))
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  this->write_register(THRESHOLD, NOISE_FLOOR_MASK, noise_level, 4);
 | 
			
		||||
}
 | 
			
		||||
// REG0x02, bits [3:0], manufacturer default: 0010 (2).
 | 
			
		||||
// This setting, like the watchdog threshold, can help determine between false
 | 
			
		||||
// events and actual lightning. The shape of the spike is analyzed during the
 | 
			
		||||
// chip's signal validation routine. Increasing this value increases robustness
 | 
			
		||||
// at the cost of sensitivity to distant events.
 | 
			
		||||
void AS3935Component::write_spike_rejection(uint8_t spike_rejection) {
 | 
			
		||||
  ESP_LOGV(TAG, "Setting spike rejection to %d", spike_rejection);
 | 
			
		||||
  if ((spike_rejection < 1) || (spike_rejection > 11))
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  this->write_register(LIGHTNING_REG, SPIKE_MASK, spike_rejection, 0);
 | 
			
		||||
}
 | 
			
		||||
// REG0x02, bits [5:4], manufacturer default: 0 (single lightning strike).
 | 
			
		||||
// The number of lightning events before IRQ is set high. 15 minutes is The
 | 
			
		||||
// window of time before the number of detected lightning events is reset.
 | 
			
		||||
// The number of lightning strikes can be set to 1,5,9, or 16.
 | 
			
		||||
void AS3935Component::write_lightning_threshold(uint8_t lightning_threshold) {
 | 
			
		||||
  ESP_LOGV(TAG, "Setting lightning threshold to %d", lightning_threshold);
 | 
			
		||||
  switch (lightning_threshold) {
 | 
			
		||||
    case 1:
 | 
			
		||||
      this->write_register(LIGHTNING_REG, ((1 << 5) | (1 << 4)), 0, 4);  // Demonstrative
 | 
			
		||||
      break;
 | 
			
		||||
    case 5:
 | 
			
		||||
      this->write_register(LIGHTNING_REG, ((1 << 5) | (1 << 4)), 1, 4);
 | 
			
		||||
      break;
 | 
			
		||||
    case 9:
 | 
			
		||||
      this->write_register(LIGHTNING_REG, ((1 << 5) | (1 << 4)), 1, 5);
 | 
			
		||||
      break;
 | 
			
		||||
    case 16:
 | 
			
		||||
      this->write_register(LIGHTNING_REG, ((1 << 5) | (1 << 4)), 3, 4);
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      return;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
// REG0x03, bit [5], manufacturer default: 0.
 | 
			
		||||
// This setting will return whether or not disturbers trigger the IRQ Pin.
 | 
			
		||||
void AS3935Component::write_mask_disturber(bool enabled) {
 | 
			
		||||
  ESP_LOGV(TAG, "Setting mask disturber to %d", enabled);
 | 
			
		||||
  if (enabled) {
 | 
			
		||||
    this->write_register(INT_MASK_ANT, (1 << 5), 1, 5);
 | 
			
		||||
  } else {
 | 
			
		||||
    this->write_register(INT_MASK_ANT, (1 << 5), 0, 5);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
// REG0x03, bit [7:6], manufacturer default: 0 (16 division ratio).
 | 
			
		||||
// The antenna is designed to resonate at 500kHz and so can be tuned with the
 | 
			
		||||
// following setting. The accuracy of the antenna must be within 3.5 percent of
 | 
			
		||||
// that value for proper signal validation and distance estimation.
 | 
			
		||||
void AS3935Component::write_div_ratio(uint8_t div_ratio) {
 | 
			
		||||
  ESP_LOGV(TAG, "Setting div ratio to %d", div_ratio);
 | 
			
		||||
  switch (div_ratio) {
 | 
			
		||||
    case 16:
 | 
			
		||||
      this->write_register(INT_MASK_ANT, ((1 << 7) | (1 << 6)), 0, 6);
 | 
			
		||||
      break;
 | 
			
		||||
    case 22:
 | 
			
		||||
      this->write_register(INT_MASK_ANT, ((1 << 7) | (1 << 6)), 1, 6);
 | 
			
		||||
      break;
 | 
			
		||||
    case 64:
 | 
			
		||||
      this->write_register(INT_MASK_ANT, ((1 << 7) | (1 << 6)), 1, 7);
 | 
			
		||||
      break;
 | 
			
		||||
    case 128:
 | 
			
		||||
      this->write_register(INT_MASK_ANT, ((1 << 7) | (1 << 6)), 3, 6);
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      return;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
// REG0x08, bits [3:0], manufacturer default: 0.
 | 
			
		||||
// This setting will add capacitance to the series RLC antenna on the product
 | 
			
		||||
// to help tune its resonance. The datasheet specifies being within 3.5 percent
 | 
			
		||||
// of 500kHz to get optimal lightning detection and distance sensing.
 | 
			
		||||
// It's possible to add up to 120pF in steps of 8pF to the antenna.
 | 
			
		||||
void AS3935Component::write_capacitance(uint8_t capacitance) {
 | 
			
		||||
  ESP_LOGV(TAG, "Setting tune cap to %d pF", capacitance * 8);
 | 
			
		||||
  this->write_register(FREQ_DISP_IRQ, CAP_MASK, capacitance, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// REG0x03, bits [3:0], manufacturer default: 0.
 | 
			
		||||
// When there is an event that exceeds the watchdog threshold, the register is written
 | 
			
		||||
// with the type of event. This consists of two messages: INT_D (disturber detected) and
 | 
			
		||||
// INT_L (Lightning detected). A third interrupt INT_NH (noise level too HIGH)
 | 
			
		||||
// indicates that the noise level has been exceeded and will persist until the
 | 
			
		||||
// noise has ended. Events are active HIGH. There is a one second window of time to
 | 
			
		||||
// read the interrupt register after lightning is detected, and 1.5 after
 | 
			
		||||
// disturber.
 | 
			
		||||
uint8_t AS3935Component::read_interrupt_register_() {
 | 
			
		||||
  // A 2ms delay is added to allow for the memory register to be populated
 | 
			
		||||
  // after the interrupt pin goes HIGH. See "Interrupt Management" in
 | 
			
		||||
  // datasheet.
 | 
			
		||||
  ESP_LOGV(TAG, "Calling read_interrupt_register_");
 | 
			
		||||
  delay(2);
 | 
			
		||||
  return this->read_register_(INT_MASK_ANT, INT_MASK);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// REG0x02, bit [6], manufacturer default: 1.
 | 
			
		||||
// This register clears the number of lightning strikes that has been read in
 | 
			
		||||
// the last 15 minute block.
 | 
			
		||||
void AS3935Component::clear_statistics_() {
 | 
			
		||||
  // Write high, then low, then high to clear.
 | 
			
		||||
  ESP_LOGV(TAG, "Calling clear_statistics_");
 | 
			
		||||
  this->write_register(LIGHTNING_REG, (1 << 6), 1, 6);
 | 
			
		||||
  this->write_register(LIGHTNING_REG, (1 << 6), 0, 6);
 | 
			
		||||
  this->write_register(LIGHTNING_REG, (1 << 6), 1, 6);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// REG0x07, bit [5:0], manufacturer default: 0.
 | 
			
		||||
// This register holds the distance to the front of the storm and not the
 | 
			
		||||
// distance to a lightning strike.
 | 
			
		||||
uint8_t AS3935Component::get_distance_to_storm_() {
 | 
			
		||||
  ESP_LOGV(TAG, "Calling get_distance_to_storm_");
 | 
			
		||||
  return this->read_register_(DISTANCE, DISTANCE_MASK);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t AS3935Component::get_lightning_energy_() {
 | 
			
		||||
  ESP_LOGV(TAG, "Calling get_lightning_energy_");
 | 
			
		||||
  uint32_t pure_light = 0;  // Variable for lightning energy which is just a pure number.
 | 
			
		||||
  uint32_t temp = 0;
 | 
			
		||||
  // Temp variable for lightning energy.
 | 
			
		||||
  temp = this->read_register_(ENERGY_LIGHT_MMSB, ENERGY_MASK);
 | 
			
		||||
  // Temporary Value is large enough to handle a shift of 16 bits.
 | 
			
		||||
  pure_light = temp << 16;
 | 
			
		||||
  temp = this->read_register(ENERGY_LIGHT_MSB);
 | 
			
		||||
  // Temporary value is large enough to handle a shift of 8 bits.
 | 
			
		||||
  pure_light |= temp << 8;
 | 
			
		||||
  // No shift here, directly OR'ed into pure_light variable.
 | 
			
		||||
  temp = this->read_register(ENERGY_LIGHT_LSB);
 | 
			
		||||
  pure_light |= temp;
 | 
			
		||||
  return pure_light;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t AS3935Component::read_register_(uint8_t reg, uint8_t mask) {
 | 
			
		||||
  uint8_t value = this->read_register(reg);
 | 
			
		||||
  value &= (~mask);
 | 
			
		||||
  return value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace as3935
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										110
									
								
								esphome/components/as3935/as3935.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								esphome/components/as3935/as3935.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,110 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/components/sensor/sensor.h"
 | 
			
		||||
#include "esphome/components/binary_sensor/binary_sensor.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace as3935 {
 | 
			
		||||
 | 
			
		||||
enum AS3935RegisterNames {
 | 
			
		||||
  AFE_GAIN = 0x00,
 | 
			
		||||
  THRESHOLD,
 | 
			
		||||
  LIGHTNING_REG,
 | 
			
		||||
  INT_MASK_ANT,
 | 
			
		||||
  ENERGY_LIGHT_LSB,
 | 
			
		||||
  ENERGY_LIGHT_MSB,
 | 
			
		||||
  ENERGY_LIGHT_MMSB,
 | 
			
		||||
  DISTANCE,
 | 
			
		||||
  FREQ_DISP_IRQ,
 | 
			
		||||
  CALIB_TRCO = 0x3A,
 | 
			
		||||
  CALIB_SRCO = 0x3B,
 | 
			
		||||
  DEFAULT_RESET = 0x3C,
 | 
			
		||||
  CALIB_RCO = 0x3D
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum AS3935RegisterMasks {
 | 
			
		||||
  GAIN_MASK = 0x3E,
 | 
			
		||||
  SPIKE_MASK = 0xF,
 | 
			
		||||
  IO_MASK = 0xC1,
 | 
			
		||||
  DISTANCE_MASK = 0xC0,
 | 
			
		||||
  INT_MASK = 0xF0,
 | 
			
		||||
  THRESH_MASK = 0x0F,
 | 
			
		||||
  R_SPIKE_MASK = 0xF0,
 | 
			
		||||
  ENERGY_MASK = 0xF0,
 | 
			
		||||
  CAP_MASK = 0xF0,
 | 
			
		||||
  LIGHT_MASK = 0xCF,
 | 
			
		||||
  DISTURB_MASK = 0xDF,
 | 
			
		||||
  NOISE_FLOOR_MASK = 0x70,
 | 
			
		||||
  OSC_MASK = 0xE0,
 | 
			
		||||
  CALIB_MASK = 0x7F,
 | 
			
		||||
  DIV_MASK = 0x3F
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum AS3935Values {
 | 
			
		||||
  AS3935_ADDR = 0x03,
 | 
			
		||||
  INDOOR = 0x12,
 | 
			
		||||
  OUTDOOR = 0xE,
 | 
			
		||||
  LIGHTNING_INT = 0x08,
 | 
			
		||||
  DISTURBER_INT = 0x04,
 | 
			
		||||
  NOISE_INT = 0x01
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class AS3935Component : public Component {
 | 
			
		||||
 public:
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
  float get_setup_priority() const override;
 | 
			
		||||
  void loop() override;
 | 
			
		||||
 | 
			
		||||
  void set_irq_pin(GPIOPin *irq_pin) { irq_pin_ = irq_pin; }
 | 
			
		||||
  void set_distance_sensor(sensor::Sensor *distance_sensor) { distance_sensor_ = distance_sensor; }
 | 
			
		||||
  void set_energy_sensor(sensor::Sensor *energy_sensor) { energy_sensor_ = energy_sensor; }
 | 
			
		||||
  void set_thunder_alert_binary_sensor(binary_sensor::BinarySensor *thunder_alert_binary_sensor) {
 | 
			
		||||
    thunder_alert_binary_sensor_ = thunder_alert_binary_sensor;
 | 
			
		||||
  }
 | 
			
		||||
  void set_indoor(bool indoor) { indoor_ = indoor; }
 | 
			
		||||
  void write_indoor(bool indoor);
 | 
			
		||||
  void set_noise_level(uint8_t noise_level) { noise_level_ = noise_level; }
 | 
			
		||||
  void write_noise_level(uint8_t noise_level);
 | 
			
		||||
  void set_watchdog_threshold(uint8_t watchdog_threshold) { watchdog_threshold_ = watchdog_threshold; }
 | 
			
		||||
  void write_watchdog_threshold(uint8_t watchdog_threshold);
 | 
			
		||||
  void set_spike_rejection(uint8_t spike_rejection) { spike_rejection_ = spike_rejection; }
 | 
			
		||||
  void write_spike_rejection(uint8_t write_spike_rejection);
 | 
			
		||||
  void set_lightning_threshold(uint8_t lightning_threshold) { lightning_threshold_ = lightning_threshold; }
 | 
			
		||||
  void write_lightning_threshold(uint8_t lightning_threshold);
 | 
			
		||||
  void set_mask_disturber(bool mask_disturber) { mask_disturber_ = mask_disturber; }
 | 
			
		||||
  void write_mask_disturber(bool enabled);
 | 
			
		||||
  void set_div_ratio(uint8_t div_ratio) { div_ratio_ = div_ratio; }
 | 
			
		||||
  void write_div_ratio(uint8_t div_ratio);
 | 
			
		||||
  void set_capacitance(uint8_t capacitance) { capacitance_ = capacitance; }
 | 
			
		||||
  void write_capacitance(uint8_t capacitance);
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  uint8_t read_interrupt_register_();
 | 
			
		||||
  void clear_statistics_();
 | 
			
		||||
  uint8_t get_distance_to_storm_();
 | 
			
		||||
  uint32_t get_lightning_energy_();
 | 
			
		||||
 | 
			
		||||
  virtual uint8_t read_register(uint8_t reg) = 0;
 | 
			
		||||
  uint8_t read_register_(uint8_t reg, uint8_t mask);
 | 
			
		||||
 | 
			
		||||
  virtual void write_register(uint8_t reg, uint8_t mask, uint8_t bits, uint8_t start_position) = 0;
 | 
			
		||||
 | 
			
		||||
  sensor::Sensor *distance_sensor_;
 | 
			
		||||
  sensor::Sensor *energy_sensor_;
 | 
			
		||||
  binary_sensor::BinarySensor *thunder_alert_binary_sensor_;
 | 
			
		||||
  GPIOPin *irq_pin_;
 | 
			
		||||
 | 
			
		||||
  bool indoor_;
 | 
			
		||||
  uint8_t noise_level_;
 | 
			
		||||
  uint8_t watchdog_threshold_;
 | 
			
		||||
  uint8_t spike_rejection_;
 | 
			
		||||
  uint8_t lightning_threshold_;
 | 
			
		||||
  bool mask_disturber_;
 | 
			
		||||
  uint8_t div_ratio_;
 | 
			
		||||
  uint8_t capacitance_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace as3935
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										16
									
								
								esphome/components/as3935/binary_sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								esphome/components/as3935/binary_sensor.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import binary_sensor
 | 
			
		||||
from . import AS3935, CONF_AS3935_ID
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ['as3935']
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID(CONF_AS3935_ID): cv.use_id(AS3935),
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    hub = yield cg.get_variable(config[CONF_AS3935_ID])
 | 
			
		||||
    var = yield binary_sensor.new_binary_sensor(config)
 | 
			
		||||
    cg.add(hub.set_thunder_alert_binary_sensor(var))
 | 
			
		||||
							
								
								
									
										30
									
								
								esphome/components/as3935/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								esphome/components/as3935/sensor.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import sensor
 | 
			
		||||
from esphome.const import CONF_DISTANCE, CONF_LIGHTNING_ENERGY, \
 | 
			
		||||
    UNIT_KILOMETER, UNIT_EMPTY, ICON_SIGNAL_DISTANCE_VARIANT, ICON_FLASH
 | 
			
		||||
from . import AS3935, CONF_AS3935_ID
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ['as3935']
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.Schema({
 | 
			
		||||
    cv.GenerateID(CONF_AS3935_ID): cv.use_id(AS3935),
 | 
			
		||||
    cv.Optional(CONF_DISTANCE):
 | 
			
		||||
        sensor.sensor_schema(UNIT_KILOMETER, ICON_SIGNAL_DISTANCE_VARIANT, 1),
 | 
			
		||||
    cv.Optional(CONF_LIGHTNING_ENERGY):
 | 
			
		||||
        sensor.sensor_schema(UNIT_EMPTY, ICON_FLASH, 1),
 | 
			
		||||
}).extend(cv.COMPONENT_SCHEMA)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    hub = yield cg.get_variable(config[CONF_AS3935_ID])
 | 
			
		||||
 | 
			
		||||
    if CONF_DISTANCE in config:
 | 
			
		||||
        conf = config[CONF_DISTANCE]
 | 
			
		||||
        distance_sensor = yield sensor.new_sensor(conf)
 | 
			
		||||
        cg.add(hub.set_distance_sensor(distance_sensor))
 | 
			
		||||
 | 
			
		||||
    if CONF_LIGHTNING_ENERGY in config:
 | 
			
		||||
        conf = config[CONF_LIGHTNING_ENERGY]
 | 
			
		||||
        lightning_energy_sensor = yield sensor.new_sensor(conf)
 | 
			
		||||
        cg.add(hub.set_energy_sensor(lightning_energy_sensor))
 | 
			
		||||
							
								
								
									
										20
									
								
								esphome/components/as3935_i2c/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								esphome/components/as3935_i2c/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import as3935, i2c
 | 
			
		||||
from esphome.const import CONF_ID
 | 
			
		||||
 | 
			
		||||
AUTO_LOAD = ['as3935']
 | 
			
		||||
DEPENDENCIES = ['i2c']
 | 
			
		||||
 | 
			
		||||
as3935_i2c_ns = cg.esphome_ns.namespace('as3935_i2c')
 | 
			
		||||
I2CAS3935 = as3935_i2c_ns.class_('I2CAS3935Component', as3935.AS3935, i2c.I2CDevice)
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.All(as3935.AS3935_SCHEMA.extend({
 | 
			
		||||
    cv.GenerateID(): cv.declare_id(I2CAS3935),
 | 
			
		||||
}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x03)))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    yield as3935.setup_as3935(var, config)
 | 
			
		||||
    yield i2c.register_i2c_device(var, config)
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user