mirror of
				https://github.com/esphome/esphome.git
				synced 2025-11-03 00:21:56 +00:00 
			
		
		
		
	Compare commits
	
		
			765 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					60e6366521 | ||
| 
						 | 
					072b2c445c | ||
| 
						 | 
					219fe41831 | ||
| 
						 | 
					dcc8bb83af | ||
| 
						 | 
					a8e3521f3c | ||
| 
						 | 
					706dc6d116 | ||
| 
						 | 
					84accb6df6 | ||
| 
						 | 
					8421570b18 | ||
| 
						 | 
					d355543ac9 | ||
| 
						 | 
					3a597c5aa6 | ||
| 
						 | 
					c7dddaded4 | ||
| 
						 | 
					aae4b2ea5d | ||
| 
						 | 
					310e2a0b20 | ||
| 
						 | 
					0b04d143ac | ||
| 
						 | 
					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 | ||
| 
						 | 
					fdbc59a159 | ||
| 
						 | 
					0db37bb55c | ||
| 
						 | 
					2ff2750628 | ||
| 
						 | 
					eae5c17b87 | ||
| 
						 | 
					a59cde91ad | ||
| 
						 | 
					1f243ae37e | ||
| 
						 | 
					603f82977e | ||
| 
						 | 
					2d70422a6f | ||
| 
						 | 
					e2c8b21195 | ||
| 
						 | 
					7adaeacd0b | ||
| 
						 | 
					3aaa92fdff | ||
| 
						 | 
					5efd076c08 | ||
| 
						 | 
					5f535e9756 | ||
| 
						 | 
					70faeb2fa8 | ||
| 
						 | 
					469c0db981 | ||
| 
						 | 
					440e428aa4 | ||
| 
						 | 
					dde70c95a4 | ||
| 
						 | 
					09d1846261 | ||
| 
						 | 
					34d26a517d | ||
| 
						 | 
					d24b88271c | ||
| 
						 | 
					f22115792a | ||
| 
						 | 
					82a30558e1 | ||
| 
						 | 
					847fe5adca | ||
| 
						 | 
					775b51c6a1 | ||
| 
						 | 
					e0ad5a9009 | ||
| 
						 | 
					1bf01a9081 | ||
| 
						 | 
					6ae59bb43d | ||
| 
						 | 
					929600d7f7 | ||
| 
						 | 
					163d0c55ab | ||
| 
						 | 
					327ccb241e | ||
| 
						 | 
					6b3c7b0854 | ||
| 
						 | 
					681dcb51da | ||
| 
						 | 
					576d5021fd | ||
| 
						 | 
					6cd76f00ac | ||
| 
						 | 
					6f63a62a8d | ||
| 
						 | 
					8867a0fcfb | ||
| 
						 | 
					42b4a166ec | ||
| 
						 | 
					c27fd0f01a | ||
| 
						 | 
					dcb4a0a81e | ||
| 
						 | 
					17da9fddc3 | ||
| 
						 | 
					31aa3c55ca | ||
| 
						 | 
					eca3685ea0 | ||
| 
						 | 
					bd216c5c63 | ||
| 
						 | 
					31ff76427c | ||
| 
						 | 
					2229aa6ccc | ||
| 
						 | 
					872b468415 | ||
| 
						 | 
					9f022a7433 | ||
| 
						 | 
					85a958e300 | ||
| 
						 | 
					0ee56195ae | ||
| 
						 | 
					48f52db1d9 | ||
| 
						 | 
					d2c7afeef0 | ||
| 
						 | 
					644aec791e | ||
| 
						 | 
					b70a0325c5 | ||
| 
						 | 
					268387f829 | ||
| 
						 | 
					b975caef1e | ||
| 
						 | 
					54fe1c7d55 | ||
| 
						 | 
					89c1274d56 | ||
| 
						 | 
					f9ca3f1c27 | ||
| 
						 | 
					26dbc30279 | ||
| 
						 | 
					4bee316425 | ||
| 
						 | 
					0759140dc2 | ||
| 
						 | 
					a943bc6c80 | ||
| 
						 | 
					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 | ||
| 
						 | 
					3ddf5a4ec7 | ||
| 
						 | 
					f2d6817d8a | ||
| 
						 | 
					31821e6309 | ||
| 
						 | 
					1ce257c721 | ||
| 
						 | 
					8dd971b25e | ||
| 
						 | 
					7f507935b1 | ||
| 
						 | 
					3b1ba27043 | ||
| 
						 | 
					4b7c5aa05c | ||
| 
						 | 
					5fca02c712 | ||
| 
						 | 
					31ddd3f668 | ||
| 
						 | 
					f35f6d2348 | ||
| 
						 | 
					02d34a0238 | ||
| 
						 | 
					e4bbb56f6b | ||
| 
						 | 
					96d30e28d4 | ||
| 
						 | 
					41b73ff892 | ||
| 
						 | 
					afc4e45fb0 | ||
| 
						 | 
					8778ddd5c5 | ||
| 
						 | 
					3089ffa8e7 | ||
| 
						 | 
					15cb0e4ff3 | ||
| 
						 | 
					2f3c6d5b58 | ||
| 
						 | 
					ead5e8d855 | ||
| 
						 | 
					fd9a9ecc63 | ||
| 
						 | 
					bbb8ea7ec2 | ||
| 
						 | 
					667ed94e29 | ||
| 
						 | 
					23f1798d20 | ||
| 
						 | 
					6f3c126805 | ||
| 
						 | 
					a9ae70cff1 | ||
| 
						 | 
					d7a8c50c98 | ||
| 
						 | 
					9e56318498 | ||
| 
						 | 
					2570f2d6f2 | ||
| 
						 | 
					928df2dcd1 | ||
| 
						 | 
					2decb8115c | ||
| 
						 | 
					9d26c16471 | ||
| 
						 | 
					5893506528 | ||
| 
						 | 
					df0d33c3cd | ||
| 
						 | 
					359f54d3c1 | ||
| 
						 | 
					68ce1b18c4 | ||
| 
						 | 
					61ba2e0f35 | ||
| 
						 | 
					76d7802650 | ||
| 
						 | 
					9fa1a334e6 | ||
| 
						 | 
					9be16916b7 | ||
| 
						 | 
					4a5365f6a0 | ||
| 
						 | 
					0ced5509fc | ||
| 
						 | 
					bd6b9ff1da | ||
| 
						 | 
					edee28acf0 | ||
| 
						 | 
					127efe9a52 | ||
| 
						 | 
					53e8b3ed3e | ||
| 
						 | 
					a23ebead68 | ||
| 
						 | 
					f39d459555 | ||
| 
						 | 
					6a17fe375e | ||
| 
						 | 
					6e7d25ed42 | ||
| 
						 | 
					a676ff23de | ||
| 
						 | 
					85513476ce | ||
| 
						 | 
					e62443933c | ||
| 
						 | 
					3b48aa5911 | ||
| 
						 | 
					b7daee533a | ||
| 
						 | 
					7a14ab825e | ||
| 
						 | 
					4323ca88c3 | ||
| 
						 | 
					4bc3067725 | ||
| 
						 | 
					28f2a7f99c | ||
| 
						 | 
					72218171b3 | ||
| 
						 | 
					0d9f5ef363 | ||
| 
						 | 
					7b5c4359c6 | ||
| 
						 | 
					72a80f559a | ||
| 
						 | 
					dac624231f | ||
| 
						 | 
					510e53de70 | ||
| 
						 | 
					d8963ea25a | ||
| 
						 | 
					9ed06444e1 | ||
| 
						 | 
					df50b95e5a | ||
| 
						 | 
					12ff280d3b | ||
| 
						 | 
					16c2929bb4 | ||
| 
						 | 
					422754ed63 | ||
| 
						 | 
					aa7389432e | ||
| 
						 | 
					bd45f6bd8e | ||
| 
						 | 
					999c1a5357 | ||
| 
						 | 
					a323679771 | ||
| 
						 | 
					1fa4a2d256 | ||
| 
						 | 
					8c73558165 | ||
| 
						 | 
					ed61c1dd58 | ||
| 
						 | 
					8be444a25e | ||
| 
						 | 
					6306d44955 | ||
| 
						 | 
					6fff2e5957 | ||
| 
						 | 
					dd79e37933 | ||
| 
						 | 
					a1b6a91642 | ||
| 
						 | 
					60d67e5428 | ||
| 
						 | 
					5bb963fa82 | ||
| 
						 | 
					83fa51a580 | ||
| 
						 | 
					0281914507 | ||
| 
						 | 
					9231b80aa9 | ||
| 
						 | 
					3e044db9f1 | ||
| 
						 | 
					855e9367d4 | ||
| 
						 | 
					db0dd6af09 | ||
| 
						 | 
					b7afb8c887 | ||
| 
						 | 
					02c9ada876 | ||
| 
						 | 
					f811b1157c | ||
| 
						 | 
					797aadaf26 | ||
| 
						 | 
					f1a0e5a313 | ||
| 
						 | 
					f2540bae23 | ||
| 
						 | 
					a1a7448868 | ||
| 
						 | 
					e373620393 | ||
| 
						 | 
					23dcfe5075 | ||
| 
						 | 
					7bab279c6a | ||
| 
						 | 
					953d7f6193 | ||
| 
						 | 
					1d6dd3aa5e | ||
| 
						 | 
					9dd9e523ed | ||
| 
						 | 
					66cbfca99c | ||
| 
						 | 
					b0e6b48c50 | ||
| 
						 | 
					bb1937ab88 | ||
| 
						 | 
					86848b39db | ||
| 
						 | 
					f2506bb23b | ||
| 
						 | 
					3372ddc63d | ||
| 
						 | 
					3fe9c20188 | ||
| 
						 | 
					95428b4cfe | ||
| 
						 | 
					968ff4b619 | ||
| 
						 | 
					a07a835eb0 | ||
| 
						 | 
					667457989e | ||
| 
						 | 
					bedf1b7483 | ||
| 
						 | 
					53c182ad37 | ||
| 
						 | 
					ce45c81069 | ||
| 
						 | 
					1ee85295f2 | ||
| 
						 | 
					521c080989 | ||
| 
						 | 
					64541e0e35 | ||
| 
						 | 
					c3d4ef284d | ||
| 
						 | 
					ebb5fadba1 | ||
| 
						 | 
					c569d022ec | ||
| 
						 | 
					58586ea840 | ||
| 
						 | 
					27f431a027 | ||
| 
						 | 
					595dfe7e24 | ||
| 
						 | 
					766f6c045d | ||
| 
						 | 
					0a0713f0e2 | ||
| 
						 | 
					5e5b9f2205 | ||
| 
						 | 
					e6ff3c287d | ||
| 
						 | 
					295585379e | ||
| 
						 | 
					c7609ba5e7 | ||
| 
						 | 
					8e75980ebd | ||
| 
						 | 
					6682c43dfa | ||
| 
						 | 
					049807e3ab | ||
| 
						 | 
					336f12b24d | ||
| 
						 | 
					be5330b6ae | ||
| 
						 | 
					e90829eef2 | ||
| 
						 | 
					dc4c1bc225 | ||
| 
						 | 
					a14d12b0c1 | ||
| 
						 | 
					526a414743 | ||
| 
						 | 
					7fd5634a51 | ||
| 
						 | 
					cb9f36a153 | ||
| 
						 | 
					ac0b095941 | ||
| 
						 | 
					cda9bad233 | ||
| 
						 | 
					41db8a1264 | ||
| 
						 | 
					e7e785fd60 | ||
| 
						 | 
					300d3a1f46 | ||
| 
						 | 
					356554c08d | ||
| 
						 | 
					ced28ad006 | ||
| 
						 | 
					0272048899 | ||
| 
						 | 
					5b0d30bc09 | ||
| 
						 | 
					5859d4b01f | ||
| 
						 | 
					1dab1314ff | ||
| 
						 | 
					1f7e1daa73 | ||
| 
						 | 
					d27b01f02c | ||
| 
						 | 
					67c56ab97b | ||
| 
						 | 
					6ccab2bef9 | ||
| 
						 | 
					475aa4879c | ||
| 
						 | 
					4452473735 | ||
| 
						 | 
					e0a556e987 | ||
| 
						 | 
					de51bdda5b | ||
| 
						 | 
					386cd4f35f | ||
| 
						 | 
					477311dac6 | ||
| 
						 | 
					948adfd28c | ||
| 
						 | 
					aebbd7673b | ||
| 
						 | 
					3baaf6b7c4 | ||
| 
						 | 
					7ad2d86929 | ||
| 
						 | 
					4a14221e2b | ||
| 
						 | 
					114ebf9fe1 | ||
| 
						 | 
					0d7bb1286a | ||
| 
						 | 
					92a4ee5652 | ||
| 
						 | 
					9fea094b4e | ||
| 
						 | 
					d332e491ad | ||
| 
						 | 
					0ccfe33427 | ||
| 
						 | 
					596c334fcb | ||
| 
						 | 
					ca4450858f | ||
| 
						 | 
					f3ec83fe31 | ||
| 
						 | 
					c4ada8c9f0 | ||
| 
						 | 
					23df5d8af7 | ||
| 
						 | 
					289acade1e | ||
| 
						 | 
					a99f99779a | ||
| 
						 | 
					5c14ca030a | ||
| 
						 | 
					0fc6a027a7 | ||
| 
						 | 
					3c0d97ef69 | ||
| 
						 | 
					5b4f98d414 | ||
| 
						 | 
					7ebfcd3807 | ||
| 
						 | 
					3ef0634dd2 | ||
| 
						 | 
					0928c9739f | ||
| 
						 | 
					e009f21a72 | ||
| 
						 | 
					606c412616 | ||
| 
						 | 
					975b5127d6 | ||
| 
						 | 
					60c9ffef30 | ||
| 
						 | 
					99861259d7 | ||
| 
						 | 
					067ec30c56 | ||
| 
						 | 
					5a102c2ab7 | ||
| 
						 | 
					4b017e2096 | ||
| 
						 | 
					8495ce96a3 | ||
| 
						 | 
					55caf4f648 | ||
| 
						 | 
					c123f0091d | ||
| 
						 | 
					7c65d44976 | ||
| 
						 | 
					5b8d12a80c | ||
| 
						 | 
					3951a2b22a | ||
| 
						 | 
					69a74a30e8 | ||
| 
						 | 
					f3ee5b55e9 | ||
| 
						 | 
					f6cc9f7caa | ||
| 
						 | 
					a5d0ecdb13 | ||
| 
						 | 
					71cbc9cfb0 | ||
| 
						 | 
					88625c656d | ||
| 
						 | 
					971b15ac67 | ||
| 
						 | 
					a4edcc48ca | ||
| 
						 | 
					1778dd4df9 | ||
| 
						 | 
					311e837196 | ||
| 
						 | 
					3b00cfd6c4 | ||
| 
						 | 
					1c7ca4bc6f | ||
| 
						 | 
					38e7b597d6 | ||
| 
						 | 
					808ee19180 | ||
| 
						 | 
					c2a0c22bd9 | ||
| 
						 | 
					e785ad5401 | ||
| 
						 | 
					bacddc3673 | ||
| 
						 | 
					7dd0dabaf5 | ||
| 
						 | 
					92f8b043ce | ||
| 
						 | 
					2e5fd7e90d | ||
| 
						 | 
					407c46cb03 | ||
| 
						 | 
					12ce448f2d | ||
| 
						 | 
					340530566c | ||
| 
						 | 
					f4a55eafd7 | ||
| 
						 | 
					fc8f270a9f | ||
| 
						 | 
					e7ce8f7a13 | ||
| 
						 | 
					3d1cce2a29 | ||
| 
						 | 
					6be47488a4 | ||
| 
						 | 
					5b94bb894e | ||
| 
						 | 
					26aa399bea | ||
| 
						 | 
					af0c213024 | ||
| 
						 | 
					88e036ddb2 | ||
| 
						 | 
					8012de3ba4 | ||
| 
						 | 
					e2b1d5438d | ||
| 
						 | 
					fe66f93a01 | ||
| 
						 | 
					581dffe2d4 | ||
| 
						 | 
					6f22006bf7 | ||
| 
						 | 
					fe54700687 | ||
| 
						 | 
					f0c0131ed1 | ||
| 
						 | 
					52750d933b | ||
| 
						 | 
					7e7a85abfd | ||
| 
						 | 
					c1b8107aaf | ||
| 
						 | 
					0bdcce609f | ||
| 
						 | 
					6b702f8014 | ||
| 
						 | 
					078ab26fd2 | ||
| 
						 | 
					efe81e0856 | ||
| 
						 | 
					41d637affe | ||
| 
						 | 
					10cc11bab1 | ||
| 
						 | 
					34ca5d6d8a | ||
| 
						 | 
					b27c778cb7 | ||
| 
						 | 
					f311a1bb30 | ||
| 
						 | 
					7a450ed41c | ||
| 
						 | 
					944f0169cb | ||
| 
						 | 
					4da0e0c223 | ||
| 
						 | 
					52c0b45f41 | ||
| 
						 | 
					fcef3be5c7 | ||
| 
						 | 
					ef7b936d60 | ||
| 
						 | 
					e0eac6ba25 | ||
| 
						 | 
					50af0da13f | ||
| 
						 | 
					a8d87a1fee | ||
| 
						 | 
					a66ed437d6 | ||
| 
						 | 
					b95f53a7c7 | ||
| 
						 | 
					d7eacb2a2f | ||
| 
						 | 
					41ec337ba0 | ||
| 
						 | 
					5e61014e98 | ||
| 
						 | 
					0a4d49accb | ||
| 
						 | 
					3d9301a0f7 | ||
| 
						 | 
					1b8d242505 | ||
| 
						 | 
					463ad6f94b | ||
| 
						 | 
					e4e315f723 | ||
| 
						 | 
					7fecec128f | ||
| 
						 | 
					a15b647d13 | ||
| 
						 | 
					feab956ea9 | ||
| 
						 | 
					4b7a41922c | ||
| 
						 | 
					61762bf299 | ||
| 
						 | 
					6eb769e6d5 | ||
| 
						 | 
					4c8bd7a70c | ||
| 
						 | 
					d9cf91210e | ||
| 
						 | 
					fcf5da66c0 | ||
| 
						 | 
					b6edbf9d0b | ||
| 
						 | 
					596f995af8 | ||
| 
						 | 
					8ce176aaba | ||
| 
						 | 
					f185ba7a21 | ||
| 
						 | 
					7c28eca6de | ||
| 
						 | 
					9fa95a9a21 | ||
| 
						 | 
					6ccb68aaf1 | ||
| 
						 | 
					6a76a3642e | ||
| 
						 | 
					a50eb3579a | ||
| 
						 | 
					4a74027848 | ||
| 
						 | 
					98bdfc821e | ||
| 
						 | 
					176c712eeb | ||
| 
						 | 
					7b26ecc0dc | ||
| 
						 | 
					92e909568c | ||
| 
						 | 
					e3f7e0d14a | ||
| 
						 | 
					5fcda34c45 | ||
| 
						 | 
					bd23584073 | ||
| 
						 | 
					6d6876ca39 | ||
| 
						 | 
					9ebf6439b2 | ||
| 
						 | 
					1404d39ffe | ||
| 
						 | 
					7153c7a941 | ||
| 
						 | 
					3437e23c8e | ||
| 
						 | 
					eb39add6fc | ||
| 
						 | 
					e13bffbb2f | ||
| 
						 | 
					e347cb538d | ||
| 
						 | 
					d799c03f0c | ||
| 
						 | 
					c86675f644 | ||
| 
						 | 
					a00fe09c66 | ||
| 
						 | 
					e0fe0a2835 | ||
| 
						 | 
					e1f48b5028 | ||
| 
						 | 
					e4a2677de4 | ||
| 
						 | 
					ab40156c16 | ||
| 
						 | 
					804a1ed7b3 | ||
| 
						 | 
					4de98fd782 | ||
| 
						 | 
					d8fd5e9bb4 | ||
| 
						 | 
					8424f9c34b | ||
| 
						 | 
					70016d7641 | ||
| 
						 | 
					7ced977f2a | ||
| 
						 | 
					5ac9b8d545 | ||
| 
						 | 
					a683108b5d | ||
| 
						 | 
					54dd9e94ab | ||
| 
						 | 
					4f5389998f | ||
| 
						 | 
					8a61c21051 | ||
| 
						 | 
					9b2b7dabd1 | ||
| 
						 | 
					86130b2954 | ||
| 
						 | 
					c2787c1ce5 | ||
| 
						 | 
					e81338b4f5 | ||
| 
						 | 
					40ab2d5e5a | ||
| 
						 | 
					7a703d10f4 | ||
| 
						 | 
					4c5f908e3a | ||
| 
						 | 
					27e1294630 | ||
| 
						 | 
					c2fb71c41f | ||
| 
						 | 
					7685b32ac0 | ||
| 
						 | 
					5a1e806d14 | ||
| 
						 | 
					cf73d777db | ||
| 
						 | 
					47231977d7 | ||
| 
						 | 
					d29b280d82 | ||
| 
						 | 
					77514c0a06 | ||
| 
						 | 
					341a27e56b | ||
| 
						 | 
					739b7bfc05 | ||
| 
						 | 
					01b49e8d59 | ||
| 
						 | 
					52dbd35118 | ||
| 
						 | 
					92439abeb4 | ||
| 
						 | 
					9bce35e335 | ||
| 
						 | 
					94cb7bf6bd | ||
| 
						 | 
					cdf53cb3e8 | ||
| 
						 | 
					30bd74cb6c | ||
| 
						 | 
					e72ef9e061 | ||
| 
						 | 
					3f4bba57f4 | ||
| 
						 | 
					38c24fd100 | ||
| 
						 | 
					c6ed88579f | ||
| 
						 | 
					7f9462ebf3 | ||
| 
						 | 
					4e44081fdf | ||
| 
						 | 
					3cd1c2d723 | ||
| 
						 | 
					907be3025c | ||
| 
						 | 
					815da05b29 | ||
| 
						 | 
					a2083fc901 | ||
| 
						 | 
					06cb7497c8 | ||
| 
						 | 
					1e12cba176 | ||
| 
						 | 
					1d0c812e44 | ||
| 
						 | 
					4948ad95b0 | ||
| 
						 | 
					dd801636af | ||
| 
						 | 
					a110911371 | ||
| 
						 | 
					22fd4ec722 | ||
| 
						 | 
					5db70bea3c | ||
| 
						 | 
					00ff99cc7d | ||
| 
						 | 
					a51eaa93b5 | ||
| 
						 | 
					eddaee3a80 | ||
| 
						 | 
					2b432ea829 | ||
| 
						 | 
					09ab55b85d | ||
| 
						 | 
					183e0f4d23 | ||
| 
						 | 
					a8c17e5d05 | ||
| 
						 | 
					2e65d6c02c | ||
| 
						 | 
					07da8950c4 | ||
| 
						 | 
					c206846816 | ||
| 
						 | 
					6aac4191f8 | ||
| 
						 | 
					5a7b66e207 | ||
| 
						 | 
					da2821ab36 | ||
| 
						 | 
					7556845079 | ||
| 
						 | 
					e646f02e27 | ||
| 
						 | 
					39ead80df3 | ||
| 
						 | 
					71e221f6ec | ||
| 
						 | 
					7c7032c59e | ||
| 
						 | 
					2b88c987da | ||
| 
						 | 
					3d49ffa134 | ||
| 
						 | 
					f2f869db89 | ||
| 
						 | 
					e37e986276 | ||
| 
						 | 
					5d5529ff15 | ||
| 
						 | 
					db90a7a334 | ||
| 
						 | 
					bb9c1faffa | ||
| 
						 | 
					b4c4dc8cfb | ||
| 
						 | 
					c3b2ab0085 | ||
| 
						 | 
					f087e313d4 | ||
| 
						 | 
					f88cf99b67 | ||
| 
						 | 
					f394968bd0 | ||
| 
						 | 
					70def85ba1 | 
							
								
								
									
										137
									
								
								.clang-format
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								.clang-format
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,137 @@
 | 
			
		||||
Language:        Cpp
 | 
			
		||||
AccessModifierOffset: -1
 | 
			
		||||
AlignAfterOpenBracket: Align
 | 
			
		||||
AlignConsecutiveAssignments: false
 | 
			
		||||
AlignConsecutiveDeclarations: false
 | 
			
		||||
AlignEscapedNewlines: DontAlign
 | 
			
		||||
AlignOperands:   true
 | 
			
		||||
AlignTrailingComments: true
 | 
			
		||||
AllowAllParametersOfDeclarationOnNextLine: true
 | 
			
		||||
AllowShortBlocksOnASingleLine: false
 | 
			
		||||
AllowShortCaseLabelsOnASingleLine: false
 | 
			
		||||
AllowShortFunctionsOnASingleLine: All
 | 
			
		||||
AllowShortIfStatementsOnASingleLine: false
 | 
			
		||||
AllowShortLoopsOnASingleLine: false
 | 
			
		||||
AlwaysBreakAfterReturnType: None
 | 
			
		||||
AlwaysBreakBeforeMultilineStrings: false
 | 
			
		||||
AlwaysBreakTemplateDeclarations: MultiLine
 | 
			
		||||
BinPackArguments: true
 | 
			
		||||
BinPackParameters: true
 | 
			
		||||
BraceWrapping:
 | 
			
		||||
  AfterClass:      false
 | 
			
		||||
  AfterControlStatement: false
 | 
			
		||||
  AfterEnum:       false
 | 
			
		||||
  AfterFunction:   false
 | 
			
		||||
  AfterNamespace:  false
 | 
			
		||||
  AfterObjCDeclaration: false
 | 
			
		||||
  AfterStruct:     false
 | 
			
		||||
  AfterUnion:      false
 | 
			
		||||
  AfterExternBlock: false
 | 
			
		||||
  BeforeCatch:     false
 | 
			
		||||
  BeforeElse:      false
 | 
			
		||||
  IndentBraces:    false
 | 
			
		||||
  SplitEmptyFunction: true
 | 
			
		||||
  SplitEmptyRecord: true
 | 
			
		||||
  SplitEmptyNamespace: true
 | 
			
		||||
BreakBeforeBinaryOperators: None
 | 
			
		||||
BreakBeforeBraces: Attach
 | 
			
		||||
BreakBeforeInheritanceComma: false
 | 
			
		||||
BreakInheritanceList: BeforeColon
 | 
			
		||||
BreakBeforeTernaryOperators: true
 | 
			
		||||
BreakConstructorInitializersBeforeComma: false
 | 
			
		||||
BreakConstructorInitializers: BeforeColon
 | 
			
		||||
BreakAfterJavaFieldAnnotations: false
 | 
			
		||||
BreakStringLiterals: true
 | 
			
		||||
ColumnLimit:     120
 | 
			
		||||
CommentPragmas:  '^ IWYU pragma:'
 | 
			
		||||
CompactNamespaces: false
 | 
			
		||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
 | 
			
		||||
ConstructorInitializerIndentWidth: 4
 | 
			
		||||
ContinuationIndentWidth: 4
 | 
			
		||||
Cpp11BracedListStyle: true
 | 
			
		||||
DerivePointerAlignment: true
 | 
			
		||||
DisableFormat:   false
 | 
			
		||||
ExperimentalAutoDetectBinPacking: false
 | 
			
		||||
FixNamespaceComments: true
 | 
			
		||||
ForEachMacros:
 | 
			
		||||
  - foreach
 | 
			
		||||
  - Q_FOREACH
 | 
			
		||||
  - BOOST_FOREACH
 | 
			
		||||
IncludeBlocks:   Preserve
 | 
			
		||||
IncludeCategories:
 | 
			
		||||
  - Regex:           '^<ext/.*\.h>'
 | 
			
		||||
    Priority:        2
 | 
			
		||||
  - Regex:           '^<.*\.h>'
 | 
			
		||||
    Priority:        1
 | 
			
		||||
  - Regex:           '^<.*'
 | 
			
		||||
    Priority:        2
 | 
			
		||||
  - Regex:           '.*'
 | 
			
		||||
    Priority:        3
 | 
			
		||||
IncludeIsMainRegex: '([-_](test|unittest))?$'
 | 
			
		||||
IndentCaseLabels: true
 | 
			
		||||
IndentPPDirectives: None
 | 
			
		||||
IndentWidth:     2
 | 
			
		||||
IndentWrappedFunctionNames: false
 | 
			
		||||
KeepEmptyLinesAtTheStartOfBlocks: false
 | 
			
		||||
MacroBlockBegin: ''
 | 
			
		||||
MacroBlockEnd:   ''
 | 
			
		||||
MaxEmptyLinesToKeep: 1
 | 
			
		||||
NamespaceIndentation: None
 | 
			
		||||
PenaltyBreakAssignment: 2
 | 
			
		||||
PenaltyBreakBeforeFirstCallParameter: 1
 | 
			
		||||
PenaltyBreakComment: 300
 | 
			
		||||
PenaltyBreakFirstLessLess: 120
 | 
			
		||||
PenaltyBreakString: 1000
 | 
			
		||||
PenaltyBreakTemplateDeclaration: 10
 | 
			
		||||
PenaltyExcessCharacter: 1000000
 | 
			
		||||
PenaltyReturnTypeOnItsOwnLine: 2000
 | 
			
		||||
PointerAlignment: Right
 | 
			
		||||
RawStringFormats:
 | 
			
		||||
  - Language:        Cpp
 | 
			
		||||
    Delimiters:
 | 
			
		||||
      - cc
 | 
			
		||||
      - CC
 | 
			
		||||
      - cpp
 | 
			
		||||
      - Cpp
 | 
			
		||||
      - CPP
 | 
			
		||||
      - 'c++'
 | 
			
		||||
      - 'C++'
 | 
			
		||||
    CanonicalDelimiter: ''
 | 
			
		||||
    BasedOnStyle:    google
 | 
			
		||||
  - Language:        TextProto
 | 
			
		||||
    Delimiters:
 | 
			
		||||
      - pb
 | 
			
		||||
      - PB
 | 
			
		||||
      - proto
 | 
			
		||||
      - PROTO
 | 
			
		||||
    EnclosingFunctions:
 | 
			
		||||
      - EqualsProto
 | 
			
		||||
      - EquivToProto
 | 
			
		||||
      - PARSE_PARTIAL_TEXT_PROTO
 | 
			
		||||
      - PARSE_TEST_PROTO
 | 
			
		||||
      - PARSE_TEXT_PROTO
 | 
			
		||||
      - ParseTextOrDie
 | 
			
		||||
      - ParseTextProtoOrDie
 | 
			
		||||
    CanonicalDelimiter: ''
 | 
			
		||||
    BasedOnStyle:    google
 | 
			
		||||
ReflowComments:  true
 | 
			
		||||
SortIncludes:    false
 | 
			
		||||
SortUsingDeclarations: false
 | 
			
		||||
SpaceAfterCStyleCast: true
 | 
			
		||||
SpaceAfterTemplateKeyword: false
 | 
			
		||||
SpaceBeforeAssignmentOperators: true
 | 
			
		||||
SpaceBeforeCpp11BracedList: false
 | 
			
		||||
SpaceBeforeCtorInitializerColon: true
 | 
			
		||||
SpaceBeforeInheritanceColon: true
 | 
			
		||||
SpaceBeforeParens: ControlStatements
 | 
			
		||||
SpaceBeforeRangeBasedForLoopColon: true
 | 
			
		||||
SpaceInEmptyParentheses: false
 | 
			
		||||
SpacesBeforeTrailingComments: 2
 | 
			
		||||
SpacesInAngles:  false
 | 
			
		||||
SpacesInContainerLiterals: false
 | 
			
		||||
SpacesInCStyleCastParentheses: false
 | 
			
		||||
SpacesInParentheses: false
 | 
			
		||||
SpacesInSquareBrackets: false
 | 
			
		||||
Standard:        Auto
 | 
			
		||||
TabWidth:        2
 | 
			
		||||
UseTab:          Never
 | 
			
		||||
							
								
								
									
										127
									
								
								.clang-tidy
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								.clang-tidy
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,127 @@
 | 
			
		||||
---
 | 
			
		||||
Checks: >-
 | 
			
		||||
  *,
 | 
			
		||||
  -abseil-*,
 | 
			
		||||
  -android-*,
 | 
			
		||||
  -boost-*,
 | 
			
		||||
  -bugprone-macro-parentheses,
 | 
			
		||||
  -cert-dcl50-cpp,
 | 
			
		||||
  -cert-err58-cpp,
 | 
			
		||||
  -clang-analyzer-core.CallAndMessage,
 | 
			
		||||
  -clang-analyzer-osx.*,
 | 
			
		||||
  -clang-analyzer-security.*,
 | 
			
		||||
  -cppcoreguidelines-avoid-goto,
 | 
			
		||||
  -cppcoreguidelines-c-copy-assignment-signature,
 | 
			
		||||
  -cppcoreguidelines-owning-memory,
 | 
			
		||||
  -cppcoreguidelines-pro-bounds-array-to-pointer-decay,
 | 
			
		||||
  -cppcoreguidelines-pro-bounds-constant-array-index,
 | 
			
		||||
  -cppcoreguidelines-pro-bounds-pointer-arithmetic,
 | 
			
		||||
  -cppcoreguidelines-pro-type-const-cast,
 | 
			
		||||
  -cppcoreguidelines-pro-type-cstyle-cast,
 | 
			
		||||
  -cppcoreguidelines-pro-type-member-init,
 | 
			
		||||
  -cppcoreguidelines-pro-type-reinterpret-cast,
 | 
			
		||||
  -cppcoreguidelines-pro-type-static-cast-downcast,
 | 
			
		||||
  -cppcoreguidelines-pro-type-union-access,
 | 
			
		||||
  -cppcoreguidelines-pro-type-vararg,
 | 
			
		||||
  -cppcoreguidelines-special-member-functions,
 | 
			
		||||
  -fuchsia-*,
 | 
			
		||||
  -fuchsia-default-arguments,
 | 
			
		||||
  -fuchsia-multiple-inheritance,
 | 
			
		||||
  -fuchsia-overloaded-operator,
 | 
			
		||||
  -fuchsia-statically-constructed-objects,
 | 
			
		||||
  -google-build-using-namespace,
 | 
			
		||||
  -google-explicit-constructor,
 | 
			
		||||
  -google-readability-braces-around-statements,
 | 
			
		||||
  -google-readability-casting,
 | 
			
		||||
  -google-readability-todo,
 | 
			
		||||
  -google-runtime-int,
 | 
			
		||||
  -google-runtime-references,
 | 
			
		||||
  -hicpp-*,
 | 
			
		||||
  -llvm-header-guard,
 | 
			
		||||
  -llvm-include-order,
 | 
			
		||||
  -misc-unconventional-assign-operator,
 | 
			
		||||
  -misc-unused-parameters,
 | 
			
		||||
  -modernize-deprecated-headers,
 | 
			
		||||
  -modernize-pass-by-value,
 | 
			
		||||
  -modernize-pass-by-value,
 | 
			
		||||
  -modernize-return-braced-init-list,
 | 
			
		||||
  -modernize-use-auto,
 | 
			
		||||
  -modernize-use-default-member-init,
 | 
			
		||||
  -modernize-use-equals-default,
 | 
			
		||||
  -mpi-*,
 | 
			
		||||
  -objc-*,
 | 
			
		||||
  -performance-unnecessary-value-param,
 | 
			
		||||
  -readability-braces-around-statements,
 | 
			
		||||
  -readability-else-after-return,
 | 
			
		||||
  -readability-implicit-bool-conversion,
 | 
			
		||||
  -readability-named-parameter,
 | 
			
		||||
  -readability-redundant-member-init,
 | 
			
		||||
  -warnings-as-errors,
 | 
			
		||||
  -zircon-*
 | 
			
		||||
WarningsAsErrors: '*'
 | 
			
		||||
HeaderFilterRegex: '^.*/src/esphome/.*'
 | 
			
		||||
AnalyzeTemporaryDtors: false
 | 
			
		||||
FormatStyle:     google
 | 
			
		||||
CheckOptions:
 | 
			
		||||
  - key:             google-readability-braces-around-statements.ShortStatementLines
 | 
			
		||||
    value:           '1'
 | 
			
		||||
  - key:             google-readability-function-size.StatementThreshold
 | 
			
		||||
    value:           '800'
 | 
			
		||||
  - key:             google-readability-namespace-comments.ShortNamespaceLines
 | 
			
		||||
    value:           '10'
 | 
			
		||||
  - key:             google-readability-namespace-comments.SpacesBeforeComments
 | 
			
		||||
    value:           '2'
 | 
			
		||||
  - key:             modernize-loop-convert.MaxCopySize
 | 
			
		||||
    value:           '16'
 | 
			
		||||
  - key:             modernize-loop-convert.MinConfidence
 | 
			
		||||
    value:           reasonable
 | 
			
		||||
  - key:             modernize-loop-convert.NamingStyle
 | 
			
		||||
    value:           CamelCase
 | 
			
		||||
  - key:             modernize-pass-by-value.IncludeStyle
 | 
			
		||||
    value:           llvm
 | 
			
		||||
  - key:             modernize-replace-auto-ptr.IncludeStyle
 | 
			
		||||
    value:           llvm
 | 
			
		||||
  - key:             modernize-use-nullptr.NullMacros
 | 
			
		||||
    value:           'NULL'
 | 
			
		||||
  - key:             readability-identifier-naming.LocalVariableCase
 | 
			
		||||
    value:           'lower_case'
 | 
			
		||||
  - key:             readability-identifier-naming.ClassCase
 | 
			
		||||
    value:           'CamelCase'
 | 
			
		||||
  - key:             readability-identifier-naming.StructCase
 | 
			
		||||
    value:           'CamelCase'
 | 
			
		||||
  - key:             readability-identifier-naming.EnumCase
 | 
			
		||||
    value:           'CamelCase'
 | 
			
		||||
  - key:             readability-identifier-naming.EnumConstantCase
 | 
			
		||||
    value:           'UPPER_CASE'
 | 
			
		||||
  - key:             readability-identifier-naming.StaticConstantCase
 | 
			
		||||
    value:           'UPPER_CASE'
 | 
			
		||||
  - key:             readability-identifier-naming.StaticVariableCase
 | 
			
		||||
    value:           'UPPER_CASE'
 | 
			
		||||
  - key:             readability-identifier-naming.GlobalConstantCase
 | 
			
		||||
    value:           'UPPER_CASE'
 | 
			
		||||
  - key:             readability-identifier-naming.ParameterCase
 | 
			
		||||
    value:           'lower_case'
 | 
			
		||||
  - key:             readability-identifier-naming.PrivateMemberPrefix
 | 
			
		||||
    value:           'NO_PRIVATE_MEMBERS_ALWAYS_USE_PROTECTED'
 | 
			
		||||
  - key:             readability-identifier-naming.PrivateMethodPrefix
 | 
			
		||||
    value:           'NO_PRIVATE_METHODS_ALWAYS_USE_PROTECTED'
 | 
			
		||||
  - key:             readability-identifier-naming.ClassMemberCase
 | 
			
		||||
    value:           'lower_case'
 | 
			
		||||
  - key:             readability-identifier-naming.ClassMemberCase
 | 
			
		||||
    value:           'lower_case'
 | 
			
		||||
  - key:             readability-identifier-naming.ProtectedMemberCase
 | 
			
		||||
    value:           'lower_case'
 | 
			
		||||
  - key:             readability-identifier-naming.ProtectedMemberSuffix
 | 
			
		||||
    value:           '_'
 | 
			
		||||
  - key:             readability-identifier-naming.FunctionCase
 | 
			
		||||
    value:           'lower_case'
 | 
			
		||||
  - key:             readability-identifier-naming.ClassMethodCase
 | 
			
		||||
    value:           'lower_case'
 | 
			
		||||
  - key:             readability-identifier-naming.ProtectedMethodCase
 | 
			
		||||
    value:           'lower_case'
 | 
			
		||||
  - key:             readability-identifier-naming.ProtectedMethodSuffix
 | 
			
		||||
    value:           '_'
 | 
			
		||||
  - key:             readability-identifier-naming.VirtualMethodCase
 | 
			
		||||
    value:           'lower_case'
 | 
			
		||||
  - key:             readability-identifier-naming.VirtualMethodSuffix
 | 
			
		||||
    value:           ''
 | 
			
		||||
							
								
								
									
										27
									
								
								.editorconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								.editorconfig
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
root = true
 | 
			
		||||
 | 
			
		||||
# general
 | 
			
		||||
[*]
 | 
			
		||||
end_of_line = lf
 | 
			
		||||
insert_final_newline = true
 | 
			
		||||
charset = utf-8
 | 
			
		||||
 | 
			
		||||
# python
 | 
			
		||||
[*.{py}]
 | 
			
		||||
indent_style = space
 | 
			
		||||
indent_size = 4
 | 
			
		||||
 | 
			
		||||
# C++
 | 
			
		||||
[*.{cpp,h,tcc}]
 | 
			
		||||
indent_style = space
 | 
			
		||||
indent_size = 2
 | 
			
		||||
 | 
			
		||||
# Web
 | 
			
		||||
[*.{js,html,css}]
 | 
			
		||||
indent_style = space
 | 
			
		||||
indent_size = 2
 | 
			
		||||
 | 
			
		||||
# YAML
 | 
			
		||||
[*.{yaml,yml}]
 | 
			
		||||
indent_style = space
 | 
			
		||||
indent_size = 2
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
							
								
								
									
										49
									
								
								.github/ISSUE_TEMPLATE/bug_report.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										49
									
								
								.github/ISSUE_TEMPLATE/bug_report.md
									
									
									
									
										vendored
									
									
								
							@@ -1,49 +0,0 @@
 | 
			
		||||
---
 | 
			
		||||
name: Bug report
 | 
			
		||||
about: Create a report to help esphomelib improve
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
<!-- Thanks for reporting a bug for this project. READ THIS FIRST:
 | 
			
		||||
- Please make sure to submit issues in the right GitHub repository, if unsure just post it here:
 | 
			
		||||
  - esphomeyaml [here] - This is mostly for reporting bugs when compiling and when you get a long stack trace while compiling or if a configuration fails to validate.
 | 
			
		||||
  - esphomelib [https://github.com/OttoWinter/esphomelib] - Report bugs there if the ESP is crashing or a feature is not working as expected.
 | 
			
		||||
  - esphomedocs [https://github.com/OttoWinter/esphomedocs] - Report bugs there if the documentation is wrong/outdated.
 | 
			
		||||
- Provide as many details as possible. Paste logs, configuration sample and code into the backticks (```).
 | 
			
		||||
 
 | 
			
		||||
  DO NOT DELETE ANY TEXT from this template! Otherwise the issue may be closed without a comment.
 | 
			
		||||
-->
 | 
			
		||||
 | 
			
		||||
**Operating environment (Hass.io/Docker/pip/etc.):**
 | 
			
		||||
<!--
 | 
			
		||||
Please provide details about your environment.
 | 
			
		||||
-->
 | 
			
		||||
 | 
			
		||||
**ESP (ESP32/ESP8266/Board/Sonoff):**
 | 
			
		||||
<!--
 | 
			
		||||
Please provide details about which ESP you're using.
 | 
			
		||||
-->
 | 
			
		||||
 | 
			
		||||
**Affected component:**
 | 
			
		||||
<!--
 | 
			
		||||
Please add the link to the documentation at https://esphomelib.com/esphomeyaml/index.html of the component in question.
 | 
			
		||||
-->
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
**Description of problem:**
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
**Problem-relevant YAML-configuration entries:**
 | 
			
		||||
```yaml
 | 
			
		||||
PASTE YAML FILE HERE
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
**Traceback (if applicable):**
 | 
			
		||||
<!--
 | 
			
		||||
Please copy the traceback here if compilation is failing. If possible, also connect to the ESP and copy its logs into the backticks.
 | 
			
		||||
-->
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
**Additional information:**
 | 
			
		||||
							
								
								
									
										21
									
								
								.github/ISSUE_TEMPLATE/feature_request.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										21
									
								
								.github/ISSUE_TEMPLATE/feature_request.md
									
									
									
									
										vendored
									
									
								
							@@ -1,21 +0,0 @@
 | 
			
		||||
---
 | 
			
		||||
name: Feature request
 | 
			
		||||
about: Suggest an idea for this project
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
<!-- READ THIS FIRST:
 | 
			
		||||
 - This is for feature requests only, if you want to have a certain new sensor/module supported, please use the "new integration" template.
 | 
			
		||||
 - Please be as descriptive as possible, especially use-cases that can otherwise not be solved boost the problem's priority.
 | 
			
		||||
 
 | 
			
		||||
   DO NOT DELETE ANY TEXT from this template! Otherwise the issue may be closed without a comment.
 | 
			
		||||
-->
 | 
			
		||||
 | 
			
		||||
**Is your feature request related to a problem/use-case? Please describe.**
 | 
			
		||||
<!-- A clear and concise description of what the problem is. -->
 | 
			
		||||
 | 
			
		||||
**Describe the solution you'd like:**
 | 
			
		||||
<!-- A description of what you want to happen. -->
 | 
			
		||||
 | 
			
		||||
**Additional context:**
 | 
			
		||||
<!-- Add any other context about the feature request here. -->
 | 
			
		||||
							
								
								
									
										13
									
								
								.github/ISSUE_TEMPLATE/new-integration.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										13
									
								
								.github/ISSUE_TEMPLATE/new-integration.md
									
									
									
									
										vendored
									
									
								
							@@ -1,13 +0,0 @@
 | 
			
		||||
---
 | 
			
		||||
name: New integration
 | 
			
		||||
about: Suggest a new integration for esphomelib
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
DO NOT POST NEW INTEGRATION REQUESTS HERE!
 | 
			
		||||
 | 
			
		||||
Please post all new integration requests in the esphomelib repository:
 | 
			
		||||
 | 
			
		||||
https://github.com/OttoWinter/esphomelib/issues
 | 
			
		||||
 | 
			
		||||
Thank you!
 | 
			
		||||
							
								
								
									
										5
									
								
								.github/PULL_REQUEST_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.github/PULL_REQUEST_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							@@ -3,12 +3,11 @@
 | 
			
		||||
 | 
			
		||||
**Related issue (if applicable):** fixes <link to issue>
 | 
			
		||||
 | 
			
		||||
**Pull request in [esphomedocs](https://github.com/OttoWinter/esphomedocs) with documentation (if applicable):** OttoWinter/esphomedocs#<esphomedocs PR number goes here>
 | 
			
		||||
**Pull request in [esphomelib](https://github.com/OttoWinter/esphomelib) with C++ framework changes (if applicable):** OttoWinter/esphomelib#<esphomelib PR number goes here>
 | 
			
		||||
**Pull request in [esphome-docs](https://github.com/esphome/esphome-docs) with documentation (if applicable):** esphome/esphome-docs#<esphome-docs PR number goes here>
 | 
			
		||||
 | 
			
		||||
## Checklist:
 | 
			
		||||
  - [ ] The code change is tested and works locally.
 | 
			
		||||
  - [ ] Tests have been added to verify that the new code works (under `tests/` folder).
 | 
			
		||||
 | 
			
		||||
If user exposed functionality or configuration variables are added/changed:
 | 
			
		||||
  - [ ] Documentation added/updated in [esphomedocs](https://github.com/OttoWinter/esphomedocs).
 | 
			
		||||
  - [ ] Documentation added/updated in [esphome-docs](https://github.com/esphome/esphome-docs).
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										7
									
								
								.github/issue-close-app.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								.github/issue-close-app.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
comment: >-
 | 
			
		||||
  https://github.com/esphome/esphome/issues/430
 | 
			
		||||
issueConfigs:
 | 
			
		||||
- content:
 | 
			
		||||
  - "OTHERWISE THE ISSUE WILL BE CLOSED AUTOMATICALLY"
 | 
			
		||||
 | 
			
		||||
caseInsensitive: false
 | 
			
		||||
							
								
								
									
										97
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										97
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -6,6 +6,19 @@ __pycache__/
 | 
			
		||||
# C extensions
 | 
			
		||||
*.so
 | 
			
		||||
 | 
			
		||||
# Hide sublime text stuff
 | 
			
		||||
*.sublime-project
 | 
			
		||||
*.sublime-workspace
 | 
			
		||||
 | 
			
		||||
# Hide some OS X stuff
 | 
			
		||||
.DS_Store
 | 
			
		||||
.AppleDouble
 | 
			
		||||
.LSOverride
 | 
			
		||||
Icon
 | 
			
		||||
 | 
			
		||||
# Thumbnails
 | 
			
		||||
._*
 | 
			
		||||
 | 
			
		||||
# Distribution / packaging
 | 
			
		||||
.Python
 | 
			
		||||
build/
 | 
			
		||||
@@ -25,12 +38,6 @@ wheels/
 | 
			
		||||
*.egg
 | 
			
		||||
MANIFEST
 | 
			
		||||
 | 
			
		||||
# PyInstaller
 | 
			
		||||
#  Usually these files are written by a python script from a template
 | 
			
		||||
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
 | 
			
		||||
*.manifest
 | 
			
		||||
*.spec
 | 
			
		||||
 | 
			
		||||
# Installer logs
 | 
			
		||||
pip-log.txt
 | 
			
		||||
pip-delete-this-directory.txt
 | 
			
		||||
@@ -51,36 +58,9 @@ coverage.xml
 | 
			
		||||
*.mo
 | 
			
		||||
*.pot
 | 
			
		||||
 | 
			
		||||
# Django stuff:
 | 
			
		||||
*.log
 | 
			
		||||
local_settings.py
 | 
			
		||||
db.sqlite3
 | 
			
		||||
 | 
			
		||||
# Flask stuff:
 | 
			
		||||
instance/
 | 
			
		||||
.webassets-cache
 | 
			
		||||
 | 
			
		||||
# Scrapy stuff:
 | 
			
		||||
.scrapy
 | 
			
		||||
 | 
			
		||||
# Sphinx documentation
 | 
			
		||||
docs/_build/
 | 
			
		||||
 | 
			
		||||
# PyBuilder
 | 
			
		||||
target/
 | 
			
		||||
 | 
			
		||||
# Jupyter Notebook
 | 
			
		||||
.ipynb_checkpoints
 | 
			
		||||
 | 
			
		||||
# pyenv
 | 
			
		||||
.python-version
 | 
			
		||||
 | 
			
		||||
# celery beat schedule file
 | 
			
		||||
celerybeat-schedule
 | 
			
		||||
 | 
			
		||||
# SageMath parsed files
 | 
			
		||||
*.sage.py
 | 
			
		||||
 | 
			
		||||
# Environments
 | 
			
		||||
.env
 | 
			
		||||
.venv
 | 
			
		||||
@@ -90,19 +70,48 @@ ENV/
 | 
			
		||||
env.bak/
 | 
			
		||||
venv.bak/
 | 
			
		||||
 | 
			
		||||
# Spyder project settings
 | 
			
		||||
.spyderproject
 | 
			
		||||
.spyproject
 | 
			
		||||
 | 
			
		||||
# Rope project settings
 | 
			
		||||
.ropeproject
 | 
			
		||||
 | 
			
		||||
# mkdocs documentation
 | 
			
		||||
/site
 | 
			
		||||
 | 
			
		||||
# mypy
 | 
			
		||||
.mypy_cache/
 | 
			
		||||
 | 
			
		||||
.pioenvs
 | 
			
		||||
.piolibdeps
 | 
			
		||||
.pio
 | 
			
		||||
.vscode
 | 
			
		||||
CMakeListsPrivate.txt
 | 
			
		||||
CMakeLists.txt
 | 
			
		||||
 | 
			
		||||
# User-specific stuff:
 | 
			
		||||
.idea/**/workspace.xml
 | 
			
		||||
.idea/**/tasks.xml
 | 
			
		||||
.idea/dictionaries
 | 
			
		||||
 | 
			
		||||
# Sensitive or high-churn files:
 | 
			
		||||
.idea/**/dataSources/
 | 
			
		||||
.idea/**/dataSources.ids
 | 
			
		||||
.idea/**/dataSources.xml
 | 
			
		||||
.idea/**/dataSources.local.xml
 | 
			
		||||
.idea/**/dynamic.xml
 | 
			
		||||
 | 
			
		||||
# CMake
 | 
			
		||||
cmake-build-debug/
 | 
			
		||||
cmake-build-release/
 | 
			
		||||
 | 
			
		||||
CMakeCache.txt
 | 
			
		||||
CMakeFiles
 | 
			
		||||
CMakeScripts
 | 
			
		||||
Testing
 | 
			
		||||
Makefile
 | 
			
		||||
cmake_install.cmake
 | 
			
		||||
install_manifest.txt
 | 
			
		||||
compile_commands.json
 | 
			
		||||
CTestTestfile.cmake
 | 
			
		||||
/*.cbp
 | 
			
		||||
 | 
			
		||||
.clang_complete
 | 
			
		||||
.gcc-flags.json
 | 
			
		||||
 | 
			
		||||
config/
 | 
			
		||||
tests/build/
 | 
			
		||||
tests/.esphomeyaml/
 | 
			
		||||
tests/.esphome/
 | 
			
		||||
/.temp-clang-tidy.cpp
 | 
			
		||||
/.idea/
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										538
									
								
								.gitlab-ci.yml
									
									
									
									
									
								
							
							
						
						
									
										538
									
								
								.gitlab-ci.yml
									
									
									
									
									
								
							@@ -2,302 +2,142 @@
 | 
			
		||||
# Based on https://gitlab.com/hassio-addons/addon-node-red/blob/master/.gitlab-ci.yml
 | 
			
		||||
variables:
 | 
			
		||||
  DOCKER_DRIVER: overlay2
 | 
			
		||||
  DOCKER_HOST: tcp://docker:2375/
 | 
			
		||||
  BASE_VERSION: '2.1.1'
 | 
			
		||||
  TZ: UTC
 | 
			
		||||
 | 
			
		||||
stages:
 | 
			
		||||
  - lint
 | 
			
		||||
  - test
 | 
			
		||||
  - build
 | 
			
		||||
  - deploy
 | 
			
		||||
 | 
			
		||||
.lint: &lint
 | 
			
		||||
  image: esphome/esphome-lint:latest
 | 
			
		||||
  stage: lint
 | 
			
		||||
  before_script:
 | 
			
		||||
    - pip install -e .
 | 
			
		||||
    - script/setup
 | 
			
		||||
  tags:
 | 
			
		||||
    - python2.7
 | 
			
		||||
    - esphomeyaml-lint
 | 
			
		||||
    - docker
 | 
			
		||||
 | 
			
		||||
.test: &test
 | 
			
		||||
  image: esphome/esphome-lint:latest
 | 
			
		||||
  stage: test
 | 
			
		||||
  before_script:
 | 
			
		||||
    - pip install -e .
 | 
			
		||||
    - script/setup
 | 
			
		||||
  tags:
 | 
			
		||||
    - python2.7
 | 
			
		||||
    - esphomeyaml-test
 | 
			
		||||
  variables:
 | 
			
		||||
    TZ: UTC
 | 
			
		||||
    - docker
 | 
			
		||||
 | 
			
		||||
.docker-builder: &docker-builder
 | 
			
		||||
.docker-base: &docker-base
 | 
			
		||||
  image: esphome/esphome-base-builder
 | 
			
		||||
  before_script:
 | 
			
		||||
    - docker info
 | 
			
		||||
    - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
 | 
			
		||||
    - docker login -u "$DOCKER_USER" -p "$DOCKER_PASSWORD"
 | 
			
		||||
  script:
 | 
			
		||||
    - docker run --rm --privileged multiarch/qemu-user-static:5.0.0-2 --reset -p yes
 | 
			
		||||
    - TAG="${CI_COMMIT_TAG#v}"
 | 
			
		||||
    - TAG="${TAG:-${CI_COMMIT_SHA:0:7}}"
 | 
			
		||||
    - echo "Tag ${TAG}"
 | 
			
		||||
 | 
			
		||||
    - |
 | 
			
		||||
      if [[ "${IS_HASSIO}" == "YES" ]]; then
 | 
			
		||||
        BUILD_FROM=esphome/esphome-hassio-base-${BUILD_ARCH}:${BASE_VERSION}
 | 
			
		||||
        BUILD_TO=esphome/esphome-hassio-${BUILD_ARCH}
 | 
			
		||||
        DOCKERFILE=docker/Dockerfile.hassio
 | 
			
		||||
      else
 | 
			
		||||
        BUILD_FROM=esphome/esphome-base-${BUILD_ARCH}:${BASE_VERSION}
 | 
			
		||||
        if [[ "${BUILD_ARCH}" == "amd64" ]]; then
 | 
			
		||||
          BUILD_TO=esphome/esphome
 | 
			
		||||
        else
 | 
			
		||||
          BUILD_TO=esphome/esphome-${BUILD_ARCH}
 | 
			
		||||
        fi
 | 
			
		||||
        DOCKERFILE=docker/Dockerfile
 | 
			
		||||
      fi
 | 
			
		||||
 | 
			
		||||
    - |
 | 
			
		||||
      docker build \
 | 
			
		||||
        --build-arg "BUILD_FROM=${BUILD_FROM}" \
 | 
			
		||||
        --build-arg "BUILD_VERSION=${TAG}" \
 | 
			
		||||
        --tag "${BUILD_TO}:${TAG}" \
 | 
			
		||||
        --file "${DOCKERFILE}" \
 | 
			
		||||
        .
 | 
			
		||||
    - |
 | 
			
		||||
      if [[ "${RELEASE}" = "YES" ]]; then
 | 
			
		||||
        echo "Pushing to ${BUILD_TO}:${TAG}"
 | 
			
		||||
        docker push "${BUILD_TO}:${TAG}"
 | 
			
		||||
      fi
 | 
			
		||||
    - |
 | 
			
		||||
      if [[ "${LATEST}" = "YES" ]]; then
 | 
			
		||||
        echo "Pushing to :latest"
 | 
			
		||||
        docker tag ${BUILD_TO}:${TAG} ${BUILD_TO}:latest
 | 
			
		||||
        docker push ${BUILD_TO}:latest
 | 
			
		||||
      fi
 | 
			
		||||
    - |
 | 
			
		||||
      if [[ "${BETA}" = "YES" ]]; then
 | 
			
		||||
        echo "Pushing to :beta"
 | 
			
		||||
        docker tag \
 | 
			
		||||
          ${BUILD_TO}:${TAG} \
 | 
			
		||||
          ${BUILD_TO}:beta
 | 
			
		||||
        docker push ${BUILD_TO}:beta
 | 
			
		||||
      fi
 | 
			
		||||
    - |
 | 
			
		||||
      if [[ "${DEV}" = "YES" ]]; then
 | 
			
		||||
        echo "Pushing to :dev"
 | 
			
		||||
        docker tag \
 | 
			
		||||
          ${BUILD_TO}:${TAG} \
 | 
			
		||||
          ${BUILD_TO}:dev
 | 
			
		||||
        docker push ${BUILD_TO}:dev
 | 
			
		||||
      fi
 | 
			
		||||
  services:
 | 
			
		||||
    - docker:dind
 | 
			
		||||
  tags:
 | 
			
		||||
    - hassio-builder
 | 
			
		||||
    - docker
 | 
			
		||||
  stage: deploy
 | 
			
		||||
 | 
			
		||||
flake8:
 | 
			
		||||
lint-custom:
 | 
			
		||||
  <<: *lint
 | 
			
		||||
  script:
 | 
			
		||||
    - flake8 esphomeyaml
 | 
			
		||||
    - script/ci-custom.py
 | 
			
		||||
 | 
			
		||||
pylint:
 | 
			
		||||
lint-python:
 | 
			
		||||
  <<: *lint
 | 
			
		||||
  script:
 | 
			
		||||
    - pylint esphomeyaml
 | 
			
		||||
    - script/lint-python
 | 
			
		||||
 | 
			
		||||
lint-tidy:
 | 
			
		||||
  <<: *lint
 | 
			
		||||
  script:
 | 
			
		||||
    - pio init --ide atom
 | 
			
		||||
    - script/clang-tidy --all-headers --fix
 | 
			
		||||
    - script/ci-suggest-changes
 | 
			
		||||
 | 
			
		||||
lint-format:
 | 
			
		||||
  <<: *lint
 | 
			
		||||
  script:
 | 
			
		||||
    - script/clang-format -i
 | 
			
		||||
    - script/ci-suggest-changes
 | 
			
		||||
 | 
			
		||||
test1:
 | 
			
		||||
  <<: *test
 | 
			
		||||
  script:
 | 
			
		||||
    - esphomeyaml tests/test1.yaml compile
 | 
			
		||||
    - esphome tests/test1.yaml compile
 | 
			
		||||
 | 
			
		||||
test2:
 | 
			
		||||
  <<: *test
 | 
			
		||||
  script:
 | 
			
		||||
    - esphomeyaml tests/test2.yaml compile
 | 
			
		||||
    - esphome tests/test2.yaml compile
 | 
			
		||||
 | 
			
		||||
.build-hassio: &build-hassio
 | 
			
		||||
  <<: *docker-builder
 | 
			
		||||
  stage: build
 | 
			
		||||
test3:
 | 
			
		||||
  <<: *test
 | 
			
		||||
  script:
 | 
			
		||||
    - docker run --rm --privileged hassioaddons/qemu-user-static:latest
 | 
			
		||||
    - BUILD_FROM=hassioaddons/ubuntu-base-${ADDON_ARCH}:2.2.0
 | 
			
		||||
    - ADDON_VERSION="${CI_COMMIT_TAG#v}"
 | 
			
		||||
    - ADDON_VERSION="${ADDON_VERSION:-${CI_COMMIT_SHA:0:7}}"
 | 
			
		||||
    - echo "Build from ${BUILD_FROM}"
 | 
			
		||||
    - echo "Add-on version ${ADDON_VERSION}"
 | 
			
		||||
    - echo "Tag ${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:dev"
 | 
			
		||||
    - echo "Tag ${CI_REGISTRY}/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}"
 | 
			
		||||
    - |
 | 
			
		||||
      docker build \
 | 
			
		||||
        --build-arg "BUILD_FROM=${BUILD_FROM}" \
 | 
			
		||||
        --build-arg "BUILD_DATE=$(date +"%Y-%m-%dT%H:%M:%SZ")" \
 | 
			
		||||
        --build-arg "BUILD_ARCH=${ADDON_ARCH}" \
 | 
			
		||||
        --build-arg "BUILD_REF=${CI_COMMIT_SHA}" \
 | 
			
		||||
        --build-arg "BUILD_VERSION=${ADDON_VERSION}" \
 | 
			
		||||
        --tag "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:dev" \
 | 
			
		||||
        --tag "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
 | 
			
		||||
        --file "docker/Dockerfile.hassio" \
 | 
			
		||||
        .
 | 
			
		||||
    - |
 | 
			
		||||
      if [ "${DO_PUSH:-true}" = true ]; then
 | 
			
		||||
        echo "Pushing to CI registry"
 | 
			
		||||
        docker push ${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}
 | 
			
		||||
        docker push ${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:dev
 | 
			
		||||
      fi
 | 
			
		||||
 | 
			
		||||
# Generic deploy template
 | 
			
		||||
.deploy-release: &deploy-release
 | 
			
		||||
  <<: *docker-builder
 | 
			
		||||
  stage: deploy
 | 
			
		||||
  script:
 | 
			
		||||
    - version="${CI_COMMIT_TAG#v}"
 | 
			
		||||
    - echo "Publishing release version ${version}"
 | 
			
		||||
    - docker pull "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}"
 | 
			
		||||
    - docker login -u "$DOCKER_USER" -p "$DOCKER_PASSWORD"
 | 
			
		||||
 | 
			
		||||
    - echo "Tag ${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
 | 
			
		||||
    - |
 | 
			
		||||
      docker tag \
 | 
			
		||||
        "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
 | 
			
		||||
        "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
 | 
			
		||||
    - docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
 | 
			
		||||
 | 
			
		||||
    - echo "Tag ${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest"
 | 
			
		||||
    - |
 | 
			
		||||
      docker tag \
 | 
			
		||||
        "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
 | 
			
		||||
        "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest"
 | 
			
		||||
    - docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest"
 | 
			
		||||
 | 
			
		||||
    - echo "Tag ${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
 | 
			
		||||
    - |
 | 
			
		||||
      docker tag \
 | 
			
		||||
        "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
 | 
			
		||||
        "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
 | 
			
		||||
    - docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
 | 
			
		||||
 | 
			
		||||
    - echo "Tag ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
 | 
			
		||||
    - |
 | 
			
		||||
      docker tag \
 | 
			
		||||
        "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
 | 
			
		||||
        "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
 | 
			
		||||
    - docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
 | 
			
		||||
 | 
			
		||||
    - echo "Tag ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest"
 | 
			
		||||
    - |
 | 
			
		||||
      docker tag \
 | 
			
		||||
        "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" \
 | 
			
		||||
        "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest"
 | 
			
		||||
    - docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:latest"
 | 
			
		||||
 | 
			
		||||
    - echo "Tag ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
 | 
			
		||||
    - |
 | 
			
		||||
      docker tag \
 | 
			
		||||
        "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" \
 | 
			
		||||
        "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
 | 
			
		||||
    - docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
 | 
			
		||||
  only:
 | 
			
		||||
  - /^v\d+\.\d+\.\d+$/
 | 
			
		||||
  except:
 | 
			
		||||
  - /^(?!master).+@/
 | 
			
		||||
 | 
			
		||||
.deploy-beta: &deploy-beta
 | 
			
		||||
  <<: *docker-builder
 | 
			
		||||
  stage: deploy
 | 
			
		||||
  script:
 | 
			
		||||
    - version="${CI_COMMIT_TAG#v}"
 | 
			
		||||
    - echo "Publishing beta version ${version}"
 | 
			
		||||
    - docker pull "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}"
 | 
			
		||||
    - docker login -u "$DOCKER_USER" -p "$DOCKER_PASSWORD"
 | 
			
		||||
 | 
			
		||||
    - echo "Tag ${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
 | 
			
		||||
    - |
 | 
			
		||||
      docker tag \
 | 
			
		||||
        "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
 | 
			
		||||
        "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
 | 
			
		||||
    - docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
 | 
			
		||||
 | 
			
		||||
    - echo "Tag ${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
 | 
			
		||||
    - |
 | 
			
		||||
      docker tag \
 | 
			
		||||
        "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
 | 
			
		||||
        "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
 | 
			
		||||
    - docker push "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
 | 
			
		||||
 | 
			
		||||
    - echo "Tag ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
 | 
			
		||||
    - |
 | 
			
		||||
      docker tag \
 | 
			
		||||
        "${CI_REGISTRY}/ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${CI_COMMIT_SHA}" \
 | 
			
		||||
        "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
 | 
			
		||||
    - docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}"
 | 
			
		||||
 | 
			
		||||
    - echo "Tag ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
 | 
			
		||||
    - |
 | 
			
		||||
      docker tag \
 | 
			
		||||
        "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:${version}" \
 | 
			
		||||
        "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
 | 
			
		||||
    - docker push "ottowinter/esphomeyaml-hassio-${ADDON_ARCH}:rc"
 | 
			
		||||
  only:
 | 
			
		||||
  - /^v\d+\.\d+\.\d+b\d+$/
 | 
			
		||||
  except:
 | 
			
		||||
  - /^(?!rc).+@/
 | 
			
		||||
 | 
			
		||||
# Build jobs
 | 
			
		||||
build:normal:
 | 
			
		||||
  <<: *docker-builder
 | 
			
		||||
  stage: build
 | 
			
		||||
  script:
 | 
			
		||||
    - docker build -t "${CI_REGISTRY}/ottowinter/esphomeyaml:dev" .
 | 
			
		||||
 | 
			
		||||
.build-hassio-edge: &build-hassio-edge
 | 
			
		||||
  <<: *build-hassio
 | 
			
		||||
  except:
 | 
			
		||||
    - /^v\d+\.\d+\.\d+$/
 | 
			
		||||
    - /^v\d+\.\d+\.\d+b\d+$/
 | 
			
		||||
 | 
			
		||||
.build-hassio-release: &build-hassio-release
 | 
			
		||||
  <<: *build-hassio
 | 
			
		||||
  only:
 | 
			
		||||
    - /^v\d+\.\d+\.\d+$/
 | 
			
		||||
    - /^v\d+\.\d+\.\d+b\d+$/
 | 
			
		||||
 | 
			
		||||
build:hassio-armhf-edge:
 | 
			
		||||
  <<: *build-hassio-edge
 | 
			
		||||
  variables:
 | 
			
		||||
    ADDON_ARCH: armhf
 | 
			
		||||
    DO_PUSH: "false"
 | 
			
		||||
 | 
			
		||||
build:hassio-armhf:
 | 
			
		||||
  <<: *build-hassio-release
 | 
			
		||||
  variables:
 | 
			
		||||
    ADDON_ARCH: armhf
 | 
			
		||||
 | 
			
		||||
#build:hassio-aarch64-edge:
 | 
			
		||||
#  <<: *build-hassio-edge
 | 
			
		||||
#  variables:
 | 
			
		||||
#    ADDON_ARCH: aarch64
 | 
			
		||||
#    DO_PUSH: "false"
 | 
			
		||||
 | 
			
		||||
#build:hassio-aarch64:
 | 
			
		||||
#  <<: *build-hassio-release
 | 
			
		||||
#  variables:
 | 
			
		||||
#    ADDON_ARCH: aarch64
 | 
			
		||||
 | 
			
		||||
build:hassio-i386-edge:
 | 
			
		||||
  <<: *build-hassio-edge
 | 
			
		||||
  variables:
 | 
			
		||||
    ADDON_ARCH: i386
 | 
			
		||||
    DO_PUSH: "false"
 | 
			
		||||
 | 
			
		||||
build:hassio-i386:
 | 
			
		||||
  <<: *build-hassio-release
 | 
			
		||||
  variables:
 | 
			
		||||
    ADDON_ARCH: i386
 | 
			
		||||
 | 
			
		||||
build:hassio-amd64-edge:
 | 
			
		||||
  <<: *build-hassio-edge
 | 
			
		||||
  variables:
 | 
			
		||||
    ADDON_ARCH: amd64
 | 
			
		||||
    DO_PUSH: "false"
 | 
			
		||||
 | 
			
		||||
build:hassio-amd64:
 | 
			
		||||
  <<: *build-hassio-release
 | 
			
		||||
  variables:
 | 
			
		||||
    ADDON_ARCH: amd64
 | 
			
		||||
 | 
			
		||||
# Deploy jobs
 | 
			
		||||
deploy-release:armhf:
 | 
			
		||||
  <<: *deploy-release
 | 
			
		||||
  variables:
 | 
			
		||||
    ADDON_ARCH: armhf
 | 
			
		||||
 | 
			
		||||
deploy-beta:armhf:
 | 
			
		||||
  <<: *deploy-beta
 | 
			
		||||
  variables:
 | 
			
		||||
    ADDON_ARCH: armhf
 | 
			
		||||
 | 
			
		||||
#deploy-release:aarch64:
 | 
			
		||||
#  <<: *deploy-release
 | 
			
		||||
#  variables:
 | 
			
		||||
#    ADDON_ARCH: aarch64
 | 
			
		||||
 | 
			
		||||
#deploy-beta:aarch64:
 | 
			
		||||
#  <<: *deploy-beta
 | 
			
		||||
#  variables:
 | 
			
		||||
#    ADDON_ARCH: aarch64
 | 
			
		||||
 | 
			
		||||
deploy-release:i386:
 | 
			
		||||
  <<: *deploy-release
 | 
			
		||||
  variables:
 | 
			
		||||
    ADDON_ARCH: i386
 | 
			
		||||
 | 
			
		||||
deploy-beta:i386:
 | 
			
		||||
  <<: *deploy-beta
 | 
			
		||||
  variables:
 | 
			
		||||
    ADDON_ARCH: i386
 | 
			
		||||
 | 
			
		||||
deploy-release:amd64:
 | 
			
		||||
  <<: *deploy-release
 | 
			
		||||
  variables:
 | 
			
		||||
    ADDON_ARCH: amd64
 | 
			
		||||
 | 
			
		||||
deploy-beta:amd64:
 | 
			
		||||
  <<: *deploy-beta
 | 
			
		||||
  variables:
 | 
			
		||||
    ADDON_ARCH: amd64
 | 
			
		||||
    - esphome tests/test3.yaml compile
 | 
			
		||||
 | 
			
		||||
.deploy-pypi: &deploy-pypi
 | 
			
		||||
  <<: *lint
 | 
			
		||||
  stage: deploy
 | 
			
		||||
  before_script:
 | 
			
		||||
  - pip install -e .
 | 
			
		||||
  - pip install twine
 | 
			
		||||
  script:
 | 
			
		||||
  - python setup.py sdist
 | 
			
		||||
  - twine upload dist/*
 | 
			
		||||
  tags:
 | 
			
		||||
  - python2.7
 | 
			
		||||
  - esphomeyaml-test
 | 
			
		||||
    - pip install twine wheel
 | 
			
		||||
    - python setup.py sdist bdist_wheel
 | 
			
		||||
    - twine upload dist/*
 | 
			
		||||
 | 
			
		||||
deploy-release:pypi:
 | 
			
		||||
  <<: *deploy-pypi
 | 
			
		||||
@@ -312,3 +152,191 @@ deploy-beta:pypi:
 | 
			
		||||
    - /^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"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								.gitpod.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.gitpod.yml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
ports:
 | 
			
		||||
- port: 6052
 | 
			
		||||
  onOpen: open-preview
 | 
			
		||||
tasks:
 | 
			
		||||
- before: script/setup
 | 
			
		||||
  command: python -m esphome config dashboard
 | 
			
		||||
							
								
								
									
										55
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										55
									
								
								.travis.yml
									
									
									
									
									
								
							@@ -1,30 +1,49 @@
 | 
			
		||||
sudo: false
 | 
			
		||||
language: python
 | 
			
		||||
python: '3.6'
 | 
			
		||||
install: script/setup
 | 
			
		||||
cache:
 | 
			
		||||
  directories:
 | 
			
		||||
    - "~/.platformio"
 | 
			
		||||
 | 
			
		||||
matrix:
 | 
			
		||||
  fast_finish: true
 | 
			
		||||
  include:
 | 
			
		||||
    - python: "2.7"
 | 
			
		||||
      env: TARGET=Lint2.7
 | 
			
		||||
      install: pip install -e . && pip install flake8==3.6.0 pylint==1.9.4 pillow
 | 
			
		||||
    - python: "3.7"
 | 
			
		||||
      env: TARGET=Lint3.7
 | 
			
		||||
      script:
 | 
			
		||||
        - flake8 esphomeyaml
 | 
			
		||||
        - pylint esphomeyaml
 | 
			
		||||
    - python: "3.5.3"
 | 
			
		||||
      env: TARGET=Lint3.5
 | 
			
		||||
      install: pip install -U https://github.com/platformio/platformio-core/archive/develop.zip && pip install -e . && pip install flake8==3.6.0 pylint==2.2.2 pillow
 | 
			
		||||
        - script/ci-custom.py
 | 
			
		||||
        - flake8 esphome
 | 
			
		||||
        - pylint esphome
 | 
			
		||||
    - python: "3.6"
 | 
			
		||||
      env: TARGET=Test3.6
 | 
			
		||||
      script:
 | 
			
		||||
        - flake8 esphomeyaml
 | 
			
		||||
        - pylint esphomeyaml
 | 
			
		||||
        - esphome tests/test1.yaml compile
 | 
			
		||||
        - esphome tests/test2.yaml compile
 | 
			
		||||
        - esphome tests/test3.yaml compile
 | 
			
		||||
    - python: "2.7"
 | 
			
		||||
      env: TARGET=Test2.7
 | 
			
		||||
      install: pip install -e . && pip install flake8==3.6.0 pylint==1.9.4 pillow
 | 
			
		||||
      script:
 | 
			
		||||
        - esphomeyaml tests/test1.yaml compile
 | 
			
		||||
        - esphomeyaml tests/test2.yaml compile
 | 
			
		||||
    - python: "3.5.3"
 | 
			
		||||
      env: TARGET=Test3.5
 | 
			
		||||
      install: pip install -U https://github.com/platformio/platformio-core/archive/develop.zip && pip install -e . && pip install flake8==3.6.0 pylint==2.2.2 pillow
 | 
			
		||||
        - esphome tests/test1.yaml compile
 | 
			
		||||
        - esphome tests/test2.yaml compile
 | 
			
		||||
        - esphome tests/test3.yaml compile
 | 
			
		||||
    - env: TARGET=Cpp-Lint
 | 
			
		||||
      dist: trusty
 | 
			
		||||
      sudo: required
 | 
			
		||||
      addons:
 | 
			
		||||
        apt:
 | 
			
		||||
          sources:
 | 
			
		||||
            - ubuntu-toolchain-r-test
 | 
			
		||||
            - llvm-toolchain-trusty-7
 | 
			
		||||
          packages:
 | 
			
		||||
            - clang-tidy-7
 | 
			
		||||
            - clang-format-7
 | 
			
		||||
      before_script:
 | 
			
		||||
        - pio init --ide atom
 | 
			
		||||
        - clang-tidy-7 -version
 | 
			
		||||
        - clang-format-7 -version
 | 
			
		||||
        - clang-apply-replacements-7 -version
 | 
			
		||||
      script:
 | 
			
		||||
        - esphomeyaml tests/test1.yaml compile
 | 
			
		||||
        - esphomeyaml tests/test2.yaml compile
 | 
			
		||||
        - script/clang-tidy --all-headers -j 2 --fix
 | 
			
		||||
        - script/clang-format -i -j 2
 | 
			
		||||
        - script/ci-suggest-changes
 | 
			
		||||
 
 | 
			
		||||
@@ -1,16 +1,16 @@
 | 
			
		||||
# Contributing to esphomeyaml
 | 
			
		||||
# Contributing to ESPHome
 | 
			
		||||
 | 
			
		||||
esphomeyaml is a part of esphomelib and is responsible for reading in YAML configuration files,
 | 
			
		||||
This python project is responsible for reading in YAML configuration files,
 | 
			
		||||
converting them to C++ code. This code is then converted to a platformio project and compiled
 | 
			
		||||
with [esphomelib](https://github.com/OttoWinter/esphomelib), the C++ framework behind the project.
 | 
			
		||||
with [esphome-core](https://github.com/esphome/esphome-core), the C++ framework behind the project.
 | 
			
		||||
 | 
			
		||||
For a detailed guide, please see https://esphomelib.com/esphomeyaml/guides/contributing.html#contributing-to-esphomeyaml
 | 
			
		||||
For a detailed guide, please see https://esphome.io/guides/contributing.html#contributing-to-esphomeyaml
 | 
			
		||||
 | 
			
		||||
Things to note when contributing:
 | 
			
		||||
 | 
			
		||||
 - Please test your changes :)
 | 
			
		||||
 - If a new feature is added or an existing user-facing feature is changed, you should also 
 | 
			
		||||
   update the [docs](https://github.com/OttoWinter/esphomedocs). See [contributing to esphomedocs](https://esphomelib.com/esphomeyaml/guides/contributing.html#contributing-to-esphomedocs)
 | 
			
		||||
   update the [docs](https://github.com/esphome/esphome-docs). See [contributing to esphome-docs](https://esphome.io/guides/contributing.html#contributing-to-esphomedocs)
 | 
			
		||||
   for more information.
 | 
			
		||||
 - Please also update the tests in the `tests/` folder. You can do so by just adding a line in one of the YAML files
 | 
			
		||||
   which checks if your new feature compiles correctly.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										28
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								Dockerfile
									
									
									
									
									
								
							@@ -1,28 +0,0 @@
 | 
			
		||||
ARG BUILD_FROM=python:2.7
 | 
			
		||||
FROM ${BUILD_FROM}
 | 
			
		||||
MAINTAINER Otto Winter <contact@otto-winter.com>
 | 
			
		||||
 | 
			
		||||
RUN apt-get update && apt-get install -y \
 | 
			
		||||
        python-pil \
 | 
			
		||||
        git \
 | 
			
		||||
    && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* && \
 | 
			
		||||
    pip install --no-cache-dir --no-binary :all: 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
 | 
			
		||||
 | 
			
		||||
ENV ESPHOMEYAML_OTA_HOST_PORT=6123
 | 
			
		||||
EXPOSE 6123
 | 
			
		||||
VOLUME /config
 | 
			
		||||
WORKDIR /usr/src/app
 | 
			
		||||
 | 
			
		||||
COPY docker/platformio.ini /pio/platformio.ini
 | 
			
		||||
RUN platformio run -d /pio; rm -rf /pio
 | 
			
		||||
 | 
			
		||||
COPY . .
 | 
			
		||||
RUN pip install --no-cache-dir --no-binary :all: -e .
 | 
			
		||||
 | 
			
		||||
WORKDIR /config
 | 
			
		||||
ENTRYPOINT ["esphomeyaml"]
 | 
			
		||||
CMD ["/config", "dashboard"]
 | 
			
		||||
							
								
								
									
										692
									
								
								LICENSE
									
									
									
									
									
								
							
							
						
						
									
										692
									
								
								LICENSE
									
									
									
									
									
								
							@@ -1,6 +1,17 @@
 | 
			
		||||
MIT License
 | 
			
		||||
# ESPHome License
 | 
			
		||||
 | 
			
		||||
Copyright (c) 2018 Otto Winter
 | 
			
		||||
Copyright (c) 2019 ESPHome
 | 
			
		||||
 | 
			
		||||
The ESPHome License is made up of two base licenses: MIT and the GNU GENERAL PUBLIC LICENSE.
 | 
			
		||||
The C++/runtime codebase of the ESPHome project (file extensions .c, .cpp, .h, .hpp, .tcc, .ino) are
 | 
			
		||||
published under the GPLv3 license. The python codebase and all other parts of this codebase are
 | 
			
		||||
published under the MIT license.
 | 
			
		||||
 | 
			
		||||
Both MIT and GPLv3 licenses are attached to this document.
 | 
			
		||||
 | 
			
		||||
## MIT License
 | 
			
		||||
 | 
			
		||||
Copyright (c) 2019 ESPHome
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
@@ -19,3 +30,680 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
			
		||||
SOFTWARE.
 | 
			
		||||
 | 
			
		||||
## GPLv3 License
 | 
			
		||||
 | 
			
		||||
                    GNU GENERAL PUBLIC LICENSE
 | 
			
		||||
                       Version 3, 29 June 2007
 | 
			
		||||
 | 
			
		||||
 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
 | 
			
		||||
 Everyone is permitted to copy and distribute verbatim copies
 | 
			
		||||
 of this license document, but changing it is not allowed.
 | 
			
		||||
 | 
			
		||||
                            Preamble
 | 
			
		||||
 | 
			
		||||
  The GNU General Public License is a free, copyleft license for
 | 
			
		||||
software and other kinds of works.
 | 
			
		||||
 | 
			
		||||
  The licenses for most software and other practical works are designed
 | 
			
		||||
to take away your freedom to share and change the works.  By contrast,
 | 
			
		||||
the GNU General Public License is intended to guarantee your freedom to
 | 
			
		||||
share and change all versions of a program--to make sure it remains free
 | 
			
		||||
software for all its users.  We, the Free Software Foundation, use the
 | 
			
		||||
GNU General Public License for most of our software; it applies also to
 | 
			
		||||
any other work released this way by its authors.  You can apply it to
 | 
			
		||||
your programs, too.
 | 
			
		||||
 | 
			
		||||
  When we speak of free software, we are referring to freedom, not
 | 
			
		||||
price.  Our General Public Licenses are designed to make sure that you
 | 
			
		||||
have the freedom to distribute copies of free software (and charge for
 | 
			
		||||
them if you wish), that you receive source code or can get it if you
 | 
			
		||||
want it, that you can change the software or use pieces of it in new
 | 
			
		||||
free programs, and that you know you can do these things.
 | 
			
		||||
 | 
			
		||||
  To protect your rights, we need to prevent others from denying you
 | 
			
		||||
these rights or asking you to surrender the rights.  Therefore, you have
 | 
			
		||||
certain responsibilities if you distribute copies of the software, or if
 | 
			
		||||
you modify it: responsibilities to respect the freedom of others.
 | 
			
		||||
 | 
			
		||||
  For example, if you distribute copies of such a program, whether
 | 
			
		||||
gratis or for a fee, you must pass on to the recipients the same
 | 
			
		||||
freedoms that you received.  You must make sure that they, too, receive
 | 
			
		||||
or can get the source code.  And you must show them these terms so they
 | 
			
		||||
know their rights.
 | 
			
		||||
 | 
			
		||||
  Developers that use the GNU GPL protect your rights with two steps:
 | 
			
		||||
(1) assert copyright on the software, and (2) offer you this License
 | 
			
		||||
giving you legal permission to copy, distribute and/or modify it.
 | 
			
		||||
 | 
			
		||||
  For the developers' and authors' protection, the GPL clearly explains
 | 
			
		||||
that there is no warranty for this free software.  For both users' and
 | 
			
		||||
authors' sake, the GPL requires that modified versions be marked as
 | 
			
		||||
changed, so that their problems will not be attributed erroneously to
 | 
			
		||||
authors of previous versions.
 | 
			
		||||
 | 
			
		||||
  Some devices are designed to deny users access to install or run
 | 
			
		||||
modified versions of the software inside them, although the manufacturer
 | 
			
		||||
can do so.  This is fundamentally incompatible with the aim of
 | 
			
		||||
protecting users' freedom to change the software.  The systematic
 | 
			
		||||
pattern of such abuse occurs in the area of products for individuals to
 | 
			
		||||
use, which is precisely where it is most unacceptable.  Therefore, we
 | 
			
		||||
have designed this version of the GPL to prohibit the practice for those
 | 
			
		||||
products.  If such problems arise substantially in other domains, we
 | 
			
		||||
stand ready to extend this provision to those domains in future versions
 | 
			
		||||
of the GPL, as needed to protect the freedom of users.
 | 
			
		||||
 | 
			
		||||
  Finally, every program is threatened constantly by software patents.
 | 
			
		||||
States should not allow patents to restrict development and use of
 | 
			
		||||
software on general-purpose computers, but in those that do, we wish to
 | 
			
		||||
avoid the special danger that patents applied to a free program could
 | 
			
		||||
make it effectively proprietary.  To prevent this, the GPL assures that
 | 
			
		||||
patents cannot be used to render the program non-free.
 | 
			
		||||
 | 
			
		||||
  The precise terms and conditions for copying, distribution and
 | 
			
		||||
modification follow.
 | 
			
		||||
 | 
			
		||||
                       TERMS AND CONDITIONS
 | 
			
		||||
 | 
			
		||||
  0. Definitions.
 | 
			
		||||
 | 
			
		||||
  "This License" refers to version 3 of the GNU General Public License.
 | 
			
		||||
 | 
			
		||||
  "Copyright" also means copyright-like laws that apply to other kinds of
 | 
			
		||||
works, such as semiconductor masks.
 | 
			
		||||
 | 
			
		||||
  "The Program" refers to any copyrightable work licensed under this
 | 
			
		||||
License.  Each licensee is addressed as "you".  "Licensees" and
 | 
			
		||||
"recipients" may be individuals or organizations.
 | 
			
		||||
 | 
			
		||||
  To "modify" a work means to copy from or adapt all or part of the work
 | 
			
		||||
in a fashion requiring copyright permission, other than the making of an
 | 
			
		||||
exact copy.  The resulting work is called a "modified version" of the
 | 
			
		||||
earlier work or a work "based on" the earlier work.
 | 
			
		||||
 | 
			
		||||
  A "covered work" means either the unmodified Program or a work based
 | 
			
		||||
on the Program.
 | 
			
		||||
 | 
			
		||||
  To "propagate" a work means to do anything with it that, without
 | 
			
		||||
permission, would make you directly or secondarily liable for
 | 
			
		||||
infringement under applicable copyright law, except executing it on a
 | 
			
		||||
computer or modifying a private copy.  Propagation includes copying,
 | 
			
		||||
distribution (with or without modification), making available to the
 | 
			
		||||
public, and in some countries other activities as well.
 | 
			
		||||
 | 
			
		||||
  To "convey" a work means any kind of propagation that enables other
 | 
			
		||||
parties to make or receive copies.  Mere interaction with a user through
 | 
			
		||||
a computer network, with no transfer of a copy, is not conveying.
 | 
			
		||||
 | 
			
		||||
  An interactive user interface displays "Appropriate Legal Notices"
 | 
			
		||||
to the extent that it includes a convenient and prominently visible
 | 
			
		||||
feature that (1) displays an appropriate copyright notice, and (2)
 | 
			
		||||
tells the user that there is no warranty for the work (except to the
 | 
			
		||||
extent that warranties are provided), that licensees may convey the
 | 
			
		||||
work under this License, and how to view a copy of this License.  If
 | 
			
		||||
the interface presents a list of user commands or options, such as a
 | 
			
		||||
menu, a prominent item in the list meets this criterion.
 | 
			
		||||
 | 
			
		||||
  1. Source Code.
 | 
			
		||||
 | 
			
		||||
  The "source code" for a work means the preferred form of the work
 | 
			
		||||
for making modifications to it.  "Object code" means any non-source
 | 
			
		||||
form of a work.
 | 
			
		||||
 | 
			
		||||
  A "Standard Interface" means an interface that either is an official
 | 
			
		||||
standard defined by a recognized standards body, or, in the case of
 | 
			
		||||
interfaces specified for a particular programming language, one that
 | 
			
		||||
is widely used among developers working in that language.
 | 
			
		||||
 | 
			
		||||
  The "System Libraries" of an executable work include anything, other
 | 
			
		||||
than the work as a whole, that (a) is included in the normal form of
 | 
			
		||||
packaging a Major Component, but which is not part of that Major
 | 
			
		||||
Component, and (b) serves only to enable use of the work with that
 | 
			
		||||
Major Component, or to implement a Standard Interface for which an
 | 
			
		||||
implementation is available to the public in source code form.  A
 | 
			
		||||
"Major Component", in this context, means a major essential component
 | 
			
		||||
(kernel, window system, and so on) of the specific operating system
 | 
			
		||||
(if any) on which the executable work runs, or a compiler used to
 | 
			
		||||
produce the work, or an object code interpreter used to run it.
 | 
			
		||||
 | 
			
		||||
  The "Corresponding Source" for a work in object code form means all
 | 
			
		||||
the source code needed to generate, install, and (for an executable
 | 
			
		||||
work) run the object code and to modify the work, including scripts to
 | 
			
		||||
control those activities.  However, it does not include the work's
 | 
			
		||||
System Libraries, or general-purpose tools or generally available free
 | 
			
		||||
programs which are used unmodified in performing those activities but
 | 
			
		||||
which are not part of the work.  For example, Corresponding Source
 | 
			
		||||
includes interface definition files associated with source files for
 | 
			
		||||
the work, and the source code for shared libraries and dynamically
 | 
			
		||||
linked subprograms that the work is specifically designed to require,
 | 
			
		||||
such as by intimate data communication or control flow between those
 | 
			
		||||
subprograms and other parts of the work.
 | 
			
		||||
 | 
			
		||||
  The Corresponding Source need not include anything that users
 | 
			
		||||
can regenerate automatically from other parts of the Corresponding
 | 
			
		||||
Source.
 | 
			
		||||
 | 
			
		||||
  The Corresponding Source for a work in source code form is that
 | 
			
		||||
same work.
 | 
			
		||||
 | 
			
		||||
  2. Basic Permissions.
 | 
			
		||||
 | 
			
		||||
  All rights granted under this License are granted for the term of
 | 
			
		||||
copyright on the Program, and are irrevocable provided the stated
 | 
			
		||||
conditions are met.  This License explicitly affirms your unlimited
 | 
			
		||||
permission to run the unmodified Program.  The output from running a
 | 
			
		||||
covered work is covered by this License only if the output, given its
 | 
			
		||||
content, constitutes a covered work.  This License acknowledges your
 | 
			
		||||
rights of fair use or other equivalent, as provided by copyright law.
 | 
			
		||||
 | 
			
		||||
  You may make, run and propagate covered works that you do not
 | 
			
		||||
convey, without conditions so long as your license otherwise remains
 | 
			
		||||
in force.  You may convey covered works to others for the sole purpose
 | 
			
		||||
of having them make modifications exclusively for you, or provide you
 | 
			
		||||
with facilities for running those works, provided that you comply with
 | 
			
		||||
the terms of this License in conveying all material for which you do
 | 
			
		||||
not control copyright.  Those thus making or running the covered works
 | 
			
		||||
for you must do so exclusively on your behalf, under your direction
 | 
			
		||||
and control, on terms that prohibit them from making any copies of
 | 
			
		||||
your copyrighted material outside their relationship with you.
 | 
			
		||||
 | 
			
		||||
  Conveying under any other circumstances is permitted solely under
 | 
			
		||||
the conditions stated below.  Sublicensing is not allowed; section 10
 | 
			
		||||
makes it unnecessary.
 | 
			
		||||
 | 
			
		||||
  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
 | 
			
		||||
 | 
			
		||||
  No covered work shall be deemed part of an effective technological
 | 
			
		||||
measure under any applicable law fulfilling obligations under article
 | 
			
		||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
 | 
			
		||||
similar laws prohibiting or restricting circumvention of such
 | 
			
		||||
measures.
 | 
			
		||||
 | 
			
		||||
  When you convey a covered work, you waive any legal power to forbid
 | 
			
		||||
circumvention of technological measures to the extent such circumvention
 | 
			
		||||
is effected by exercising rights under this License with respect to
 | 
			
		||||
the covered work, and you disclaim any intention to limit operation or
 | 
			
		||||
modification of the work as a means of enforcing, against the work's
 | 
			
		||||
users, your or third parties' legal rights to forbid circumvention of
 | 
			
		||||
technological measures.
 | 
			
		||||
 | 
			
		||||
  4. Conveying Verbatim Copies.
 | 
			
		||||
 | 
			
		||||
  You may convey verbatim copies of the Program's source code as you
 | 
			
		||||
receive it, in any medium, provided that you conspicuously and
 | 
			
		||||
appropriately publish on each copy an appropriate copyright notice;
 | 
			
		||||
keep intact all notices stating that this License and any
 | 
			
		||||
non-permissive terms added in accord with section 7 apply to the code;
 | 
			
		||||
keep intact all notices of the absence of any warranty; and give all
 | 
			
		||||
recipients a copy of this License along with the Program.
 | 
			
		||||
 | 
			
		||||
  You may charge any price or no price for each copy that you convey,
 | 
			
		||||
and you may offer support or warranty protection for a fee.
 | 
			
		||||
 | 
			
		||||
  5. Conveying Modified Source Versions.
 | 
			
		||||
 | 
			
		||||
  You may convey a work based on the Program, or the modifications to
 | 
			
		||||
produce it from the Program, in the form of source code under the
 | 
			
		||||
terms of section 4, provided that you also meet all of these conditions:
 | 
			
		||||
 | 
			
		||||
    a) The work must carry prominent notices stating that you modified
 | 
			
		||||
    it, and giving a relevant date.
 | 
			
		||||
 | 
			
		||||
    b) The work must carry prominent notices stating that it is
 | 
			
		||||
    released under this License and any conditions added under section
 | 
			
		||||
    7.  This requirement modifies the requirement in section 4 to
 | 
			
		||||
    "keep intact all notices".
 | 
			
		||||
 | 
			
		||||
    c) You must license the entire work, as a whole, under this
 | 
			
		||||
    License to anyone who comes into possession of a copy.  This
 | 
			
		||||
    License will therefore apply, along with any applicable section 7
 | 
			
		||||
    additional terms, to the whole of the work, and all its parts,
 | 
			
		||||
    regardless of how they are packaged.  This License gives no
 | 
			
		||||
    permission to license the work in any other way, but it does not
 | 
			
		||||
    invalidate such permission if you have separately received it.
 | 
			
		||||
 | 
			
		||||
    d) If the work has interactive user interfaces, each must display
 | 
			
		||||
    Appropriate Legal Notices; however, if the Program has interactive
 | 
			
		||||
    interfaces that do not display Appropriate Legal Notices, your
 | 
			
		||||
    work need not make them do so.
 | 
			
		||||
 | 
			
		||||
  A compilation of a covered work with other separate and independent
 | 
			
		||||
works, which are not by their nature extensions of the covered work,
 | 
			
		||||
and which are not combined with it such as to form a larger program,
 | 
			
		||||
in or on a volume of a storage or distribution medium, is called an
 | 
			
		||||
"aggregate" if the compilation and its resulting copyright are not
 | 
			
		||||
used to limit the access or legal rights of the compilation's users
 | 
			
		||||
beyond what the individual works permit.  Inclusion of a covered work
 | 
			
		||||
in an aggregate does not cause this License to apply to the other
 | 
			
		||||
parts of the aggregate.
 | 
			
		||||
 | 
			
		||||
  6. Conveying Non-Source Forms.
 | 
			
		||||
 | 
			
		||||
  You may convey a covered work in object code form under the terms
 | 
			
		||||
of sections 4 and 5, provided that you also convey the
 | 
			
		||||
machine-readable Corresponding Source under the terms of this License,
 | 
			
		||||
in one of these ways:
 | 
			
		||||
 | 
			
		||||
    a) Convey the object code in, or embodied in, a physical product
 | 
			
		||||
    (including a physical distribution medium), accompanied by the
 | 
			
		||||
    Corresponding Source fixed on a durable physical medium
 | 
			
		||||
    customarily used for software interchange.
 | 
			
		||||
 | 
			
		||||
    b) Convey the object code in, or embodied in, a physical product
 | 
			
		||||
    (including a physical distribution medium), accompanied by a
 | 
			
		||||
    written offer, valid for at least three years and valid for as
 | 
			
		||||
    long as you offer spare parts or customer support for that product
 | 
			
		||||
    model, to give anyone who possesses the object code either (1) a
 | 
			
		||||
    copy of the Corresponding Source for all the software in the
 | 
			
		||||
    product that is covered by this License, on a durable physical
 | 
			
		||||
    medium customarily used for software interchange, for a price no
 | 
			
		||||
    more than your reasonable cost of physically performing this
 | 
			
		||||
    conveying of source, or (2) access to copy the
 | 
			
		||||
    Corresponding Source from a network server at no charge.
 | 
			
		||||
 | 
			
		||||
    c) Convey individual copies of the object code with a copy of the
 | 
			
		||||
    written offer to provide the Corresponding Source.  This
 | 
			
		||||
    alternative is allowed only occasionally and noncommercially, and
 | 
			
		||||
    only if you received the object code with such an offer, in accord
 | 
			
		||||
    with subsection 6b.
 | 
			
		||||
 | 
			
		||||
    d) Convey the object code by offering access from a designated
 | 
			
		||||
    place (gratis or for a charge), and offer equivalent access to the
 | 
			
		||||
    Corresponding Source in the same way through the same place at no
 | 
			
		||||
    further charge.  You need not require recipients to copy the
 | 
			
		||||
    Corresponding Source along with the object code.  If the place to
 | 
			
		||||
    copy the object code is a network server, the Corresponding Source
 | 
			
		||||
    may be on a different server (operated by you or a third party)
 | 
			
		||||
    that supports equivalent copying facilities, provided you maintain
 | 
			
		||||
    clear directions next to the object code saying where to find the
 | 
			
		||||
    Corresponding Source.  Regardless of what server hosts the
 | 
			
		||||
    Corresponding Source, you remain obligated to ensure that it is
 | 
			
		||||
    available for as long as needed to satisfy these requirements.
 | 
			
		||||
 | 
			
		||||
    e) Convey the object code using peer-to-peer transmission, provided
 | 
			
		||||
    you inform other peers where the object code and Corresponding
 | 
			
		||||
    Source of the work are being offered to the general public at no
 | 
			
		||||
    charge under subsection 6d.
 | 
			
		||||
 | 
			
		||||
  A separable portion of the object code, whose source code is excluded
 | 
			
		||||
from the Corresponding Source as a System Library, need not be
 | 
			
		||||
included in conveying the object code work.
 | 
			
		||||
 | 
			
		||||
  A "User Product" is either (1) a "consumer product", which means any
 | 
			
		||||
tangible personal property which is normally used for personal, family,
 | 
			
		||||
or household purposes, or (2) anything designed or sold for incorporation
 | 
			
		||||
into a dwelling.  In determining whether a product is a consumer product,
 | 
			
		||||
doubtful cases shall be resolved in favor of coverage.  For a particular
 | 
			
		||||
product received by a particular user, "normally used" refers to a
 | 
			
		||||
typical or common use of that class of product, regardless of the status
 | 
			
		||||
of the particular user or of the way in which the particular user
 | 
			
		||||
actually uses, or expects or is expected to use, the product.  A product
 | 
			
		||||
is a consumer product regardless of whether the product has substantial
 | 
			
		||||
commercial, industrial or non-consumer uses, unless such uses represent
 | 
			
		||||
the only significant mode of use of the product.
 | 
			
		||||
 | 
			
		||||
  "Installation Information" for a User Product means any methods,
 | 
			
		||||
procedures, authorization keys, or other information required to install
 | 
			
		||||
and execute modified versions of a covered work in that User Product from
 | 
			
		||||
a modified version of its Corresponding Source.  The information must
 | 
			
		||||
suffice to ensure that the continued functioning of the modified object
 | 
			
		||||
code is in no case prevented or interfered with solely because
 | 
			
		||||
modification has been made.
 | 
			
		||||
 | 
			
		||||
  If you convey an object code work under this section in, or with, or
 | 
			
		||||
specifically for use in, a User Product, and the conveying occurs as
 | 
			
		||||
part of a transaction in which the right of possession and use of the
 | 
			
		||||
User Product is transferred to the recipient in perpetuity or for a
 | 
			
		||||
fixed term (regardless of how the transaction is characterized), the
 | 
			
		||||
Corresponding Source conveyed under this section must be accompanied
 | 
			
		||||
by the Installation Information.  But this requirement does not apply
 | 
			
		||||
if neither you nor any third party retains the ability to install
 | 
			
		||||
modified object code on the User Product (for example, the work has
 | 
			
		||||
been installed in ROM).
 | 
			
		||||
 | 
			
		||||
  The requirement to provide Installation Information does not include a
 | 
			
		||||
requirement to continue to provide support service, warranty, or updates
 | 
			
		||||
for a work that has been modified or installed by the recipient, or for
 | 
			
		||||
the User Product in which it has been modified or installed.  Access to a
 | 
			
		||||
network may be denied when the modification itself materially and
 | 
			
		||||
adversely affects the operation of the network or violates the rules and
 | 
			
		||||
protocols for communication across the network.
 | 
			
		||||
 | 
			
		||||
  Corresponding Source conveyed, and Installation Information provided,
 | 
			
		||||
in accord with this section must be in a format that is publicly
 | 
			
		||||
documented (and with an implementation available to the public in
 | 
			
		||||
source code form), and must require no special password or key for
 | 
			
		||||
unpacking, reading or copying.
 | 
			
		||||
 | 
			
		||||
  7. Additional Terms.
 | 
			
		||||
 | 
			
		||||
  "Additional permissions" are terms that supplement the terms of this
 | 
			
		||||
License by making exceptions from one or more of its conditions.
 | 
			
		||||
Additional permissions that are applicable to the entire Program shall
 | 
			
		||||
be treated as though they were included in this License, to the extent
 | 
			
		||||
that they are valid under applicable law.  If additional permissions
 | 
			
		||||
apply only to part of the Program, that part may be used separately
 | 
			
		||||
under those permissions, but the entire Program remains governed by
 | 
			
		||||
this License without regard to the additional permissions.
 | 
			
		||||
 | 
			
		||||
  When you convey a copy of a covered work, you may at your option
 | 
			
		||||
remove any additional permissions from that copy, or from any part of
 | 
			
		||||
it.  (Additional permissions may be written to require their own
 | 
			
		||||
removal in certain cases when you modify the work.)  You may place
 | 
			
		||||
additional permissions on material, added by you to a covered work,
 | 
			
		||||
for which you have or can give appropriate copyright permission.
 | 
			
		||||
 | 
			
		||||
  Notwithstanding any other provision of this License, for material you
 | 
			
		||||
add to a covered work, you may (if authorized by the copyright holders of
 | 
			
		||||
that material) supplement the terms of this License with terms:
 | 
			
		||||
 | 
			
		||||
    a) Disclaiming warranty or limiting liability differently from the
 | 
			
		||||
    terms of sections 15 and 16 of this License; or
 | 
			
		||||
 | 
			
		||||
    b) Requiring preservation of specified reasonable legal notices or
 | 
			
		||||
    author attributions in that material or in the Appropriate Legal
 | 
			
		||||
    Notices displayed by works containing it; or
 | 
			
		||||
 | 
			
		||||
    c) Prohibiting misrepresentation of the origin of that material, or
 | 
			
		||||
    requiring that modified versions of such material be marked in
 | 
			
		||||
    reasonable ways as different from the original version; or
 | 
			
		||||
 | 
			
		||||
    d) Limiting the use for publicity purposes of names of licensors or
 | 
			
		||||
    authors of the material; or
 | 
			
		||||
 | 
			
		||||
    e) Declining to grant rights under trademark law for use of some
 | 
			
		||||
    trade names, trademarks, or service marks; or
 | 
			
		||||
 | 
			
		||||
    f) Requiring indemnification of licensors and authors of that
 | 
			
		||||
    material by anyone who conveys the material (or modified versions of
 | 
			
		||||
    it) with contractual assumptions of liability to the recipient, for
 | 
			
		||||
    any liability that these contractual assumptions directly impose on
 | 
			
		||||
    those licensors and authors.
 | 
			
		||||
 | 
			
		||||
  All other non-permissive additional terms are considered "further
 | 
			
		||||
restrictions" within the meaning of section 10.  If the Program as you
 | 
			
		||||
received it, or any part of it, contains a notice stating that it is
 | 
			
		||||
governed by this License along with a term that is a further
 | 
			
		||||
restriction, you may remove that term.  If a license document contains
 | 
			
		||||
a further restriction but permits relicensing or conveying under this
 | 
			
		||||
License, you may add to a covered work material governed by the terms
 | 
			
		||||
of that license document, provided that the further restriction does
 | 
			
		||||
not survive such relicensing or conveying.
 | 
			
		||||
 | 
			
		||||
  If you add terms to a covered work in accord with this section, you
 | 
			
		||||
must place, in the relevant source files, a statement of the
 | 
			
		||||
additional terms that apply to those files, or a notice indicating
 | 
			
		||||
where to find the applicable terms.
 | 
			
		||||
 | 
			
		||||
  Additional terms, permissive or non-permissive, may be stated in the
 | 
			
		||||
form of a separately written license, or stated as exceptions;
 | 
			
		||||
the above requirements apply either way.
 | 
			
		||||
 | 
			
		||||
  8. Termination.
 | 
			
		||||
 | 
			
		||||
  You may not propagate or modify a covered work except as expressly
 | 
			
		||||
provided under this License.  Any attempt otherwise to propagate or
 | 
			
		||||
modify it is void, and will automatically terminate your rights under
 | 
			
		||||
this License (including any patent licenses granted under the third
 | 
			
		||||
paragraph of section 11).
 | 
			
		||||
 | 
			
		||||
  However, if you cease all violation of this License, then your
 | 
			
		||||
license from a particular copyright holder is reinstated (a)
 | 
			
		||||
provisionally, unless and until the copyright holder explicitly and
 | 
			
		||||
finally terminates your license, and (b) permanently, if the copyright
 | 
			
		||||
holder fails to notify you of the violation by some reasonable means
 | 
			
		||||
prior to 60 days after the cessation.
 | 
			
		||||
 | 
			
		||||
  Moreover, your license from a particular copyright holder is
 | 
			
		||||
reinstated permanently if the copyright holder notifies you of the
 | 
			
		||||
violation by some reasonable means, this is the first time you have
 | 
			
		||||
received notice of violation of this License (for any work) from that
 | 
			
		||||
copyright holder, and you cure the violation prior to 30 days after
 | 
			
		||||
your receipt of the notice.
 | 
			
		||||
 | 
			
		||||
  Termination of your rights under this section does not terminate the
 | 
			
		||||
licenses of parties who have received copies or rights from you under
 | 
			
		||||
this License.  If your rights have been terminated and not permanently
 | 
			
		||||
reinstated, you do not qualify to receive new licenses for the same
 | 
			
		||||
material under section 10.
 | 
			
		||||
 | 
			
		||||
  9. Acceptance Not Required for Having Copies.
 | 
			
		||||
 | 
			
		||||
  You are not required to accept this License in order to receive or
 | 
			
		||||
run a copy of the Program.  Ancillary propagation of a covered work
 | 
			
		||||
occurring solely as a consequence of using peer-to-peer transmission
 | 
			
		||||
to receive a copy likewise does not require acceptance.  However,
 | 
			
		||||
nothing other than this License grants you permission to propagate or
 | 
			
		||||
modify any covered work.  These actions infringe copyright if you do
 | 
			
		||||
not accept this License.  Therefore, by modifying or propagating a
 | 
			
		||||
covered work, you indicate your acceptance of this License to do so.
 | 
			
		||||
 | 
			
		||||
  10. Automatic Licensing of Downstream Recipients.
 | 
			
		||||
 | 
			
		||||
  Each time you convey a covered work, the recipient automatically
 | 
			
		||||
receives a license from the original licensors, to run, modify and
 | 
			
		||||
propagate that work, subject to this License.  You are not responsible
 | 
			
		||||
for enforcing compliance by third parties with this License.
 | 
			
		||||
 | 
			
		||||
  An "entity transaction" is a transaction transferring control of an
 | 
			
		||||
organization, or substantially all assets of one, or subdividing an
 | 
			
		||||
organization, or merging organizations.  If propagation of a covered
 | 
			
		||||
work results from an entity transaction, each party to that
 | 
			
		||||
transaction who receives a copy of the work also receives whatever
 | 
			
		||||
licenses to the work the party's predecessor in interest had or could
 | 
			
		||||
give under the previous paragraph, plus a right to possession of the
 | 
			
		||||
Corresponding Source of the work from the predecessor in interest, if
 | 
			
		||||
the predecessor has it or can get it with reasonable efforts.
 | 
			
		||||
 | 
			
		||||
  You may not impose any further restrictions on the exercise of the
 | 
			
		||||
rights granted or affirmed under this License.  For example, you may
 | 
			
		||||
not impose a license fee, royalty, or other charge for exercise of
 | 
			
		||||
rights granted under this License, and you may not initiate litigation
 | 
			
		||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
 | 
			
		||||
any patent claim is infringed by making, using, selling, offering for
 | 
			
		||||
sale, or importing the Program or any portion of it.
 | 
			
		||||
 | 
			
		||||
  11. Patents.
 | 
			
		||||
 | 
			
		||||
  A "contributor" is a copyright holder who authorizes use under this
 | 
			
		||||
License of the Program or a work on which the Program is based.  The
 | 
			
		||||
work thus licensed is called the contributor's "contributor version".
 | 
			
		||||
 | 
			
		||||
  A contributor's "essential patent claims" are all patent claims
 | 
			
		||||
owned or controlled by the contributor, whether already acquired or
 | 
			
		||||
hereafter acquired, that would be infringed by some manner, permitted
 | 
			
		||||
by this License, of making, using, or selling its contributor version,
 | 
			
		||||
but do not include claims that would be infringed only as a
 | 
			
		||||
consequence of further modification of the contributor version.  For
 | 
			
		||||
purposes of this definition, "control" includes the right to grant
 | 
			
		||||
patent sublicenses in a manner consistent with the requirements of
 | 
			
		||||
this License.
 | 
			
		||||
 | 
			
		||||
  Each contributor grants you a non-exclusive, worldwide, royalty-free
 | 
			
		||||
patent license under the contributor's essential patent claims, to
 | 
			
		||||
make, use, sell, offer for sale, import and otherwise run, modify and
 | 
			
		||||
propagate the contents of its contributor version.
 | 
			
		||||
 | 
			
		||||
  In the following three paragraphs, a "patent license" is any express
 | 
			
		||||
agreement or commitment, however denominated, not to enforce a patent
 | 
			
		||||
(such as an express permission to practice a patent or covenant not to
 | 
			
		||||
sue for patent infringement).  To "grant" such a patent license to a
 | 
			
		||||
party means to make such an agreement or commitment not to enforce a
 | 
			
		||||
patent against the party.
 | 
			
		||||
 | 
			
		||||
  If you convey a covered work, knowingly relying on a patent license,
 | 
			
		||||
and the Corresponding Source of the work is not available for anyone
 | 
			
		||||
to copy, free of charge and under the terms of this License, through a
 | 
			
		||||
publicly available network server or other readily accessible means,
 | 
			
		||||
then you must either (1) cause the Corresponding Source to be so
 | 
			
		||||
available, or (2) arrange to deprive yourself of the benefit of the
 | 
			
		||||
patent license for this particular work, or (3) arrange, in a manner
 | 
			
		||||
consistent with the requirements of this License, to extend the patent
 | 
			
		||||
license to downstream recipients.  "Knowingly relying" means you have
 | 
			
		||||
actual knowledge that, but for the patent license, your conveying the
 | 
			
		||||
covered work in a country, or your recipient's use of the covered work
 | 
			
		||||
in a country, would infringe one or more identifiable patents in that
 | 
			
		||||
country that you have reason to believe are valid.
 | 
			
		||||
 | 
			
		||||
  If, pursuant to or in connection with a single transaction or
 | 
			
		||||
arrangement, you convey, or propagate by procuring conveyance of, a
 | 
			
		||||
covered work, and grant a patent license to some of the parties
 | 
			
		||||
receiving the covered work authorizing them to use, propagate, modify
 | 
			
		||||
or convey a specific copy of the covered work, then the patent license
 | 
			
		||||
you grant is automatically extended to all recipients of the covered
 | 
			
		||||
work and works based on it.
 | 
			
		||||
 | 
			
		||||
  A patent license is "discriminatory" if it does not include within
 | 
			
		||||
the scope of its coverage, prohibits the exercise of, or is
 | 
			
		||||
conditioned on the non-exercise of one or more of the rights that are
 | 
			
		||||
specifically granted under this License.  You may not convey a covered
 | 
			
		||||
work if you are a party to an arrangement with a third party that is
 | 
			
		||||
in the business of distributing software, under which you make payment
 | 
			
		||||
to the third party based on the extent of your activity of conveying
 | 
			
		||||
the work, and under which the third party grants, to any of the
 | 
			
		||||
parties who would receive the covered work from you, a discriminatory
 | 
			
		||||
patent license (a) in connection with copies of the covered work
 | 
			
		||||
conveyed by you (or copies made from those copies), or (b) primarily
 | 
			
		||||
for and in connection with specific products or compilations that
 | 
			
		||||
contain the covered work, unless you entered into that arrangement,
 | 
			
		||||
or that patent license was granted, prior to 28 March 2007.
 | 
			
		||||
 | 
			
		||||
  Nothing in this License shall be construed as excluding or limiting
 | 
			
		||||
any implied license or other defenses to infringement that may
 | 
			
		||||
otherwise be available to you under applicable patent law.
 | 
			
		||||
 | 
			
		||||
  12. No Surrender of Others' Freedom.
 | 
			
		||||
 | 
			
		||||
  If conditions are imposed on you (whether by court order, agreement or
 | 
			
		||||
otherwise) that contradict the conditions of this License, they do not
 | 
			
		||||
excuse you from the conditions of this License.  If you cannot convey a
 | 
			
		||||
covered work so as to satisfy simultaneously your obligations under this
 | 
			
		||||
License and any other pertinent obligations, then as a consequence you may
 | 
			
		||||
not convey it at all.  For example, if you agree to terms that obligate you
 | 
			
		||||
to collect a royalty for further conveying from those to whom you convey
 | 
			
		||||
the Program, the only way you could satisfy both those terms and this
 | 
			
		||||
License would be to refrain entirely from conveying the Program.
 | 
			
		||||
 | 
			
		||||
  13. Use with the GNU Affero General Public License.
 | 
			
		||||
 | 
			
		||||
  Notwithstanding any other provision of this License, you have
 | 
			
		||||
permission to link or combine any covered work with a work licensed
 | 
			
		||||
under version 3 of the GNU Affero General Public License into a single
 | 
			
		||||
combined work, and to convey the resulting work.  The terms of this
 | 
			
		||||
License will continue to apply to the part which is the covered work,
 | 
			
		||||
but the special requirements of the GNU Affero General Public License,
 | 
			
		||||
section 13, concerning interaction through a network will apply to the
 | 
			
		||||
combination as such.
 | 
			
		||||
 | 
			
		||||
  14. Revised Versions of this License.
 | 
			
		||||
 | 
			
		||||
  The Free Software Foundation may publish revised and/or new versions of
 | 
			
		||||
the GNU General Public License from time to time.  Such new versions will
 | 
			
		||||
be similar in spirit to the present version, but may differ in detail to
 | 
			
		||||
address new problems or concerns.
 | 
			
		||||
 | 
			
		||||
  Each version is given a distinguishing version number.  If the
 | 
			
		||||
Program specifies that a certain numbered version of the GNU General
 | 
			
		||||
Public License "or any later version" applies to it, you have the
 | 
			
		||||
option of following the terms and conditions either of that numbered
 | 
			
		||||
version or of any later version published by the Free Software
 | 
			
		||||
Foundation.  If the Program does not specify a version number of the
 | 
			
		||||
GNU General Public License, you may choose any version ever published
 | 
			
		||||
by the Free Software Foundation.
 | 
			
		||||
 | 
			
		||||
  If the Program specifies that a proxy can decide which future
 | 
			
		||||
versions of the GNU General Public License can be used, that proxy's
 | 
			
		||||
public statement of acceptance of a version permanently authorizes you
 | 
			
		||||
to choose that version for the Program.
 | 
			
		||||
 | 
			
		||||
  Later license versions may give you additional or different
 | 
			
		||||
permissions.  However, no additional obligations are imposed on any
 | 
			
		||||
author or copyright holder as a result of your choosing to follow a
 | 
			
		||||
later version.
 | 
			
		||||
 | 
			
		||||
  15. Disclaimer of Warranty.
 | 
			
		||||
 | 
			
		||||
  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
 | 
			
		||||
APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
 | 
			
		||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
 | 
			
		||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
 | 
			
		||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | 
			
		||||
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
 | 
			
		||||
IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
 | 
			
		||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
 | 
			
		||||
 | 
			
		||||
  16. Limitation of Liability.
 | 
			
		||||
 | 
			
		||||
  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
 | 
			
		||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
 | 
			
		||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
 | 
			
		||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
 | 
			
		||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
 | 
			
		||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
 | 
			
		||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
 | 
			
		||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
 | 
			
		||||
SUCH DAMAGES.
 | 
			
		||||
 | 
			
		||||
  17. Interpretation of Sections 15 and 16.
 | 
			
		||||
 | 
			
		||||
  If the disclaimer of warranty and limitation of liability provided
 | 
			
		||||
above cannot be given local legal effect according to their terms,
 | 
			
		||||
reviewing courts shall apply local law that most closely approximates
 | 
			
		||||
an absolute waiver of all civil liability in connection with the
 | 
			
		||||
Program, unless a warranty or assumption of liability accompanies a
 | 
			
		||||
copy of the Program in return for a fee.
 | 
			
		||||
 | 
			
		||||
                     END OF TERMS AND CONDITIONS
 | 
			
		||||
 | 
			
		||||
            How to Apply These Terms to Your New Programs
 | 
			
		||||
 | 
			
		||||
  If you develop a new program, and you want it to be of the greatest
 | 
			
		||||
possible use to the public, the best way to achieve this is to make it
 | 
			
		||||
free software which everyone can redistribute and change under these terms.
 | 
			
		||||
 | 
			
		||||
  To do so, attach the following notices to the program.  It is safest
 | 
			
		||||
to attach them to the start of each source file to most effectively
 | 
			
		||||
state the exclusion of warranty; and each file should have at least
 | 
			
		||||
the "copyright" line and a pointer to where the full notice is found.
 | 
			
		||||
 | 
			
		||||
    <one line to give the program's name and a brief idea of what it does.>
 | 
			
		||||
    Copyright (C) <year>  <name of author>
 | 
			
		||||
 | 
			
		||||
    This program is free software: you can redistribute it and/or modify
 | 
			
		||||
    it under the terms of the GNU General Public License as published by
 | 
			
		||||
    the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
    (at your option) any later version.
 | 
			
		||||
 | 
			
		||||
    This program is distributed in the hope that it will be useful,
 | 
			
		||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
    GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
    You should have received a copy of the GNU General Public License
 | 
			
		||||
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
Also add information on how to contact you by electronic and paper mail.
 | 
			
		||||
 | 
			
		||||
  If the program does terminal interaction, make it output a short
 | 
			
		||||
notice like this when it starts in an interactive mode:
 | 
			
		||||
 | 
			
		||||
    <program>  Copyright (C) <year>  <name of author>
 | 
			
		||||
    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
 | 
			
		||||
    This is free software, and you are welcome to redistribute it
 | 
			
		||||
    under certain conditions; type `show c' for details.
 | 
			
		||||
 | 
			
		||||
The hypothetical commands `show w' and `show c' should show the appropriate
 | 
			
		||||
parts of the General Public License.  Of course, your program's commands
 | 
			
		||||
might be different; for a GUI interface, you would use an "about box".
 | 
			
		||||
 | 
			
		||||
  You should also get your employer (if you work as a programmer) or school,
 | 
			
		||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
 | 
			
		||||
For more information on this, and how to apply and follow the GNU GPL, see
 | 
			
		||||
<http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
  The GNU General Public License does not permit incorporating your program
 | 
			
		||||
into proprietary programs.  If your program is a subroutine library, you
 | 
			
		||||
may consider it more useful to permit linking proprietary applications with
 | 
			
		||||
the library.  If this is what you want to do, use the GNU Lesser General
 | 
			
		||||
Public License instead of this License.  But first, please read
 | 
			
		||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										21
									
								
								MANIFEST.in
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								MANIFEST.in
									
									
									
									
									
								
							@@ -1,17 +1,6 @@
 | 
			
		||||
include LICENSE
 | 
			
		||||
include README.md
 | 
			
		||||
include esphomeyaml/dashboard/templates/index.html
 | 
			
		||||
include esphomeyaml/dashboard/templates/login.html
 | 
			
		||||
include esphomeyaml/dashboard/static/ace.js
 | 
			
		||||
include esphomeyaml/dashboard/static/esphomeyaml.css
 | 
			
		||||
include esphomeyaml/dashboard/static/esphomeyaml.js
 | 
			
		||||
include esphomeyaml/dashboard/static/favicon.ico
 | 
			
		||||
include esphomeyaml/dashboard/static/jquery.min.js
 | 
			
		||||
include esphomeyaml/dashboard/static/jquery.validate.min.js
 | 
			
		||||
include esphomeyaml/dashboard/static/jquery-ui.min.js
 | 
			
		||||
include esphomeyaml/dashboard/static/materialize.min.css
 | 
			
		||||
include esphomeyaml/dashboard/static/materialize.min.js
 | 
			
		||||
include esphomeyaml/dashboard/static/materialize-stepper.min.css
 | 
			
		||||
include esphomeyaml/dashboard/static/materialize-stepper.min.js
 | 
			
		||||
include esphomeyaml/dashboard/static/mode-yaml.js
 | 
			
		||||
include esphomeyaml/dashboard/static/theme-dreamweaver.js
 | 
			
		||||
include esphomeyaml/dashboard/static/ext-searchbox.js
 | 
			
		||||
include esphome/dashboard/templates/*.html
 | 
			
		||||
recursive-include esphome/dashboard/static *.ico *.js *.css *.woff* LICENSE
 | 
			
		||||
recursive-include esphome *.cpp *.h *.tcc
 | 
			
		||||
recursive-include esphome LICENSE.txt
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										39
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										39
									
								
								README.md
									
									
									
									
									
								
							@@ -1,38 +1,9 @@
 | 
			
		||||
# esphomeyaml for [esphomelib](https://github.com/OttoWinter/esphomelib)
 | 
			
		||||
# ESPHome [](https://travis-ci.org/esphome/esphome) [](https://discord.gg/KhAMKrd) [](https://GitHub.com/esphome/esphome/releases/)
 | 
			
		||||
 | 
			
		||||
### Getting Started Guide: https://esphomelib.com/esphomeyaml/guides/getting_started_command_line.html
 | 
			
		||||
[](https://esphome.io/)
 | 
			
		||||
 | 
			
		||||
### Available Components: https://esphomelib.com/esphomeyaml/index.html
 | 
			
		||||
**Documentation:** https://esphome.io/
 | 
			
		||||
 | 
			
		||||
esphomeyaml is the solution for your ESP8266/ESP32 projects with Home Assistant. It allows you to create **custom firmwares** for your microcontrollers with no programming experience required. All you need to know is the YAML configuration format which is also used by [Home Assistant](https://www.home-assistant.io).
 | 
			
		||||
For issues, please go to [the issue tracker](https://github.com/esphome/issues/issues).
 | 
			
		||||
 | 
			
		||||
esphomeyaml will:
 | 
			
		||||
 | 
			
		||||
 * Read your configuration file and warn you about potential errors (like using the invalid pins.)
 | 
			
		||||
 * Create a custom C++ sketch file for you using esphomeyaml's powerful C++ generation engine.
 | 
			
		||||
 * Compile the sketch file for you using [platformio](http://platformio.org/).
 | 
			
		||||
 * Upload the binary to your ESP via Over the Air updates.
 | 
			
		||||
 * Automatically start remote logs via MQTT.
 | 
			
		||||
 | 
			
		||||
And all of that with a single command 🎉:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
esphomeyaml configuration.yaml run
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Features
 | 
			
		||||
 | 
			
		||||
 * **No programming experience required:** just edit YAML configuration
 | 
			
		||||
    files like you're used to with Home Assistant.
 | 
			
		||||
 * **Flexible:** Use [esphomelib](https://github.com/OttoWinter/esphomelib)'s powerful core to create custom sensors/outputs.
 | 
			
		||||
 * **Fast and efficient:** Written in C++ and keeps memory consumption to a minimum.
 | 
			
		||||
 * **Made for [Home Assistant](https://www.home-assistant.io):** Almost all [Home Assistant](https://www.home-assistant.io) features are supported out of the box. Including RGB lights and many more.
 | 
			
		||||
 * **Easy reproducible configuration:** No need to go through a long setup process for every single node. Just copy a configuration file and run a single command.
 | 
			
		||||
 * **Smart Over The Air Updates:** esphomeyaml has OTA updates deeply integrated into the system. It even automatically enters a recovery mode if a boot loop is detected.
 | 
			
		||||
 * **Powerful logging engine:** View colorful logs and debug issues remotely.
 | 
			
		||||
 * **Open Source**
 | 
			
		||||
 * For me: Makes documenting esphomelib's features a lot easier.
 | 
			
		||||
 | 
			
		||||
## Special Thanks
 | 
			
		||||
 | 
			
		||||
Special Thanks to the Home Assistant project. Lots of the code base of esphomeyaml is based off of Home Assistant, for example the loading and config validation code.
 | 
			
		||||
For feature requests, please see [feature requests](https://github.com/esphome/feature-requests/issues).
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								docker/Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								docker/Dockerfile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
ARG BUILD_FROM=esphome/esphome-base-amd64:2.1.1
 | 
			
		||||
FROM ${BUILD_FROM}
 | 
			
		||||
 | 
			
		||||
COPY . .
 | 
			
		||||
RUN pip3 install --no-cache-dir -e .
 | 
			
		||||
 | 
			
		||||
ENV USERNAME=""
 | 
			
		||||
ENV PASSWORD=""
 | 
			
		||||
 | 
			
		||||
WORKDIR /config
 | 
			
		||||
ENTRYPOINT ["esphome"]
 | 
			
		||||
CMD ["/config", "dashboard"]
 | 
			
		||||
@@ -1,30 +0,0 @@
 | 
			
		||||
FROM multiarch/ubuntu-core:amd64-xenial
 | 
			
		||||
 | 
			
		||||
# setup locals
 | 
			
		||||
RUN apt-get update && apt-get install -y \
 | 
			
		||||
        jq \
 | 
			
		||||
        git \
 | 
			
		||||
        python3-setuptools \
 | 
			
		||||
    && rm -rf /var/lib/apt/lists/* \
 | 
			
		||||
ENV LANG C.UTF-8
 | 
			
		||||
 | 
			
		||||
# Install docker
 | 
			
		||||
# https://docs.docker.com/engine/installation/linux/docker-ce/ubuntu/
 | 
			
		||||
RUN apt-get update && apt-get install -y \
 | 
			
		||||
        apt-transport-https \
 | 
			
		||||
        ca-certificates \
 | 
			
		||||
        curl \
 | 
			
		||||
        software-properties-common \
 | 
			
		||||
    && rm -rf /var/lib/apt/lists/* \
 | 
			
		||||
    && curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add - \
 | 
			
		||||
    && add-apt-repository "deb https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" \
 | 
			
		||||
    && apt-get update && apt-get install -y docker-ce \
 | 
			
		||||
    && rm -rf /var/lib/apt/lists/*
 | 
			
		||||
 | 
			
		||||
# setup arm binary support
 | 
			
		||||
RUN apt-get update && apt-get install -y \
 | 
			
		||||
        qemu-user-static \
 | 
			
		||||
        binfmt-support \
 | 
			
		||||
    && rm -rf /var/lib/apt/lists/*
 | 
			
		||||
 | 
			
		||||
WORKDIR /data
 | 
			
		||||
@@ -1,75 +1,19 @@
 | 
			
		||||
ARG BUILD_FROM=hassioaddons/ubuntu-base:2.2.0
 | 
			
		||||
# hadolint ignore=DL3006
 | 
			
		||||
ARG BUILD_FROM
 | 
			
		||||
FROM ${BUILD_FROM}
 | 
			
		||||
 | 
			
		||||
# Set shell
 | 
			
		||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
 | 
			
		||||
 | 
			
		||||
# Copy root filesystem
 | 
			
		||||
COPY esphomeyaml-edge/rootfs /
 | 
			
		||||
COPY setup.py setup.cfg MANIFEST.in /opt/esphomeyaml/
 | 
			
		||||
COPY esphomeyaml /opt/esphomeyaml/esphomeyaml
 | 
			
		||||
COPY docker/rootfs/ /
 | 
			
		||||
COPY setup.py setup.cfg MANIFEST.in /opt/esphome/
 | 
			
		||||
COPY esphome /opt/esphome/esphome
 | 
			
		||||
 | 
			
		||||
RUN \
 | 
			
		||||
    # Temporarily move nginx.conf (otherwise dpkg fails)
 | 
			
		||||
    mv /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bkp \
 | 
			
		||||
    # Install add-on dependencies
 | 
			
		||||
    && apt-get update \
 | 
			
		||||
    && apt-get install -y --no-install-recommends \
 | 
			
		||||
        # Python for esphomeyaml
 | 
			
		||||
        python \
 | 
			
		||||
        python-pip \
 | 
			
		||||
        python-setuptools \
 | 
			
		||||
        # Python Pillow for display component
 | 
			
		||||
        python-pil \
 | 
			
		||||
        # Git for esphomelib downloads
 | 
			
		||||
        git \
 | 
			
		||||
        # Ping for dashboard online/offline status
 | 
			
		||||
        iputils-ping \
 | 
			
		||||
        # NGINX proxy
 | 
			
		||||
        nginx \
 | 
			
		||||
    \
 | 
			
		||||
    && mv /etc/nginx/nginx.conf.bkp /etc/nginx/nginx.conf \
 | 
			
		||||
    \
 | 
			
		||||
    && pip2 install --no-cache-dir --no-binary :all: -e /opt/esphomeyaml \
 | 
			
		||||
    \
 | 
			
		||||
    # Change some platformio settings
 | 
			
		||||
    && 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 \
 | 
			
		||||
    \
 | 
			
		||||
    # Build an empty platformio project to force platformio to install all fw build dependencies
 | 
			
		||||
    # The return-code will be non-zero since there's nothing to build.
 | 
			
		||||
    && (platformio run -d /opt/pio; echo "Done") \
 | 
			
		||||
    \
 | 
			
		||||
    # Cleanup
 | 
			
		||||
    && rm -fr \
 | 
			
		||||
        /tmp/* \
 | 
			
		||||
        /var/{cache,log}/* \
 | 
			
		||||
        /var/lib/apt/lists/* \
 | 
			
		||||
        /opt/pio/
 | 
			
		||||
RUN pip3 install --no-cache-dir -e /opt/esphome
 | 
			
		||||
 | 
			
		||||
# Build arugments
 | 
			
		||||
ARG BUILD_ARCH=amd64
 | 
			
		||||
ARG BUILD_DATE
 | 
			
		||||
ARG BUILD_REF
 | 
			
		||||
ARG BUILD_VERSION
 | 
			
		||||
# Build arguments
 | 
			
		||||
ARG BUILD_VERSION=dev
 | 
			
		||||
 | 
			
		||||
# Labels
 | 
			
		||||
LABEL \
 | 
			
		||||
    io.hass.name="esphomeyaml" \
 | 
			
		||||
    io.hass.name="ESPHome" \
 | 
			
		||||
    io.hass.description="Manage and program ESP8266/ESP32 microcontrollers through YAML configuration files" \
 | 
			
		||||
    io.hass.arch="${BUILD_ARCH}" \
 | 
			
		||||
    io.hass.type="addon" \
 | 
			
		||||
    io.hass.version=${BUILD_VERSION} \
 | 
			
		||||
    maintainer="Otto Winter <contact@otto-winter.com>" \
 | 
			
		||||
    org.label-schema.description="Manage and program ESP8266/ESP32 microcontrollers through YAML configuration files" \
 | 
			
		||||
    org.label-schema.build-date=${BUILD_DATE} \
 | 
			
		||||
    org.label-schema.name="esphomeyaml" \
 | 
			
		||||
    org.label-schema.schema-version="1.0" \
 | 
			
		||||
    org.label-schema.url="https://esphomelib.com" \
 | 
			
		||||
    org.label-schema.usage="https://github.com/OttoWinter/esphomeyaml/tree/dev/esphomeyaml/README.md" \
 | 
			
		||||
    org.label-schema.vcs-ref=${BUILD_REF} \
 | 
			
		||||
    org.label-schema.vcs-url="https://github.com/OttoWinter/esphomeyaml" \
 | 
			
		||||
    org.label-schema.vendor="esphomelib"
 | 
			
		||||
    io.hass.version=${BUILD_VERSION}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,20 @@
 | 
			
		||||
FROM python:2.7
 | 
			
		||||
FROM esphome/esphome-base-amd64:2.1.1
 | 
			
		||||
 | 
			
		||||
COPY requirements.txt /requirements.txt
 | 
			
		||||
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/*
 | 
			
		||||
 | 
			
		||||
RUN pip install -r /requirements.txt && \
 | 
			
		||||
    pip install flake8==3.6.0 pylint==1.9.4 pillow
 | 
			
		||||
COPY requirements_test.txt /requirements_test.txt
 | 
			
		||||
RUN pip3 install --no-cache-dir wheel && pip3 install --no-cache-dir -r /requirements_test.txt
 | 
			
		||||
 | 
			
		||||
RUN ln -s /usr/bin/pip3 /usr/bin/pip && ln -f -s /usr/bin/python3 /usr/bin/python
 | 
			
		||||
 | 
			
		||||
VOLUME ["/esphome"]
 | 
			
		||||
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 --no-binary :all: 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
 | 
			
		||||
							
								
								
									
										41
									
								
								docker/rootfs/etc/cont-init.d/10-requirements.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										41
									
								
								docker/rootfs/etc/cont-init.d/10-requirements.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,41 @@
 | 
			
		||||
#!/usr/bin/with-contenv bashio
 | 
			
		||||
# ==============================================================================
 | 
			
		||||
# Community Hass.io Add-ons: ESPHome
 | 
			
		||||
# This files check if all user configuration requirements are met
 | 
			
		||||
# ==============================================================================
 | 
			
		||||
 | 
			
		||||
# Check SSL requirements, if enabled
 | 
			
		||||
if bashio::config.true 'ssl'; then
 | 
			
		||||
    if ! bashio::config.has_value 'certfile'; then
 | 
			
		||||
        bashio::fatal 'SSL is enabled, but no certfile was specified.'
 | 
			
		||||
        bashio::exit.nok
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if ! bashio::config.has_value 'keyfile'; then
 | 
			
		||||
        bashio::fatal 'SSL is enabled, but no keyfile was specified'
 | 
			
		||||
        bashio::exit.nok
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    certfile="/ssl/$(bashio::config 'certfile')"
 | 
			
		||||
    keyfile="/ssl/$(bashio::config 'keyfile')"
 | 
			
		||||
 | 
			
		||||
    if ! bashio::fs.file_exists "${certfile}"; then
 | 
			
		||||
        if ! bashio::fs.file_exists "${keyfile}"; then
 | 
			
		||||
            # Both files are missing, let's print a friendlier error message
 | 
			
		||||
            bashio::log.fatal 'You enabled encrypted connections using the "ssl": true option.'
 | 
			
		||||
            bashio::log.fatal "However, the SSL files '${certfile}' and '${keyfile}'"
 | 
			
		||||
            bashio::log.fatal "were not found. If you're using Hass.io on your local network and don't want"
 | 
			
		||||
            bashio::log.fatal 'to encrypt connections to the ESPHome dashboard, you can manually disable'
 | 
			
		||||
            bashio::log.fatal 'SSL by setting "ssl" to false."'
 | 
			
		||||
            bashio::exit.nok
 | 
			
		||||
        fi
 | 
			
		||||
        bashio::log.fatal "The configured certfile '${certfile}' was not found."
 | 
			
		||||
        bashio::exit.nok
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if ! bashio::fs.file_exists "/ssl/$(bashio::config 'keyfile')"; then
 | 
			
		||||
        bashio::log.fatal "The configured keyfile '${keyfile}' was not found."
 | 
			
		||||
        bashio::exit.nok
 | 
			
		||||
    fi
 | 
			
		||||
fi
 | 
			
		||||
							
								
								
									
										34
									
								
								docker/rootfs/etc/cont-init.d/20-nginx.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										34
									
								
								docker/rootfs/etc/cont-init.d/20-nginx.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,34 @@
 | 
			
		||||
#!/usr/bin/with-contenv bashio
 | 
			
		||||
# ==============================================================================
 | 
			
		||||
# Community Hass.io Add-ons: ESPHome
 | 
			
		||||
# Configures NGINX for use with ESPHome
 | 
			
		||||
# ==============================================================================
 | 
			
		||||
 | 
			
		||||
declare certfile
 | 
			
		||||
declare keyfile
 | 
			
		||||
declare direct_port
 | 
			
		||||
declare ingress_interface
 | 
			
		||||
declare ingress_port
 | 
			
		||||
 | 
			
		||||
mkdir -p /var/log/nginx
 | 
			
		||||
 | 
			
		||||
direct_port=$(bashio::addon.port 6052)
 | 
			
		||||
if bashio::var.has_value "${direct_port}"; then
 | 
			
		||||
    if bashio::config.true 'ssl'; then
 | 
			
		||||
        certfile=$(bashio::config 'certfile')
 | 
			
		||||
        keyfile=$(bashio::config 'keyfile')
 | 
			
		||||
 | 
			
		||||
        mv /etc/nginx/servers/direct-ssl.disabled /etc/nginx/servers/direct.conf
 | 
			
		||||
        sed -i "s/%%certfile%%/${certfile}/g" /etc/nginx/servers/direct.conf
 | 
			
		||||
        sed -i "s/%%keyfile%%/${keyfile}/g" /etc/nginx/servers/direct.conf
 | 
			
		||||
    else
 | 
			
		||||
        mv /etc/nginx/servers/direct.disabled /etc/nginx/servers/direct.conf
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    sed -i "s/%%port%%/${direct_port}/g" /etc/nginx/servers/direct.conf
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
ingress_port=$(bashio::addon.ingress_port)
 | 
			
		||||
ingress_interface=$(bashio::addon.ip_address)
 | 
			
		||||
sed -i "s/%%port%%/${ingress_port}/g" /etc/nginx/servers/ingress.conf
 | 
			
		||||
sed -i "s/%%interface%%/${ingress_interface}/g" /etc/nginx/servers/ingress.conf
 | 
			
		||||
							
								
								
									
										15
									
								
								docker/rootfs/etc/cont-init.d/30-esphome.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								docker/rootfs/etc/cont-init.d/30-esphome.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
#!/usr/bin/with-contenv bashio
 | 
			
		||||
# ==============================================================================
 | 
			
		||||
# Community Hass.io Add-ons: ESPHome
 | 
			
		||||
# This files installs the user ESPHome version if specified
 | 
			
		||||
# ==============================================================================
 | 
			
		||||
 | 
			
		||||
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"
 | 
			
		||||
    bashio::log.info "Installing esphome version '${esphome_version}' (${full_url})..."
 | 
			
		||||
    pip3 install -U --no-cache-dir "${full_url}" \
 | 
			
		||||
      || bashio::exit.nok "Failed installing esphome pinned version."
 | 
			
		||||
fi
 | 
			
		||||
							
								
								
									
										11
									
								
								docker/rootfs/etc/cont-init.d/40-migrate.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								docker/rootfs/etc/cont-init.d/40-migrate.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
#!/usr/bin/with-contenv bashio
 | 
			
		||||
# ==============================================================================
 | 
			
		||||
# Community Hass.io Add-ons: ESPHome
 | 
			
		||||
# This files migrates the esphome config directory from the old path
 | 
			
		||||
# ==============================================================================
 | 
			
		||||
 | 
			
		||||
if [[ ! -d /config/esphome && -d /config/esphomeyaml ]]; then
 | 
			
		||||
    echo "Moving config directory from /config/esphomeyaml to /config/esphome"
 | 
			
		||||
    mv /config/esphomeyaml /config/esphome
 | 
			
		||||
    mv /config/esphome/.esphomeyaml /config/esphome/.esphome
 | 
			
		||||
fi
 | 
			
		||||
							
								
								
									
										96
									
								
								docker/rootfs/etc/nginx/includes/mime.types
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								docker/rootfs/etc/nginx/includes/mime.types
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,96 @@
 | 
			
		||||
types {
 | 
			
		||||
    text/html                                        html htm shtml;
 | 
			
		||||
    text/css                                         css;
 | 
			
		||||
    text/xml                                         xml;
 | 
			
		||||
    image/gif                                        gif;
 | 
			
		||||
    image/jpeg                                       jpeg jpg;
 | 
			
		||||
    application/javascript                           js;
 | 
			
		||||
    application/atom+xml                             atom;
 | 
			
		||||
    application/rss+xml                              rss;
 | 
			
		||||
 | 
			
		||||
    text/mathml                                      mml;
 | 
			
		||||
    text/plain                                       txt;
 | 
			
		||||
    text/vnd.sun.j2me.app-descriptor                 jad;
 | 
			
		||||
    text/vnd.wap.wml                                 wml;
 | 
			
		||||
    text/x-component                                 htc;
 | 
			
		||||
 | 
			
		||||
    image/png                                        png;
 | 
			
		||||
    image/svg+xml                                    svg svgz;
 | 
			
		||||
    image/tiff                                       tif tiff;
 | 
			
		||||
    image/vnd.wap.wbmp                               wbmp;
 | 
			
		||||
    image/webp                                       webp;
 | 
			
		||||
    image/x-icon                                     ico;
 | 
			
		||||
    image/x-jng                                      jng;
 | 
			
		||||
    image/x-ms-bmp                                   bmp;
 | 
			
		||||
 | 
			
		||||
    font/woff                                        woff;
 | 
			
		||||
    font/woff2                                       woff2;
 | 
			
		||||
 | 
			
		||||
    application/java-archive                         jar war ear;
 | 
			
		||||
    application/json                                 json;
 | 
			
		||||
    application/mac-binhex40                         hqx;
 | 
			
		||||
    application/msword                               doc;
 | 
			
		||||
    application/pdf                                  pdf;
 | 
			
		||||
    application/postscript                           ps eps ai;
 | 
			
		||||
    application/rtf                                  rtf;
 | 
			
		||||
    application/vnd.apple.mpegurl                    m3u8;
 | 
			
		||||
    application/vnd.google-earth.kml+xml             kml;
 | 
			
		||||
    application/vnd.google-earth.kmz                 kmz;
 | 
			
		||||
    application/vnd.ms-excel                         xls;
 | 
			
		||||
    application/vnd.ms-fontobject                    eot;
 | 
			
		||||
    application/vnd.ms-powerpoint                    ppt;
 | 
			
		||||
    application/vnd.oasis.opendocument.graphics      odg;
 | 
			
		||||
    application/vnd.oasis.opendocument.presentation  odp;
 | 
			
		||||
    application/vnd.oasis.opendocument.spreadsheet   ods;
 | 
			
		||||
    application/vnd.oasis.opendocument.text          odt;
 | 
			
		||||
    application/vnd.openxmlformats-officedocument.presentationml.presentation
 | 
			
		||||
                                                     pptx;
 | 
			
		||||
    application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
 | 
			
		||||
                                                     xlsx;
 | 
			
		||||
    application/vnd.openxmlformats-officedocument.wordprocessingml.document
 | 
			
		||||
                                                     docx;
 | 
			
		||||
    application/vnd.wap.wmlc                         wmlc;
 | 
			
		||||
    application/x-7z-compressed                      7z;
 | 
			
		||||
    application/x-cocoa                              cco;
 | 
			
		||||
    application/x-java-archive-diff                  jardiff;
 | 
			
		||||
    application/x-java-jnlp-file                     jnlp;
 | 
			
		||||
    application/x-makeself                           run;
 | 
			
		||||
    application/x-perl                               pl pm;
 | 
			
		||||
    application/x-pilot                              prc pdb;
 | 
			
		||||
    application/x-rar-compressed                     rar;
 | 
			
		||||
    application/x-redhat-package-manager             rpm;
 | 
			
		||||
    application/x-sea                                sea;
 | 
			
		||||
    application/x-shockwave-flash                    swf;
 | 
			
		||||
    application/x-stuffit                            sit;
 | 
			
		||||
    application/x-tcl                                tcl tk;
 | 
			
		||||
    application/x-x509-ca-cert                       der pem crt;
 | 
			
		||||
    application/x-xpinstall                          xpi;
 | 
			
		||||
    application/xhtml+xml                            xhtml;
 | 
			
		||||
    application/xspf+xml                             xspf;
 | 
			
		||||
    application/zip                                  zip;
 | 
			
		||||
 | 
			
		||||
    application/octet-stream                         bin exe dll;
 | 
			
		||||
    application/octet-stream                         deb;
 | 
			
		||||
    application/octet-stream                         dmg;
 | 
			
		||||
    application/octet-stream                         iso img;
 | 
			
		||||
    application/octet-stream                         msi msp msm;
 | 
			
		||||
 | 
			
		||||
    audio/midi                                       mid midi kar;
 | 
			
		||||
    audio/mpeg                                       mp3;
 | 
			
		||||
    audio/ogg                                        ogg;
 | 
			
		||||
    audio/x-m4a                                      m4a;
 | 
			
		||||
    audio/x-realaudio                                ra;
 | 
			
		||||
 | 
			
		||||
    video/3gpp                                       3gpp 3gp;
 | 
			
		||||
    video/mp2t                                       ts;
 | 
			
		||||
    video/mp4                                        mp4;
 | 
			
		||||
    video/mpeg                                       mpeg mpg;
 | 
			
		||||
    video/quicktime                                  mov;
 | 
			
		||||
    video/webm                                       webm;
 | 
			
		||||
    video/x-flv                                      flv;
 | 
			
		||||
    video/x-m4v                                      m4v;
 | 
			
		||||
    video/x-mng                                      mng;
 | 
			
		||||
    video/x-ms-asf                                   asx asf;
 | 
			
		||||
    video/x-ms-wmv                                   wmv;
 | 
			
		||||
    video/x-msvideo                                  avi;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										16
									
								
								docker/rootfs/etc/nginx/includes/proxy_params.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								docker/rootfs/etc/nginx/includes/proxy_params.conf
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
proxy_http_version 1.1;
 | 
			
		||||
proxy_ignore_client_abort off;
 | 
			
		||||
proxy_read_timeout 86400s;
 | 
			
		||||
proxy_redirect off;
 | 
			
		||||
proxy_send_timeout 86400s;
 | 
			
		||||
proxy_max_temp_file_size 0;
 | 
			
		||||
 | 
			
		||||
proxy_set_header Accept-Encoding "";
 | 
			
		||||
proxy_set_header Connection $connection_upgrade;
 | 
			
		||||
proxy_set_header Host $http_host;
 | 
			
		||||
proxy_set_header Upgrade $http_upgrade;
 | 
			
		||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 | 
			
		||||
proxy_set_header X-Forwarded-Proto $scheme;
 | 
			
		||||
proxy_set_header X-NginX-Proxy true;
 | 
			
		||||
proxy_set_header X-Real-IP $remote_addr;
 | 
			
		||||
proxy_set_header Authorization "";
 | 
			
		||||
							
								
								
									
										6
									
								
								docker/rootfs/etc/nginx/includes/server_params.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								docker/rootfs/etc/nginx/includes/server_params.conf
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
root /dev/null;
 | 
			
		||||
server_name $hostname;
 | 
			
		||||
 | 
			
		||||
add_header X-Content-Type-Options nosniff;
 | 
			
		||||
add_header X-XSS-Protection "1; mode=block";
 | 
			
		||||
add_header X-Robots-Tag none;
 | 
			
		||||
							
								
								
									
										9
									
								
								docker/rootfs/etc/nginx/includes/ssl_params.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								docker/rootfs/etc/nginx/includes/ssl_params.conf
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
ssl_protocols TLSv1.2;
 | 
			
		||||
ssl_prefer_server_ciphers on;
 | 
			
		||||
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:DHE-RSA-AES256-SHA;
 | 
			
		||||
ssl_ecdh_curve secp384r1;
 | 
			
		||||
ssl_session_timeout  10m;
 | 
			
		||||
ssl_session_cache shared:SSL:10m;
 | 
			
		||||
ssl_session_tickets off;
 | 
			
		||||
ssl_stapling on;
 | 
			
		||||
ssl_stapling_verify on;
 | 
			
		||||
							
								
								
									
										33
									
								
								docker/rootfs/etc/nginx/nginx.conf
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										33
									
								
								docker/rootfs/etc/nginx/nginx.conf
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
daemon off;
 | 
			
		||||
user root;
 | 
			
		||||
pid /var/run/nginx.pid;
 | 
			
		||||
worker_processes 1;
 | 
			
		||||
# Hass.io addon log
 | 
			
		||||
error_log /proc/1/fd/1 error;
 | 
			
		||||
events {
 | 
			
		||||
    worker_connections 1024;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
http {
 | 
			
		||||
    include /etc/nginx/includes/mime.types;
 | 
			
		||||
    access_log stdout;
 | 
			
		||||
    default_type application/octet-stream;
 | 
			
		||||
    gzip on;
 | 
			
		||||
    keepalive_timeout 65;
 | 
			
		||||
    sendfile on;
 | 
			
		||||
    server_tokens off;
 | 
			
		||||
 | 
			
		||||
    map $http_upgrade $connection_upgrade {
 | 
			
		||||
        default upgrade;
 | 
			
		||||
        ''      close;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    # Use Hass.io supervisor as resolver
 | 
			
		||||
    resolver 172.30.32.2;
 | 
			
		||||
 | 
			
		||||
    upstream esphome {
 | 
			
		||||
        server unix:/var/run/esphome.sock;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    include /etc/nginx/servers/*.conf;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										22
									
								
								docker/rootfs/etc/nginx/servers/direct-ssl.disabled
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								docker/rootfs/etc/nginx/servers/direct-ssl.disabled
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
server {
 | 
			
		||||
    listen %%port%% default_server ssl http2;
 | 
			
		||||
 | 
			
		||||
    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 "";
 | 
			
		||||
 | 
			
		||||
    # Redirect http requests to https on the same port.
 | 
			
		||||
    # https://rageagainstshell.com/2016/11/redirect-http-to-https-on-the-same-port-in-nginx/
 | 
			
		||||
    error_page 497 https://$http_host$request_uri;
 | 
			
		||||
 | 
			
		||||
    location / {
 | 
			
		||||
        proxy_pass http://esphome;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								docker/rootfs/etc/nginx/servers/direct.disabled
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								docker/rootfs/etc/nginx/servers/direct.disabled
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
server {
 | 
			
		||||
    listen %%port%% default_server;
 | 
			
		||||
 | 
			
		||||
    include /etc/nginx/includes/server_params.conf;
 | 
			
		||||
    include /etc/nginx/includes/proxy_params.conf;
 | 
			
		||||
    # Clear Hass.io Ingress header
 | 
			
		||||
    proxy_set_header X-Hassio-Ingress "";
 | 
			
		||||
 | 
			
		||||
    location / {
 | 
			
		||||
        proxy_pass http://esphome;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										16
									
								
								docker/rootfs/etc/nginx/servers/ingress.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								docker/rootfs/etc/nginx/servers/ingress.conf
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
server {
 | 
			
		||||
    listen %%interface%%:%%port%% default_server;
 | 
			
		||||
 | 
			
		||||
    include /etc/nginx/includes/server_params.conf;
 | 
			
		||||
    include /etc/nginx/includes/proxy_params.conf;
 | 
			
		||||
    # Set Hass.io Ingress header
 | 
			
		||||
    proxy_set_header X-Hassio-Ingress "YES";
 | 
			
		||||
 | 
			
		||||
    location / {
 | 
			
		||||
        # Only allow from Hass.io supervisor
 | 
			
		||||
        allow   172.30.32.2;
 | 
			
		||||
        deny    all;
 | 
			
		||||
 | 
			
		||||
        proxy_pass http://esphome;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
#!/usr/bin/execlineb -S0
 | 
			
		||||
# ==============================================================================
 | 
			
		||||
# Community Hass.io Add-ons: esphomeyaml
 | 
			
		||||
# Take down the S6 supervision tree when esphomeyaml fails
 | 
			
		||||
# Community Hass.io Add-ons: ESPHome
 | 
			
		||||
# Take down the S6 supervision tree when ESPHome fails
 | 
			
		||||
# ==============================================================================
 | 
			
		||||
if -n { s6-test $# -ne 0 }
 | 
			
		||||
if -n { s6-test ${1} -eq 256 }
 | 
			
		||||
							
								
								
									
										26
									
								
								docker/rootfs/etc/services.d/esphome/run
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										26
									
								
								docker/rootfs/etc/services.d/esphome/run
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
#!/usr/bin/with-contenv bashio
 | 
			
		||||
# ==============================================================================
 | 
			
		||||
# Community Hass.io Add-ons: ESPHome
 | 
			
		||||
# Runs the ESPHome dashboard
 | 
			
		||||
# ==============================================================================
 | 
			
		||||
 | 
			
		||||
export ESPHOME_IS_HASSIO=true
 | 
			
		||||
 | 
			
		||||
if bashio::config.true 'leave_front_door_open'; then
 | 
			
		||||
    export DISABLE_HA_AUTHENTICATION=true
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if bashio::config.true 'streamer_mode'; then
 | 
			
		||||
    export ESPHOME_STREAMER_MODE=true
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if bashio::config.true 'status_use_ping'; then
 | 
			
		||||
    export ESPHOME_DASHBOARD_USE_PING=true
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if bashio::config.has_value 'relative_url'; then
 | 
			
		||||
    export ESPHOME_DASHBOARD_RELATIVE_URL=$(bashio::config 'relative_url')
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
bashio::log.info "Starting ESPHome dashboard..."
 | 
			
		||||
exec esphome /config/esphome dashboard --socket /var/run/esphome.sock --hassio
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
#!/usr/bin/execlineb -S0
 | 
			
		||||
# ==============================================================================
 | 
			
		||||
# Community Hass.io Add-ons: esphomeyaml
 | 
			
		||||
# Community Hass.io Add-ons: ESPHome
 | 
			
		||||
# Take down the S6 supervision tree when NGINX fails
 | 
			
		||||
# ==============================================================================
 | 
			
		||||
if -n { s6-test $# -ne 0 }
 | 
			
		||||
							
								
								
									
										14
									
								
								docker/rootfs/etc/services.d/nginx/run
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										14
									
								
								docker/rootfs/etc/services.d/nginx/run
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
#!/usr/bin/with-contenv bashio
 | 
			
		||||
# ==============================================================================
 | 
			
		||||
# Community Hass.io Add-ons: ESPHome
 | 
			
		||||
# Runs the NGINX proxy
 | 
			
		||||
# ==============================================================================
 | 
			
		||||
 | 
			
		||||
bashio::log.info "Waiting for dashboard to come up..."
 | 
			
		||||
 | 
			
		||||
while [[ ! -S /var/run/esphome.sock ]]; do
 | 
			
		||||
  sleep 0.5
 | 
			
		||||
done
 | 
			
		||||
 | 
			
		||||
bashio::log.info "Starting NGINX..."
 | 
			
		||||
exec nginx
 | 
			
		||||
@@ -1,37 +1,30 @@
 | 
			
		||||
from __future__ import print_function
 | 
			
		||||
 | 
			
		||||
import argparse
 | 
			
		||||
from collections import OrderedDict
 | 
			
		||||
from datetime import datetime
 | 
			
		||||
import functools
 | 
			
		||||
import logging
 | 
			
		||||
import os
 | 
			
		||||
import random
 | 
			
		||||
import sys
 | 
			
		||||
from datetime import datetime
 | 
			
		||||
 | 
			
		||||
from esphomeyaml import const, core_config, mqtt, platformio_api, wizard, writer, yaml_util
 | 
			
		||||
from esphomeyaml.api.client import run_logs
 | 
			
		||||
from esphomeyaml.config import get_component, iter_components, read_config, strip_default_ids
 | 
			
		||||
from esphomeyaml.const import CONF_BAUD_RATE, CONF_ESPHOMEYAML, CONF_LOGGER, CONF_USE_CUSTOM_CODE, \
 | 
			
		||||
    CONF_BROKER
 | 
			
		||||
from esphomeyaml.core import CORE, EsphomeyamlError
 | 
			
		||||
from esphomeyaml.cpp_generator import Expression, RawStatement, add, statement
 | 
			
		||||
from esphomeyaml.helpers import color, indent
 | 
			
		||||
from esphomeyaml.py_compat import safe_input, text_type, IS_PY2
 | 
			
		||||
from esphomeyaml.storage_json import StorageJSON, esphomeyaml_storage_path, \
 | 
			
		||||
    start_update_check_thread, storage_path
 | 
			
		||||
from esphomeyaml.util import run_external_command, safe_print
 | 
			
		||||
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_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, IS_PY3
 | 
			
		||||
from esphome.util import run_external_command, run_external_process, safe_print, list_yaml_files
 | 
			
		||||
 | 
			
		||||
_LOGGER = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
PRE_INITIALIZE = ['esphomeyaml', 'logger', 'wifi', 'ethernet', 'ota', 'mqtt', 'web_server', 'api',
 | 
			
		||||
                  'i2c']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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():
 | 
			
		||||
    for port, desc, info in comports(include_links=True):
 | 
			
		||||
        if not port:
 | 
			
		||||
            continue
 | 
			
		||||
        if "VID:PID" in info:
 | 
			
		||||
@@ -42,7 +35,9 @@ def get_serial_ports():
 | 
			
		||||
 | 
			
		||||
def choose_prompt(options):
 | 
			
		||||
    if not options:
 | 
			
		||||
        raise ValueError
 | 
			
		||||
        raise EsphomeError("Found no valid options for upload/logging, please make sure relevant "
 | 
			
		||||
                           "sections (ota, mqtt, ...) are in your configuration and/or the device "
 | 
			
		||||
                           "is plugged in.")
 | 
			
		||||
 | 
			
		||||
    if len(options) == 1:
 | 
			
		||||
        return options[0][1]
 | 
			
		||||
@@ -95,6 +90,8 @@ def get_port_type(port):
 | 
			
		||||
 | 
			
		||||
def run_miniterm(config, port):
 | 
			
		||||
    import serial
 | 
			
		||||
    from esphome import platformio_api
 | 
			
		||||
 | 
			
		||||
    if CONF_LOGGER not in config:
 | 
			
		||||
        _LOGGER.info("Logger is not enabled. Not starting UART logs.")
 | 
			
		||||
        return
 | 
			
		||||
@@ -124,112 +121,122 @@ def run_miniterm(config, port):
 | 
			
		||||
                config, line, backtrace_state=backtrace_state)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def wrap_to_code(name, comp):
 | 
			
		||||
    coro = coroutine(comp.to_code)
 | 
			
		||||
 | 
			
		||||
    @functools.wraps(comp.to_code)
 | 
			
		||||
    @coroutine_with_priority(coro.priority)
 | 
			
		||||
    def wrapped(conf):
 | 
			
		||||
        cg.add(cg.LineComment(u"{}:".format(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)
 | 
			
		||||
 | 
			
		||||
    return wrapped
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def write_cpp(config):
 | 
			
		||||
    _LOGGER.info("Generating C++ source...")
 | 
			
		||||
 | 
			
		||||
    CORE.add_job(core_config.to_code, config[CONF_ESPHOMEYAML], domain='esphomeyaml')
 | 
			
		||||
    for domain in PRE_INITIALIZE:
 | 
			
		||||
        if domain == CONF_ESPHOMEYAML or domain not in config:
 | 
			
		||||
            continue
 | 
			
		||||
        CORE.add_job(get_component(domain).to_code, config[domain], domain=domain)
 | 
			
		||||
 | 
			
		||||
    for domain, component, conf in iter_components(config):
 | 
			
		||||
        if domain in PRE_INITIALIZE or not hasattr(component, 'to_code'):
 | 
			
		||||
            continue
 | 
			
		||||
        CORE.add_job(component.to_code, conf, domain=domain)
 | 
			
		||||
    for name, component, conf in iter_components(CORE.config):
 | 
			
		||||
        if component.to_code is not None:
 | 
			
		||||
            coro = wrap_to_code(name, component)
 | 
			
		||||
            CORE.add_job(coro, conf)
 | 
			
		||||
 | 
			
		||||
    CORE.flush_tasks()
 | 
			
		||||
    add(RawStatement(''))
 | 
			
		||||
    add(RawStatement(''))
 | 
			
		||||
    all_code = []
 | 
			
		||||
    for exp in CORE.expressions:
 | 
			
		||||
        if not config[CONF_ESPHOMEYAML][CONF_USE_CUSTOM_CODE]:
 | 
			
		||||
            if isinstance(exp, Expression) and not exp.required:
 | 
			
		||||
                continue
 | 
			
		||||
        all_code.append(text_type(statement(exp)))
 | 
			
		||||
 | 
			
		||||
    writer.write_platformio_project()
 | 
			
		||||
 | 
			
		||||
    code_s = indent('\n'.join(line.rstrip() for line in all_code))
 | 
			
		||||
    code_s = indent(CORE.cpp_main_section)
 | 
			
		||||
    writer.write_cpp(code_s)
 | 
			
		||||
    return 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def compile_program(args, config):
 | 
			
		||||
    from esphome import platformio_api
 | 
			
		||||
 | 
			
		||||
    _LOGGER.info("Compiling app...")
 | 
			
		||||
    update_check = not os.getenv('ESPHOMEYAML_NO_UPDATE_CHECK', '')
 | 
			
		||||
    if update_check:
 | 
			
		||||
        thread = start_update_check_thread(esphomeyaml_storage_path(CORE.config_dir))
 | 
			
		||||
    rc = platformio_api.run_compile(config, args.verbose)
 | 
			
		||||
    if update_check:
 | 
			
		||||
        thread.join()
 | 
			
		||||
    return rc
 | 
			
		||||
    return platformio_api.run_compile(config, CORE.verbose)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def upload_using_esptool(config, port):
 | 
			
		||||
    import esptool
 | 
			
		||||
    path = CORE.firmware_bin
 | 
			
		||||
    first_baudrate = config[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS].get('upload_speed', 460800)
 | 
			
		||||
 | 
			
		||||
    path = os.path.join(CORE.build_path, '.pioenvs', CORE.name, 'firmware.bin')
 | 
			
		||||
    cmd = ['esptool.py', '--before', 'default_reset', '--after', 'hard_reset',
 | 
			
		||||
           '--chip', 'esp8266', '--port', port, 'write_flash', '0x0', path]
 | 
			
		||||
    # 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]
 | 
			
		||||
 | 
			
		||||
        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):
 | 
			
		||||
    # if upload is to a serial port use platformio, otherwise assume ota
 | 
			
		||||
    if get_port_type(host) == 'SERIAL':
 | 
			
		||||
        from esphome import platformio_api
 | 
			
		||||
 | 
			
		||||
        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 esphomeyaml.components import ota
 | 
			
		||||
    from esphomeyaml import espota2
 | 
			
		||||
    from esphome import espota2
 | 
			
		||||
 | 
			
		||||
    if args.host_port is not None:
 | 
			
		||||
        host_port = args.host_port
 | 
			
		||||
    else:
 | 
			
		||||
        host_port = int(os.getenv('ESPHOMEYAML_OTA_HOST_PORT', random.randint(10000, 60000)))
 | 
			
		||||
 | 
			
		||||
    verbose = args.verbose
 | 
			
		||||
    remote_port = ota.get_port(config)
 | 
			
		||||
    password = ota.get_auth(config)
 | 
			
		||||
 | 
			
		||||
    storage = StorageJSON.load(storage_path())
 | 
			
		||||
    res = espota2.run_ota(host, remote_port, password, CORE.firmware_bin)
 | 
			
		||||
    if res == 0:
 | 
			
		||||
        if storage is not None and storage.use_legacy_ota:
 | 
			
		||||
            storage.use_legacy_ota = False
 | 
			
		||||
            storage.save(storage_path())
 | 
			
		||||
        return res
 | 
			
		||||
    if storage is not None and not storage.use_legacy_ota:
 | 
			
		||||
        return res
 | 
			
		||||
 | 
			
		||||
    _LOGGER.warning("OTA v2 method failed. Trying with legacy OTA...")
 | 
			
		||||
    return espota2.run_legacy_ota(verbose, host_port, host, remote_port, password,
 | 
			
		||||
                                  CORE.firmware_bin)
 | 
			
		||||
    ota_conf = config[CONF_OTA]
 | 
			
		||||
    remote_port = ota_conf[CONF_PORT]
 | 
			
		||||
    password = ota_conf[CONF_PASSWORD]
 | 
			
		||||
    return espota2.run_ota(host, remote_port, password, CORE.firmware_bin)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def show_logs(config, args, port):
 | 
			
		||||
    if 'logger' not in config:
 | 
			
		||||
        raise EsphomeyamlError("Logger is not configured!")
 | 
			
		||||
        raise EsphomeError("Logger is not configured!")
 | 
			
		||||
    if get_port_type(port) == 'SERIAL':
 | 
			
		||||
        run_miniterm(config, port)
 | 
			
		||||
        return 0
 | 
			
		||||
    if get_port_type(port) == 'NETWORK' and 'api' in config:
 | 
			
		||||
        from esphome.api.client import run_logs
 | 
			
		||||
 | 
			
		||||
        return run_logs(config, port)
 | 
			
		||||
    if get_port_type(port) == 'MQTT' and 'mqtt' in config:
 | 
			
		||||
        from esphome import mqtt
 | 
			
		||||
 | 
			
		||||
        return mqtt.show_logs(config, args.topic, args.username, args.password, args.client_id)
 | 
			
		||||
 | 
			
		||||
    raise ValueError
 | 
			
		||||
    raise EsphomeError("No remote or local logging method configured (api/mqtt/logger)")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def clean_mqtt(config, args):
 | 
			
		||||
    from esphome import mqtt
 | 
			
		||||
 | 
			
		||||
    return mqtt.clear_topic(config, args.topic, args.username, args.password, args.client_id)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def setup_log(debug=False):
 | 
			
		||||
    log_level = logging.DEBUG if debug else logging.INFO
 | 
			
		||||
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)
 | 
			
		||||
@@ -256,17 +263,26 @@ def setup_log(debug=False):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def command_wizard(args):
 | 
			
		||||
    return wizard.wizard(args.configuration)
 | 
			
		||||
    from esphome import wizard
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def command_vscode(args):
 | 
			
		||||
    from esphome import vscode
 | 
			
		||||
 | 
			
		||||
    CORE.config_path = args.configuration[0]
 | 
			
		||||
    vscode.read_config(args)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def command_compile(args, config):
 | 
			
		||||
    exit_code = write_cpp(config)
 | 
			
		||||
    if exit_code != 0:
 | 
			
		||||
@@ -323,6 +339,8 @@ def command_clean_mqtt(args, config):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def command_mqtt_fingerprint(args, config):
 | 
			
		||||
    from esphome import mqtt
 | 
			
		||||
 | 
			
		||||
    return mqtt.get_fingerprint(config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -341,38 +359,59 @@ def command_clean(args, config):
 | 
			
		||||
    return 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def command_hass_config(args, config):
 | 
			
		||||
    from esphomeyaml.components import mqtt as mqtt_component
 | 
			
		||||
 | 
			
		||||
    _LOGGER.info("This is what you should put in your Home Assistant YAML configuration.")
 | 
			
		||||
    _LOGGER.info("Please note this is only necessary if you're not using MQTT discovery.")
 | 
			
		||||
    data = mqtt_component.GenerateHassConfigData(config)
 | 
			
		||||
    hass_config = OrderedDict()
 | 
			
		||||
    for domain, component, conf in iter_components(config):
 | 
			
		||||
        if not hasattr(component, 'to_hass_config'):
 | 
			
		||||
            continue
 | 
			
		||||
        func = getattr(component, 'to_hass_config')
 | 
			
		||||
        ret = func(data, conf)
 | 
			
		||||
        if not isinstance(ret, (list, tuple)):
 | 
			
		||||
            ret = [ret]
 | 
			
		||||
        ret = [x for x in ret if x is not None]
 | 
			
		||||
        domain_conf = hass_config.setdefault(domain.split('.')[0], [])
 | 
			
		||||
        domain_conf += ret
 | 
			
		||||
 | 
			
		||||
    safe_print(yaml_util.dump(hass_config))
 | 
			
		||||
    return 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def command_dashboard(args):
 | 
			
		||||
    from esphomeyaml.dashboard import dashboard
 | 
			
		||||
    from esphome.dashboard import dashboard
 | 
			
		||||
 | 
			
		||||
    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 = " {} ".format(middle_text)
 | 
			
		||||
        width = len(click.unstyle(middle_text))
 | 
			
		||||
        half_line = "=" * ((twidth - width) // 2)
 | 
			
		||||
        click.echo("%s%s%s" % (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
 | 
			
		||||
    'dashboard': command_dashboard,
 | 
			
		||||
    'vscode': command_vscode,
 | 
			
		||||
    'update-all': command_update_all,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
POST_CONFIG_ACTIONS = {
 | 
			
		||||
@@ -384,17 +423,17 @@ POST_CONFIG_ACTIONS = {
 | 
			
		||||
    'clean-mqtt': command_clean_mqtt,
 | 
			
		||||
    'mqtt-fingerprint': command_mqtt_fingerprint,
 | 
			
		||||
    'clean': command_clean,
 | 
			
		||||
    'hass-config': command_hass_config,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def parse_args(argv):
 | 
			
		||||
    parser = argparse.ArgumentParser(prog='esphomeyaml')
 | 
			
		||||
    parser.add_argument('-v', '--verbose', help="Enable verbose esphomeyaml logs.",
 | 
			
		||||
    parser = argparse.ArgumentParser(description='ESPHome v{}'.format(const.__version__))
 | 
			
		||||
    parser.add_argument('-v', '--verbose', help="Enable verbose esphome logs.",
 | 
			
		||||
                        action='store_true')
 | 
			
		||||
    parser.add_argument('--dashboard', help="Internal flag to set if the command is run from the "
 | 
			
		||||
                                            "dashboard.", action='store_true')
 | 
			
		||||
    parser.add_argument('configuration', help='Your YAML configuration file.')
 | 
			
		||||
    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.', nargs='*')
 | 
			
		||||
 | 
			
		||||
    subparsers = parser.add_subparsers(help='Commands', dest='command')
 | 
			
		||||
    subparsers.required = True
 | 
			
		||||
@@ -410,7 +449,6 @@ def parse_args(argv):
 | 
			
		||||
                                                         'and upload the latest binary.')
 | 
			
		||||
    parser_upload.add_argument('--upload-port', help="Manually specify the upload port to use. "
 | 
			
		||||
                                                     "For example /dev/cu.SLAB_USBtoUART.")
 | 
			
		||||
    parser_upload.add_argument('--host-port', help="Specify the host port.", type=int)
 | 
			
		||||
 | 
			
		||||
    parser_logs = subparsers.add_parser('logs', help='Validate the configuration '
 | 
			
		||||
                                                     'and show all MQTT logs.')
 | 
			
		||||
@@ -425,7 +463,6 @@ def parse_args(argv):
 | 
			
		||||
                                                   'upload it, and start MQTT logs.')
 | 
			
		||||
    parser_run.add_argument('--upload-port', help="Manually specify the upload port/ip to use. "
 | 
			
		||||
                                                  "For example /dev/cu.SLAB_USBtoUART.")
 | 
			
		||||
    parser_run.add_argument('--host-port', help="Specify the host port to use for OTA", type=int)
 | 
			
		||||
    parser_run.add_argument('--no-logs', help='Disable starting MQTT logs.',
 | 
			
		||||
                            action='store_true')
 | 
			
		||||
    parser_run.add_argument('--topic', help='Manually set the topic to subscribe to for logs.')
 | 
			
		||||
@@ -441,11 +478,11 @@ def parse_args(argv):
 | 
			
		||||
    parser_clean.add_argument('--client-id', help='Manually set the client id.')
 | 
			
		||||
 | 
			
		||||
    subparsers.add_parser('wizard', help="A helpful setup wizard that will guide "
 | 
			
		||||
                                         "you through setting up esphomeyaml.")
 | 
			
		||||
                                         "you through setting up esphome.")
 | 
			
		||||
 | 
			
		||||
    subparsers.add_parser('mqtt-fingerprint', help="Get the SSL fingerprint from a MQTT broker.")
 | 
			
		||||
 | 
			
		||||
    subparsers.add_parser('version', help="Print the esphomeyaml version and exit.")
 | 
			
		||||
    subparsers.add_parser('version', help="Print the esphome version and exit.")
 | 
			
		||||
 | 
			
		||||
    subparsers.add_parser('clean', help="Delete all temporary build files.")
 | 
			
		||||
 | 
			
		||||
@@ -453,57 +490,81 @@ 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')
 | 
			
		||||
    dashboard.add_argument("--hassio",
 | 
			
		||||
                           help="Internal flag used to tell esphomeyaml is started as a Hass.io "
 | 
			
		||||
                                "add-on.",
 | 
			
		||||
                           help=argparse.SUPPRESS,
 | 
			
		||||
                           action="store_true")
 | 
			
		||||
    dashboard.add_argument("--socket",
 | 
			
		||||
                           help="Make the dashboard serve under a unix socket", type=str)
 | 
			
		||||
 | 
			
		||||
    subparsers.add_parser('hass-config',
 | 
			
		||||
                          help="Dump the configuration entries that should be added "
 | 
			
		||||
                               "to Home Assistant when not using MQTT discovery.")
 | 
			
		||||
    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:])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def run_esphomeyaml(argv):
 | 
			
		||||
def run_esphome(argv):
 | 
			
		||||
    args = parse_args(argv)
 | 
			
		||||
    CORE.dashboard = args.dashboard
 | 
			
		||||
 | 
			
		||||
    setup_log(args.verbose)
 | 
			
		||||
    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 IS_PY2:
 | 
			
		||||
        _LOGGER.warning("You're using ESPHome with python 2. Support for python 2 is deprecated "
 | 
			
		||||
                        "and will be removed in 1.15.0. Please reinstall ESPHome with python 3.6 "
 | 
			
		||||
                        "or higher.")
 | 
			
		||||
    elif IS_PY3 and sys.version_info < (3, 6, 0):
 | 
			
		||||
        _LOGGER.warning("You're using ESPHome with python 3.5. Support for python 3.5 is "
 | 
			
		||||
                        "deprecated and will be removed in 1.15.0. Please reinstall ESPHome with "
 | 
			
		||||
                        "python 3.6 or higher.")
 | 
			
		||||
 | 
			
		||||
    if args.command in PRE_CONFIG_ACTIONS:
 | 
			
		||||
        try:
 | 
			
		||||
            return PRE_CONFIG_ACTIONS[args.command](args)
 | 
			
		||||
        except EsphomeyamlError as e:
 | 
			
		||||
        except EsphomeError as e:
 | 
			
		||||
            _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()
 | 
			
		||||
        if config is None:
 | 
			
		||||
            return 1
 | 
			
		||||
        CORE.config = config
 | 
			
		||||
 | 
			
		||||
        if args.command not in POST_CONFIG_ACTIONS:
 | 
			
		||||
            safe_print(u"Unknown command {}".format(args.command))
 | 
			
		||||
 | 
			
		||||
    if args.command in POST_CONFIG_ACTIONS:
 | 
			
		||||
        try:
 | 
			
		||||
            return POST_CONFIG_ACTIONS[args.command](args, config)
 | 
			
		||||
        except EsphomeyamlError as e:
 | 
			
		||||
            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():
 | 
			
		||||
    try:
 | 
			
		||||
        return run_esphomeyaml(sys.argv)
 | 
			
		||||
    except EsphomeyamlError as e:
 | 
			
		||||
        return run_esphome(sys.argv)
 | 
			
		||||
    except EsphomeError as e:
 | 
			
		||||
        _LOGGER.error(e)
 | 
			
		||||
        return 1
 | 
			
		||||
    except KeyboardInterrupt:
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
# Generated by the protocol buffer compiler.  DO NOT EDIT!
 | 
			
		||||
# source: api.proto
 | 
			
		||||
 | 
			
		||||
@@ -9,18 +9,18 @@ import time
 | 
			
		||||
from typing import Optional  # noqa
 | 
			
		||||
from google.protobuf import message  # noqa
 | 
			
		||||
 | 
			
		||||
from esphomeyaml import const
 | 
			
		||||
import esphomeyaml.api.api_pb2 as pb
 | 
			
		||||
from esphomeyaml.const import CONF_PASSWORD, CONF_PORT
 | 
			
		||||
from esphomeyaml.core import EsphomeyamlError
 | 
			
		||||
from esphomeyaml.helpers import resolve_ip_address, indent, color
 | 
			
		||||
from esphomeyaml.py_compat import text_type, IS_PY2, byte_to_bytes, char_to_byte, format_bytes
 | 
			
		||||
from esphomeyaml.util import safe_print
 | 
			
		||||
from esphome import const
 | 
			
		||||
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
 | 
			
		||||
from esphome.util import safe_print
 | 
			
		||||
 | 
			
		||||
_LOGGER = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class APIConnectionError(EsphomeyamlError):
 | 
			
		||||
class APIConnectionError(EsphomeError):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -108,7 +108,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 +131,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,15 +174,15 @@ 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)
 | 
			
		||||
        except EsphomeyamlError as err:
 | 
			
		||||
        except EsphomeError as err:
 | 
			
		||||
            _LOGGER.warning("Error resolving IP address of %s. Is it connected to WiFi?",
 | 
			
		||||
                            self._address)
 | 
			
		||||
            _LOGGER.warning("(If this error persists, please set a static IP address: "
 | 
			
		||||
                            "https://esphomelib.com/esphomeyaml/components/wifi.html#manual-ips)")
 | 
			
		||||
                            "https://esphome.io/components/wifi.html#manual-ips)")
 | 
			
		||||
            raise APIConnectionError(err)
 | 
			
		||||
 | 
			
		||||
        _LOGGER.info("Connecting to %s:%s (%s)", self._address, self._port, ip)
 | 
			
		||||
@@ -193,29 +192,32 @@ class APIClient(threading.Thread):
 | 
			
		||||
        try:
 | 
			
		||||
            self._socket.connect((ip, self._port))
 | 
			
		||||
        except socket.error as err:
 | 
			
		||||
            self._fatal_error()
 | 
			
		||||
            raise APIConnectionError("Error connecting to {}: {}".format(ip, err))
 | 
			
		||||
            err = APIConnectionError("Error connecting to {}: {}".format(ip, err))
 | 
			
		||||
            self._fatal_error(err)
 | 
			
		||||
            raise err
 | 
			
		||||
        self._socket.settimeout(0.1)
 | 
			
		||||
 | 
			
		||||
        self._socket_open_event.set()
 | 
			
		||||
 | 
			
		||||
        hello = pb.HelloRequest()
 | 
			
		||||
        hello.client_info = 'esphomeyaml v{}'.format(const.__version__)
 | 
			
		||||
        hello.client_info = 'ESPHome v{}'.format(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 +235,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))
 | 
			
		||||
                err = APIConnectionError("Error while writing data: {}".format(err))
 | 
			
		||||
                self._fatal_error(err)
 | 
			
		||||
                raise err
 | 
			
		||||
 | 
			
		||||
    def _send_message(self, msg):
 | 
			
		||||
        # type: (message.Message) -> None
 | 
			
		||||
@@ -271,9 +274,8 @@ class APIClient(threading.Thread):
 | 
			
		||||
        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 +296,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 +311,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 +320,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 +336,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):
 | 
			
		||||
@@ -387,7 +388,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 +399,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 +410,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):
 | 
			
		||||
@@ -423,18 +423,20 @@ def run_logs(config, address):
 | 
			
		||||
    conf = config['api']
 | 
			
		||||
    port = conf[CONF_PORT]
 | 
			
		||||
    password = conf[CONF_PASSWORD]
 | 
			
		||||
    _LOGGER.info("Starting log output from %s using esphomelib API", address)
 | 
			
		||||
    _LOGGER.info("Starting log output from %s using esphome API", address)
 | 
			
		||||
 | 
			
		||||
    cli = APIClient(address, port, password)
 | 
			
		||||
    stopping = False
 | 
			
		||||
    retry_timer = []
 | 
			
		||||
 | 
			
		||||
    def try_connect(tries=0, is_disconnect=True):
 | 
			
		||||
    has_connects = []
 | 
			
		||||
 | 
			
		||||
    def try_connect(err, tries=0):
 | 
			
		||||
        if stopping:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        if is_disconnect:
 | 
			
		||||
            _LOGGER.warning(u"Disconnected from API.")
 | 
			
		||||
        if err:
 | 
			
		||||
            _LOGGER.warning(u"Disconnected from API: %s", err)
 | 
			
		||||
 | 
			
		||||
        while retry_timer:
 | 
			
		||||
            retry_timer.pop(0).cancel()
 | 
			
		||||
@@ -443,17 +445,22 @@ 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)
 | 
			
		||||
        _LOGGER.warning(u"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))
 | 
			
		||||
        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",
 | 
			
		||||
                            error, wait_time)
 | 
			
		||||
        else:
 | 
			
		||||
            _LOGGER.warning(u"Couldn't connect to API (%s). Trying to reconnect in %s seconds",
 | 
			
		||||
                            error, wait_time)
 | 
			
		||||
        timer = threading.Timer(wait_time, functools.partial(try_connect, None, tries + 1))
 | 
			
		||||
        timer.start()
 | 
			
		||||
        retry_timer.append(timer)
 | 
			
		||||
 | 
			
		||||
@@ -465,8 +472,6 @@ def run_logs(config, address):
 | 
			
		||||
                                  'TCP buffer - This is only cosmetic)')
 | 
			
		||||
        safe_print(time_ + text)
 | 
			
		||||
 | 
			
		||||
    has_connects = []
 | 
			
		||||
 | 
			
		||||
    def on_login():
 | 
			
		||||
        try:
 | 
			
		||||
            cli.subscribe_logs(on_log, dump_config=not has_connects)
 | 
			
		||||
@@ -479,7 +484,7 @@ def run_logs(config, address):
 | 
			
		||||
    cli.start()
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        try_connect(is_disconnect=False)
 | 
			
		||||
        try_connect(None)
 | 
			
		||||
        while True:
 | 
			
		||||
            time.sleep(1)
 | 
			
		||||
    except KeyboardInterrupt:
 | 
			
		||||
							
								
								
									
										266
									
								
								esphome/automation.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										266
									
								
								esphome/automation.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,266 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.const import CONF_AUTOMATION_ID, CONF_CONDITION, CONF_ELSE, CONF_ID, CONF_THEN, \
 | 
			
		||||
    CONF_TRIGGER_ID, CONF_TYPE_ID, CONF_TIME
 | 
			
		||||
from esphome.core import coroutine
 | 
			
		||||
from esphome.util import Registry
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def maybe_simple_id(*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})
 | 
			
		||||
 | 
			
		||||
    return validate
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def register_action(name, action_type, schema):
 | 
			
		||||
    return ACTION_REGISTRY.register(name, action_type, schema)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def register_condition(name, condition_type, schema):
 | 
			
		||||
    return CONDITION_REGISTRY.register(name, condition_type, schema)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Action = cg.esphome_ns.class_('Action')
 | 
			
		||||
Trigger = cg.esphome_ns.class_('Trigger')
 | 
			
		||||
ACTION_REGISTRY = Registry()
 | 
			
		||||
Condition = cg.esphome_ns.class_('Condition')
 | 
			
		||||
CONDITION_REGISTRY = Registry()
 | 
			
		||||
validate_action = cv.validate_registry_entry('action', ACTION_REGISTRY)
 | 
			
		||||
validate_action_list = cv.validate_registry('action', ACTION_REGISTRY)
 | 
			
		||||
validate_condition = cv.validate_registry_entry('condition', CONDITION_REGISTRY)
 | 
			
		||||
validate_condition_list = cv.validate_registry('condition', CONDITION_REGISTRY)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_potentially_and_condition(value):
 | 
			
		||||
    if isinstance(value, list):
 | 
			
		||||
        with cv.remove_prepend_path(['and']):
 | 
			
		||||
            return validate_condition({
 | 
			
		||||
                'and': value
 | 
			
		||||
            })
 | 
			
		||||
    return validate_condition(value)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
DelayAction = cg.esphome_ns.class_('DelayAction', Action, cg.Component)
 | 
			
		||||
LambdaAction = cg.esphome_ns.class_('LambdaAction', Action)
 | 
			
		||||
IfAction = cg.esphome_ns.class_('IfAction', Action)
 | 
			
		||||
WhileAction = cg.esphome_ns.class_('WhileAction', Action)
 | 
			
		||||
WaitUntilAction = cg.esphome_ns.class_('WaitUntilAction', Action, cg.Component)
 | 
			
		||||
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, cg.Component)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_automation(extra_schema=None, extra_validators=None, single=False):
 | 
			
		||||
    if extra_schema is None:
 | 
			
		||||
        extra_schema = {}
 | 
			
		||||
    if isinstance(extra_schema, cv.Schema):
 | 
			
		||||
        extra_schema = extra_schema.schema
 | 
			
		||||
    schema = AUTOMATION_SCHEMA.extend(extra_schema)
 | 
			
		||||
 | 
			
		||||
    def validator_(value):
 | 
			
		||||
        if isinstance(value, list):
 | 
			
		||||
            # List of items, there are two possible options here, either a sequence of
 | 
			
		||||
            # actions (no then:) or a list of automations.
 | 
			
		||||
            try:
 | 
			
		||||
                # First try as a sequence of actions
 | 
			
		||||
                # If that succeeds, return immediately
 | 
			
		||||
                with cv.remove_prepend_path([CONF_THEN]):
 | 
			
		||||
                    return [schema({CONF_THEN: value})]
 | 
			
		||||
            except cv.Invalid as err:
 | 
			
		||||
                # Next try as a sequence of automations
 | 
			
		||||
                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:
 | 
			
		||||
                        raise err
 | 
			
		||||
                    if u'Unable to find action' in str(err):
 | 
			
		||||
                        raise err2
 | 
			
		||||
                    raise cv.MultipleInvalid([err, err2])
 | 
			
		||||
        elif isinstance(value, dict):
 | 
			
		||||
            if CONF_THEN in value:
 | 
			
		||||
                return [schema(value)]
 | 
			
		||||
            with cv.remove_prepend_path([CONF_THEN]):
 | 
			
		||||
                return [schema({CONF_THEN: value})]
 | 
			
		||||
        # This should only happen with invalid configs, but let's have a nice error message.
 | 
			
		||||
        return [schema(value)]
 | 
			
		||||
 | 
			
		||||
    def validator(value):
 | 
			
		||||
        value = validator_(value)
 | 
			
		||||
        if extra_validators is not None:
 | 
			
		||||
            value = cv.Schema([extra_validators])(value)
 | 
			
		||||
        if single:
 | 
			
		||||
            if len(value) != 1:
 | 
			
		||||
                raise cv.Invalid("Cannot have more than 1 automation for templates")
 | 
			
		||||
            return value[0]
 | 
			
		||||
        return value
 | 
			
		||||
 | 
			
		||||
    return validator
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
AUTOMATION_SCHEMA = cv.Schema({
 | 
			
		||||
    cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(Trigger),
 | 
			
		||||
    cv.GenerateID(CONF_AUTOMATION_ID): cv.declare_id(Automation),
 | 
			
		||||
    cv.Required(CONF_THEN): validate_action_list,
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
AndCondition = cg.esphome_ns.class_('AndCondition', Condition)
 | 
			
		||||
OrCondition = cg.esphome_ns.class_('OrCondition', Condition)
 | 
			
		||||
NotCondition = cg.esphome_ns.class_('NotCondition', Condition)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@register_condition('and', AndCondition, validate_condition_list)
 | 
			
		||||
def and_condition_to_code(config, condition_id, template_arg, args):
 | 
			
		||||
    conditions = yield build_condition_list(config, template_arg, args)
 | 
			
		||||
    yield cg.new_Pvariable(condition_id, template_arg, conditions)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@register_condition('or', OrCondition, validate_condition_list)
 | 
			
		||||
def or_condition_to_code(config, condition_id, template_arg, args):
 | 
			
		||||
    conditions = yield build_condition_list(config, template_arg, args)
 | 
			
		||||
    yield cg.new_Pvariable(condition_id, template_arg, conditions)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@register_condition('not', NotCondition, validate_potentially_and_condition)
 | 
			
		||||
def not_condition_to_code(config, condition_id, template_arg, args):
 | 
			
		||||
    condition = yield build_condition(config, template_arg, args)
 | 
			
		||||
    yield cg.new_Pvariable(condition_id, template_arg, condition)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@register_condition('lambda', LambdaCondition, cv.lambda_)
 | 
			
		||||
def lambda_condition_to_code(config, condition_id, template_arg, args):
 | 
			
		||||
    lambda_ = yield cg.process_lambda(config, args, return_type=bool)
 | 
			
		||||
    yield cg.new_Pvariable(condition_id, template_arg, lambda_)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@register_condition('for', ForCondition, cv.Schema({
 | 
			
		||||
    cv.Required(CONF_TIME): cv.templatable(cv.positive_time_period_milliseconds),
 | 
			
		||||
    cv.Required(CONF_CONDITION): validate_potentially_and_condition,
 | 
			
		||||
}).extend(cv.COMPONENT_SCHEMA))
 | 
			
		||||
def for_condition_to_code(config, condition_id, template_arg, args):
 | 
			
		||||
    condition = yield build_condition(config[CONF_CONDITION], cg.TemplateArguments(), [])
 | 
			
		||||
    var = cg.new_Pvariable(condition_id, template_arg, condition)
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    templ = yield cg.templatable(config[CONF_TIME], args, cg.uint32)
 | 
			
		||||
    cg.add(var.set_time(templ))
 | 
			
		||||
    yield var
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@register_action('delay', DelayAction, cv.templatable(cv.positive_time_period_milliseconds))
 | 
			
		||||
def delay_action_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    var = cg.new_Pvariable(action_id, template_arg)
 | 
			
		||||
    yield cg.register_component(var, {})
 | 
			
		||||
    template_ = yield cg.templatable(config, args, cg.uint32)
 | 
			
		||||
    cg.add(var.set_delay(template_))
 | 
			
		||||
    yield var
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@register_action('if', IfAction, cv.All({
 | 
			
		||||
    cv.Required(CONF_CONDITION): validate_potentially_and_condition,
 | 
			
		||||
    cv.Optional(CONF_THEN): validate_action_list,
 | 
			
		||||
    cv.Optional(CONF_ELSE): validate_action_list,
 | 
			
		||||
}, cv.has_at_least_one_key(CONF_THEN, CONF_ELSE)))
 | 
			
		||||
def if_action_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    conditions = yield build_condition(config[CONF_CONDITION], template_arg, args)
 | 
			
		||||
    var = cg.new_Pvariable(action_id, template_arg, conditions)
 | 
			
		||||
    if CONF_THEN in config:
 | 
			
		||||
        actions = yield build_action_list(config[CONF_THEN], template_arg, args)
 | 
			
		||||
        cg.add(var.add_then(actions))
 | 
			
		||||
    if CONF_ELSE in config:
 | 
			
		||||
        actions = yield build_action_list(config[CONF_ELSE], template_arg, args)
 | 
			
		||||
        cg.add(var.add_else(actions))
 | 
			
		||||
    yield var
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@register_action('while', WhileAction, cv.Schema({
 | 
			
		||||
    cv.Required(CONF_CONDITION): validate_potentially_and_condition,
 | 
			
		||||
    cv.Required(CONF_THEN): validate_action_list,
 | 
			
		||||
}))
 | 
			
		||||
def while_action_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    conditions = yield build_condition(config[CONF_CONDITION], template_arg, args)
 | 
			
		||||
    var = cg.new_Pvariable(action_id, template_arg, conditions)
 | 
			
		||||
    actions = yield build_action_list(config[CONF_THEN], template_arg, args)
 | 
			
		||||
    cg.add(var.add_then(actions))
 | 
			
		||||
    yield var
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_wait_until(value):
 | 
			
		||||
    schema = cv.Schema({
 | 
			
		||||
        cv.Required(CONF_CONDITION): validate_potentially_and_condition,
 | 
			
		||||
    })
 | 
			
		||||
    if isinstance(value, dict) and CONF_CONDITION in value:
 | 
			
		||||
        return schema(value)
 | 
			
		||||
    return validate_wait_until({CONF_CONDITION: value})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@register_action('wait_until', WaitUntilAction, validate_wait_until)
 | 
			
		||||
def wait_until_action_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    conditions = yield build_condition(config[CONF_CONDITION], template_arg, args)
 | 
			
		||||
    var = cg.new_Pvariable(action_id, template_arg, conditions)
 | 
			
		||||
    yield cg.register_component(var, {})
 | 
			
		||||
    yield var
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@register_action('lambda', LambdaAction, cv.lambda_)
 | 
			
		||||
def lambda_action_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    lambda_ = yield cg.process_lambda(config, args, return_type=cg.void)
 | 
			
		||||
    yield cg.new_Pvariable(action_id, template_arg, lambda_)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@register_action('component.update', UpdateComponentAction, maybe_simple_id({
 | 
			
		||||
    cv.Required(CONF_ID): cv.use_id(cg.PollingComponent),
 | 
			
		||||
}))
 | 
			
		||||
def component_update_action_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    comp = yield cg.get_variable(config[CONF_ID])
 | 
			
		||||
    yield cg.new_Pvariable(action_id, template_arg, comp)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@coroutine
 | 
			
		||||
def build_action(full_config, template_arg, args):
 | 
			
		||||
    registry_entry, config = cg.extract_registry_entry_config(ACTION_REGISTRY, full_config)
 | 
			
		||||
    action_id = full_config[CONF_TYPE_ID]
 | 
			
		||||
    builder = registry_entry.coroutine_fun
 | 
			
		||||
    yield builder(config, action_id, template_arg, args)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@coroutine
 | 
			
		||||
def build_action_list(config, templ, arg_type):
 | 
			
		||||
    actions = []
 | 
			
		||||
    for conf in config:
 | 
			
		||||
        action = yield build_action(conf, templ, arg_type)
 | 
			
		||||
        actions.append(action)
 | 
			
		||||
    yield actions
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@coroutine
 | 
			
		||||
def build_condition(full_config, template_arg, args):
 | 
			
		||||
    registry_entry, config = cg.extract_registry_entry_config(CONDITION_REGISTRY, full_config)
 | 
			
		||||
    action_id = full_config[CONF_TYPE_ID]
 | 
			
		||||
    builder = registry_entry.coroutine_fun
 | 
			
		||||
    yield builder(config, action_id, template_arg, args)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@coroutine
 | 
			
		||||
def build_condition_list(config, templ, args):
 | 
			
		||||
    conditions = []
 | 
			
		||||
    for conf in config:
 | 
			
		||||
        condition = yield build_condition(conf, templ, args)
 | 
			
		||||
        conditions.append(condition)
 | 
			
		||||
    yield conditions
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@coroutine
 | 
			
		||||
def build_automation(trigger, args, config):
 | 
			
		||||
    arg_types = [arg[0] for arg in args]
 | 
			
		||||
    templ = cg.TemplateArguments(*arg_types)
 | 
			
		||||
    obj = cg.new_Pvariable(config[CONF_AUTOMATION_ID], templ, trigger)
 | 
			
		||||
    actions = yield build_action_list(config[CONF_THEN], templ, args)
 | 
			
		||||
    cg.add(obj.add_actions(actions))
 | 
			
		||||
    yield obj
 | 
			
		||||
							
								
								
									
										26
									
								
								esphome/codegen.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								esphome/codegen.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
# Base file for all codegen-related imports
 | 
			
		||||
# All integrations should have a line in the import section like this
 | 
			
		||||
#
 | 
			
		||||
# >>> import esphome.codegen as cg
 | 
			
		||||
#
 | 
			
		||||
# Integrations should specifically *NOT* import directly from the
 | 
			
		||||
# other helper modules (cpp_generator etc) directly if they don't
 | 
			
		||||
# want to break suddenly due to a rename (this file will get backports for features).
 | 
			
		||||
 | 
			
		||||
# pylint: disable=unused-import
 | 
			
		||||
from esphome.cpp_generator import (  # noqa
 | 
			
		||||
    Expression, RawExpression, RawStatement, TemplateArguments,
 | 
			
		||||
    StructInitializer, ArrayInitializer, safe_exp, Statement, LineComment,
 | 
			
		||||
    progmem_array, statement, variable, Pvariable, new_Pvariable,
 | 
			
		||||
    add, add_global, add_library, add_build_flag, add_define,
 | 
			
		||||
    get_variable, get_variable_with_full_id, process_lambda, is_template, templatable, MockObj,
 | 
			
		||||
    MockObjClass)
 | 
			
		||||
from esphome.cpp_helpers import (  # noqa
 | 
			
		||||
    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,
 | 
			
		||||
    std_vector, uint8, uint16, uint32, int32, const_char_ptr, NAN,
 | 
			
		||||
    esphome_ns, App, Nameable, Component, ComponentPtr,
 | 
			
		||||
    PollingComponent, Application, optional, arduino_json_ns, JsonObject,
 | 
			
		||||
    JsonObjectRef, JsonObjectConstRef, Controller, GPIOPin)
 | 
			
		||||
							
								
								
									
										49
									
								
								esphome/components/a4988/a4988.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								esphome/components/a4988/a4988.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,49 @@
 | 
			
		||||
#include "a4988.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace a4988 {
 | 
			
		||||
 | 
			
		||||
static const char *TAG = "a4988.stepper";
 | 
			
		||||
 | 
			
		||||
void A4988::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up A4988...");
 | 
			
		||||
  if (this->sleep_pin_ != nullptr) {
 | 
			
		||||
    this->sleep_pin_->setup();
 | 
			
		||||
    this->sleep_pin_->digital_write(false);
 | 
			
		||||
  }
 | 
			
		||||
  this->step_pin_->setup();
 | 
			
		||||
  this->step_pin_->digital_write(false);
 | 
			
		||||
  this->dir_pin_->setup();
 | 
			
		||||
  this->dir_pin_->digital_write(false);
 | 
			
		||||
}
 | 
			
		||||
void A4988::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "A4988:");
 | 
			
		||||
  LOG_PIN("  Step Pin: ", this->step_pin_);
 | 
			
		||||
  LOG_PIN("  Dir Pin: ", this->dir_pin_);
 | 
			
		||||
  LOG_PIN("  Sleep Pin: ", this->sleep_pin_);
 | 
			
		||||
  LOG_STEPPER(this);
 | 
			
		||||
}
 | 
			
		||||
void A4988::loop() {
 | 
			
		||||
  bool at_target = this->has_reached_target();
 | 
			
		||||
  if (this->sleep_pin_ != nullptr) {
 | 
			
		||||
    this->sleep_pin_->digital_write(!at_target);
 | 
			
		||||
  }
 | 
			
		||||
  if (at_target) {
 | 
			
		||||
    this->high_freq_.stop();
 | 
			
		||||
  } else {
 | 
			
		||||
    this->high_freq_.start();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  int32_t dir = this->should_step_();
 | 
			
		||||
  if (dir == 0)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  this->dir_pin_->digital_write(dir == 1);
 | 
			
		||||
  this->step_pin_->digital_write(true);
 | 
			
		||||
  delayMicroseconds(5);
 | 
			
		||||
  this->step_pin_->digital_write(false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace a4988
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										28
									
								
								esphome/components/a4988/a4988.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								esphome/components/a4988/a4988.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/esphal.h"
 | 
			
		||||
#include "esphome/components/stepper/stepper.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace a4988 {
 | 
			
		||||
 | 
			
		||||
class A4988 : public stepper::Stepper, public Component {
 | 
			
		||||
 public:
 | 
			
		||||
  void set_step_pin(GPIOPin *step_pin) { step_pin_ = step_pin; }
 | 
			
		||||
  void set_dir_pin(GPIOPin *dir_pin) { dir_pin_ = dir_pin; }
 | 
			
		||||
  void set_sleep_pin(GPIOPin *sleep_pin) { this->sleep_pin_ = sleep_pin; }
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
  void loop() override;
 | 
			
		||||
  float get_setup_priority() const override { return setup_priority::HARDWARE; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  GPIOPin *step_pin_;
 | 
			
		||||
  GPIOPin *dir_pin_;
 | 
			
		||||
  GPIOPin *sleep_pin_{nullptr};
 | 
			
		||||
  HighFrequencyLoopRequester high_freq_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace a4988
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										31
									
								
								esphome/components/a4988/stepper.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								esphome/components/a4988/stepper.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
			
		||||
from esphome import pins
 | 
			
		||||
from esphome.components import stepper
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
from esphome.const import CONF_DIR_PIN, CONF_ID, CONF_SLEEP_PIN, CONF_STEP_PIN
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
a4988_ns = cg.esphome_ns.namespace('a4988')
 | 
			
		||||
A4988 = a4988_ns.class_('A4988', stepper.Stepper, cg.Component)
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = stepper.STEPPER_SCHEMA.extend({
 | 
			
		||||
    cv.Required(CONF_ID): cv.declare_id(A4988),
 | 
			
		||||
    cv.Required(CONF_STEP_PIN): pins.gpio_output_pin_schema,
 | 
			
		||||
    cv.Required(CONF_DIR_PIN): pins.gpio_output_pin_schema,
 | 
			
		||||
    cv.Optional(CONF_SLEEP_PIN): pins.gpio_output_pin_schema,
 | 
			
		||||
}).extend(cv.COMPONENT_SCHEMA)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    yield stepper.register_stepper(var, config)
 | 
			
		||||
 | 
			
		||||
    step_pin = yield cg.gpio_pin_expression(config[CONF_STEP_PIN])
 | 
			
		||||
    cg.add(var.set_step_pin(step_pin))
 | 
			
		||||
    dir_pin = yield cg.gpio_pin_expression(config[CONF_DIR_PIN])
 | 
			
		||||
    cg.add(var.set_dir_pin(dir_pin))
 | 
			
		||||
 | 
			
		||||
    if CONF_SLEEP_PIN in config:
 | 
			
		||||
        sleep_pin = yield cg.gpio_pin_expression(config[CONF_SLEEP_PIN])
 | 
			
		||||
        cg.add(var.set_sleep_pin(sleep_pin))
 | 
			
		||||
							
								
								
									
										0
									
								
								esphome/components/adc/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								esphome/components/adc/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										92
									
								
								esphome/components/adc/adc_sensor.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								esphome/components/adc/adc_sensor.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,92 @@
 | 
			
		||||
#include "adc_sensor.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ADC_SENSOR_VCC
 | 
			
		||||
ADC_MODE(ADC_VCC)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace adc {
 | 
			
		||||
 | 
			
		||||
static const char *TAG = "adc";
 | 
			
		||||
 | 
			
		||||
#ifdef ARDUINO_ARCH_ESP32
 | 
			
		||||
void ADCSensor::set_attenuation(adc_attenuation_t attenuation) { this->attenuation_ = attenuation; }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void ADCSensor::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
 | 
			
		||||
  GPIOPin(this->pin_, INPUT).setup();
 | 
			
		||||
 | 
			
		||||
#ifdef ARDUINO_ARCH_ESP32
 | 
			
		||||
  analogSetPinAttenuation(this->pin_, this->attenuation_);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
void ADCSensor::dump_config() {
 | 
			
		||||
  LOG_SENSOR("", "ADC Sensor", this);
 | 
			
		||||
#ifdef ARDUINO_ARCH_ESP8266
 | 
			
		||||
#ifdef USE_ADC_SENSOR_VCC
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Pin: VCC");
 | 
			
		||||
#else
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Pin: %u", this->pin_);
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef ARDUINO_ARCH_ESP32
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Pin: %u", this->pin_);
 | 
			
		||||
  switch (this->attenuation_) {
 | 
			
		||||
    case ADC_0db:
 | 
			
		||||
      ESP_LOGCONFIG(TAG, " Attenuation: 0db (max 1.1V)");
 | 
			
		||||
      break;
 | 
			
		||||
    case ADC_2_5db:
 | 
			
		||||
      ESP_LOGCONFIG(TAG, " Attenuation: 2.5db (max 1.5V)");
 | 
			
		||||
      break;
 | 
			
		||||
    case ADC_6db:
 | 
			
		||||
      ESP_LOGCONFIG(TAG, " Attenuation: 6db (max 2.2V)");
 | 
			
		||||
      break;
 | 
			
		||||
    case ADC_11db:
 | 
			
		||||
      ESP_LOGCONFIG(TAG, " Attenuation: 11db (max 3.9V)");
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
  LOG_UPDATE_INTERVAL(this);
 | 
			
		||||
}
 | 
			
		||||
float ADCSensor::get_setup_priority() const { return setup_priority::DATA; }
 | 
			
		||||
void ADCSensor::update() {
 | 
			
		||||
  float value_v = this->sample();
 | 
			
		||||
  ESP_LOGD(TAG, "'%s': Got voltage=%.2fV", this->get_name().c_str(), value_v);
 | 
			
		||||
  this->publish_state(value_v);
 | 
			
		||||
}
 | 
			
		||||
float ADCSensor::sample() {
 | 
			
		||||
#ifdef ARDUINO_ARCH_ESP32
 | 
			
		||||
  float value_v = analogRead(this->pin_) / 4095.0f;
 | 
			
		||||
  switch (this->attenuation_) {
 | 
			
		||||
    case ADC_0db:
 | 
			
		||||
      value_v *= 1.1;
 | 
			
		||||
      break;
 | 
			
		||||
    case ADC_2_5db:
 | 
			
		||||
      value_v *= 1.5;
 | 
			
		||||
      break;
 | 
			
		||||
    case ADC_6db:
 | 
			
		||||
      value_v *= 2.2;
 | 
			
		||||
      break;
 | 
			
		||||
    case ADC_11db:
 | 
			
		||||
      value_v *= 3.9;
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
  return value_v;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef ARDUINO_ARCH_ESP8266
 | 
			
		||||
#ifdef USE_ADC_SENSOR_VCC
 | 
			
		||||
  return ESP.getVcc() / 1024.0f;
 | 
			
		||||
#else
 | 
			
		||||
  return analogRead(this->pin_) / 1024.0f;
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
#ifdef ARDUINO_ARCH_ESP8266
 | 
			
		||||
std::string ADCSensor::unique_id() { return get_mac_address() + "-adc"; }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
}  // namespace adc
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										42
									
								
								esphome/components/adc/adc_sensor.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								esphome/components/adc/adc_sensor.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/esphal.h"
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#include "esphome/components/sensor/sensor.h"
 | 
			
		||||
#include "esphome/components/voltage_sampler/voltage_sampler.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace adc {
 | 
			
		||||
 | 
			
		||||
class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage_sampler::VoltageSampler {
 | 
			
		||||
 public:
 | 
			
		||||
#ifdef ARDUINO_ARCH_ESP32
 | 
			
		||||
  /// Set the attenuation for this pin. Only available on the ESP32.
 | 
			
		||||
  void set_attenuation(adc_attenuation_t attenuation);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  /// Update adc values.
 | 
			
		||||
  void update() override;
 | 
			
		||||
  /// Setup ADc
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
  /// `HARDWARE_LATE` setup priority.
 | 
			
		||||
  float get_setup_priority() const override;
 | 
			
		||||
  void set_pin(uint8_t pin) { this->pin_ = pin; }
 | 
			
		||||
  float sample() override;
 | 
			
		||||
 | 
			
		||||
#ifdef ARDUINO_ARCH_ESP8266
 | 
			
		||||
  std::string unique_id() override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  uint8_t pin_;
 | 
			
		||||
 | 
			
		||||
#ifdef ARDUINO_ARCH_ESP32
 | 
			
		||||
  adc_attenuation_t attenuation_{ADC_0db};
 | 
			
		||||
#endif
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace adc
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										48
									
								
								esphome/components/adc/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								esphome/components/adc/sensor.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,48 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome import pins
 | 
			
		||||
from esphome.components import sensor, voltage_sampler
 | 
			
		||||
from esphome.const import CONF_ATTENUATION, CONF_ID, CONF_PIN, ICON_FLASH, UNIT_VOLT
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
AUTO_LOAD = ['voltage_sampler']
 | 
			
		||||
 | 
			
		||||
ATTENUATION_MODES = {
 | 
			
		||||
    '0db': cg.global_ns.ADC_0db,
 | 
			
		||||
    '2.5db': cg.global_ns.ADC_2_5db,
 | 
			
		||||
    '6db': cg.global_ns.ADC_6db,
 | 
			
		||||
    '11db': cg.global_ns.ADC_11db,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_adc_pin(value):
 | 
			
		||||
    vcc = str(value).upper()
 | 
			
		||||
    if vcc == 'VCC':
 | 
			
		||||
        return cv.only_on_esp8266(vcc)
 | 
			
		||||
    return pins.analog_pin(value)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
adc_ns = cg.esphome_ns.namespace('adc')
 | 
			
		||||
ADCSensor = adc_ns.class_('ADCSensor', sensor.Sensor, cg.PollingComponent,
 | 
			
		||||
                          voltage_sampler.VoltageSampler)
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 2).extend({
 | 
			
		||||
    cv.GenerateID(): cv.declare_id(ADCSensor),
 | 
			
		||||
    cv.Required(CONF_PIN): validate_adc_pin,
 | 
			
		||||
    cv.SplitDefault(CONF_ATTENUATION, esp32='0db'):
 | 
			
		||||
        cv.All(cv.only_on_esp32, cv.enum(ATTENUATION_MODES, lower=True)),
 | 
			
		||||
}).extend(cv.polling_component_schema('60s'))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    yield sensor.register_sensor(var, config)
 | 
			
		||||
 | 
			
		||||
    if config[CONF_PIN] == 'VCC':
 | 
			
		||||
        cg.add_define('USE_ADC_SENSOR_VCC')
 | 
			
		||||
    else:
 | 
			
		||||
        cg.add(var.set_pin(config[CONF_PIN]))
 | 
			
		||||
 | 
			
		||||
    if CONF_ATTENUATION in config:
 | 
			
		||||
        cg.add(var.set_attenuation(config[CONF_ATTENUATION]))
 | 
			
		||||
							
								
								
									
										0
									
								
								esphome/components/ade7953/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								esphome/components/ade7953/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										51
									
								
								esphome/components/ade7953/ade7953.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								esphome/components/ade7953/ade7953.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,51 @@
 | 
			
		||||
#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:");
 | 
			
		||||
  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) { \
 | 
			
		||||
    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
 | 
			
		||||
							
								
								
									
										67
									
								
								esphome/components/ade7953/ade7953.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								esphome/components/ade7953/ade7953.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,67 @@
 | 
			
		||||
#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_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 {
 | 
			
		||||
    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 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
 | 
			
		||||
							
								
								
									
										39
									
								
								esphome/components/ade7953/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								esphome/components/ade7953/sensor.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import sensor, i2c
 | 
			
		||||
from esphome.const import CONF_ID, CONF_VOLTAGE, \
 | 
			
		||||
    UNIT_VOLT, ICON_FLASH, UNIT_AMPERE, UNIT_WATT
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ['i2c']
 | 
			
		||||
 | 
			
		||||
ace7953_ns = cg.esphome_ns.namespace('ade7953')
 | 
			
		||||
ADE7953 = ace7953_ns.class_('ADE7953', cg.PollingComponent, i2c.I2CDevice)
 | 
			
		||||
 | 
			
		||||
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_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)
 | 
			
		||||
 | 
			
		||||
    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, 'set_{}_sensor'.format(key))(sens))
 | 
			
		||||
							
								
								
									
										25
									
								
								esphome/components/ads1115/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								esphome/components/ads1115/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import i2c
 | 
			
		||||
from esphome.const import CONF_ID
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ['i2c']
 | 
			
		||||
AUTO_LOAD = ['sensor', 'voltage_sampler']
 | 
			
		||||
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))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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]))
 | 
			
		||||
							
								
								
									
										169
									
								
								esphome/components/ads1115/ads1115.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										169
									
								
								esphome/components/ads1115/ads1115.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,169 @@
 | 
			
		||||
#include "ads1115.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace ads1115 {
 | 
			
		||||
 | 
			
		||||
static const char *TAG = "ads1115";
 | 
			
		||||
static const uint8_t ADS1115_REGISTER_CONVERSION = 0x00;
 | 
			
		||||
static const uint8_t ADS1115_REGISTER_CONFIG = 0x01;
 | 
			
		||||
 | 
			
		||||
static const uint8_t ADS1115_DATA_RATE_860_SPS = 0b111;
 | 
			
		||||
 | 
			
		||||
void ADS1115Component::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up ADS1115...");
 | 
			
		||||
  uint16_t value;
 | 
			
		||||
  if (!this->read_byte_16(ADS1115_REGISTER_CONVERSION, &value)) {
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  uint16_t config = 0;
 | 
			
		||||
  // Clear single-shot bit
 | 
			
		||||
  //        0b0xxxxxxxxxxxxxxx
 | 
			
		||||
  config |= 0b0000000000000000;
 | 
			
		||||
  // Setup multiplexer
 | 
			
		||||
  //        0bx000xxxxxxxxxxxx
 | 
			
		||||
  config |= ADS1115_MULTIPLEXER_P0_N1 << 12;
 | 
			
		||||
 | 
			
		||||
  // Setup Gain
 | 
			
		||||
  //        0bxxxx000xxxxxxxxx
 | 
			
		||||
  config |= ADS1115_GAIN_6P144 << 9;
 | 
			
		||||
 | 
			
		||||
  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
 | 
			
		||||
  config |= ADS1115_DATA_RATE_860_SPS << 5;
 | 
			
		||||
 | 
			
		||||
  // Set comparator mode - hysteresis
 | 
			
		||||
  //        0bxxxxxxxxxxx0xxxx
 | 
			
		||||
  config |= 0b0000000000000000;
 | 
			
		||||
 | 
			
		||||
  // Set comparator polarity - active low
 | 
			
		||||
  //        0bxxxxxxxxxxxx0xxx
 | 
			
		||||
  config |= 0b0000000000000000;
 | 
			
		||||
 | 
			
		||||
  // Set comparator latch enabled - false
 | 
			
		||||
  //        0bxxxxxxxxxxxxx0xx
 | 
			
		||||
  config |= 0b0000000000000000;
 | 
			
		||||
 | 
			
		||||
  // Set comparator que mode - disabled
 | 
			
		||||
  //        0bxxxxxxxxxxxxxx11
 | 
			
		||||
  config |= 0b0000000000000011;
 | 
			
		||||
 | 
			
		||||
  if (!this->write_byte_16(ADS1115_REGISTER_CONFIG, config)) {
 | 
			
		||||
    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); });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void ADS1115Component::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up ADS1115...");
 | 
			
		||||
  LOG_I2C_DEVICE(this);
 | 
			
		||||
  if (this->is_failed()) {
 | 
			
		||||
    ESP_LOGE(TAG, "Communication with ADS1115 failed!");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for (auto *sensor : this->sensors_) {
 | 
			
		||||
    LOG_SENSOR("  ", "Sensor", sensor);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "    Multiplexer: %u", sensor->get_multiplexer());
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "    Gain: %u", sensor->get_gain());
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
float ADS1115Component::request_measurement(ADS1115Sensor *sensor) {
 | 
			
		||||
  uint16_t config = this->prev_config_;
 | 
			
		||||
  // Multiplexer
 | 
			
		||||
  //        0bxBBBxxxxxxxxxxxx
 | 
			
		||||
  config &= 0b1000111111111111;
 | 
			
		||||
  config |= (sensor->get_multiplexer() & 0b111) << 12;
 | 
			
		||||
 | 
			
		||||
  // Gain
 | 
			
		||||
  //        0bxxxxBBBxxxxxxxxx
 | 
			
		||||
  config &= 0b1111000111111111;
 | 
			
		||||
  config |= (sensor->get_gain() & 0b111) << 9;
 | 
			
		||||
 | 
			
		||||
  if (!this->continuous_mode_) {
 | 
			
		||||
    // Start conversion
 | 
			
		||||
    config |= 0b1000000000000000;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!this->continuous_mode_ || this->prev_config_ != config) {
 | 
			
		||||
    if (!this->write_byte_16(ADS1115_REGISTER_CONFIG, config)) {
 | 
			
		||||
      this->status_set_warning();
 | 
			
		||||
      return NAN;
 | 
			
		||||
    }
 | 
			
		||||
    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;
 | 
			
		||||
  if (!this->read_byte_16(ADS1115_REGISTER_CONVERSION, &raw_conversion)) {
 | 
			
		||||
    this->status_set_warning();
 | 
			
		||||
    return NAN;
 | 
			
		||||
  }
 | 
			
		||||
  auto signed_conversion = static_cast<int16_t>(raw_conversion);
 | 
			
		||||
 | 
			
		||||
  float millivolts;
 | 
			
		||||
  switch (sensor->get_gain()) {
 | 
			
		||||
    case ADS1115_GAIN_6P144:
 | 
			
		||||
      millivolts = signed_conversion * 0.187500f;
 | 
			
		||||
      break;
 | 
			
		||||
    case ADS1115_GAIN_4P096:
 | 
			
		||||
      millivolts = signed_conversion * 0.125000f;
 | 
			
		||||
      break;
 | 
			
		||||
    case ADS1115_GAIN_2P048:
 | 
			
		||||
      millivolts = signed_conversion * 0.062500f;
 | 
			
		||||
      break;
 | 
			
		||||
    case ADS1115_GAIN_1P024:
 | 
			
		||||
      millivolts = signed_conversion * 0.031250f;
 | 
			
		||||
      break;
 | 
			
		||||
    case ADS1115_GAIN_0P512:
 | 
			
		||||
      millivolts = signed_conversion * 0.015625f;
 | 
			
		||||
      break;
 | 
			
		||||
    case ADS1115_GAIN_0P256:
 | 
			
		||||
      millivolts = signed_conversion * 0.007813f;
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      millivolts = NAN;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this->status_clear_warning();
 | 
			
		||||
  return millivolts / 1e3f;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float ADS1115Sensor::sample() { return this->parent_->request_measurement(this); }
 | 
			
		||||
void ADS1115Sensor::update() {
 | 
			
		||||
  float v = this->parent_->request_measurement(this);
 | 
			
		||||
  if (!isnan(v)) {
 | 
			
		||||
    ESP_LOGD(TAG, "'%s': Got Voltage=%fV", this->get_name().c_str(), v);
 | 
			
		||||
    this->publish_state(v);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace ads1115
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										71
									
								
								esphome/components/ads1115/ads1115.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								esphome/components/ads1115/ads1115.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,71 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/components/sensor/sensor.h"
 | 
			
		||||
#include "esphome/components/i2c/i2c.h"
 | 
			
		||||
#include "esphome/components/voltage_sampler/voltage_sampler.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace ads1115 {
 | 
			
		||||
 | 
			
		||||
enum ADS1115Multiplexer {
 | 
			
		||||
  ADS1115_MULTIPLEXER_P0_N1 = 0b000,
 | 
			
		||||
  ADS1115_MULTIPLEXER_P0_N3 = 0b001,
 | 
			
		||||
  ADS1115_MULTIPLEXER_P1_N3 = 0b010,
 | 
			
		||||
  ADS1115_MULTIPLEXER_P2_N3 = 0b011,
 | 
			
		||||
  ADS1115_MULTIPLEXER_P0_NG = 0b100,
 | 
			
		||||
  ADS1115_MULTIPLEXER_P1_NG = 0b101,
 | 
			
		||||
  ADS1115_MULTIPLEXER_P2_NG = 0b110,
 | 
			
		||||
  ADS1115_MULTIPLEXER_P3_NG = 0b111,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum ADS1115Gain {
 | 
			
		||||
  ADS1115_GAIN_6P144 = 0b000,
 | 
			
		||||
  ADS1115_GAIN_4P096 = 0b001,
 | 
			
		||||
  ADS1115_GAIN_2P048 = 0b010,
 | 
			
		||||
  ADS1115_GAIN_1P024 = 0b011,
 | 
			
		||||
  ADS1115_GAIN_0P512 = 0b100,
 | 
			
		||||
  ADS1115_GAIN_0P256 = 0b101,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ADS1115Sensor;
 | 
			
		||||
 | 
			
		||||
class ADS1115Component : public Component, public i2c::I2CDevice {
 | 
			
		||||
 public:
 | 
			
		||||
  void register_sensor(ADS1115Sensor *obj) { this->sensors_.push_back(obj); }
 | 
			
		||||
  /// Set up the internal sensor array.
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
  /// HARDWARE_LATE setup priority
 | 
			
		||||
  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.
 | 
			
		||||
class ADS1115Sensor : public sensor::Sensor, public PollingComponent, public voltage_sampler::VoltageSampler {
 | 
			
		||||
 public:
 | 
			
		||||
  ADS1115Sensor(ADS1115Component *parent) : parent_(parent) {}
 | 
			
		||||
  void update() override;
 | 
			
		||||
  void set_multiplexer(ADS1115Multiplexer multiplexer) { multiplexer_ = multiplexer; }
 | 
			
		||||
  void set_gain(ADS1115Gain gain) { gain_ = gain; }
 | 
			
		||||
 | 
			
		||||
  float sample() override;
 | 
			
		||||
  uint8_t get_multiplexer() const { return multiplexer_; }
 | 
			
		||||
  uint8_t get_gain() const { return gain_; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  ADS1115Component *parent_;
 | 
			
		||||
  ADS1115Multiplexer multiplexer_;
 | 
			
		||||
  ADS1115Gain gain_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace ads1115
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										63
									
								
								esphome/components/ads1115/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								esphome/components/ads1115/sensor.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,63 @@
 | 
			
		||||
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']
 | 
			
		||||
 | 
			
		||||
ADS1115Multiplexer = ads1115_ns.enum('ADS1115Multiplexer')
 | 
			
		||||
MUX = {
 | 
			
		||||
    'A0_A1': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P0_N1,
 | 
			
		||||
    'A0_A3': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P0_N3,
 | 
			
		||||
    'A1_A3': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P1_N3,
 | 
			
		||||
    'A2_A3': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P2_N3,
 | 
			
		||||
    'A0_GND': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P0_NG,
 | 
			
		||||
    'A1_GND': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P1_NG,
 | 
			
		||||
    'A2_GND': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P2_NG,
 | 
			
		||||
    'A3_GND': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P3_NG,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ADS1115Gain = ads1115_ns.enum('ADS1115Gain')
 | 
			
		||||
GAIN = {
 | 
			
		||||
    '6.144': ADS1115Gain.ADS1115_GAIN_6P144,
 | 
			
		||||
    '4.096': ADS1115Gain.ADS1115_GAIN_4P096,
 | 
			
		||||
    '2.048': ADS1115Gain.ADS1115_GAIN_2P048,
 | 
			
		||||
    '1.024': ADS1115Gain.ADS1115_GAIN_1P024,
 | 
			
		||||
    '0.512': ADS1115Gain.ADS1115_GAIN_0P512,
 | 
			
		||||
    '0.256': ADS1115Gain.ADS1115_GAIN_0P256,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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))
 | 
			
		||||
 | 
			
		||||
    return cv.enum(GAIN)(value)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
ADS1115Sensor = ads1115_ns.class_('ADS1115Sensor', sensor.Sensor, cg.PollingComponent,
 | 
			
		||||
                                  voltage_sampler.VoltageSampler)
 | 
			
		||||
 | 
			
		||||
CONF_ADS1115_ID = 'ads1115_id'
 | 
			
		||||
CONFIG_SCHEMA = sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 3).extend({
 | 
			
		||||
    cv.GenerateID(): cv.declare_id(ADS1115Sensor),
 | 
			
		||||
    cv.GenerateID(CONF_ADS1115_ID): cv.use_id(ADS1115Component),
 | 
			
		||||
    cv.Required(CONF_MULTIPLEXER): cv.enum(MUX, upper=True, space='_'),
 | 
			
		||||
    cv.Required(CONF_GAIN): validate_gain,
 | 
			
		||||
}).extend(cv.polling_component_schema('60s'))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    paren = yield cg.get_variable(config[CONF_ADS1115_ID])
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID], paren)
 | 
			
		||||
    yield sensor.register_sensor(var, config)
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_multiplexer(config[CONF_MULTIPLEXER]))
 | 
			
		||||
    cg.add(var.set_gain(config[CONF_GAIN]))
 | 
			
		||||
 | 
			
		||||
    cg.add(paren.register_sensor(var))
 | 
			
		||||
							
								
								
									
										0
									
								
								esphome/components/am2320/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								esphome/components/am2320/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										107
									
								
								esphome/components/am2320/am2320.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								esphome/components/am2320/am2320.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,107 @@
 | 
			
		||||
// Implementation based on:
 | 
			
		||||
//  - ESPEasy: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P034_DHT12.ino
 | 
			
		||||
//  - DHT12_sensor_library: https://github.com/xreef/DHT12_sensor_library/blob/master/DHT12.cpp
 | 
			
		||||
//  - Arduino - AM2320: https://github.com/EngDial/AM2320/blob/master/src/AM2320.cpp
 | 
			
		||||
 | 
			
		||||
#include "am2320.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace am2320 {
 | 
			
		||||
 | 
			
		||||
static const char *TAG = "am2320";
 | 
			
		||||
 | 
			
		||||
// ---=== Calc CRC16 ===---
 | 
			
		||||
uint16_t crc_16(uint8_t *ptr, uint8_t length) {
 | 
			
		||||
  uint16_t crc = 0xFFFF;
 | 
			
		||||
  uint8_t i;
 | 
			
		||||
  //------------------------------
 | 
			
		||||
  while (length--) {
 | 
			
		||||
    crc ^= *ptr++;
 | 
			
		||||
    for (i = 0; i < 8; i++)
 | 
			
		||||
      if ((crc & 0x01) != 0) {
 | 
			
		||||
        crc >>= 1;
 | 
			
		||||
        crc ^= 0xA001;
 | 
			
		||||
      } else
 | 
			
		||||
        crc >>= 1;
 | 
			
		||||
  }
 | 
			
		||||
  return crc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AM2320Component::update() {
 | 
			
		||||
  uint8_t data[8];
 | 
			
		||||
  data[0] = 0;
 | 
			
		||||
  data[1] = 4;
 | 
			
		||||
  if (!this->read_data_(data)) {
 | 
			
		||||
    this->status_set_warning();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  float temperature = (((data[4] & 0x7F) << 8) + data[5]) / 10.0;
 | 
			
		||||
  temperature = (data[4] & 0x80) ? -temperature : temperature;
 | 
			
		||||
  float humidity = ((data[2] << 8) + data[3]) / 10.0;
 | 
			
		||||
 | 
			
		||||
  ESP_LOGD(TAG, "Got temperature=%.1f°C humidity=%.1f%%", temperature, humidity);
 | 
			
		||||
  if (this->temperature_sensor_ != nullptr)
 | 
			
		||||
    this->temperature_sensor_->publish_state(temperature);
 | 
			
		||||
  if (this->humidity_sensor_ != nullptr)
 | 
			
		||||
    this->humidity_sensor_->publish_state(humidity);
 | 
			
		||||
  this->status_clear_warning();
 | 
			
		||||
}
 | 
			
		||||
void AM2320Component::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up AM2320...");
 | 
			
		||||
  uint8_t data[8];
 | 
			
		||||
  data[0] = 0;
 | 
			
		||||
  data[1] = 4;
 | 
			
		||||
  if (!this->read_data_(data)) {
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void AM2320Component::dump_config() {
 | 
			
		||||
  ESP_LOGD(TAG, "AM2320:");
 | 
			
		||||
  LOG_I2C_DEVICE(this);
 | 
			
		||||
  if (this->is_failed()) {
 | 
			
		||||
    ESP_LOGE(TAG, "Communication with AM2320 failed!");
 | 
			
		||||
  }
 | 
			
		||||
  LOG_SENSOR("  ", "Temperature", this->temperature_sensor_);
 | 
			
		||||
  LOG_SENSOR("  ", "Humidity", this->humidity_sensor_);
 | 
			
		||||
}
 | 
			
		||||
float AM2320Component::get_setup_priority() const { return setup_priority::DATA; }
 | 
			
		||||
 | 
			
		||||
bool AM2320Component::read_bytes_(uint8_t a_register, uint8_t *data, uint8_t len, uint32_t conversion) {
 | 
			
		||||
  if (!this->write_bytes(a_register, data, 2)) {
 | 
			
		||||
    ESP_LOGW(TAG, "Writing bytes for AM2320 failed!");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (conversion > 0)
 | 
			
		||||
    delay(conversion);
 | 
			
		||||
  return this->parent_->raw_receive(this->address_, data, len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AM2320Component::read_data_(uint8_t *data) {
 | 
			
		||||
  // Wake up
 | 
			
		||||
  this->write_bytes(0, data, 0);
 | 
			
		||||
 | 
			
		||||
  // Write instruction 3, 2 bytes, get 8 bytes back (2 preamble, 2 bytes temperature, 2 bytes humidity, 2 bytes CRC)
 | 
			
		||||
  if (!this->read_bytes_(3, data, 8, 2)) {
 | 
			
		||||
    ESP_LOGW(TAG, "Updating AM2320 failed!");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint16_t checksum;
 | 
			
		||||
 | 
			
		||||
  checksum = data[7] << 8;
 | 
			
		||||
  checksum += data[6];
 | 
			
		||||
 | 
			
		||||
  if (crc_16(data, 6) != checksum) {
 | 
			
		||||
    ESP_LOGW(TAG, "AM2320 Checksum invalid!");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace am2320
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										29
									
								
								esphome/components/am2320/am2320.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								esphome/components/am2320/am2320.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/components/sensor/sensor.h"
 | 
			
		||||
#include "esphome/components/i2c/i2c.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace am2320 {
 | 
			
		||||
 | 
			
		||||
class AM2320Component : public PollingComponent, public i2c::I2CDevice {
 | 
			
		||||
 public:
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
  float get_setup_priority() const override;
 | 
			
		||||
  void update() 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:
 | 
			
		||||
  bool read_data_(uint8_t *data);
 | 
			
		||||
  bool read_bytes_(uint8_t a_register, uint8_t *data, uint8_t len, uint32_t conversion = 0);
 | 
			
		||||
 | 
			
		||||
  sensor::Sensor *temperature_sensor_;
 | 
			
		||||
  sensor::Sensor *humidity_sensor_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace am2320
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										30
									
								
								esphome/components/am2320/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								esphome/components/am2320/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']
 | 
			
		||||
 | 
			
		||||
am2320_ns = cg.esphome_ns.namespace('am2320')
 | 
			
		||||
AM2320Component = am2320_ns.class_('AM2320Component', cg.PollingComponent, i2c.I2CDevice)
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.Schema({
 | 
			
		||||
    cv.GenerateID(): cv.declare_id(AM2320Component),
 | 
			
		||||
    cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1),
 | 
			
		||||
    cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1),
 | 
			
		||||
}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x5C))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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))
 | 
			
		||||
							
								
								
									
										23
									
								
								esphome/components/apds9960/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								esphome/components/apds9960/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import i2c
 | 
			
		||||
from esphome.const import CONF_ID
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ['i2c']
 | 
			
		||||
AUTO_LOAD = ['sensor', 'binary_sensor']
 | 
			
		||||
MULTI_CONF = True
 | 
			
		||||
 | 
			
		||||
CONF_APDS9960_ID = 'apds9960_id'
 | 
			
		||||
 | 
			
		||||
apds9960_nds = cg.esphome_ns.namespace('apds9960')
 | 
			
		||||
APDS9960 = apds9960_nds.class_('APDS9960', cg.PollingComponent, i2c.I2CDevice)
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.Schema({
 | 
			
		||||
    cv.GenerateID(): cv.declare_id(APDS9960),
 | 
			
		||||
}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x39))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
    yield i2c.register_i2c_device(var, config)
 | 
			
		||||
							
								
								
									
										374
									
								
								esphome/components/apds9960/apds9960.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										374
									
								
								esphome/components/apds9960/apds9960.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,374 @@
 | 
			
		||||
#include "apds9960.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace apds9960 {
 | 
			
		||||
 | 
			
		||||
static const char *TAG = "apds9960";
 | 
			
		||||
 | 
			
		||||
#define APDS9960_ERROR_CHECK(func) \
 | 
			
		||||
  if (!func) { \
 | 
			
		||||
    this->mark_failed(); \
 | 
			
		||||
    return; \
 | 
			
		||||
  }
 | 
			
		||||
#define APDS9960_WRITE_BYTE(reg, value) APDS9960_ERROR_CHECK(this->write_byte(reg, value));
 | 
			
		||||
 | 
			
		||||
void APDS9960::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up APDS9960...");
 | 
			
		||||
  uint8_t id;
 | 
			
		||||
  if (!this->read_byte(0x92, &id)) {  // ID register
 | 
			
		||||
    this->error_code_ = COMMUNICATION_FAILED;
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (id != 0xAB && id != 0x9C) {  // APDS9960 all should have one of these IDs
 | 
			
		||||
    this->error_code_ = WRONG_ID;
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // ATime (ADC integration time, 2.78ms increments, 0x81) -> 0xDB (103ms)
 | 
			
		||||
  APDS9960_WRITE_BYTE(0x81, 0xDB);
 | 
			
		||||
  // WTime (Wait time, 0x83) -> 0xF6 (27ms)
 | 
			
		||||
  APDS9960_WRITE_BYTE(0x83, 0xF6);
 | 
			
		||||
  // PPulse (0x8E) -> 0x87 (16us, 8 pulses)
 | 
			
		||||
  APDS9960_WRITE_BYTE(0x8E, 0x87);
 | 
			
		||||
  // POffset UR (0x9D) -> 0 (no offset)
 | 
			
		||||
  APDS9960_WRITE_BYTE(0x9D, 0x00);
 | 
			
		||||
  // POffset DL (0x9E) -> 0 (no offset)
 | 
			
		||||
  APDS9960_WRITE_BYTE(0x9E, 0x00);
 | 
			
		||||
  // Config 1 (0x8D) -> 0x60 (no wtime factor)
 | 
			
		||||
  APDS9960_WRITE_BYTE(0x8D, 0x60);
 | 
			
		||||
 | 
			
		||||
  // Control (0x8F) ->
 | 
			
		||||
  uint8_t val = 0;
 | 
			
		||||
  APDS9960_ERROR_CHECK(this->read_byte(0x8F, &val));
 | 
			
		||||
  val &= 0b00111111;
 | 
			
		||||
  uint8_t led_drive = 0;  // led drive, 0 -> 100mA, 1 -> 50mA, 2 -> 25mA, 3 -> 12.5mA
 | 
			
		||||
  val |= (led_drive & 0b11) << 6;
 | 
			
		||||
 | 
			
		||||
  val &= 0b11110011;
 | 
			
		||||
  uint8_t proximity_gain = 2;  // proximity gain, 0 -> 1x, 1 -> 2X, 2 -> 4X, 4 -> 8X
 | 
			
		||||
  val |= (proximity_gain & 0b11) << 2;
 | 
			
		||||
 | 
			
		||||
  val &= 0b11111100;
 | 
			
		||||
  uint8_t ambient_gain = 1;  // ambient light gain, 0 -> 1x, 1 -> 4x, 2 -> 16x, 3 -> 64x
 | 
			
		||||
  val |= (ambient_gain & 0b11) << 0;
 | 
			
		||||
  APDS9960_WRITE_BYTE(0x8F, val);
 | 
			
		||||
 | 
			
		||||
  // Pers (0x8C) -> 0x11 (2 consecutive proximity or ALS for interrupt)
 | 
			
		||||
  APDS9960_WRITE_BYTE(0x8C, 0x11);
 | 
			
		||||
  // Config 2 (0x90) -> 0x01 (no saturation interrupts or LED boost)
 | 
			
		||||
  APDS9960_WRITE_BYTE(0x90, 0x01);
 | 
			
		||||
  // Config 3 (0x9F) -> 0x00 (enable all photodiodes, no SAI)
 | 
			
		||||
  APDS9960_WRITE_BYTE(0x9F, 0x00);
 | 
			
		||||
  // GPenTh (0xA0, gesture enter threshold) -> 0x28 (also 0x32)
 | 
			
		||||
  APDS9960_WRITE_BYTE(0xA0, 0x28);
 | 
			
		||||
  // GPexTh (0xA1, gesture exit threshold) -> 0x1E
 | 
			
		||||
  APDS9960_WRITE_BYTE(0xA1, 0x1E);
 | 
			
		||||
 | 
			
		||||
  // GConf 1 (0xA2, gesture config 1) -> 0x40 (4 gesture events for interrupt (GFIFO 3), 1 for exit)
 | 
			
		||||
  APDS9960_WRITE_BYTE(0xA2, 0x40);
 | 
			
		||||
 | 
			
		||||
  // GConf 2 (0xA3, gesture config 2) ->
 | 
			
		||||
  APDS9960_ERROR_CHECK(this->read_byte(0xA3, &val));
 | 
			
		||||
  val &= 0b10011111;
 | 
			
		||||
  uint8_t gesture_gain = 2;  // gesture gain, 0 -> 1x, 1 -> 2x, 2 -> 4x, 3 -> 8x
 | 
			
		||||
  val |= (gesture_gain & 0b11) << 5;
 | 
			
		||||
 | 
			
		||||
  val &= 0b11100111;
 | 
			
		||||
  uint8_t gesture_led_drive = 0;  // gesture led drive, 0 -> 100mA, 1 -> 50mA, 2 -> 25mA, 3 -> 12.5mA
 | 
			
		||||
  val |= (gesture_led_drive & 0b11) << 3;
 | 
			
		||||
 | 
			
		||||
  val &= 0b11111000;
 | 
			
		||||
  // gesture wait time
 | 
			
		||||
  // 0 -> 0ms, 1 -> 2.8ms, 2 -> 5.6ms, 3 -> 8.4ms
 | 
			
		||||
  // 4 -> 14.0ms, 5 -> 22.4 ms, 6 -> 30.8ms, 7 -> 39.2 ms
 | 
			
		||||
  uint8_t gesture_wait_time = 1;  // gesture wait time
 | 
			
		||||
  val |= (gesture_wait_time & 0b111) << 0;
 | 
			
		||||
  APDS9960_WRITE_BYTE(0xA3, val);
 | 
			
		||||
 | 
			
		||||
  // GOffsetU (0xA4) -> 0x00 (no offset)
 | 
			
		||||
  APDS9960_WRITE_BYTE(0xA4, 0x00);
 | 
			
		||||
  // GOffsetD (0xA5) -> 0x00 (no offset)
 | 
			
		||||
  APDS9960_WRITE_BYTE(0xA5, 0x00);
 | 
			
		||||
  // GOffsetL (0xA7) -> 0x00 (no offset)
 | 
			
		||||
  APDS9960_WRITE_BYTE(0xA7, 0x00);
 | 
			
		||||
  // GOffsetR (0xA9) -> 0x00 (no offset)
 | 
			
		||||
  APDS9960_WRITE_BYTE(0xA9, 0x00);
 | 
			
		||||
  // GPulse (0xA6) -> 0xC9 (32 µs, 10 pulses)
 | 
			
		||||
  APDS9960_WRITE_BYTE(0xA6, 0xC9);
 | 
			
		||||
 | 
			
		||||
  // GConf 3 (0xAA, gesture config 3) -> 0x00 (all photodiodes active during gesture, all gesture dimensions enabled)
 | 
			
		||||
  // 0x00 -> all dimensions, 0x01 -> up down, 0x02 -> left right
 | 
			
		||||
  APDS9960_WRITE_BYTE(0xAA, 0x00);
 | 
			
		||||
 | 
			
		||||
  // Enable (0x80) ->
 | 
			
		||||
  val = 0;
 | 
			
		||||
  val |= (0b1) << 0;  // power on
 | 
			
		||||
  val |= (this->is_color_enabled_() & 0b1) << 1;
 | 
			
		||||
  val |= (this->is_proximity_enabled_() & 0b1) << 2;
 | 
			
		||||
  val |= 0b0 << 3;                                  // wait timer disabled
 | 
			
		||||
  val |= 0b0 << 4;                                  // color interrupt disabled
 | 
			
		||||
  val |= 0b0 << 5;                                  // proximity interrupt disabled
 | 
			
		||||
  val |= (this->is_gesture_enabled_() & 0b1) << 6;  // proximity is required for gestures
 | 
			
		||||
  APDS9960_WRITE_BYTE(0x80, val);
 | 
			
		||||
}
 | 
			
		||||
bool APDS9960::is_color_enabled_() const {
 | 
			
		||||
  return this->red_channel_ != nullptr || this->green_channel_ != nullptr || this->blue_channel_ != nullptr ||
 | 
			
		||||
         this->clear_channel_ != nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void APDS9960::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "APDS9960:");
 | 
			
		||||
  LOG_I2C_DEVICE(this);
 | 
			
		||||
 | 
			
		||||
  LOG_UPDATE_INTERVAL(this);
 | 
			
		||||
  if (this->is_failed()) {
 | 
			
		||||
    switch (this->error_code_) {
 | 
			
		||||
      case COMMUNICATION_FAILED:
 | 
			
		||||
        ESP_LOGE(TAG, "Communication with APDS9960 failed!");
 | 
			
		||||
        break;
 | 
			
		||||
      case WRONG_ID:
 | 
			
		||||
        ESP_LOGE(TAG, "APDS9960 has invalid id!");
 | 
			
		||||
        break;
 | 
			
		||||
      default:
 | 
			
		||||
        ESP_LOGE(TAG, "Setting up APDS9960 registers failed!");
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define APDS9960_WARNING_CHECK(func, warning) \
 | 
			
		||||
  if (!(func)) { \
 | 
			
		||||
    ESP_LOGW(TAG, warning); \
 | 
			
		||||
    this->status_set_warning(); \
 | 
			
		||||
    return; \
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
void APDS9960::update() {
 | 
			
		||||
  uint8_t status;
 | 
			
		||||
  APDS9960_WARNING_CHECK(this->read_byte(0x93, &status), "Reading status bit failed.");
 | 
			
		||||
  this->status_clear_warning();
 | 
			
		||||
 | 
			
		||||
  this->read_color_data_(status);
 | 
			
		||||
  this->read_proximity_data_(status);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void APDS9960::loop() { this->read_gesture_data_(); }
 | 
			
		||||
 | 
			
		||||
void APDS9960::read_color_data_(uint8_t status) {
 | 
			
		||||
  if (!this->is_color_enabled_())
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  if ((status & 0x01) == 0x00) {
 | 
			
		||||
    // color data not ready yet.
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint8_t raw[8];
 | 
			
		||||
  APDS9960_WARNING_CHECK(this->read_bytes(0x94, raw, 8), "Reading color values failed.");
 | 
			
		||||
 | 
			
		||||
  uint16_t uint_clear = (uint16_t(raw[1]) << 8) | raw[0];
 | 
			
		||||
  uint16_t uint_red = (uint16_t(raw[3]) << 8) | raw[2];
 | 
			
		||||
  uint16_t uint_green = (uint16_t(raw[5]) << 8) | raw[4];
 | 
			
		||||
  uint16_t uint_blue = (uint16_t(raw[7]) << 8) | raw[6];
 | 
			
		||||
 | 
			
		||||
  float clear_perc = (uint_clear / float(UINT16_MAX)) * 100.0f;
 | 
			
		||||
  float red_perc = (uint_red / float(UINT16_MAX)) * 100.0f;
 | 
			
		||||
  float green_perc = (uint_green / float(UINT16_MAX)) * 100.0f;
 | 
			
		||||
  float blue_perc = (uint_blue / float(UINT16_MAX)) * 100.0f;
 | 
			
		||||
 | 
			
		||||
  ESP_LOGD(TAG, "Got clear=%.1f%% red=%.1f%% green=%.1f%% blue=%.1f%%", clear_perc, red_perc, green_perc, blue_perc);
 | 
			
		||||
  if (this->clear_channel_ != nullptr)
 | 
			
		||||
    this->clear_channel_->publish_state(clear_perc);
 | 
			
		||||
  if (this->red_channel_ != nullptr)
 | 
			
		||||
    this->red_channel_->publish_state(red_perc);
 | 
			
		||||
  if (this->green_channel_ != nullptr)
 | 
			
		||||
    this->green_channel_->publish_state(green_perc);
 | 
			
		||||
  if (this->blue_channel_ != nullptr)
 | 
			
		||||
    this->blue_channel_->publish_state(blue_perc);
 | 
			
		||||
}
 | 
			
		||||
void APDS9960::read_proximity_data_(uint8_t status) {
 | 
			
		||||
  if (this->proximity_ == nullptr)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  if ((status & 0b10) == 0x00) {
 | 
			
		||||
    // proximity data not ready yet.
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint8_t prox;
 | 
			
		||||
  APDS9960_WARNING_CHECK(this->read_byte(0x9C, &prox), "Reading proximity values failed.");
 | 
			
		||||
 | 
			
		||||
  float prox_perc = (prox / float(UINT8_MAX)) * 100.0f;
 | 
			
		||||
  ESP_LOGD(TAG, "Got proximity=%.1f%%", prox_perc);
 | 
			
		||||
  this->proximity_->publish_state(prox_perc);
 | 
			
		||||
}
 | 
			
		||||
void APDS9960::read_gesture_data_() {
 | 
			
		||||
  if (!this->is_gesture_enabled_())
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  uint8_t status;
 | 
			
		||||
  APDS9960_WARNING_CHECK(this->read_byte(0xAF, &status), "Reading gesture status failed.");
 | 
			
		||||
 | 
			
		||||
  if ((status & 0b01) == 0) {
 | 
			
		||||
    // GVALID is false
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if ((status & 0b10) == 0b10) {
 | 
			
		||||
    ESP_LOGV(TAG, "FIFO buffer has filled to capacity!");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint8_t fifo_level;
 | 
			
		||||
  APDS9960_WARNING_CHECK(this->read_byte(0xAE, &fifo_level), "Reading FIFO level failed.");
 | 
			
		||||
  if (fifo_level == 0)
 | 
			
		||||
    // no data to process
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  APDS9960_WARNING_CHECK(fifo_level <= 32, "FIFO level has invalid value.")
 | 
			
		||||
 | 
			
		||||
  uint8_t buf[128];
 | 
			
		||||
  for (uint8_t pos = 0; pos < fifo_level * 4; pos += 32) {
 | 
			
		||||
    // The ESP's i2c driver has a limited buffer size.
 | 
			
		||||
    // This way of retrieving the data should be wrong according to the datasheet
 | 
			
		||||
    // but it seems to work.
 | 
			
		||||
    uint8_t read = std::min(32, fifo_level * 4 - pos);
 | 
			
		||||
    APDS9960_WARNING_CHECK(this->read_bytes(0xFC + pos, buf + pos, read), "Reading FIFO buffer failed.");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (millis() - this->gesture_start_ > 500) {
 | 
			
		||||
    this->gesture_up_started_ = false;
 | 
			
		||||
    this->gesture_down_started_ = false;
 | 
			
		||||
    this->gesture_left_started_ = false;
 | 
			
		||||
    this->gesture_right_started_ = false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for (uint32_t i = 0; i < fifo_level * 4; i += 4) {
 | 
			
		||||
    const int up = buf[i + 0];  // NOLINT
 | 
			
		||||
    const int down = buf[i + 1];
 | 
			
		||||
    const int left = buf[i + 2];
 | 
			
		||||
    const int right = buf[i + 3];
 | 
			
		||||
    this->process_dataset_(up, down, left, right);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void APDS9960::report_gesture_(int gesture) {
 | 
			
		||||
  binary_sensor::BinarySensor *bin;
 | 
			
		||||
  switch (gesture) {
 | 
			
		||||
    case 1:
 | 
			
		||||
      bin = this->up_direction_;
 | 
			
		||||
      this->gesture_up_started_ = false;
 | 
			
		||||
      this->gesture_down_started_ = false;
 | 
			
		||||
      ESP_LOGD(TAG, "Got gesture UP");
 | 
			
		||||
      break;
 | 
			
		||||
    case 2:
 | 
			
		||||
      bin = this->down_direction_;
 | 
			
		||||
      this->gesture_up_started_ = false;
 | 
			
		||||
      this->gesture_down_started_ = false;
 | 
			
		||||
      ESP_LOGD(TAG, "Got gesture DOWN");
 | 
			
		||||
      break;
 | 
			
		||||
    case 3:
 | 
			
		||||
      bin = this->left_direction_;
 | 
			
		||||
      this->gesture_left_started_ = false;
 | 
			
		||||
      this->gesture_right_started_ = false;
 | 
			
		||||
      ESP_LOGD(TAG, "Got gesture LEFT");
 | 
			
		||||
      break;
 | 
			
		||||
    case 4:
 | 
			
		||||
      bin = this->right_direction_;
 | 
			
		||||
      this->gesture_left_started_ = false;
 | 
			
		||||
      this->gesture_right_started_ = false;
 | 
			
		||||
      ESP_LOGD(TAG, "Got gesture RIGHT");
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (bin != nullptr) {
 | 
			
		||||
    bin->publish_state(true);
 | 
			
		||||
    bin->publish_state(false);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void APDS9960::process_dataset_(int up, int down, int left, int right) {
 | 
			
		||||
  /* Algorithm: (see Figure 11 in datasheet)
 | 
			
		||||
   *
 | 
			
		||||
   * Observation: When a gesture is started, we will see a short amount of time where
 | 
			
		||||
   * the photodiode in the direction of the motion has a much higher count value
 | 
			
		||||
   * than where the gesture originates.
 | 
			
		||||
   *
 | 
			
		||||
   * In this algorithm we continually check the difference between the count values of opposing
 | 
			
		||||
   * directions. For example in the down/up direction we continually look at the difference of the
 | 
			
		||||
   * up count and down count. When DOWN gesture begins, this difference will be positive with a
 | 
			
		||||
   * high magnitude for a short amount of time (magic value here is the difference is at least 13).
 | 
			
		||||
   *
 | 
			
		||||
   * If we see such a pattern, we store that we saw the first part of a gesture (the leading edge).
 | 
			
		||||
   * After that some time can pass during which the difference is zero again (though the count values
 | 
			
		||||
   * are not zero). At the end of a gesture, we will see this difference go into the opposite direction
 | 
			
		||||
   * for a short period of time.
 | 
			
		||||
   *
 | 
			
		||||
   * If a gesture is not ended within 500 milliseconds, we consider the initial trailing edge invalid
 | 
			
		||||
   * and reset the state.
 | 
			
		||||
   *
 | 
			
		||||
   * This algorithm does work, but not too well. Some good signal processing algorithms could
 | 
			
		||||
   * probably improve this a lot, especially since the incoming signal has such a characteristic
 | 
			
		||||
   * and quite noise-free pattern.
 | 
			
		||||
   */
 | 
			
		||||
  const int up_down_delta = up - down;
 | 
			
		||||
  const int left_right_delta = left - right;
 | 
			
		||||
  const bool up_down_significant = abs(up_down_delta) > 13;
 | 
			
		||||
  const bool left_right_significant = abs(left_right_delta) > 13;
 | 
			
		||||
 | 
			
		||||
  if (up_down_significant) {
 | 
			
		||||
    if (up_down_delta < 0) {
 | 
			
		||||
      if (this->gesture_up_started_) {
 | 
			
		||||
        // trailing edge of gesture up
 | 
			
		||||
        this->report_gesture_(1);  // UP
 | 
			
		||||
      } else {
 | 
			
		||||
        // leading edge of gesture down
 | 
			
		||||
        this->gesture_down_started_ = true;
 | 
			
		||||
        this->gesture_start_ = millis();
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      if (this->gesture_down_started_) {
 | 
			
		||||
        // trailing edge of gesture down
 | 
			
		||||
        this->report_gesture_(2);  // DOWN
 | 
			
		||||
      } else {
 | 
			
		||||
        // leading edge of gesture up
 | 
			
		||||
        this->gesture_up_started_ = true;
 | 
			
		||||
        this->gesture_start_ = millis();
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (left_right_significant) {
 | 
			
		||||
    if (left_right_delta < 0) {
 | 
			
		||||
      if (this->gesture_left_started_) {
 | 
			
		||||
        // trailing edge of gesture left
 | 
			
		||||
        this->report_gesture_(3);  // LEFT
 | 
			
		||||
      } else {
 | 
			
		||||
        // leading edge of gesture right
 | 
			
		||||
        this->gesture_right_started_ = true;
 | 
			
		||||
        this->gesture_start_ = millis();
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      if (this->gesture_right_started_) {
 | 
			
		||||
        // trailing edge of gesture right
 | 
			
		||||
        this->report_gesture_(4);  // RIGHT
 | 
			
		||||
      } else {
 | 
			
		||||
        // leading edge of gesture left
 | 
			
		||||
        this->gesture_left_started_ = true;
 | 
			
		||||
        this->gesture_start_ = millis();
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
float APDS9960::get_setup_priority() const { return setup_priority::DATA; }
 | 
			
		||||
bool APDS9960::is_proximity_enabled_() const { return this->proximity_ != nullptr || this->is_gesture_enabled_(); }
 | 
			
		||||
bool APDS9960::is_gesture_enabled_() const {
 | 
			
		||||
  return this->up_direction_ != nullptr || this->left_direction_ != nullptr || this->down_direction_ != nullptr ||
 | 
			
		||||
         this->right_direction_ != nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace apds9960
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										61
									
								
								esphome/components/apds9960/apds9960.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								esphome/components/apds9960/apds9960.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,61 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/components/i2c/i2c.h"
 | 
			
		||||
#include "esphome/components/sensor/sensor.h"
 | 
			
		||||
#include "esphome/components/binary_sensor/binary_sensor.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace apds9960 {
 | 
			
		||||
 | 
			
		||||
class APDS9960 : public PollingComponent, public i2c::I2CDevice {
 | 
			
		||||
 public:
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
  float get_setup_priority() const override;
 | 
			
		||||
  void update() override;
 | 
			
		||||
  void loop() override;
 | 
			
		||||
 | 
			
		||||
  void set_red_channel(sensor::Sensor *red_channel) { red_channel_ = red_channel; }
 | 
			
		||||
  void set_green_channel(sensor::Sensor *green_channel) { green_channel_ = green_channel; }
 | 
			
		||||
  void set_blue_channel(sensor::Sensor *blue_channel) { blue_channel_ = blue_channel; }
 | 
			
		||||
  void set_clear_channel(sensor::Sensor *clear_channel) { clear_channel_ = clear_channel; }
 | 
			
		||||
  void set_up_direction(binary_sensor::BinarySensor *up_direction) { up_direction_ = up_direction; }
 | 
			
		||||
  void set_right_direction(binary_sensor::BinarySensor *right_direction) { right_direction_ = right_direction; }
 | 
			
		||||
  void set_down_direction(binary_sensor::BinarySensor *down_direction) { down_direction_ = down_direction; }
 | 
			
		||||
  void set_left_direction(binary_sensor::BinarySensor *left_direction) { left_direction_ = left_direction; }
 | 
			
		||||
  void set_proximity(sensor::Sensor *proximity) { proximity_ = proximity; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool is_color_enabled_() const;
 | 
			
		||||
  bool is_proximity_enabled_() const;
 | 
			
		||||
  bool is_gesture_enabled_() const;
 | 
			
		||||
  void read_color_data_(uint8_t status);
 | 
			
		||||
  void read_proximity_data_(uint8_t status);
 | 
			
		||||
  void read_gesture_data_();
 | 
			
		||||
  void report_gesture_(int gesture);
 | 
			
		||||
  void process_dataset_(int up, int down, int left, int right);
 | 
			
		||||
 | 
			
		||||
  sensor::Sensor *red_channel_{nullptr};
 | 
			
		||||
  sensor::Sensor *green_channel_{nullptr};
 | 
			
		||||
  sensor::Sensor *blue_channel_{nullptr};
 | 
			
		||||
  sensor::Sensor *clear_channel_{nullptr};
 | 
			
		||||
  binary_sensor::BinarySensor *up_direction_{nullptr};
 | 
			
		||||
  binary_sensor::BinarySensor *right_direction_{nullptr};
 | 
			
		||||
  binary_sensor::BinarySensor *down_direction_{nullptr};
 | 
			
		||||
  binary_sensor::BinarySensor *left_direction_{nullptr};
 | 
			
		||||
  sensor::Sensor *proximity_{nullptr};
 | 
			
		||||
  enum ErrorCode {
 | 
			
		||||
    NONE = 0,
 | 
			
		||||
    COMMUNICATION_FAILED,
 | 
			
		||||
    WRONG_ID,
 | 
			
		||||
  } error_code_{NONE};
 | 
			
		||||
  bool gesture_up_started_{false};
 | 
			
		||||
  bool gesture_down_started_{false};
 | 
			
		||||
  bool gesture_left_started_{false};
 | 
			
		||||
  bool gesture_right_started_{false};
 | 
			
		||||
  uint32_t gesture_start_{0};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace apds9960
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										27
									
								
								esphome/components/apds9960/binary_sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								esphome/components/apds9960/binary_sensor.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import binary_sensor
 | 
			
		||||
from esphome.const import CONF_DIRECTION, CONF_DEVICE_CLASS, DEVICE_CLASS_MOVING
 | 
			
		||||
from . import APDS9960, CONF_APDS9960_ID
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ['apds9960']
 | 
			
		||||
 | 
			
		||||
DIRECTIONS = {
 | 
			
		||||
    'UP': 'set_up_direction',
 | 
			
		||||
    'DOWN': 'set_down_direction',
 | 
			
		||||
    'LEFT': 'set_left_direction',
 | 
			
		||||
    'RIGHT': 'set_right_direction',
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({
 | 
			
		||||
    cv.Required(CONF_DIRECTION): cv.one_of(*DIRECTIONS, upper=True),
 | 
			
		||||
    cv.GenerateID(CONF_APDS9960_ID): cv.use_id(APDS9960),
 | 
			
		||||
    cv.Optional(CONF_DEVICE_CLASS, default=DEVICE_CLASS_MOVING): binary_sensor.device_class,
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    hub = yield cg.get_variable(config[CONF_APDS9960_ID])
 | 
			
		||||
    var = yield binary_sensor.new_binary_sensor(config)
 | 
			
		||||
    func = getattr(hub, DIRECTIONS[config[CONF_DIRECTION]])
 | 
			
		||||
    cg.add(func(var))
 | 
			
		||||
							
								
								
									
										27
									
								
								esphome/components/apds9960/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								esphome/components/apds9960/sensor.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import sensor
 | 
			
		||||
from esphome.const import CONF_TYPE, UNIT_PERCENT, ICON_LIGHTBULB
 | 
			
		||||
from . import APDS9960, CONF_APDS9960_ID
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ['apds9960']
 | 
			
		||||
 | 
			
		||||
TYPES = {
 | 
			
		||||
    'CLEAR': 'set_clear_channel',
 | 
			
		||||
    'RED': 'set_red_channel',
 | 
			
		||||
    'GREEN': 'set_green_channel',
 | 
			
		||||
    'BLUE': 'set_blue_channel',
 | 
			
		||||
    'PROXIMITY': 'set_proximity',
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = sensor.sensor_schema(UNIT_PERCENT, ICON_LIGHTBULB, 1).extend({
 | 
			
		||||
    cv.Required(CONF_TYPE): cv.one_of(*TYPES, upper=True),
 | 
			
		||||
    cv.GenerateID(CONF_APDS9960_ID): cv.use_id(APDS9960),
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    hub = yield cg.get_variable(config[CONF_APDS9960_ID])
 | 
			
		||||
    var = yield sensor.new_sensor(config)
 | 
			
		||||
    func = getattr(hub, TYPES[config[CONF_TYPE]])
 | 
			
		||||
    cg.add(func(var))
 | 
			
		||||
							
								
								
									
										141
									
								
								esphome/components/api/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								esphome/components/api/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,141 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
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, CONF_EVENT
 | 
			
		||||
from esphome.core import coroutine_with_priority
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ['network']
 | 
			
		||||
AUTO_LOAD = ['async_tcp']
 | 
			
		||||
 | 
			
		||||
api_ns = cg.esphome_ns.namespace('api')
 | 
			
		||||
APIServer = api_ns.class_('APIServer', cg.Component, cg.Controller)
 | 
			
		||||
HomeAssistantServiceCallAction = api_ns.class_('HomeAssistantServiceCallAction', automation.Action)
 | 
			
		||||
APIConnectedCondition = api_ns.class_('APIConnectedCondition', Condition)
 | 
			
		||||
 | 
			
		||||
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='15min'): cv.positive_time_period_milliseconds,
 | 
			
		||||
    cv.Optional(CONF_SERVICES): automation.validate_automation({
 | 
			
		||||
        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_NATIVE_TYPES, lower=True),
 | 
			
		||||
        }),
 | 
			
		||||
    }),
 | 
			
		||||
}).extend(cv.COMPONENT_SCHEMA)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@coroutine_with_priority(40.0)
 | 
			
		||||
def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    yield cg.register_component(var, config)
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_port(config[CONF_PORT]))
 | 
			
		||||
    cg.add(var.set_password(config[CONF_PASSWORD]))
 | 
			
		||||
    cg.add(var.set_reboot_timeout(config[CONF_REBOOT_TIMEOUT]))
 | 
			
		||||
 | 
			
		||||
    for conf in config.get(CONF_SERVICES, []):
 | 
			
		||||
        template_args = []
 | 
			
		||||
        func_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_arg_names.append(name)
 | 
			
		||||
        templ = cg.TemplateArguments(*template_args)
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], templ,
 | 
			
		||||
                                   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')
 | 
			
		||||
    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.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}),
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@automation.register_action('homeassistant.service', HomeAssistantServiceCallAction,
 | 
			
		||||
                            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, 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(u'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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@automation.register_condition('api.connected', APIConnectedCondition, {})
 | 
			
		||||
def api_connected_to_code(config, condition_id, template_arg, args):
 | 
			
		||||
    yield cg.new_Pvariable(condition_id, template_arg)
 | 
			
		||||
							
								
								
									
										714
									
								
								esphome/components/api/api.proto
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										714
									
								
								esphome/components/api/api.proto
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,714 @@
 | 
			
		||||
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 ====================
 | 
			
		||||
 | 
			
		||||
// 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 {
 | 
			
		||||
  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
 | 
			
		||||
  // 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 {
 | 
			
		||||
  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_
 | 
			
		||||
  // 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 {
 | 
			
		||||
  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
 | 
			
		||||
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
 | 
			
		||||
message DisconnectRequest {
 | 
			
		||||
  option (id) = 5;
 | 
			
		||||
  option (source) = SOURCE_BOTH;
 | 
			
		||||
  option (no_delay) = true;
 | 
			
		||||
 | 
			
		||||
  // Do not close the connection before the acknowledgement arrives
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message PingRequest {
 | 
			
		||||
  option (id) = 7;
 | 
			
		||||
  option (source) = SOURCE_BOTH;
 | 
			
		||||
  // Empty
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message PingResponse {
 | 
			
		||||
  option (id) = 8;
 | 
			
		||||
  option (source) = SOURCE_BOTH;
 | 
			
		||||
  // Empty
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message DeviceInfoRequest {
 | 
			
		||||
  option (id) = 9;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
  // Empty
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message DeviceInfoResponse {
 | 
			
		||||
  option (id) = 10;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
 | 
			
		||||
  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_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 {
 | 
			
		||||
  option (id) = 11;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
  // Empty
 | 
			
		||||
}
 | 
			
		||||
message ListEntitiesDoneResponse {
 | 
			
		||||
  option (id) = 19;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (no_delay) = true;
 | 
			
		||||
  // Empty
 | 
			
		||||
}
 | 
			
		||||
message SubscribeStatesRequest {
 | 
			
		||||
  option (id) = 20;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
  // Empty
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ==================== BINARY SENSOR ====================
 | 
			
		||||
message ListEntitiesBinarySensorResponse {
 | 
			
		||||
  option (id) = 12;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_BINARY_SENSOR";
 | 
			
		||||
 | 
			
		||||
  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 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 ====================
 | 
			
		||||
message ListEntitiesCoverResponse {
 | 
			
		||||
  option (id) = 13;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_COVER";
 | 
			
		||||
 | 
			
		||||
  string object_id = 1;
 | 
			
		||||
  fixed32 key = 2;
 | 
			
		||||
  string name = 3;
 | 
			
		||||
  string unique_id = 4;
 | 
			
		||||
 | 
			
		||||
  bool assumed_state = 5;
 | 
			
		||||
  bool supports_position = 6;
 | 
			
		||||
  bool supports_tilt = 7;
 | 
			
		||||
  string device_class = 8;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
  LegacyCoverState legacy_state = 2;
 | 
			
		||||
 | 
			
		||||
  float position = 3;
 | 
			
		||||
  float tilt = 4;
 | 
			
		||||
  CoverOperation current_operation = 5;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
  bool has_legacy_command = 2;
 | 
			
		||||
  LegacyCoverCommand legacy_command = 3;
 | 
			
		||||
 | 
			
		||||
  bool has_position = 4;
 | 
			
		||||
  float position = 5;
 | 
			
		||||
  bool has_tilt = 6;
 | 
			
		||||
  float tilt = 7;
 | 
			
		||||
  bool stop = 8;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ==================== FAN ====================
 | 
			
		||||
message ListEntitiesFanResponse {
 | 
			
		||||
  option (id) = 14;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_FAN";
 | 
			
		||||
 | 
			
		||||
  string object_id = 1;
 | 
			
		||||
  fixed32 key = 2;
 | 
			
		||||
  string name = 3;
 | 
			
		||||
  string unique_id = 4;
 | 
			
		||||
 | 
			
		||||
  bool supports_oscillation = 5;
 | 
			
		||||
  bool supports_speed = 6;
 | 
			
		||||
}
 | 
			
		||||
enum FanSpeed {
 | 
			
		||||
  FAN_SPEED_LOW = 0;
 | 
			
		||||
  FAN_SPEED_MEDIUM = 1;
 | 
			
		||||
  FAN_SPEED_HIGH = 2;
 | 
			
		||||
}
 | 
			
		||||
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;
 | 
			
		||||
}
 | 
			
		||||
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;
 | 
			
		||||
  bool has_speed = 4;
 | 
			
		||||
  FanSpeed speed = 5;
 | 
			
		||||
  bool has_oscillating = 6;
 | 
			
		||||
  bool oscillating = 7;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ==================== LIGHT ====================
 | 
			
		||||
message ListEntitiesLightResponse {
 | 
			
		||||
  option (id) = 15;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_LIGHT";
 | 
			
		||||
 | 
			
		||||
  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 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;
 | 
			
		||||
  float red = 4;
 | 
			
		||||
  float green = 5;
 | 
			
		||||
  float blue = 6;
 | 
			
		||||
  float white = 7;
 | 
			
		||||
  float color_temperature = 8;
 | 
			
		||||
  string effect = 9;
 | 
			
		||||
}
 | 
			
		||||
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;
 | 
			
		||||
  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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ==================== SENSOR ====================
 | 
			
		||||
message ListEntitiesSensorResponse {
 | 
			
		||||
  option (id) = 16;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_SENSOR";
 | 
			
		||||
 | 
			
		||||
  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;
 | 
			
		||||
  bool force_update = 8;
 | 
			
		||||
}
 | 
			
		||||
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 ====================
 | 
			
		||||
message ListEntitiesSwitchResponse {
 | 
			
		||||
  option (id) = 17;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_SWITCH";
 | 
			
		||||
 | 
			
		||||
  string object_id = 1;
 | 
			
		||||
  fixed32 key = 2;
 | 
			
		||||
  string name = 3;
 | 
			
		||||
  string unique_id = 4;
 | 
			
		||||
 | 
			
		||||
  string icon = 5;
 | 
			
		||||
  bool assumed_state = 6;
 | 
			
		||||
}
 | 
			
		||||
message SwitchStateResponse {
 | 
			
		||||
  option (id) = 26;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_SWITCH";
 | 
			
		||||
  option (no_delay) = true;
 | 
			
		||||
 | 
			
		||||
  fixed32 key = 1;
 | 
			
		||||
  bool state = 2;
 | 
			
		||||
}
 | 
			
		||||
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 ====================
 | 
			
		||||
message ListEntitiesTextSensorResponse {
 | 
			
		||||
  option (id) = 18;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_TEXT_SENSOR";
 | 
			
		||||
 | 
			
		||||
  string object_id = 1;
 | 
			
		||||
  fixed32 key = 2;
 | 
			
		||||
  string name = 3;
 | 
			
		||||
  string unique_id = 4;
 | 
			
		||||
 | 
			
		||||
  string icon = 5;
 | 
			
		||||
}
 | 
			
		||||
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 {
 | 
			
		||||
  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;
 | 
			
		||||
}
 | 
			
		||||
message SubscribeLogsRequest {
 | 
			
		||||
  option (id) = 28;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
  LogLevel level = 1;
 | 
			
		||||
  bool dump_config = 2;
 | 
			
		||||
}
 | 
			
		||||
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;
 | 
			
		||||
  bool send_failed = 4;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ==================== HOMEASSISTANT.SERVICE ====================
 | 
			
		||||
message SubscribeHomeassistantServicesRequest {
 | 
			
		||||
  option (id) = 34;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message HomeassistantServiceMap {
 | 
			
		||||
  string key = 1;
 | 
			
		||||
  string value = 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message HomeassistantServiceResponse {
 | 
			
		||||
  option (id) = 35;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (no_delay) = true;
 | 
			
		||||
 | 
			
		||||
  string service = 1;
 | 
			
		||||
  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.
 | 
			
		||||
message SubscribeHomeAssistantStatesRequest {
 | 
			
		||||
  option (id) = 38;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message SubscribeHomeAssistantStateResponse {
 | 
			
		||||
  option (id) = 39;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  string entity_id = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message HomeAssistantStateResponse {
 | 
			
		||||
  option (id) = 40;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
  option (no_delay) = true;
 | 
			
		||||
 | 
			
		||||
  string entity_id = 1;
 | 
			
		||||
  string state = 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ==================== IMPORT TIME ====================
 | 
			
		||||
message GetTimeRequest {
 | 
			
		||||
  option (id) = 36;
 | 
			
		||||
  option (source) = SOURCE_BOTH;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
  ServiceArgType type = 2;
 | 
			
		||||
}
 | 
			
		||||
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 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;
 | 
			
		||||
}
 | 
			
		||||
message ExecuteServiceRequest {
 | 
			
		||||
  option (id) = 42;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
  option (no_delay) = true;
 | 
			
		||||
 | 
			
		||||
  fixed32 key = 1;
 | 
			
		||||
  repeated ExecuteServiceArgument args = 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ==================== CAMERA ====================
 | 
			
		||||
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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message CameraImageResponse {
 | 
			
		||||
  option (id) = 44;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_ESP32_CAMERA";
 | 
			
		||||
 | 
			
		||||
  fixed32 key = 1;
 | 
			
		||||
  bytes data = 2;
 | 
			
		||||
  bool done = 3;
 | 
			
		||||
}
 | 
			
		||||
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 {
 | 
			
		||||
  CLIMATE_MODE_OFF = 0;
 | 
			
		||||
  CLIMATE_MODE_AUTO = 1;
 | 
			
		||||
  CLIMATE_MODE_COOL = 2;
 | 
			
		||||
  CLIMATE_MODE_HEAT = 3;
 | 
			
		||||
}
 | 
			
		||||
enum ClimateAction {
 | 
			
		||||
  CLIMATE_ACTION_OFF = 0;
 | 
			
		||||
  // values same as mode for readability
 | 
			
		||||
  CLIMATE_ACTION_COOLING = 2;
 | 
			
		||||
  CLIMATE_ACTION_HEATING = 3;
 | 
			
		||||
}
 | 
			
		||||
message ListEntitiesClimateResponse {
 | 
			
		||||
  option (id) = 46;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_CLIMATE";
 | 
			
		||||
 | 
			
		||||
  string object_id = 1;
 | 
			
		||||
  fixed32 key = 2;
 | 
			
		||||
  string name = 3;
 | 
			
		||||
  string unique_id = 4;
 | 
			
		||||
 | 
			
		||||
  bool supports_current_temperature = 5;
 | 
			
		||||
  bool supports_two_point_target_temperature = 6;
 | 
			
		||||
  repeated ClimateMode supported_modes = 7;
 | 
			
		||||
  float visual_min_temperature = 8;
 | 
			
		||||
  float visual_max_temperature = 9;
 | 
			
		||||
  float visual_temperature_step = 10;
 | 
			
		||||
  bool supports_away = 11;
 | 
			
		||||
  bool supports_action = 12;
 | 
			
		||||
}
 | 
			
		||||
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;
 | 
			
		||||
  float target_temperature = 4;
 | 
			
		||||
  float target_temperature_low = 5;
 | 
			
		||||
  float target_temperature_high = 6;
 | 
			
		||||
  bool away = 7;
 | 
			
		||||
  ClimateAction action = 8;
 | 
			
		||||
}
 | 
			
		||||
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;
 | 
			
		||||
  bool has_target_temperature = 4;
 | 
			
		||||
  float target_temperature = 5;
 | 
			
		||||
  bool has_target_temperature_low = 6;
 | 
			
		||||
  float target_temperature_low = 7;
 | 
			
		||||
  bool has_target_temperature_high = 8;
 | 
			
		||||
  float target_temperature_high = 9;
 | 
			
		||||
  bool has_away = 10;
 | 
			
		||||
  bool away = 11;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										678
									
								
								esphome/components/api/api_connection.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										678
									
								
								esphome/components/api/api_connection.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,678 @@
 | 
			
		||||
#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);
 | 
			
		||||
      this->set_nodelay(false);
 | 
			
		||||
      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);
 | 
			
		||||
  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();
 | 
			
		||||
  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));
 | 
			
		||||
  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;
 | 
			
		||||
  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}) {
 | 
			
		||||
    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();
 | 
			
		||||
  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);
 | 
			
		||||
  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;
 | 
			
		||||
 | 
			
		||||
  this->set_nodelay(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());
 | 
			
		||||
  this->client_->add(reinterpret_cast<char *>(buffer.get_buffer()->data()), buffer.get_buffer()->size());
 | 
			
		||||
  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
 | 
			
		||||
							
								
								
									
										178
									
								
								esphome/components/api/api_connection.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										178
									
								
								esphome/components/api/api_connection.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,178 @@
 | 
			
		||||
#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_();
 | 
			
		||||
  void set_nodelay(bool nodelay) override {
 | 
			
		||||
    if (nodelay == this->current_nodelay_)
 | 
			
		||||
      return;
 | 
			
		||||
    this->client_->setNoDelay(nodelay);
 | 
			
		||||
    this->current_nodelay_ = nodelay;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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
 | 
			
		||||
							
								
								
									
										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];
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										2797
									
								
								esphome/components/api/api_pb2.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2797
									
								
								esphome/components/api/api_pb2.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										705
									
								
								esphome/components/api/api_pb2.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										705
									
								
								esphome/components/api/api_pb2.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,705 @@
 | 
			
		||||
#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 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,
 | 
			
		||||
};
 | 
			
		||||
enum ClimateAction : uint32_t {
 | 
			
		||||
  CLIMATE_ACTION_OFF = 0,
 | 
			
		||||
  CLIMATE_ACTION_COOLING = 2,
 | 
			
		||||
  CLIMATE_ACTION_HEATING = 3,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // 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
 | 
			
		||||
  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
 | 
			
		||||
  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
 | 
			
		||||
  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
 | 
			
		||||
  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
 | 
			
		||||
  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
 | 
			
		||||
  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
 | 
			
		||||
							
								
								
									
										582
									
								
								esphome/components/api/api_pb2_service.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										582
									
								
								esphome/components/api/api_pb2_service.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,582 @@
 | 
			
		||||
#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());
 | 
			
		||||
  this->set_nodelay(true);
 | 
			
		||||
  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());
 | 
			
		||||
  this->set_nodelay(true);
 | 
			
		||||
  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());
 | 
			
		||||
  this->set_nodelay(true);
 | 
			
		||||
  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());
 | 
			
		||||
  this->set_nodelay(true);
 | 
			
		||||
  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());
 | 
			
		||||
  this->set_nodelay(false);
 | 
			
		||||
  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());
 | 
			
		||||
  this->set_nodelay(false);
 | 
			
		||||
  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());
 | 
			
		||||
  this->set_nodelay(false);
 | 
			
		||||
  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());
 | 
			
		||||
  this->set_nodelay(true);
 | 
			
		||||
  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());
 | 
			
		||||
  this->set_nodelay(false);
 | 
			
		||||
  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());
 | 
			
		||||
  this->set_nodelay(true);
 | 
			
		||||
  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());
 | 
			
		||||
  this->set_nodelay(false);
 | 
			
		||||
  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());
 | 
			
		||||
  this->set_nodelay(true);
 | 
			
		||||
  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());
 | 
			
		||||
  this->set_nodelay(false);
 | 
			
		||||
  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());
 | 
			
		||||
  this->set_nodelay(true);
 | 
			
		||||
  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());
 | 
			
		||||
  this->set_nodelay(false);
 | 
			
		||||
  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());
 | 
			
		||||
  this->set_nodelay(true);
 | 
			
		||||
  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());
 | 
			
		||||
  this->set_nodelay(false);
 | 
			
		||||
  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());
 | 
			
		||||
  this->set_nodelay(true);
 | 
			
		||||
  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());
 | 
			
		||||
  this->set_nodelay(false);
 | 
			
		||||
  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());
 | 
			
		||||
  this->set_nodelay(true);
 | 
			
		||||
  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());
 | 
			
		||||
  this->set_nodelay(false);
 | 
			
		||||
  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());
 | 
			
		||||
  this->set_nodelay(true);
 | 
			
		||||
  return this->send_message_<TextSensorStateResponse>(msg, 27);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
bool APIServerConnectionBase::send_subscribe_logs_response(const SubscribeLogsResponse &msg) {
 | 
			
		||||
  this->set_nodelay(false);
 | 
			
		||||
  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());
 | 
			
		||||
  this->set_nodelay(true);
 | 
			
		||||
  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());
 | 
			
		||||
  this->set_nodelay(false);
 | 
			
		||||
  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());
 | 
			
		||||
  this->set_nodelay(false);
 | 
			
		||||
  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());
 | 
			
		||||
  this->set_nodelay(true);
 | 
			
		||||
  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());
 | 
			
		||||
  this->set_nodelay(false);
 | 
			
		||||
  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());
 | 
			
		||||
  this->set_nodelay(false);
 | 
			
		||||
  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());
 | 
			
		||||
  this->set_nodelay(false);
 | 
			
		||||
  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());
 | 
			
		||||
  this->set_nodelay(false);
 | 
			
		||||
  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());
 | 
			
		||||
  this->set_nodelay(true);
 | 
			
		||||
  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
 | 
			
		||||
							
								
								
									
										183
									
								
								esphome/components/api/api_pb2_service.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										183
									
								
								esphome/components/api/api_pb2_service.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,183 @@
 | 
			
		||||
#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
 | 
			
		||||
							
								
								
									
										239
									
								
								esphome/components/api/api_server.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										239
									
								
								esphome/components/api/api_server.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,239 @@
 | 
			
		||||
#include "api_server.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_LOGGER
 | 
			
		||||
#include "esphome/components/logger/logger.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
static const char *TAG = "api";
 | 
			
		||||
 | 
			
		||||
// APIServer
 | 
			
		||||
void APIServer::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up Home Assistant API server...");
 | 
			
		||||
  this->setup_controller();
 | 
			
		||||
  this->server_ = AsyncServer(this->port_);
 | 
			
		||||
  this->server_.setNoDelay(false);
 | 
			
		||||
  this->server_.begin();
 | 
			
		||||
  this->server_.onClient(
 | 
			
		||||
      [](void *s, AsyncClient *client) {
 | 
			
		||||
        if (client == nullptr)
 | 
			
		||||
          return;
 | 
			
		||||
 | 
			
		||||
        // can't print here because in lwIP thread
 | 
			
		||||
        // ESP_LOGD(TAG, "New client connected from %s", client->remoteIP().toString().c_str());
 | 
			
		||||
        auto *a_this = (APIServer *) s;
 | 
			
		||||
        a_this->clients_.push_back(new APIConnection(client, a_this));
 | 
			
		||||
      },
 | 
			
		||||
      this);
 | 
			
		||||
#ifdef USE_LOGGER
 | 
			
		||||
  if (logger::global_logger != nullptr) {
 | 
			
		||||
    logger::global_logger->add_on_log_callback([this](int level, const char *tag, const char *message) {
 | 
			
		||||
      for (auto *c : this->clients_) {
 | 
			
		||||
        if (!c->remove_)
 | 
			
		||||
          c->send_log_message(level, tag, message);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  this->last_connected_ = millis();
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
  if (esp32_camera::global_esp32_camera != nullptr) {
 | 
			
		||||
    esp32_camera::global_esp32_camera->add_image_callback([this](std::shared_ptr<esp32_camera::CameraImage> image) {
 | 
			
		||||
      for (auto *c : this->clients_)
 | 
			
		||||
        if (!c->remove_)
 | 
			
		||||
          c->send_camera_state(image);
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
void APIServer::loop() {
 | 
			
		||||
  // Partition clients into remove and active
 | 
			
		||||
  auto new_end =
 | 
			
		||||
      std::partition(this->clients_.begin(), this->clients_.end(), [](APIConnection *conn) { return !conn->remove_; });
 | 
			
		||||
  // print disconnection messages
 | 
			
		||||
  for (auto it = new_end; it != this->clients_.end(); ++it) {
 | 
			
		||||
    ESP_LOGD(TAG, "Disconnecting %s", (*it)->client_info_.c_str());
 | 
			
		||||
  }
 | 
			
		||||
  // only then delete the pointers, otherwise log routine
 | 
			
		||||
  // would access freed memory
 | 
			
		||||
  for (auto it = new_end; it != this->clients_.end(); ++it)
 | 
			
		||||
    delete *it;
 | 
			
		||||
  // resize vector
 | 
			
		||||
  this->clients_.erase(new_end, this->clients_.end());
 | 
			
		||||
 | 
			
		||||
  for (auto *client : this->clients_) {
 | 
			
		||||
    client->loop();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (this->reboot_timeout_ != 0) {
 | 
			
		||||
    const uint32_t now = millis();
 | 
			
		||||
    if (!this->is_connected()) {
 | 
			
		||||
      if (now - this->last_connected_ > this->reboot_timeout_) {
 | 
			
		||||
        ESP_LOGE(TAG, "No client connected to API. Rebooting...");
 | 
			
		||||
        App.reboot();
 | 
			
		||||
      }
 | 
			
		||||
      this->status_set_warning();
 | 
			
		||||
    } else {
 | 
			
		||||
      this->last_connected_ = now;
 | 
			
		||||
      this->status_clear_warning();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void APIServer::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "API Server:");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Address: %s:%u", network_get_address().c_str(), this->port_);
 | 
			
		||||
}
 | 
			
		||||
bool APIServer::uses_password() const { return !this->password_.empty(); }
 | 
			
		||||
bool APIServer::check_password(const std::string &password) const {
 | 
			
		||||
  // depend only on input password length
 | 
			
		||||
  const char *a = this->password_.c_str();
 | 
			
		||||
  uint32_t len_a = this->password_.length();
 | 
			
		||||
  const char *b = password.c_str();
 | 
			
		||||
  uint32_t len_b = password.length();
 | 
			
		||||
 | 
			
		||||
  // disable optimization with volatile
 | 
			
		||||
  volatile uint32_t length = len_b;
 | 
			
		||||
  volatile const char *left = nullptr;
 | 
			
		||||
  volatile const char *right = b;
 | 
			
		||||
  uint8_t result = 0;
 | 
			
		||||
 | 
			
		||||
  if (len_a == length) {
 | 
			
		||||
    left = *((volatile const char **) &a);
 | 
			
		||||
    result = 0;
 | 
			
		||||
  }
 | 
			
		||||
  if (len_a != length) {
 | 
			
		||||
    left = b;
 | 
			
		||||
    result = 1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for (size_t i = 0; i < length; i++) {
 | 
			
		||||
    result |= *left++ ^ *right++;  // NOLINT
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return result == 0;
 | 
			
		||||
}
 | 
			
		||||
void APIServer::handle_disconnect(APIConnection *conn) {}
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
void APIServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state) {
 | 
			
		||||
  if (obj->is_internal())
 | 
			
		||||
    return;
 | 
			
		||||
  for (auto *c : this->clients_)
 | 
			
		||||
    c->send_binary_sensor_state(obj, state);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_COVER
 | 
			
		||||
void APIServer::on_cover_update(cover::Cover *obj) {
 | 
			
		||||
  if (obj->is_internal())
 | 
			
		||||
    return;
 | 
			
		||||
  for (auto *c : this->clients_)
 | 
			
		||||
    c->send_cover_state(obj);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_FAN
 | 
			
		||||
void APIServer::on_fan_update(fan::FanState *obj) {
 | 
			
		||||
  if (obj->is_internal())
 | 
			
		||||
    return;
 | 
			
		||||
  for (auto *c : this->clients_)
 | 
			
		||||
    c->send_fan_state(obj);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_LIGHT
 | 
			
		||||
void APIServer::on_light_update(light::LightState *obj) {
 | 
			
		||||
  if (obj->is_internal())
 | 
			
		||||
    return;
 | 
			
		||||
  for (auto *c : this->clients_)
 | 
			
		||||
    c->send_light_state(obj);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
void APIServer::on_sensor_update(sensor::Sensor *obj, float state) {
 | 
			
		||||
  if (obj->is_internal())
 | 
			
		||||
    return;
 | 
			
		||||
  for (auto *c : this->clients_)
 | 
			
		||||
    c->send_sensor_state(obj, state);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
void APIServer::on_switch_update(switch_::Switch *obj, bool state) {
 | 
			
		||||
  if (obj->is_internal())
 | 
			
		||||
    return;
 | 
			
		||||
  for (auto *c : this->clients_)
 | 
			
		||||
    c->send_switch_state(obj, state);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_TEXT_SENSOR
 | 
			
		||||
void APIServer::on_text_sensor_update(text_sensor::TextSensor *obj, std::string state) {
 | 
			
		||||
  if (obj->is_internal())
 | 
			
		||||
    return;
 | 
			
		||||
  for (auto *c : this->clients_)
 | 
			
		||||
    c->send_text_sensor_state(obj, state);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_CLIMATE
 | 
			
		||||
void APIServer::on_climate_update(climate::Climate *obj) {
 | 
			
		||||
  if (obj->is_internal())
 | 
			
		||||
    return;
 | 
			
		||||
  for (auto *c : this->clients_)
 | 
			
		||||
    c->send_climate_state(obj);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
float APIServer::get_setup_priority() const { return setup_priority::AFTER_WIFI; }
 | 
			
		||||
void APIServer::set_port(uint16_t port) { this->port_ = port; }
 | 
			
		||||
APIServer *global_api_server = nullptr;
 | 
			
		||||
 | 
			
		||||
void APIServer::set_password(const std::string &password) { this->password_ = password; }
 | 
			
		||||
void APIServer::send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
 | 
			
		||||
  for (auto *client : this->clients_) {
 | 
			
		||||
    client->send_homeassistant_service_call(call);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
APIServer::APIServer() { global_api_server = this; }
 | 
			
		||||
void APIServer::subscribe_home_assistant_state(std::string entity_id, std::function<void(std::string)> f) {
 | 
			
		||||
  this->state_subs_.push_back(HomeAssistantStateSubscription{
 | 
			
		||||
      .entity_id = std::move(entity_id),
 | 
			
		||||
      .callback = std::move(f),
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
const std::vector<APIServer::HomeAssistantStateSubscription> &APIServer::get_state_subs() const {
 | 
			
		||||
  return this->state_subs_;
 | 
			
		||||
}
 | 
			
		||||
uint16_t APIServer::get_port() const { return this->port_; }
 | 
			
		||||
void APIServer::set_reboot_timeout(uint32_t reboot_timeout) { this->reboot_timeout_ = reboot_timeout; }
 | 
			
		||||
#ifdef USE_HOMEASSISTANT_TIME
 | 
			
		||||
void APIServer::request_time() {
 | 
			
		||||
  for (auto *client : this->clients_) {
 | 
			
		||||
    if (!client->remove_ && client->connection_state_ == APIConnection::ConnectionState::CONNECTED)
 | 
			
		||||
      client->send_time_request();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
bool APIServer::is_connected() const { return !this->clients_.empty(); }
 | 
			
		||||
void APIServer::on_shutdown() {
 | 
			
		||||
  for (auto *c : this->clients_) {
 | 
			
		||||
    c->send_disconnect_request(DisconnectRequest());
 | 
			
		||||
  }
 | 
			
		||||
  delay(10);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										100
									
								
								esphome/components/api/api_server.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								esphome/components/api/api_server.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,100 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#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 "list_entities.h"
 | 
			
		||||
#include "subscribe_state.h"
 | 
			
		||||
#include "homeassistant_service.h"
 | 
			
		||||
#include "user_services.h"
 | 
			
		||||
 | 
			
		||||
#ifdef ARDUINO_ARCH_ESP32
 | 
			
		||||
#include <AsyncTCP.h>
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef ARDUINO_ARCH_ESP8266
 | 
			
		||||
#include <ESPAsyncTCP.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
class APIServer : public Component, public Controller {
 | 
			
		||||
 public:
 | 
			
		||||
  APIServer();
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  uint16_t get_port() const;
 | 
			
		||||
  float get_setup_priority() const override;
 | 
			
		||||
  void loop() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
  void on_shutdown() override;
 | 
			
		||||
  bool check_password(const std::string &password) const;
 | 
			
		||||
  bool uses_password() const;
 | 
			
		||||
  void set_port(uint16_t port);
 | 
			
		||||
  void set_password(const std::string &password);
 | 
			
		||||
  void set_reboot_timeout(uint32_t reboot_timeout);
 | 
			
		||||
  void handle_disconnect(APIConnection *conn);
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
  void on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_COVER
 | 
			
		||||
  void on_cover_update(cover::Cover *obj) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_FAN
 | 
			
		||||
  void on_fan_update(fan::FanState *obj) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LIGHT
 | 
			
		||||
  void on_light_update(light::LightState *obj) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
  void on_sensor_update(sensor::Sensor *obj, float state) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
  void on_switch_update(switch_::Switch *obj, bool state) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_TEXT_SENSOR
 | 
			
		||||
  void on_text_sensor_update(text_sensor::TextSensor *obj, std::string state) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_CLIMATE
 | 
			
		||||
  void on_climate_update(climate::Climate *obj) override;
 | 
			
		||||
#endif
 | 
			
		||||
  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();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  bool is_connected() const;
 | 
			
		||||
 | 
			
		||||
  struct HomeAssistantStateSubscription {
 | 
			
		||||
    std::string entity_id;
 | 
			
		||||
    std::function<void(std::string)> callback;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  void subscribe_home_assistant_state(std::string entity_id, std::function<void(std::string)> f);
 | 
			
		||||
  const std::vector<HomeAssistantStateSubscription> &get_state_subs() const;
 | 
			
		||||
  const std::vector<UserServiceDescriptor *> &get_user_services() const { return this->user_services_; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  AsyncServer server_{0};
 | 
			
		||||
  uint16_t port_{6053};
 | 
			
		||||
  uint32_t reboot_timeout_{300000};
 | 
			
		||||
  uint32_t last_connected_{0};
 | 
			
		||||
  std::vector<APIConnection *> clients_;
 | 
			
		||||
  std::string password_;
 | 
			
		||||
  std::vector<HomeAssistantStateSubscription> state_subs_;
 | 
			
		||||
  std::vector<UserServiceDescriptor *> user_services_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern APIServer *global_api_server;
 | 
			
		||||
 | 
			
		||||
template<typename... Ts> class APIConnectedCondition : public Condition<Ts...> {
 | 
			
		||||
 public:
 | 
			
		||||
  bool check(Ts... x) override { return global_api_server->is_connected(); }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // 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
 | 
			
		||||
							
								
								
									
										66
									
								
								esphome/components/api/homeassistant_service.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								esphome/components/api/homeassistant_service.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,66 @@
 | 
			
		||||
#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
 | 
			
		||||
							
								
								
									
										55
									
								
								esphome/components/api/list_entities.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								esphome/components/api/list_entities.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
			
		||||
#include "list_entities.h"
 | 
			
		||||
#include "esphome/core/util.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/core/application.h"
 | 
			
		||||
#include "api_connection.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
bool ListEntitiesIterator::on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) {
 | 
			
		||||
  return this->client_->send_binary_sensor_info(binary_sensor);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_COVER
 | 
			
		||||
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) { return this->client_->send_fan_info(fan); }
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LIGHT
 | 
			
		||||
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) { return this->client_->send_sensor_info(sensor); }
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
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) {
 | 
			
		||||
  return this->client_->send_text_sensor_info(text_sensor);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
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 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) {
 | 
			
		||||
  return this->client_->send_camera_info(camera);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_CLIMATE
 | 
			
		||||
bool ListEntitiesIterator::on_climate(climate::Climate *climate) { return this->client_->send_climate_info(climate); }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										52
									
								
								esphome/components/api/list_entities.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								esphome/components/api/list_entities.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,52 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#include "util.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
class APIConnection;
 | 
			
		||||
 | 
			
		||||
class ListEntitiesIterator : public ComponentIterator {
 | 
			
		||||
 public:
 | 
			
		||||
  ListEntitiesIterator(APIServer *server, APIConnection *client);
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
  bool on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_COVER
 | 
			
		||||
  bool on_cover(cover::Cover *cover) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_FAN
 | 
			
		||||
  bool on_fan(fan::FanState *fan) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LIGHT
 | 
			
		||||
  bool on_light(light::LightState *light) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
  bool on_sensor(sensor::Sensor *sensor) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
  bool on_switch(switch_::Switch *a_switch) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_TEXT_SENSOR
 | 
			
		||||
  bool on_text_sensor(text_sensor::TextSensor *text_sensor) override;
 | 
			
		||||
#endif
 | 
			
		||||
  bool on_service(UserServiceDescriptor *service) override;
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
  bool on_camera(esp32_camera::ESP32Camera *camera) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_CLIMATE
 | 
			
		||||
  bool on_climate(climate::Climate *climate) override;
 | 
			
		||||
#endif
 | 
			
		||||
  bool on_end() override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  APIConnection *client_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 | 
			
		||||
#include "api_server.h"
 | 
			
		||||
							
								
								
									
										91
									
								
								esphome/components/api/proto.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								esphome/components/api/proto.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,91 @@
 | 
			
		||||
#include "proto.h"
 | 
			
		||||
#include "util.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
static const char *TAG = "api.proto";
 | 
			
		||||
 | 
			
		||||
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 = 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->as_uint32()) & 0b111;
 | 
			
		||||
    uint32_t field_id = (res->as_uint32()) >> 3;
 | 
			
		||||
    i += consumed;
 | 
			
		||||
 | 
			
		||||
    switch (field_type) {
 | 
			
		||||
      case 0: {  // VarInt
 | 
			
		||||
        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->as_uint32());
 | 
			
		||||
        }
 | 
			
		||||
        i += consumed;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      case 2: {  // Length-delimited
 | 
			
		||||
        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 (field_length > length - i) {
 | 
			
		||||
          ESP_LOGV(TAG, "Out-of-bounds Length Delimited at %u", i);
 | 
			
		||||
          error = true;
 | 
			
		||||
          break;
 | 
			
		||||
        }
 | 
			
		||||
        if (!this->decode_length(field_id, ProtoLengthDelimited(&buffer[i], field_length))) {
 | 
			
		||||
          ESP_LOGV(TAG, "Cannot decode Length Delimited field %u!", field_id);
 | 
			
		||||
        }
 | 
			
		||||
        i += field_length;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      case 5: {  // 32-bit
 | 
			
		||||
        if (length - i < 4) {
 | 
			
		||||
          ESP_LOGV(TAG, "Out-of-bounds Fixed32-bit at %u", i);
 | 
			
		||||
          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, Proto32Bit(val))) {
 | 
			
		||||
          ESP_LOGV(TAG, "Cannot decode 32-bit field %u with value %u!", field_id, val);
 | 
			
		||||
        }
 | 
			
		||||
        i += 4;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      default:
 | 
			
		||||
        ESP_LOGV(TAG, "Invalid field type at %u", i);
 | 
			
		||||
        error = true;
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    if (error) {
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string ProtoMessage::dump() const {
 | 
			
		||||
  std::string out;
 | 
			
		||||
  this->dump_to(out);
 | 
			
		||||
  return out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										279
									
								
								esphome/components/api/proto.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										279
									
								
								esphome/components/api/proto.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,279 @@
 | 
			
		||||
#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;
 | 
			
		||||
  virtual void set_nodelay(bool nodelay) = 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
 | 
			
		||||
							
								
								
									
										44
									
								
								esphome/components/api/subscribe_state.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								esphome/components/api/subscribe_state.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
			
		||||
#include "subscribe_state.h"
 | 
			
		||||
#include "api_connection.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
bool InitialStateIterator::on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) {
 | 
			
		||||
  return this->client_->send_binary_sensor_state(binary_sensor, binary_sensor->state);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_COVER
 | 
			
		||||
bool InitialStateIterator::on_cover(cover::Cover *cover) { return this->client_->send_cover_state(cover); }
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_FAN
 | 
			
		||||
bool InitialStateIterator::on_fan(fan::FanState *fan) { return this->client_->send_fan_state(fan); }
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LIGHT
 | 
			
		||||
bool InitialStateIterator::on_light(light::LightState *light) { return this->client_->send_light_state(light); }
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
bool InitialStateIterator::on_sensor(sensor::Sensor *sensor) {
 | 
			
		||||
  return this->client_->send_sensor_state(sensor, sensor->state);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
bool InitialStateIterator::on_switch(switch_::Switch *a_switch) {
 | 
			
		||||
  return this->client_->send_switch_state(a_switch, a_switch->state);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_TEXT_SENSOR
 | 
			
		||||
bool InitialStateIterator::on_text_sensor(text_sensor::TextSensor *text_sensor) {
 | 
			
		||||
  return this->client_->send_text_sensor_state(text_sensor, text_sensor->state);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_CLIMATE
 | 
			
		||||
bool InitialStateIterator::on_climate(climate::Climate *climate) { return this->client_->send_climate_state(climate); }
 | 
			
		||||
#endif
 | 
			
		||||
InitialStateIterator::InitialStateIterator(APIServer *server, APIConnection *client)
 | 
			
		||||
    : ComponentIterator(server), client_(client) {}
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										47
									
								
								esphome/components/api/subscribe_state.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								esphome/components/api/subscribe_state.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,47 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/controller.h"
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#include "util.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
class APIConnection;
 | 
			
		||||
 | 
			
		||||
class InitialStateIterator : public ComponentIterator {
 | 
			
		||||
 public:
 | 
			
		||||
  InitialStateIterator(APIServer *server, APIConnection *client);
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
  bool on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_COVER
 | 
			
		||||
  bool on_cover(cover::Cover *cover) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_FAN
 | 
			
		||||
  bool on_fan(fan::FanState *fan) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LIGHT
 | 
			
		||||
  bool on_light(light::LightState *light) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
  bool on_sensor(sensor::Sensor *sensor) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
  bool on_switch(switch_::Switch *a_switch) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_TEXT_SENSOR
 | 
			
		||||
  bool on_text_sensor(text_sensor::TextSensor *text_sensor) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_CLIMATE
 | 
			
		||||
  bool on_climate(climate::Climate *climate) override;
 | 
			
		||||
#endif
 | 
			
		||||
 protected:
 | 
			
		||||
  APIConnection *client_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 | 
			
		||||
#include "api_server.h"
 | 
			
		||||
							
								
								
									
										42
									
								
								esphome/components/api/user_services.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								esphome/components/api/user_services.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
			
		||||
#include "user_services.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
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_;
 | 
			
		||||
}
 | 
			
		||||
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;
 | 
			
		||||
}
 | 
			
		||||
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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
}
 | 
			
		||||
template<> enums::ServiceArgType to_service_arg_type<std::vector<std::string>>() {
 | 
			
		||||
  return enums::SERVICE_ARG_TYPE_STRING_ARRAY;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										72
									
								
								esphome/components/api/user_services.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								esphome/components/api/user_services.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,72 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/automation.h"
 | 
			
		||||
#include "api_pb2.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
class UserServiceDescriptor {
 | 
			
		||||
 public:
 | 
			
		||||
  virtual ListEntitiesServicesResponse encode_list_service_response() = 0;
 | 
			
		||||
 | 
			
		||||
  virtual bool execute_service(const ExecuteServiceRequest &req) = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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:
 | 
			
		||||
  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_);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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 {
 | 
			
		||||
    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:
 | 
			
		||||
  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<std::string, sizeof...(Ts)> arg_names_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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) {}
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  void execute(Ts... x) override { this->trigger(x...); }  // NOLINT
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										193
									
								
								esphome/components/api/util.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										193
									
								
								esphome/components/api/util.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,193 @@
 | 
			
		||||
#include "util.h"
 | 
			
		||||
#include "api_server.h"
 | 
			
		||||
#include "user_services.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/core/application.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
ComponentIterator::ComponentIterator(APIServer *server) : server_(server) {}
 | 
			
		||||
void ComponentIterator::begin() {
 | 
			
		||||
  this->state_ = IteratorState::BEGIN;
 | 
			
		||||
  this->at_ = 0;
 | 
			
		||||
}
 | 
			
		||||
void ComponentIterator::advance() {
 | 
			
		||||
  bool advance_platform = false;
 | 
			
		||||
  bool success = true;
 | 
			
		||||
  switch (this->state_) {
 | 
			
		||||
    case IteratorState::NONE:
 | 
			
		||||
      // not started
 | 
			
		||||
      return;
 | 
			
		||||
    case IteratorState::BEGIN:
 | 
			
		||||
      if (this->on_begin()) {
 | 
			
		||||
        advance_platform = true;
 | 
			
		||||
      } else {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
    case IteratorState::BINARY_SENSOR:
 | 
			
		||||
      if (this->at_ >= App.get_binary_sensors().size()) {
 | 
			
		||||
        advance_platform = true;
 | 
			
		||||
      } else {
 | 
			
		||||
        auto *binary_sensor = App.get_binary_sensors()[this->at_];
 | 
			
		||||
        if (binary_sensor->is_internal()) {
 | 
			
		||||
          success = true;
 | 
			
		||||
          break;
 | 
			
		||||
        } else {
 | 
			
		||||
          success = this->on_binary_sensor(binary_sensor);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_COVER
 | 
			
		||||
    case IteratorState::COVER:
 | 
			
		||||
      if (this->at_ >= App.get_covers().size()) {
 | 
			
		||||
        advance_platform = true;
 | 
			
		||||
      } else {
 | 
			
		||||
        auto *cover = App.get_covers()[this->at_];
 | 
			
		||||
        if (cover->is_internal()) {
 | 
			
		||||
          success = true;
 | 
			
		||||
          break;
 | 
			
		||||
        } else {
 | 
			
		||||
          success = this->on_cover(cover);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_FAN
 | 
			
		||||
    case IteratorState::FAN:
 | 
			
		||||
      if (this->at_ >= App.get_fans().size()) {
 | 
			
		||||
        advance_platform = true;
 | 
			
		||||
      } else {
 | 
			
		||||
        auto *fan = App.get_fans()[this->at_];
 | 
			
		||||
        if (fan->is_internal()) {
 | 
			
		||||
          success = true;
 | 
			
		||||
          break;
 | 
			
		||||
        } else {
 | 
			
		||||
          success = this->on_fan(fan);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LIGHT
 | 
			
		||||
    case IteratorState::LIGHT:
 | 
			
		||||
      if (this->at_ >= App.get_lights().size()) {
 | 
			
		||||
        advance_platform = true;
 | 
			
		||||
      } else {
 | 
			
		||||
        auto *light = App.get_lights()[this->at_];
 | 
			
		||||
        if (light->is_internal()) {
 | 
			
		||||
          success = true;
 | 
			
		||||
          break;
 | 
			
		||||
        } else {
 | 
			
		||||
          success = this->on_light(light);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
    case IteratorState::SENSOR:
 | 
			
		||||
      if (this->at_ >= App.get_sensors().size()) {
 | 
			
		||||
        advance_platform = true;
 | 
			
		||||
      } else {
 | 
			
		||||
        auto *sensor = App.get_sensors()[this->at_];
 | 
			
		||||
        if (sensor->is_internal()) {
 | 
			
		||||
          success = true;
 | 
			
		||||
          break;
 | 
			
		||||
        } else {
 | 
			
		||||
          success = this->on_sensor(sensor);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
    case IteratorState::SWITCH:
 | 
			
		||||
      if (this->at_ >= App.get_switches().size()) {
 | 
			
		||||
        advance_platform = true;
 | 
			
		||||
      } else {
 | 
			
		||||
        auto *a_switch = App.get_switches()[this->at_];
 | 
			
		||||
        if (a_switch->is_internal()) {
 | 
			
		||||
          success = true;
 | 
			
		||||
          break;
 | 
			
		||||
        } else {
 | 
			
		||||
          success = this->on_switch(a_switch);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_TEXT_SENSOR
 | 
			
		||||
    case IteratorState::TEXT_SENSOR:
 | 
			
		||||
      if (this->at_ >= App.get_text_sensors().size()) {
 | 
			
		||||
        advance_platform = true;
 | 
			
		||||
      } else {
 | 
			
		||||
        auto *text_sensor = App.get_text_sensors()[this->at_];
 | 
			
		||||
        if (text_sensor->is_internal()) {
 | 
			
		||||
          success = true;
 | 
			
		||||
          break;
 | 
			
		||||
        } else {
 | 
			
		||||
          success = this->on_text_sensor(text_sensor);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
#endif
 | 
			
		||||
    case IteratorState ::SERVICE:
 | 
			
		||||
      if (this->at_ >= this->server_->get_user_services().size()) {
 | 
			
		||||
        advance_platform = true;
 | 
			
		||||
      } else {
 | 
			
		||||
        auto *service = this->server_->get_user_services()[this->at_];
 | 
			
		||||
        success = this->on_service(service);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
    case IteratorState::CAMERA:
 | 
			
		||||
      if (esp32_camera::global_esp32_camera == nullptr) {
 | 
			
		||||
        advance_platform = true;
 | 
			
		||||
      } else {
 | 
			
		||||
        if (esp32_camera::global_esp32_camera->is_internal()) {
 | 
			
		||||
          advance_platform = success = true;
 | 
			
		||||
          break;
 | 
			
		||||
        } else {
 | 
			
		||||
          advance_platform = success = this->on_camera(esp32_camera::global_esp32_camera);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_CLIMATE
 | 
			
		||||
    case IteratorState::CLIMATE:
 | 
			
		||||
      if (this->at_ >= App.get_climates().size()) {
 | 
			
		||||
        advance_platform = true;
 | 
			
		||||
      } else {
 | 
			
		||||
        auto *climate = App.get_climates()[this->at_];
 | 
			
		||||
        if (climate->is_internal()) {
 | 
			
		||||
          success = true;
 | 
			
		||||
          break;
 | 
			
		||||
        } else {
 | 
			
		||||
          success = this->on_climate(climate);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
#endif
 | 
			
		||||
    case IteratorState::MAX:
 | 
			
		||||
      if (this->on_end()) {
 | 
			
		||||
        this->state_ = IteratorState::NONE;
 | 
			
		||||
      }
 | 
			
		||||
      return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (advance_platform) {
 | 
			
		||||
    this->state_ = static_cast<IteratorState>(static_cast<uint32_t>(this->state_) + 1);
 | 
			
		||||
    this->at_ = 0;
 | 
			
		||||
  } else if (success) {
 | 
			
		||||
    this->at_++;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
bool ComponentIterator::on_end() { return true; }
 | 
			
		||||
bool ComponentIterator::on_begin() { return true; }
 | 
			
		||||
bool ComponentIterator::on_service(UserServiceDescriptor *service) { return true; }
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
bool ComponentIterator::on_camera(esp32_camera::ESP32Camera *camera) { return true; }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										93
									
								
								esphome/components/api/util.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								esphome/components/api/util.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,93 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/controller.h"
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
#include "esphome/components/esp32_camera/esp32_camera.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
class APIServer;
 | 
			
		||||
class UserServiceDescriptor;
 | 
			
		||||
 | 
			
		||||
class ComponentIterator {
 | 
			
		||||
 public:
 | 
			
		||||
  ComponentIterator(APIServer *server);
 | 
			
		||||
 | 
			
		||||
  void begin();
 | 
			
		||||
  void advance();
 | 
			
		||||
  virtual bool on_begin();
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
  virtual bool on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_COVER
 | 
			
		||||
  virtual bool on_cover(cover::Cover *cover) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_FAN
 | 
			
		||||
  virtual bool on_fan(fan::FanState *fan) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LIGHT
 | 
			
		||||
  virtual bool on_light(light::LightState *light) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
  virtual bool on_sensor(sensor::Sensor *sensor) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
  virtual bool on_switch(switch_::Switch *a_switch) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_TEXT_SENSOR
 | 
			
		||||
  virtual bool on_text_sensor(text_sensor::TextSensor *text_sensor) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
  virtual bool on_service(UserServiceDescriptor *service);
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
  virtual bool on_camera(esp32_camera::ESP32Camera *camera);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_CLIMATE
 | 
			
		||||
  virtual bool on_climate(climate::Climate *climate) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
  virtual bool on_end();
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  enum class IteratorState {
 | 
			
		||||
    NONE = 0,
 | 
			
		||||
    BEGIN,
 | 
			
		||||
#ifdef USE_BINARY_SENSOR
 | 
			
		||||
    BINARY_SENSOR,
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_COVER
 | 
			
		||||
    COVER,
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_FAN
 | 
			
		||||
    FAN,
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_LIGHT
 | 
			
		||||
    LIGHT,
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SENSOR
 | 
			
		||||
    SENSOR,
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
    SWITCH,
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_TEXT_SENSOR
 | 
			
		||||
    TEXT_SENSOR,
 | 
			
		||||
#endif
 | 
			
		||||
    SERVICE,
 | 
			
		||||
#ifdef USE_ESP32_CAMERA
 | 
			
		||||
    CAMERA,
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_CLIMATE
 | 
			
		||||
    CLIMATE,
 | 
			
		||||
#endif
 | 
			
		||||
    MAX,
 | 
			
		||||
  } state_{IteratorState::NONE};
 | 
			
		||||
  size_t at_{0};
 | 
			
		||||
 | 
			
		||||
  APIServer *server_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										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, 22, 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]))
 | 
			
		||||
							
								
								
									
										223
									
								
								esphome/components/as3935/as3935.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										223
									
								
								esphome/components/as3935/as3935.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,223 @@
 | 
			
		||||
#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_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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))
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user