mirror of
				https://github.com/esphome/esphome.git
				synced 2025-11-02 16:11:53 +00:00 
			
		
		
		
	Compare commits
	
		
			399 Commits
		
	
	
		
			improv_ser
			...
			2022.1.0
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					318b930e9f | ||
| 
						 | 
					9296a078a7 | ||
| 
						 | 
					5dc776e55f | ||
| 
						 | 
					72d60f30f7 | ||
| 
						 | 
					869743a742 | ||
| 
						 | 
					7b03e07908 | ||
| 
						 | 
					348f880e15 | ||
| 
						 | 
					ead597d0fb | ||
| 
						 | 
					afbf989715 | ||
| 
						 | 
					01b62a16c3 | ||
| 
						 | 
					c5eba04517 | ||
| 
						 | 
					282313ab52 | ||
| 
						 | 
					d274545e77 | ||
| 
						 | 
					d3fda37615 | ||
| 
						 | 
					cbe3092404 | ||
| 
						 | 
					6dfe3039d0 | ||
| 
						 | 
					d6009453df | ||
| 
						 | 
					c81323ef91 | ||
| 
						 | 
					961c27f1c2 | ||
| 
						 | 
					fe4a14e6cc | ||
| 
						 | 
					ee58ad1ac0 | ||
| 
						 | 
					c0ff899812 | ||
| 
						 | 
					d9c938de33 | ||
| 
						 | 
					56547b3d50 | ||
| 
						 | 
					5026bc7a78 | ||
| 
						 | 
					27364ee72c | ||
| 
						 | 
					ece71a0228 | ||
| 
						 | 
					073828235f | ||
| 
						 | 
					41bcc8c0f4 | ||
| 
						 | 
					a0ea2aae6e | ||
| 
						 | 
					f34b46a621 | ||
| 
						 | 
					7217a4f7a4 | ||
| 
						 | 
					6383eca54a | ||
| 
						 | 
					e55bd1e559 | ||
| 
						 | 
					9e8b701dea | ||
| 
						 | 
					a4431abea8 | ||
| 
						 | 
					5844c1767b | ||
| 
						 | 
					9a70bfa471 | ||
| 
						 | 
					b406c6403c | ||
| 
						 | 
					499625f266 | ||
| 
						 | 
					6b773553fc | ||
| 
						 | 
					15fe049a99 | ||
| 
						 | 
					e4555f6997 | ||
| 
						 | 
					470071e0b0 | ||
| 
						 | 
					ea1be8e7bf | ||
| 
						 | 
					84a830195f | ||
| 
						 | 
					e62c3e00c1 | ||
| 
						 | 
					07e790f900 | ||
| 
						 | 
					640142fc0c | ||
| 
						 | 
					5c339d4597 | ||
| 
						 | 
					a4931f5d78 | ||
| 
						 | 
					5e1e543b06 | ||
| 
						 | 
					df929f9445 | ||
| 
						 | 
					d8e719d1c4 | ||
| 
						 | 
					3067e482fc | ||
| 
						 | 
					ed5930e934 | ||
| 
						 | 
					ffea3597f4 | ||
| 
						 | 
					193d3e0206 | ||
| 
						 | 
					c8f4fbb7dd | ||
| 
						 | 
					c855bc31b4 | ||
| 
						 | 
					b924b179ab | ||
| 
						 | 
					3df0fee3de | ||
| 
						 | 
					b601560e81 | ||
| 
						 | 
					e5775cf812 | ||
| 
						 | 
					26dd1f8532 | ||
| 
						 | 
					5143a5b5c5 | ||
| 
						 | 
					15ce27992e | ||
| 
						 | 
					dbc2812022 | ||
| 
						 | 
					dce3713f12 | ||
| 
						 | 
					f849d45bb6 | ||
| 
						 | 
					8ad06fb9ea | ||
| 
						 | 
					9124d9d6e6 | ||
| 
						 | 
					45ebe51e4f | ||
| 
						 | 
					407661d56b | ||
| 
						 | 
					998d4229af | ||
| 
						 | 
					a02d2e2e11 | ||
| 
						 | 
					72fa68849f | ||
| 
						 | 
					33f17f75a0 | ||
| 
						 | 
					23edb18d7e | ||
| 
						 | 
					07ff3a853f | ||
| 
						 | 
					2cf36bdb46 | ||
| 
						 | 
					50848c2f4d | ||
| 
						 | 
					d32633b3c7 | ||
| 
						 | 
					b37739eec2 | ||
| 
						 | 
					28f87dc804 | ||
| 
						 | 
					41879e41e6 | ||
| 
						 | 
					fc0a6546a2 | ||
| 
						 | 
					ffd4280d6c | ||
| 
						 | 
					f859b346a6 | ||
| 
						 | 
					cb0677cafe | ||
| 
						 | 
					c6956527d1 | ||
| 
						 | 
					72c6bfaa50 | ||
| 
						 | 
					7927b5f624 | ||
| 
						 | 
					b7aad39daf | ||
| 
						 | 
					f48de6dd43 | ||
| 
						 | 
					79d73d8f8b | ||
| 
						 | 
					cc5947467f | ||
| 
						 | 
					e152f128c8 | ||
| 
						 | 
					99bd808ebe | ||
| 
						 | 
					beb5f3dc9d | ||
| 
						 | 
					f5c3b3446f | ||
| 
						 | 
					db3b955b0f | ||
| 
						 | 
					f431c7402f | ||
| 
						 | 
					5516f65971 | ||
| 
						 | 
					9471df0a1b | ||
| 
						 | 
					6d39f64be7 | ||
| 
						 | 
					4907e6f6d7 | ||
| 
						 | 
					1ccee86705 | ||
| 
						 | 
					542fb2175b | ||
| 
						 | 
					6ec9cfb044 | ||
| 
						 | 
					66e0ff8392 | ||
| 
						 | 
					1fb0a7109d | ||
| 
						 | 
					b89d0a9a73 | ||
| 
						 | 
					4bb779d9a5 | ||
| 
						 | 
					386a5b6362 | ||
| 
						 | 
					e32a999cd0 | ||
| 
						 | 
					192eb49589 | ||
| 
						 | 
					5d70ff702b | ||
| 
						 | 
					a7b05db2a1 | ||
| 
						 | 
					45e346cf1b | ||
| 
						 | 
					80e2bfada3 | ||
| 
						 | 
					16e7bd0388 | ||
| 
						 | 
					b3fb35783e | ||
| 
						 | 
					a79c6aa9e0 | ||
| 
						 | 
					4bb58b2de9 | ||
| 
						 | 
					4e10881331 | ||
| 
						 | 
					cec4a81e14 | ||
| 
						 | 
					da45923d05 | ||
| 
						 | 
					31a61b598b | ||
| 
						 | 
					9c0506592b | ||
| 
						 | 
					beeb0c7c5a | ||
| 
						 | 
					b2f05faee0 | ||
| 
						 | 
					bfbc6a4bad | ||
| 
						 | 
					8c9e0e552d | ||
| 
						 | 
					8aaf9fd83f | ||
| 
						 | 
					08057720b8 | ||
| 
						 | 
					bfaa648837 | ||
| 
						 | 
					d504daef91 | ||
| 
						 | 
					8375e1d64d | ||
| 
						 | 
					cf5193d3e5 | ||
| 
						 | 
					b8d3ef2f49 | ||
| 
						 | 
					3bf6320030 | ||
| 
						 | 
					708b928c73 | ||
| 
						 | 
					649366ff44 | ||
| 
						 | 
					e5c9e87fad | ||
| 
						 | 
					c490388e80 | ||
| 
						 | 
					24ec5a6e9d | ||
| 
						 | 
					f3d9d707b6 | ||
| 
						 | 
					090e10730c | ||
| 
						 | 
					fbc84861c7 | ||
| 
						 | 
					e763469af8 | ||
| 
						 | 
					6df1d5222d | ||
| 
						 | 
					58fb7a02f6 | ||
| 
						 | 
					3d51ac8df0 | ||
| 
						 | 
					6fe4ff7f85 | ||
| 
						 | 
					3c0c514e44 | ||
| 
						 | 
					2253d4bc16 | ||
| 
						 | 
					e5cc19de43 | ||
| 
						 | 
					ed5e2dd332 | ||
| 
						 | 
					09b7c6f550 | ||
| 
						 | 
					df315a1f51 | ||
| 
						 | 
					7ee4bb621c | ||
| 
						 | 
					24874f4c3c | ||
| 
						 | 
					c128880033 | ||
| 
						 | 
					a66e94a0b0 | ||
| 
						 | 
					56870ed4a8 | ||
| 
						 | 
					3ac720df47 | ||
| 
						 | 
					1bc757ad06 | ||
| 
						 | 
					f72abc6f3d | ||
| 
						 | 
					5ac88de985 | ||
| 
						 | 
					5404617d43 | ||
| 
						 | 
					12467a18e6 | ||
| 
						 | 
					1db7043a4d | ||
| 
						 | 
					49932747b3 | ||
| 
						 | 
					55db190875 | ||
| 
						 | 
					71fe2f7ed3 | ||
| 
						 | 
					ffc112c9d0 | ||
| 
						 | 
					d3e48e296f | ||
| 
						 | 
					14f6ae75ea | ||
| 
						 | 
					c84efe64d3 | ||
| 
						 | 
					10e89a7dbb | ||
| 
						 | 
					ef44acbf10 | ||
| 
						 | 
					06da540ab0 | ||
| 
						 | 
					0826b367d6 | ||
| 
						 | 
					329bf861d6 | ||
| 
						 | 
					9dcd3d18a0 | ||
| 
						 | 
					40c017fd54 | ||
| 
						 | 
					db66cd88b6 | ||
| 
						 | 
					f0bcf81a98 | ||
| 
						 | 
					86c205fe43 | ||
| 
						 | 
					6a0b343289 | ||
| 
						 | 
					c6414138c7 | ||
| 
						 | 
					36b355eb82 | ||
| 
						 | 
					9ca4e8f32a | ||
| 
						 | 
					1b88b7a166 | ||
| 
						 | 
					caf352ff06 | ||
| 
						 | 
					54106179a1 | ||
| 
						 | 
					607601b3a4 | ||
| 
						 | 
					f58828cb82 | ||
| 
						 | 
					11330af05f | ||
| 
						 | 
					fbe1bca1b9 | ||
| 
						 | 
					24a5325db3 | ||
| 
						 | 
					1ec3140759 | ||
| 
						 | 
					ca8db7696e | ||
| 
						 | 
					c9190574a9 | ||
| 
						 | 
					bfeb0b3639 | ||
| 
						 | 
					cbc1334b8d | ||
| 
						 | 
					08cbb97ec9 | ||
| 
						 | 
					5719cc1a24 | ||
| 
						 | 
					d9513e5ff2 | ||
| 
						 | 
					b5a0e8b2c0 | ||
| 
						 | 
					b32b918936 | ||
| 
						 | 
					0f47ffd908 | ||
| 
						 | 
					cd018ad3a5 | ||
| 
						 | 
					24dfecb6f0 | ||
| 
						 | 
					ab027a6ae2 | ||
| 
						 | 
					556d071e7f | ||
| 
						 | 
					939fb313df | ||
| 
						 | 
					b5639a6472 | ||
| 
						 | 
					f50e40e0b8 | ||
| 
						 | 
					6f07421911 | ||
| 
						 | 
					adf48246a9 | ||
| 
						 | 
					7be9291b13 | ||
| 
						 | 
					ea9e75039b | ||
| 
						 | 
					a5fb036011 | ||
| 
						 | 
					e55506f9db | ||
| 
						 | 
					50ec1d0445 | ||
| 
						 | 
					3d5e1d8d91 | ||
| 
						 | 
					db2128a344 | ||
| 
						 | 
					cae283dc86 | ||
| 
						 | 
					7afcb0fb04 | ||
| 
						 | 
					10f830c3ef | ||
| 
						 | 
					7a5c3aa7ed | ||
| 
						 | 
					2b50406856 | ||
| 
						 | 
					10a2a7e0fc | ||
| 
						 | 
					7a564b222d | ||
| 
						 | 
					21db43db06 | ||
| 
						 | 
					5009b3029f | ||
| 
						 | 
					57a029189c | ||
| 
						 | 
					671d68bc2c | ||
| 
						 | 
					5946c37925 | ||
| 
						 | 
					17a37b1de9 | ||
| 
						 | 
					e7827a6997 | ||
| 
						 | 
					2347e043a9 | ||
| 
						 | 
					00965fe19e | ||
| 
						 | 
					9681dfb458 | ||
| 
						 | 
					5e631bc6ba | ||
| 
						 | 
					b5f660398c | ||
| 
						 | 
					d50bdf619f | ||
| 
						 | 
					4e448b21ff | ||
| 
						 | 
					2a78c2970d | ||
| 
						 | 
					0cb715bb76 | ||
| 
						 | 
					7d03823afd | ||
| 
						 | 
					8e1c9f5042 | ||
| 
						 | 
					980b7cda8f | ||
| 
						 | 
					3a72dd5cb6 | ||
| 
						 | 
					3178243811 | ||
| 
						 | 
					d30e2f2a4f | ||
| 
						 | 
					3637be251e | ||
| 
						 | 
					2aea27d272 | ||
| 
						 | 
					6226dae05c | ||
| 
						 | 
					9c6a475a6e | ||
| 
						 | 
					8294d10d5b | ||
| 
						 | 
					67558bec47 | ||
| 
						 | 
					84873d4074 | ||
| 
						 | 
					58a0b28a39 | ||
| 
						 | 
					b37d3a66cc | ||
| 
						 | 
					7e495a5e27 | ||
| 
						 | 
					c41547fd4a | ||
| 
						 | 
					0d47d41c85 | ||
| 
						 | 
					41a3a17456 | ||
| 
						 | 
					cbbafbcca2 | ||
| 
						 | 
					c75566b374 | ||
| 
						 | 
					7279f1fcc1 | ||
| 
						 | 
					d7432f7c10 | ||
| 
						 | 
					b0a0a153f3 | ||
| 
						 | 
					024632dbd0 | ||
| 
						 | 
					0a545a28b9 | ||
| 
						 | 
					0f2df59998 | ||
| 
						 | 
					29a7d32f77 | ||
| 
						 | 
					687a7e9b2f | ||
| 
						 | 
					09e8782318 | ||
| 
						 | 
					f2aea02210 | ||
| 
						 | 
					194f922312 | ||
| 
						 | 
					fea3c48098 | ||
| 
						 | 
					c2f57baec2 | ||
| 
						 | 
					f4a140e126 | ||
| 
						 | 
					ab506b09fe | ||
| 
						 | 
					87e1cdeedb | ||
| 
						 | 
					81a36146ef | ||
| 
						 | 
					7fa4a68a27 | ||
| 
						 | 
					f1c5e2ef81 | ||
| 
						 | 
					b526155cce | ||
| 
						 | 
					62c3f301e7 | ||
| 
						 | 
					38cb988809 | ||
| 
						 | 
					b976ac54c8 | ||
| 
						 | 
					78026e766f | ||
| 
						 | 
					b4cd8d21a5 | ||
| 
						 | 
					7552893311 | ||
| 
						 | 
					21c896d8f8 | ||
| 
						 | 
					4b7fe202ec | ||
| 
						 | 
					9f4519210f | ||
| 
						 | 
					b0506afa5b | ||
| 
						 | 
					8cbb379898 | ||
| 
						 | 
					b226215593 | ||
| 
						 | 
					19970729a9 | ||
| 
						 | 
					d2ebfd2833 | ||
| 
						 | 
					bd782fc828 | ||
| 
						 | 
					23560e608c | ||
| 
						 | 
					f1377b560e | ||
| 
						 | 
					72108684ea | ||
| 
						 | 
					c6adaaea97 | ||
| 
						 | 
					91999a38ca | ||
| 
						 | 
					b34eed125d | ||
| 
						 | 
					2abe09529a | ||
| 
						 | 
					9aaaf4dd4b | ||
| 
						 | 
					cbfbcf7f1b | ||
| 
						 | 
					c7651dc40d | ||
| 
						 | 
					eda1c471ad | ||
| 
						 | 
					c7ef18fbc4 | ||
| 
						 | 
					901ec918b1 | ||
| 
						 | 
					6bdae55ee1 | ||
| 
						 | 
					dfb96e4b7f | ||
| 
						 | 
					ff2c316b18 | ||
| 
						 | 
					5be52f71f9 | ||
| 
						 | 
					42873dd37c | ||
| 
						 | 
					f93e7d4e3a | ||
| 
						 | 
					bbcd523967 | ||
| 
						 | 
					68cbe58d00 | ||
| 
						 | 
					115bca98f1 | ||
| 
						 | 
					ed0b34b2fe | ||
| 
						 | 
					ab34401421 | ||
| 
						 | 
					eed0c18d65 | ||
| 
						 | 
					e5a38ce748 | ||
| 
						 | 
					7d9d9fcf36 | ||
| 
						 | 
					f0aba6ceb2 | ||
| 
						 | 
					ab07ee57c6 | ||
| 
						 | 
					eae3d72a4d | ||
| 
						 | 
					7b8d826704 | ||
| 
						 | 
					e7baa42e63 | ||
| 
						 | 
					2f32833a22 | ||
| 
						 | 
					f6935a4b4b | ||
| 
						 | 
					332c9e891b | ||
| 
						 | 
					b91ee4847f | ||
| 
						 | 
					625463d871 | ||
| 
						 | 
					6433a01e07 | ||
| 
						 | 
					56cc31e8e7 | ||
| 
						 | 
					3af297aa76 | ||
| 
						 | 
					996ec59d28 | ||
| 
						 | 
					95593eeeab | ||
| 
						 | 
					dad244fb7a | ||
| 
						 | 
					adb5d27d95 | ||
| 
						 | 
					8456a8cecb | ||
| 
						 | 
					b9f66373c1 | ||
| 
						 | 
					9ac365feef | ||
| 
						 | 
					43bbd58a44 | ||
| 
						 | 
					7feffa64f3 | ||
| 
						 | 
					ea0977abb4 | ||
| 
						 | 
					4c83dc7c28 | ||
| 
						 | 
					e10ab1da78 | ||
| 
						 | 
					1b0e60374b | ||
| 
						 | 
					3a760fbb44 | ||
| 
						 | 
					6ef57a2973 | ||
| 
						 | 
					3e9c7f2e9f | ||
| 
						 | 
					430598b7a1 | ||
| 
						 | 
					91611b09b4 | ||
| 
						 | 
					ecd115851f | ||
| 
						 | 
					4a1e50fed1 | ||
| 
						 | 
					d6d037047b | ||
| 
						 | 
					b5734c2b20 | ||
| 
						 | 
					723fb7eaac | ||
| 
						 | 
					63a9acaa19 | ||
| 
						 | 
					0524f8c677 | ||
| 
						 | 
					70b62f272e | ||
| 
						 | 
					f0089b7940 | ||
| 
						 | 
					4b44280d53 | ||
| 
						 | 
					f045382d20 | ||
| 
						 | 
					db3fa1ade7 | ||
| 
						 | 
					f83950fd75 | ||
| 
						 | 
					4dd1bf920d | ||
| 
						 | 
					98755f3621 | ||
| 
						 | 
					c3a8a044b9 | ||
| 
						 | 
					15b5ea43a7 | ||
| 
						 | 
					ec683fc227 | ||
| 
						 | 
					d4e65eb82a | ||
| 
						 | 
					10c6601b0a | ||
| 
						 | 
					73940bc1bd | ||
| 
						 | 
					9b7fb829f9 | ||
| 
						 | 
					c51d8c9021 | ||
| 
						 | 
					d8a6dfe5ce | ||
| 
						 | 
					5f7cef0b06 | ||
| 
						 | 
					48ff2ffc68 | ||
| 
						 | 
					b3b9ccd314 | ||
| 
						 | 
					e63c7b483b | ||
| 
						 | 
					f57980b069 | ||
| 
						 | 
					7006aa0d2a | ||
| 
						 | 
					8051c1ca99 | ||
| 
						 | 
					a779592414 | ||
| 
						 | 
					112215848d | 
							
								
								
									
										15
									
								
								.clang-tidy
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								.clang-tidy
									
									
									
									
									
								
							@@ -5,11 +5,8 @@ Checks: >-
 | 
			
		||||
  -altera-*,
 | 
			
		||||
  -android-*,
 | 
			
		||||
  -boost-*,
 | 
			
		||||
  -bugprone-branch-clone,
 | 
			
		||||
  -bugprone-easily-swappable-parameters,
 | 
			
		||||
  -bugprone-narrowing-conversions,
 | 
			
		||||
  -bugprone-signed-char-misuse,
 | 
			
		||||
  -bugprone-too-small-loop-variable,
 | 
			
		||||
  -cert-dcl50-cpp,
 | 
			
		||||
  -cert-err58-cpp,
 | 
			
		||||
  -cert-oop57-cpp,
 | 
			
		||||
@@ -19,12 +16,10 @@ Checks: >-
 | 
			
		||||
  -clang-diagnostic-delete-abstract-non-virtual-dtor,
 | 
			
		||||
  -clang-diagnostic-delete-non-abstract-non-virtual-dtor,
 | 
			
		||||
  -clang-diagnostic-shadow-field,
 | 
			
		||||
  -clang-diagnostic-sign-compare,
 | 
			
		||||
  -clang-diagnostic-unused-variable,
 | 
			
		||||
  -clang-diagnostic-unused-const-variable,
 | 
			
		||||
  -clang-diagnostic-unused-parameter,
 | 
			
		||||
  -concurrency-*,
 | 
			
		||||
  -cppcoreguidelines-avoid-c-arrays,
 | 
			
		||||
  -cppcoreguidelines-avoid-goto,
 | 
			
		||||
  -cppcoreguidelines-avoid-magic-numbers,
 | 
			
		||||
  -cppcoreguidelines-init-variables,
 | 
			
		||||
  -cppcoreguidelines-macro-usage,
 | 
			
		||||
@@ -41,7 +36,6 @@ Checks: >-
 | 
			
		||||
  -cppcoreguidelines-pro-type-union-access,
 | 
			
		||||
  -cppcoreguidelines-pro-type-vararg,
 | 
			
		||||
  -cppcoreguidelines-special-member-functions,
 | 
			
		||||
  -fuchsia-default-arguments,
 | 
			
		||||
  -fuchsia-multiple-inheritance,
 | 
			
		||||
  -fuchsia-overloaded-operator,
 | 
			
		||||
  -fuchsia-statically-constructed-objects,
 | 
			
		||||
@@ -51,6 +45,7 @@ Checks: >-
 | 
			
		||||
  -google-explicit-constructor,
 | 
			
		||||
  -google-readability-braces-around-statements,
 | 
			
		||||
  -google-readability-casting,
 | 
			
		||||
  -google-readability-namespace-comments,
 | 
			
		||||
  -google-readability-todo,
 | 
			
		||||
  -google-runtime-references,
 | 
			
		||||
  -hicpp-*,
 | 
			
		||||
@@ -97,9 +92,11 @@ CheckOptions:
 | 
			
		||||
    value:           '1'
 | 
			
		||||
  - key:             google-readability-function-size.StatementThreshold
 | 
			
		||||
    value:           '800'
 | 
			
		||||
  - key:             google-readability-namespace-comments.ShortNamespaceLines
 | 
			
		||||
  - key:             google-runtime-int.TypeSuffix
 | 
			
		||||
    value:           '_t'
 | 
			
		||||
  - key:             llvm-namespace-comment.ShortNamespaceLines
 | 
			
		||||
    value:           '10'
 | 
			
		||||
  - key:             google-readability-namespace-comments.SpacesBeforeComments
 | 
			
		||||
  - key:             llvm-namespace-comment.SpacesBeforeComments
 | 
			
		||||
    value:           '2'
 | 
			
		||||
  - key:             modernize-loop-convert.MaxCopySize
 | 
			
		||||
    value:           '16'
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										47
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										47
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,5 +1,3 @@
 | 
			
		||||
# THESE JOBS ARE COPIED IN release.yml and release-dev.yml
 | 
			
		||||
# PLEASE ALSO UPDATE THOSE FILES WHEN CHANGING LINES HERE
 | 
			
		||||
name: CI
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
@@ -11,6 +9,10 @@ on:
 | 
			
		||||
permissions:
 | 
			
		||||
  contents: read
 | 
			
		||||
 | 
			
		||||
concurrency:
 | 
			
		||||
  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
 | 
			
		||||
  cancel-in-progress: true
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  ci:
 | 
			
		||||
    name: ${{ matrix.name }}
 | 
			
		||||
@@ -34,7 +36,7 @@ jobs:
 | 
			
		||||
          - id: test
 | 
			
		||||
            file: tests/test3.yaml
 | 
			
		||||
            name: Test tests/test3.yaml
 | 
			
		||||
            pio_cache_key: test1
 | 
			
		||||
            pio_cache_key: test3
 | 
			
		||||
          - id: test
 | 
			
		||||
            file: tests/test4.yaml
 | 
			
		||||
            name: Test tests/test4.yaml
 | 
			
		||||
@@ -49,26 +51,26 @@ jobs:
 | 
			
		||||
            name: Run script/clang-format
 | 
			
		||||
          - id: clang-tidy
 | 
			
		||||
            name: Run script/clang-tidy for ESP8266
 | 
			
		||||
            options: --environment esp8266-tidy --grep USE_ESP8266
 | 
			
		||||
            options: --environment esp8266-arduino-tidy --grep USE_ESP8266
 | 
			
		||||
            pio_cache_key: tidyesp8266
 | 
			
		||||
          - id: clang-tidy
 | 
			
		||||
            name: Run script/clang-tidy for ESP32 1/4
 | 
			
		||||
            options: --environment esp32-tidy --split-num 4 --split-at 1
 | 
			
		||||
            name: Run script/clang-tidy for ESP32 Arduino 1/4
 | 
			
		||||
            options: --environment esp32-arduino-tidy --split-num 4 --split-at 1
 | 
			
		||||
            pio_cache_key: tidyesp32
 | 
			
		||||
          - id: clang-tidy
 | 
			
		||||
            name: Run script/clang-tidy for ESP32 2/4
 | 
			
		||||
            options: --environment esp32-tidy --split-num 4 --split-at 2
 | 
			
		||||
            name: Run script/clang-tidy for ESP32 Arduino 2/4
 | 
			
		||||
            options: --environment esp32-arduino-tidy --split-num 4 --split-at 2
 | 
			
		||||
            pio_cache_key: tidyesp32
 | 
			
		||||
          - id: clang-tidy
 | 
			
		||||
            name: Run script/clang-tidy for ESP32 3/4
 | 
			
		||||
            options: --environment esp32-tidy --split-num 4 --split-at 3
 | 
			
		||||
            name: Run script/clang-tidy for ESP32 Arduino 3/4
 | 
			
		||||
            options: --environment esp32-arduino-tidy --split-num 4 --split-at 3
 | 
			
		||||
            pio_cache_key: tidyesp32
 | 
			
		||||
          - id: clang-tidy
 | 
			
		||||
            name: Run script/clang-tidy for ESP32 4/4
 | 
			
		||||
            options: --environment esp32-tidy --split-num 4 --split-at 4
 | 
			
		||||
            name: Run script/clang-tidy for ESP32 Arduino 4/4
 | 
			
		||||
            options: --environment esp32-arduino-tidy --split-num 4 --split-at 4
 | 
			
		||||
            pio_cache_key: tidyesp32
 | 
			
		||||
          - id: clang-tidy
 | 
			
		||||
            name: Run script/clang-tidy for ESP32 esp-idf
 | 
			
		||||
            name: Run script/clang-tidy for ESP32 IDF
 | 
			
		||||
            options: --environment esp32-idf-tidy --grep USE_ESP_IDF
 | 
			
		||||
            pio_cache_key: tidyesp32-idf
 | 
			
		||||
 | 
			
		||||
@@ -80,18 +82,23 @@ jobs:
 | 
			
		||||
        with:
 | 
			
		||||
          python-version: '3.7'
 | 
			
		||||
 | 
			
		||||
      - name: Cache pip modules
 | 
			
		||||
      - name: Cache virtualenv
 | 
			
		||||
        uses: actions/cache@v2
 | 
			
		||||
        with:
 | 
			
		||||
          path: ~/.cache/pip
 | 
			
		||||
          key: pip-${{ steps.python.outputs.python-version }}-${{ hashFiles('requirements*.txt') }}
 | 
			
		||||
          path: .venv
 | 
			
		||||
          key: venv-${{ steps.python.outputs.python-version }}-${{ hashFiles('requirements*.txt') }}
 | 
			
		||||
          restore-keys: |
 | 
			
		||||
            pip-${{ steps.python.outputs.python-version }}-
 | 
			
		||||
            venv-${{ steps.python.outputs.python-version }}-
 | 
			
		||||
 | 
			
		||||
      - name: Set up python environment
 | 
			
		||||
      - name: Set up virtualenv
 | 
			
		||||
        run: |
 | 
			
		||||
          pip3 install -r requirements.txt -r requirements_optional.txt -r requirements_test.txt
 | 
			
		||||
          pip3 install -e .
 | 
			
		||||
          python -m venv .venv
 | 
			
		||||
          source .venv/bin/activate
 | 
			
		||||
          pip install -U pip
 | 
			
		||||
          pip install -r requirements.txt -r requirements_optional.txt -r requirements_test.txt
 | 
			
		||||
          pip install -e .
 | 
			
		||||
          echo "$GITHUB_WORKSPACE/.venv/bin" >> $GITHUB_PATH
 | 
			
		||||
          echo "VIRTUAL_ENV=$GITHUB_WORKSPACE/.venv" >> $GITHUB_ENV
 | 
			
		||||
 | 
			
		||||
      # Use per check platformio cache because checks use different parts
 | 
			
		||||
      - name: Cache platformio
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/workflows/matchers/ci-custom.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/matchers/ci-custom.json
									
									
									
									
										vendored
									
									
								
							@@ -4,7 +4,7 @@
 | 
			
		||||
            "owner": "ci-custom",
 | 
			
		||||
            "pattern": [
 | 
			
		||||
                {
 | 
			
		||||
                    "regexp": "^ERROR (.*):(\\d+):(\\d+) - (.*)$",
 | 
			
		||||
                    "regexp": "^(.*):(\\d+):(\\d+):\\s+lint:\\s+(.*)$",
 | 
			
		||||
                    "file": 1,
 | 
			
		||||
                    "line": 2,
 | 
			
		||||
                    "column": 3,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/workflows/matchers/gcc.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/matchers/gcc.json
									
									
									
									
										vendored
									
									
								
							@@ -5,7 +5,7 @@
 | 
			
		||||
      "severity": "error",
 | 
			
		||||
      "pattern": [
 | 
			
		||||
        {
 | 
			
		||||
          "regexp": "^(.*):(\\d+):(\\d+):\\s+(?:fatal\\s+)?(warning|error):\\s+(.*)$",
 | 
			
		||||
          "regexp": "^src/(.*):(\\d+):(\\d+):\\s+(?:fatal\\s+)?(warning|error):\\s+(.*)$",
 | 
			
		||||
          "file": 1,
 | 
			
		||||
          "line": 2,
 | 
			
		||||
          "column": 3,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										15
									
								
								.github/workflows/matchers/lint-python.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										15
									
								
								.github/workflows/matchers/lint-python.json
									
									
									
									
										vendored
									
									
								
							@@ -1,11 +1,22 @@
 | 
			
		||||
{
 | 
			
		||||
  "problemMatcher": [
 | 
			
		||||
    {
 | 
			
		||||
      "owner": "black",
 | 
			
		||||
      "severity": "error",
 | 
			
		||||
      "pattern": [
 | 
			
		||||
        {
 | 
			
		||||
          "regexp": "^(.*): (Please format this file with the black formatter)",
 | 
			
		||||
          "file": 1,
 | 
			
		||||
          "message": 2
 | 
			
		||||
        }
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "owner": "flake8",
 | 
			
		||||
      "severity": "error",
 | 
			
		||||
      "pattern": [
 | 
			
		||||
          {
 | 
			
		||||
          "regexp": "^(.*):(\\d+) - ([EFCDNW]\\d{3}.*)$",
 | 
			
		||||
          "regexp": "^(.*):(\\d+): ([EFCDNW]\\d{3}.*)$",
 | 
			
		||||
          "file": 1,
 | 
			
		||||
          "line": 2,
 | 
			
		||||
          "message": 3
 | 
			
		||||
@@ -17,7 +28,7 @@
 | 
			
		||||
      "severity": "error",
 | 
			
		||||
      "pattern": [
 | 
			
		||||
        {
 | 
			
		||||
          "regexp": "^(.*):(\\d+) - (\\[[EFCRW]\\d{4}\\(.*\\),.*\\].*)$",
 | 
			
		||||
          "regexp": "^(.*):(\\d+): (\\[[EFCRW]\\d{4}\\(.*\\),.*\\].*)$",
 | 
			
		||||
          "file": 1,
 | 
			
		||||
          "line": 2,
 | 
			
		||||
          "message": 3
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										18
									
								
								CODEOWNERS
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								CODEOWNERS
									
									
									
									
									
								
							@@ -28,12 +28,16 @@ esphome/components/b_parasite/* @rbaron
 | 
			
		||||
esphome/components/ballu/* @bazuchan
 | 
			
		||||
esphome/components/bang_bang/* @OttoWinter
 | 
			
		||||
esphome/components/binary_sensor/* @esphome/core
 | 
			
		||||
esphome/components/bl0940/* @tobias-
 | 
			
		||||
esphome/components/ble_client/* @buxtronix
 | 
			
		||||
esphome/components/bme680_bsec/* @trvrnrth
 | 
			
		||||
esphome/components/bmp3xx/* @martgras
 | 
			
		||||
esphome/components/button/* @esphome/core
 | 
			
		||||
esphome/components/canbus/* @danielschramm @mvturnho
 | 
			
		||||
esphome/components/cap1188/* @MrEditor97
 | 
			
		||||
esphome/components/captive_portal/* @OttoWinter
 | 
			
		||||
esphome/components/ccs811/* @habbie
 | 
			
		||||
esphome/components/cd74hc4067/* @asoehlke
 | 
			
		||||
esphome/components/climate/* @esphome/core
 | 
			
		||||
esphome/components/climate_ir/* @glmnet
 | 
			
		||||
esphome/components/color_temperature/* @jesserockz
 | 
			
		||||
@@ -54,6 +58,7 @@ esphome/components/esp32/* @esphome/core
 | 
			
		||||
esphome/components/esp32_ble/* @jesserockz
 | 
			
		||||
esphome/components/esp32_ble_server/* @jesserockz
 | 
			
		||||
esphome/components/esp32_camera_web_server/* @ayufan
 | 
			
		||||
esphome/components/esp32_can/* @Sympatron
 | 
			
		||||
esphome/components/esp32_improv/* @jesserockz
 | 
			
		||||
esphome/components/esp8266/* @esphome/core
 | 
			
		||||
esphome/components/exposure_notifications/* @OttoWinter
 | 
			
		||||
@@ -64,6 +69,7 @@ esphome/components/globals/* @esphome/core
 | 
			
		||||
esphome/components/gpio/* @esphome/core
 | 
			
		||||
esphome/components/gps/* @coogle
 | 
			
		||||
esphome/components/graph/* @synco
 | 
			
		||||
esphome/components/growatt_solar/* @leeuwte
 | 
			
		||||
esphome/components/havells_solar/* @sourabhjaiswal
 | 
			
		||||
esphome/components/hbridge/fan/* @WeekendWarrior
 | 
			
		||||
esphome/components/hbridge/light/* @DotNetDann
 | 
			
		||||
@@ -73,11 +79,13 @@ esphome/components/homeassistant/* @OttoWinter
 | 
			
		||||
esphome/components/hrxl_maxsonar_wr/* @netmikey
 | 
			
		||||
esphome/components/i2c/* @esphome/core
 | 
			
		||||
esphome/components/improv_serial/* @esphome/core
 | 
			
		||||
esphome/components/ina260/* @MrEditor97
 | 
			
		||||
esphome/components/inkbird_ibsth1_mini/* @fkirill
 | 
			
		||||
esphome/components/inkplate6/* @jesserockz
 | 
			
		||||
esphome/components/integration/* @OttoWinter
 | 
			
		||||
esphome/components/interval/* @esphome/core
 | 
			
		||||
esphome/components/json/* @OttoWinter
 | 
			
		||||
esphome/components/kalman_combinator/* @Cat-Ion
 | 
			
		||||
esphome/components/ledc/* @OttoWinter
 | 
			
		||||
esphome/components/light/* @esphome/core
 | 
			
		||||
esphome/components/logger/* @esphome/core
 | 
			
		||||
@@ -91,10 +99,13 @@ esphome/components/mcp23x08_base/* @jesserockz
 | 
			
		||||
esphome/components/mcp23x17_base/* @jesserockz
 | 
			
		||||
esphome/components/mcp23xxx_base/* @jesserockz
 | 
			
		||||
esphome/components/mcp2515/* @danielschramm @mvturnho
 | 
			
		||||
esphome/components/mcp3204/* @rsumner
 | 
			
		||||
esphome/components/mcp47a1/* @jesserockz
 | 
			
		||||
esphome/components/mcp9808/* @k7hpn
 | 
			
		||||
esphome/components/md5/* @esphome/core
 | 
			
		||||
esphome/components/mdns/* @esphome/core
 | 
			
		||||
esphome/components/midea/* @dudanov
 | 
			
		||||
esphome/components/midea_ir/* @dudanov
 | 
			
		||||
esphome/components/mitsubishi/* @RubyBailey
 | 
			
		||||
esphome/components/modbus_controller/* @martgras
 | 
			
		||||
esphome/components/modbus_controller/binary_sensor/* @martgras
 | 
			
		||||
@@ -122,6 +133,7 @@ esphome/components/pn532_i2c/* @OttoWinter @jesserockz
 | 
			
		||||
esphome/components/pn532_spi/* @OttoWinter @jesserockz
 | 
			
		||||
esphome/components/power_supply/* @esphome/core
 | 
			
		||||
esphome/components/preferences/* @esphome/core
 | 
			
		||||
esphome/components/psram/* @esphome/core
 | 
			
		||||
esphome/components/pulse_meter/* @stevebaxter
 | 
			
		||||
esphome/components/pvvx_mithermometer/* @pasiz
 | 
			
		||||
esphome/components/rc522/* @glmnet
 | 
			
		||||
@@ -131,7 +143,7 @@ esphome/components/restart/* @esphome/core
 | 
			
		||||
esphome/components/rf_bridge/* @jesserockz
 | 
			
		||||
esphome/components/rgbct/* @jesserockz
 | 
			
		||||
esphome/components/rtttl/* @glmnet
 | 
			
		||||
esphome/components/safe_mode/* @paulmonigatti
 | 
			
		||||
esphome/components/safe_mode/* @jsuanet @paulmonigatti
 | 
			
		||||
esphome/components/scd4x/* @sjtrny
 | 
			
		||||
esphome/components/script/* @esphome/core
 | 
			
		||||
esphome/components/sdm_meter/* @jesserockz @polyfaces
 | 
			
		||||
@@ -141,7 +153,7 @@ esphome/components/select/* @esphome/core
 | 
			
		||||
esphome/components/sensor/* @esphome/core
 | 
			
		||||
esphome/components/sgp40/* @SenexCrenshaw
 | 
			
		||||
esphome/components/sht4x/* @sjtrny
 | 
			
		||||
esphome/components/shutdown/* @esphome/core
 | 
			
		||||
esphome/components/shutdown/* @esphome/core @jsuanet
 | 
			
		||||
esphome/components/sim800l/* @glmnet
 | 
			
		||||
esphome/components/sm2135/* @BoukeHaarsma23
 | 
			
		||||
esphome/components/socket/* @esphome/core
 | 
			
		||||
@@ -178,8 +190,10 @@ esphome/components/toshiba/* @kbx81
 | 
			
		||||
esphome/components/tsl2591/* @wjcarpenter
 | 
			
		||||
esphome/components/tuya/binary_sensor/* @jesserockz
 | 
			
		||||
esphome/components/tuya/climate/* @jesserockz
 | 
			
		||||
esphome/components/tuya/number/* @frankiboy1
 | 
			
		||||
esphome/components/tuya/sensor/* @jesserockz
 | 
			
		||||
esphome/components/tuya/switch/* @jesserockz
 | 
			
		||||
esphome/components/tuya/text_sensor/* @dentra
 | 
			
		||||
esphome/components/uart/* @esphome/core
 | 
			
		||||
esphome/components/ultrasonic/* @OttoWinter
 | 
			
		||||
esphome/components/version/* @esphome/core
 | 
			
		||||
 
 | 
			
		||||
@@ -5,12 +5,12 @@
 | 
			
		||||
# One of "docker", "hassio"
 | 
			
		||||
ARG BASEIMGTYPE=docker
 | 
			
		||||
 | 
			
		||||
FROM ghcr.io/hassio-addons/debian-base/amd64:5.1.1 AS base-hassio-amd64
 | 
			
		||||
FROM ghcr.io/hassio-addons/debian-base/aarch64:5.1.1 AS base-hassio-arm64
 | 
			
		||||
FROM ghcr.io/hassio-addons/debian-base/armv7:5.1.1 AS base-hassio-armv7
 | 
			
		||||
FROM debian:bullseye-20211011-slim AS base-docker-amd64
 | 
			
		||||
FROM debian:bullseye-20211011-slim AS base-docker-arm64
 | 
			
		||||
FROM debian:bullseye-20211011-slim AS base-docker-armv7
 | 
			
		||||
FROM ghcr.io/hassio-addons/debian-base/amd64:5.2.3 AS base-hassio-amd64
 | 
			
		||||
FROM ghcr.io/hassio-addons/debian-base/aarch64:5.2.3 AS base-hassio-arm64
 | 
			
		||||
FROM ghcr.io/hassio-addons/debian-base/armv7:5.2.3 AS base-hassio-armv7
 | 
			
		||||
FROM debian:bullseye-20211220-slim AS base-docker-amd64
 | 
			
		||||
FROM debian:bullseye-20211220-slim AS base-docker-arm64
 | 
			
		||||
FROM debian:bullseye-20211220-slim AS base-docker-armv7
 | 
			
		||||
 | 
			
		||||
# Use TARGETARCH/TARGETVARIANT defined by docker
 | 
			
		||||
# https://docs.docker.com/engine/reference/builder/#automatic-platform-args-in-the-global-scope
 | 
			
		||||
@@ -27,7 +27,7 @@ RUN \
 | 
			
		||||
        python3-cryptography=3.3.2-1 \
 | 
			
		||||
        iputils-ping=3:20210202-1 \
 | 
			
		||||
        git=1:2.30.2-1 \
 | 
			
		||||
        curl=7.74.0-1.3+b1 \
 | 
			
		||||
        curl=7.74.0-1.3+deb11u1 \
 | 
			
		||||
    && rm -rf \
 | 
			
		||||
        /tmp/* \
 | 
			
		||||
        /var/{cache,log}/* \
 | 
			
		||||
@@ -42,8 +42,8 @@ ENV \
 | 
			
		||||
RUN \
 | 
			
		||||
    # Ubuntu python3-pip is missing wheel
 | 
			
		||||
    pip3 install --no-cache-dir \
 | 
			
		||||
        wheel==0.36.2 \
 | 
			
		||||
        platformio==5.2.2 \
 | 
			
		||||
        wheel==0.37.1 \
 | 
			
		||||
        platformio==5.2.4 \
 | 
			
		||||
    # Change some platformio settings
 | 
			
		||||
    && platformio settings set enable_telemetry No \
 | 
			
		||||
    && platformio settings set check_libraries_interval 1000000 \
 | 
			
		||||
@@ -64,7 +64,7 @@ RUN \
 | 
			
		||||
 | 
			
		||||
# Copy esphome and install
 | 
			
		||||
COPY . /esphome
 | 
			
		||||
RUN pip3 install --no-cache-dir -e /esphome
 | 
			
		||||
RUN pip3 install --no-cache-dir --no-use-pep517 -e /esphome
 | 
			
		||||
 | 
			
		||||
# Settings for dashboard
 | 
			
		||||
ENV USERNAME="" PASSWORD=""
 | 
			
		||||
@@ -112,7 +112,7 @@ RUN \
 | 
			
		||||
 | 
			
		||||
# Copy esphome and install
 | 
			
		||||
COPY . /esphome
 | 
			
		||||
RUN pip3 install --no-cache-dir -e /esphome
 | 
			
		||||
RUN pip3 install --no-cache-dir --no-use-pep517 -e /esphome
 | 
			
		||||
 | 
			
		||||
# Labels
 | 
			
		||||
LABEL \
 | 
			
		||||
 
 | 
			
		||||
@@ -32,6 +32,7 @@ parser.add_argument("--dry-run", action="store_true", help="Don't run any comman
 | 
			
		||||
subparsers = parser.add_subparsers(help="Action to perform", dest="command", required=True)
 | 
			
		||||
build_parser = subparsers.add_parser("build", help="Build the image")
 | 
			
		||||
build_parser.add_argument("--push", help="Also push the images", action="store_true")
 | 
			
		||||
build_parser.add_argument("--load", help="Load the docker image locally", action="store_true")
 | 
			
		||||
manifest_parser = subparsers.add_parser("manifest", help="Create a manifest from already pushed images")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -132,6 +133,8 @@ def main():
 | 
			
		||||
            cmd += ["--tag", img]
 | 
			
		||||
        if args.push:
 | 
			
		||||
            cmd += ["--push", "--cache-to", f"type=registry,ref={cache_img},mode=max"]
 | 
			
		||||
        if args.load:
 | 
			
		||||
            cmd += ["--load"]
 | 
			
		||||
 | 
			
		||||
        run_command(*cmd, ".")
 | 
			
		||||
    elif args.command == "manifest":
 | 
			
		||||
 
 | 
			
		||||
@@ -145,6 +145,8 @@ def wrap_to_code(name, comp):
 | 
			
		||||
        if comp.config_schema is not None:
 | 
			
		||||
            conf_str = yaml_util.dump(conf)
 | 
			
		||||
            conf_str = conf_str.replace("//", "")
 | 
			
		||||
            # remove tailing \ to avoid multi-line comment warning
 | 
			
		||||
            conf_str = conf_str.replace("\\\n", "\n")
 | 
			
		||||
            cg.add(cg.LineComment(indent(conf_str)))
 | 
			
		||||
        await coro(conf)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -75,8 +75,7 @@ from esphome.cpp_types import (  # noqa
 | 
			
		||||
    optional,
 | 
			
		||||
    arduino_json_ns,
 | 
			
		||||
    JsonObject,
 | 
			
		||||
    JsonObjectRef,
 | 
			
		||||
    JsonObjectConstRef,
 | 
			
		||||
    JsonObjectConst,
 | 
			
		||||
    Controller,
 | 
			
		||||
    GPIOPin,
 | 
			
		||||
    InternalGPIOPin,
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,7 @@ void AdalightLightEffect::stop() {
 | 
			
		||||
  AddressableLightEffect::stop();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int AdalightLightEffect::get_frame_size_(int led_count) const {
 | 
			
		||||
unsigned int AdalightLightEffect::get_frame_size_(int led_count) const {
 | 
			
		||||
  // 3 bytes: Ada
 | 
			
		||||
  // 2 bytes: LED count
 | 
			
		||||
  // 1 byte: checksum
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,7 @@ class AdalightLightEffect : public light::AddressableLightEffect, public uart::U
 | 
			
		||||
    CONSUMED,
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  int get_frame_size_(int led_count) const;
 | 
			
		||||
  unsigned int get_frame_size_(int led_count) const;
 | 
			
		||||
  void reset_frame_(light::AddressableLight &it);
 | 
			
		||||
  void blank_all_leds_(light::AddressableLight &it);
 | 
			
		||||
  Frame parse_frame_(light::AddressableLight &it);
 | 
			
		||||
 
 | 
			
		||||
@@ -91,7 +91,7 @@ void ADCSensor::dump_config() {
 | 
			
		||||
float ADCSensor::get_setup_priority() const { return setup_priority::DATA; }
 | 
			
		||||
void ADCSensor::update() {
 | 
			
		||||
  float value_v = this->sample();
 | 
			
		||||
  ESP_LOGD(TAG, "'%s': Got voltage=%.4fV", this->get_name().c_str(), value_v);
 | 
			
		||||
  ESP_LOGV(TAG, "'%s': Got voltage=%.4fV", this->get_name().c_str(), value_v);
 | 
			
		||||
  this->publish_state(value_v);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -110,12 +110,12 @@ void AHT10Component::update() {
 | 
			
		||||
  uint32_t raw_temperature = ((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5];
 | 
			
		||||
  uint32_t raw_humidity = ((data[1] << 16) | (data[2] << 8) | data[3]) >> 4;
 | 
			
		||||
 | 
			
		||||
  float temperature = ((200.0 * (float) raw_temperature) / 1048576.0) - 50.0;
 | 
			
		||||
  float temperature = ((200.0f * (float) raw_temperature) / 1048576.0f) - 50.0f;
 | 
			
		||||
  float humidity;
 | 
			
		||||
  if (raw_humidity == 0) {  // unrealistic value
 | 
			
		||||
    humidity = NAN;
 | 
			
		||||
  } else {
 | 
			
		||||
    humidity = (float) raw_humidity * 100.0 / 1048576.0;
 | 
			
		||||
    humidity = (float) raw_humidity * 100.0f / 1048576.0f;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (this->temperature_sensor_ != nullptr) {
 | 
			
		||||
 
 | 
			
		||||
@@ -38,9 +38,9 @@ void AM2320Component::update() {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  float temperature = (((data[4] & 0x7F) << 8) + data[5]) / 10.0;
 | 
			
		||||
  float temperature = (((data[4] & 0x7F) << 8) + data[5]) / 10.0f;
 | 
			
		||||
  temperature = (data[4] & 0x80) ? -temperature : temperature;
 | 
			
		||||
  float humidity = ((data[2] << 8) + data[3]) / 10.0;
 | 
			
		||||
  float humidity = ((data[2] << 8) + data[3]) / 10.0f;
 | 
			
		||||
 | 
			
		||||
  ESP_LOGD(TAG, "Got temperature=%.1f°C humidity=%.1f%%", temperature, humidity);
 | 
			
		||||
  if (this->temperature_sensor_ != nullptr)
 | 
			
		||||
 
 | 
			
		||||
@@ -76,6 +76,8 @@ async def to_code(config):
 | 
			
		||||
        pos = 0
 | 
			
		||||
        for frameIndex in range(frames):
 | 
			
		||||
            image.seek(frameIndex)
 | 
			
		||||
            if CONF_RESIZE in config:
 | 
			
		||||
                image.thumbnail(config[CONF_RESIZE])
 | 
			
		||||
            frame = image.convert("RGB")
 | 
			
		||||
            if CONF_RESIZE in config:
 | 
			
		||||
                frame = frame.resize([width, height])
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,7 @@ class Anova : public climate::Climate, public esphome::ble_client::BLEClientNode
 | 
			
		||||
  climate::ClimateTraits traits() override {
 | 
			
		||||
    auto traits = climate::ClimateTraits();
 | 
			
		||||
    traits.set_supports_current_temperature(true);
 | 
			
		||||
    traits.set_supports_heat_mode(true);
 | 
			
		||||
    traits.set_supported_modes({climate::CLIMATE_MODE_OFF, climate::ClimateMode::CLIMATE_MODE_HEAT});
 | 
			
		||||
    traits.set_visual_min_temperature(25.0);
 | 
			
		||||
    traits.set_visual_max_temperature(100.0);
 | 
			
		||||
    traits.set_visual_temperature_step(0.1);
 | 
			
		||||
 
 | 
			
		||||
@@ -73,51 +73,46 @@ AnovaPacket *AnovaCodec::get_stop_request() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AnovaCodec::decode(const uint8_t *data, uint16_t length) {
 | 
			
		||||
  memset(this->buf_, 0, 32);
 | 
			
		||||
  strncpy(this->buf_, (char *) data, length);
 | 
			
		||||
  char buf[32];
 | 
			
		||||
  memset(buf, 0, sizeof(buf));
 | 
			
		||||
  strncpy(buf, (char *) data, std::min<uint16_t>(length, sizeof(buf) - 1));
 | 
			
		||||
  this->has_target_temp_ = this->has_current_temp_ = this->has_unit_ = this->has_running_ = false;
 | 
			
		||||
  switch (this->current_query_) {
 | 
			
		||||
    case READ_DEVICE_STATUS: {
 | 
			
		||||
      if (!strncmp(this->buf_, "stopped", 7)) {
 | 
			
		||||
      if (!strncmp(buf, "stopped", 7)) {
 | 
			
		||||
        this->has_running_ = true;
 | 
			
		||||
        this->running_ = false;
 | 
			
		||||
      }
 | 
			
		||||
      if (!strncmp(this->buf_, "running", 7)) {
 | 
			
		||||
      if (!strncmp(buf, "running", 7)) {
 | 
			
		||||
        this->has_running_ = true;
 | 
			
		||||
        this->running_ = true;
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case START: {
 | 
			
		||||
      if (!strncmp(this->buf_, "start", 5)) {
 | 
			
		||||
      if (!strncmp(buf, "start", 5)) {
 | 
			
		||||
        this->has_running_ = true;
 | 
			
		||||
        this->running_ = true;
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case STOP: {
 | 
			
		||||
      if (!strncmp(this->buf_, "stop", 4)) {
 | 
			
		||||
      if (!strncmp(buf, "stop", 4)) {
 | 
			
		||||
        this->has_running_ = true;
 | 
			
		||||
        this->running_ = false;
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case READ_TARGET_TEMPERATURE: {
 | 
			
		||||
      this->target_temp_ = parse_number<float>(this->buf_, sizeof(this->buf_)).value_or(0.0f);
 | 
			
		||||
      if (this->fahrenheit_)
 | 
			
		||||
        this->target_temp_ = ftoc(this->target_temp_);
 | 
			
		||||
      this->has_target_temp_ = true;
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case READ_TARGET_TEMPERATURE:
 | 
			
		||||
    case SET_TARGET_TEMPERATURE: {
 | 
			
		||||
      this->target_temp_ = parse_number<float>(this->buf_, sizeof(this->buf_)).value_or(0.0f);
 | 
			
		||||
      this->target_temp_ = parse_number<float>(str_until(buf, '\r')).value_or(0.0f);
 | 
			
		||||
      if (this->fahrenheit_)
 | 
			
		||||
        this->target_temp_ = ftoc(this->target_temp_);
 | 
			
		||||
      this->has_target_temp_ = true;
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case READ_CURRENT_TEMPERATURE: {
 | 
			
		||||
      this->current_temp_ = parse_number<float>(this->buf_, sizeof(this->buf_)).value_or(0.0f);
 | 
			
		||||
      this->current_temp_ = parse_number<float>(str_until(buf, '\r')).value_or(0.0f);
 | 
			
		||||
      if (this->fahrenheit_)
 | 
			
		||||
        this->current_temp_ = ftoc(this->current_temp_);
 | 
			
		||||
      this->has_current_temp_ = true;
 | 
			
		||||
@@ -125,8 +120,8 @@ void AnovaCodec::decode(const uint8_t *data, uint16_t length) {
 | 
			
		||||
    }
 | 
			
		||||
    case SET_UNIT:
 | 
			
		||||
    case READ_UNIT: {
 | 
			
		||||
      this->unit_ = this->buf_[0];
 | 
			
		||||
      this->fahrenheit_ = this->buf_[0] == 'f';
 | 
			
		||||
      this->unit_ = buf[0];
 | 
			
		||||
      this->fahrenheit_ = buf[0] == 'f';
 | 
			
		||||
      this->has_unit_ = true;
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -70,7 +70,6 @@ class AnovaCodec {
 | 
			
		||||
  bool has_current_temp_;
 | 
			
		||||
  bool has_unit_;
 | 
			
		||||
  bool has_running_;
 | 
			
		||||
  char buf_[32];
 | 
			
		||||
  bool fahrenheit_;
 | 
			
		||||
 | 
			
		||||
  CurrentQuery current_query_;
 | 
			
		||||
 
 | 
			
		||||
@@ -40,6 +40,7 @@ service APIConnection {
 | 
			
		||||
  rpc climate_command (ClimateCommandRequest) returns (void) {}
 | 
			
		||||
  rpc number_command (NumberCommandRequest) returns (void) {}
 | 
			
		||||
  rpc select_command (SelectCommandRequest) returns (void) {}
 | 
			
		||||
  rpc button_command (ButtonCommandRequest) returns (void) {}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -868,6 +869,11 @@ message ClimateCommandRequest {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ==================== NUMBER ====================
 | 
			
		||||
enum NumberMode {
 | 
			
		||||
  NUMBER_MODE_AUTO = 0;
 | 
			
		||||
  NUMBER_MODE_BOX = 1;
 | 
			
		||||
  NUMBER_MODE_SLIDER = 2;
 | 
			
		||||
}
 | 
			
		||||
message ListEntitiesNumberResponse {
 | 
			
		||||
  option (id) = 49;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
@@ -884,6 +890,8 @@ message ListEntitiesNumberResponse {
 | 
			
		||||
  float step = 8;
 | 
			
		||||
  bool disabled_by_default = 9;
 | 
			
		||||
  EntityCategory entity_category = 10;
 | 
			
		||||
  string unit_of_measurement = 11;
 | 
			
		||||
  NumberMode mode = 12;
 | 
			
		||||
}
 | 
			
		||||
message NumberStateResponse {
 | 
			
		||||
  option (id) = 50;
 | 
			
		||||
@@ -944,3 +952,28 @@ message SelectCommandRequest {
 | 
			
		||||
  fixed32 key = 1;
 | 
			
		||||
  string state = 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ==================== BUTTON ====================
 | 
			
		||||
message ListEntitiesButtonResponse {
 | 
			
		||||
  option (id) = 61;
 | 
			
		||||
  option (source) = SOURCE_SERVER;
 | 
			
		||||
  option (ifdef) = "USE_BUTTON";
 | 
			
		||||
 | 
			
		||||
  string object_id = 1;
 | 
			
		||||
  fixed32 key = 2;
 | 
			
		||||
  string name = 3;
 | 
			
		||||
  string unique_id = 4;
 | 
			
		||||
 | 
			
		||||
  string icon = 5;
 | 
			
		||||
  bool disabled_by_default = 6;
 | 
			
		||||
  EntityCategory entity_category = 7;
 | 
			
		||||
  string device_class = 8;
 | 
			
		||||
}
 | 
			
		||||
message ButtonCommandRequest {
 | 
			
		||||
  option (id) = 62;
 | 
			
		||||
  option (source) = SOURCE_CLIENT;
 | 
			
		||||
  option (ifdef) = "USE_BUTTON";
 | 
			
		||||
  option (no_delay) = true;
 | 
			
		||||
 | 
			
		||||
  fixed32 key = 1;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,7 @@ namespace esphome {
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "api.connection";
 | 
			
		||||
static const int ESP32_CAMERA_STOP_STREAM = 5000;
 | 
			
		||||
 | 
			
		||||
APIConnection::APIConnection(std::unique_ptr<socket::Socket> sock, APIServer *parent)
 | 
			
		||||
    : parent_(parent), initial_state_iterator_(parent, this), list_entities_iterator_(parent, this) {
 | 
			
		||||
@@ -132,7 +133,7 @@ void APIConnection::loop() {
 | 
			
		||||
 | 
			
		||||
  if (state_subs_at_ != -1) {
 | 
			
		||||
    const auto &subs = this->parent_->get_state_subs();
 | 
			
		||||
    if (state_subs_at_ >= subs.size()) {
 | 
			
		||||
    if (state_subs_at_ >= (int) subs.size()) {
 | 
			
		||||
      state_subs_at_ = -1;
 | 
			
		||||
    } else {
 | 
			
		||||
      auto &it = subs[state_subs_at_];
 | 
			
		||||
@@ -619,6 +620,8 @@ bool APIConnection::send_number_info(number::Number *number) {
 | 
			
		||||
  msg.icon = number->get_icon();
 | 
			
		||||
  msg.disabled_by_default = number->is_disabled_by_default();
 | 
			
		||||
  msg.entity_category = static_cast<enums::EntityCategory>(number->get_entity_category());
 | 
			
		||||
  msg.unit_of_measurement = number->traits.get_unit_of_measurement();
 | 
			
		||||
  msg.mode = static_cast<enums::NumberMode>(number->traits.get_mode());
 | 
			
		||||
 | 
			
		||||
  msg.min_value = number->traits.get_min_value();
 | 
			
		||||
  msg.max_value = number->traits.get_max_value();
 | 
			
		||||
@@ -674,13 +677,37 @@ void APIConnection::select_command(const SelectCommandRequest &msg) {
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_BUTTON
 | 
			
		||||
bool APIConnection::send_button_info(button::Button *button) {
 | 
			
		||||
  ListEntitiesButtonResponse msg;
 | 
			
		||||
  msg.key = button->get_object_id_hash();
 | 
			
		||||
  msg.object_id = button->get_object_id();
 | 
			
		||||
  msg.name = button->get_name();
 | 
			
		||||
  msg.unique_id = get_default_unique_id("button", button);
 | 
			
		||||
  msg.icon = button->get_icon();
 | 
			
		||||
  msg.disabled_by_default = button->is_disabled_by_default();
 | 
			
		||||
  msg.entity_category = static_cast<enums::EntityCategory>(button->get_entity_category());
 | 
			
		||||
  msg.device_class = button->get_device_class();
 | 
			
		||||
  return this->send_list_entities_button_response(msg);
 | 
			
		||||
}
 | 
			
		||||
void APIConnection::button_command(const ButtonCommandRequest &msg) {
 | 
			
		||||
  button::Button *button = App.get_button_by_key(msg.key);
 | 
			
		||||
  if (button == nullptr)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  button->press();
 | 
			
		||||
}
 | 
			
		||||
#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(std::move(image));
 | 
			
		||||
  if (image->was_requested_by(esphome::esp32_camera::API_REQUESTER) ||
 | 
			
		||||
      image->was_requested_by(esphome::esp32_camera::IDLE))
 | 
			
		||||
    this->image_reader_.set_image(std::move(image));
 | 
			
		||||
}
 | 
			
		||||
bool APIConnection::send_camera_info(esp32_camera::ESP32Camera *camera) {
 | 
			
		||||
  ListEntitiesCameraResponse msg;
 | 
			
		||||
@@ -698,9 +725,14 @@ void APIConnection::camera_image(const CameraImageRequest &msg) {
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  if (msg.single)
 | 
			
		||||
    esp32_camera::global_esp32_camera->request_image();
 | 
			
		||||
  if (msg.stream)
 | 
			
		||||
    esp32_camera::global_esp32_camera->request_stream();
 | 
			
		||||
    esp32_camera::global_esp32_camera->request_image(esphome::esp32_camera::API_REQUESTER);
 | 
			
		||||
  if (msg.stream) {
 | 
			
		||||
    esp32_camera::global_esp32_camera->start_stream(esphome::esp32_camera::API_REQUESTER);
 | 
			
		||||
 | 
			
		||||
    App.scheduler.set_timeout(this->parent_, "api_esp32_camera_stop_stream", ESP32_CAMERA_STOP_STREAM, []() {
 | 
			
		||||
      esp32_camera::global_esp32_camera->stop_stream(esphome::esp32_camera::API_REQUESTER);
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -73,6 +73,10 @@ class APIConnection : public APIServerConnection {
 | 
			
		||||
  bool send_select_state(select::Select *select, std::string state);
 | 
			
		||||
  bool send_select_info(select::Select *select);
 | 
			
		||||
  void select_command(const SelectCommandRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BUTTON
 | 
			
		||||
  bool send_button_info(button::Button *button);
 | 
			
		||||
  void button_command(const ButtonCommandRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
  bool send_log_message(int level, const char *tag, const char *line);
 | 
			
		||||
  void send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
#include "api_frame_helper.h"
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/core/hal.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
#include "proto.h"
 | 
			
		||||
#include <cstring>
 | 
			
		||||
@@ -174,9 +175,6 @@ APIError APINoiseFrameHelper::loop() {
 | 
			
		||||
 * errno API_ERROR_HANDSHAKE_PACKET_LEN: Packet too big for this phase.
 | 
			
		||||
 */
 | 
			
		||||
APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
 | 
			
		||||
  int err;
 | 
			
		||||
  APIError aerr;
 | 
			
		||||
 | 
			
		||||
  if (frame == nullptr) {
 | 
			
		||||
    HELPER_LOG("Bad argument for try_read_frame_");
 | 
			
		||||
    return APIError::BAD_ARG;
 | 
			
		||||
@@ -200,7 +198,7 @@ APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
 | 
			
		||||
      return APIError::CONNECTION_CLOSED;
 | 
			
		||||
    }
 | 
			
		||||
    rx_header_buf_len_ += received;
 | 
			
		||||
    if (received != to_read) {
 | 
			
		||||
    if ((size_t) received != to_read) {
 | 
			
		||||
      // not a full read
 | 
			
		||||
      return APIError::WOULD_BLOCK;
 | 
			
		||||
    }
 | 
			
		||||
@@ -247,7 +245,7 @@ APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
 | 
			
		||||
      return APIError::CONNECTION_CLOSED;
 | 
			
		||||
    }
 | 
			
		||||
    rx_buf_len_ += received;
 | 
			
		||||
    if (received != to_read) {
 | 
			
		||||
    if ((size_t) received != to_read) {
 | 
			
		||||
      // not all read
 | 
			
		||||
      return APIError::WOULD_BLOCK;
 | 
			
		||||
    }
 | 
			
		||||
@@ -255,7 +253,7 @@ APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
 | 
			
		||||
 | 
			
		||||
  // uncomment for even more debugging
 | 
			
		||||
#ifdef HELPER_LOG_PACKETS
 | 
			
		||||
  ESP_LOGVV(TAG, "Received frame: %s", hexencode(rx_buf_).c_str());
 | 
			
		||||
  ESP_LOGVV(TAG, "Received frame: %s", format_hex_pretty(rx_buf_).c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  frame->msg = std::move(rx_buf_);
 | 
			
		||||
  // consume msg
 | 
			
		||||
@@ -544,13 +542,13 @@ APIError APINoiseFrameHelper::try_send_tx_buf_() {
 | 
			
		||||
APIError APINoiseFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) {
 | 
			
		||||
  if (iovcnt == 0)
 | 
			
		||||
    return APIError::OK;
 | 
			
		||||
  int err;
 | 
			
		||||
  APIError aerr;
 | 
			
		||||
 | 
			
		||||
  size_t total_write_len = 0;
 | 
			
		||||
  for (int i = 0; i < iovcnt; i++) {
 | 
			
		||||
#ifdef HELPER_LOG_PACKETS
 | 
			
		||||
    ESP_LOGVV(TAG, "Sending raw: %s", hexencode(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len).c_str());
 | 
			
		||||
    ESP_LOGVV(TAG, "Sending raw: %s",
 | 
			
		||||
              format_hex_pretty(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len).c_str());
 | 
			
		||||
#endif
 | 
			
		||||
    total_write_len += iov[i].iov_len;
 | 
			
		||||
  }
 | 
			
		||||
@@ -584,7 +582,7 @@ APIError APINoiseFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) {
 | 
			
		||||
    state_ = State::FAILED;
 | 
			
		||||
    HELPER_LOG("Socket write failed with errno %d", errno);
 | 
			
		||||
    return APIError::SOCKET_WRITE_FAILED;
 | 
			
		||||
  } else if (sent != total_write_len) {
 | 
			
		||||
  } else if ((size_t) sent != total_write_len) {
 | 
			
		||||
    // partially sent, add end to tx_buf
 | 
			
		||||
    size_t to_consume = sent;
 | 
			
		||||
    for (int i = 0; i < iovcnt; i++) {
 | 
			
		||||
@@ -724,7 +722,12 @@ APIError APINoiseFrameHelper::shutdown(int how) {
 | 
			
		||||
}
 | 
			
		||||
extern "C" {
 | 
			
		||||
// declare how noise generates random bytes (here with a good HWRNG based on the RF system)
 | 
			
		||||
void noise_rand_bytes(void *output, size_t len) { esphome::fill_random(reinterpret_cast<uint8_t *>(output), len); }
 | 
			
		||||
void noise_rand_bytes(void *output, size_t len) {
 | 
			
		||||
  if (!esphome::random_bytes(reinterpret_cast<uint8_t *>(output), len)) {
 | 
			
		||||
    ESP_LOGE(TAG, "Failed to acquire random bytes, rebooting!");
 | 
			
		||||
    arch_restart();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
#endif  // USE_API_NOISE
 | 
			
		||||
 | 
			
		||||
@@ -778,9 +781,6 @@ APIError APIPlaintextFrameHelper::loop() {
 | 
			
		||||
 * error API_ERROR_BAD_INDICATOR: Bad indicator byte at start of frame.
 | 
			
		||||
 */
 | 
			
		||||
APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
 | 
			
		||||
  int err;
 | 
			
		||||
  APIError aerr;
 | 
			
		||||
 | 
			
		||||
  if (frame == nullptr) {
 | 
			
		||||
    HELPER_LOG("Bad argument for try_read_frame_");
 | 
			
		||||
    return APIError::BAD_ARG;
 | 
			
		||||
@@ -854,7 +854,7 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
 | 
			
		||||
      return APIError::CONNECTION_CLOSED;
 | 
			
		||||
    }
 | 
			
		||||
    rx_buf_len_ += received;
 | 
			
		||||
    if (received != to_read) {
 | 
			
		||||
    if ((size_t) received != to_read) {
 | 
			
		||||
      // not all read
 | 
			
		||||
      return APIError::WOULD_BLOCK;
 | 
			
		||||
    }
 | 
			
		||||
@@ -862,7 +862,7 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
 | 
			
		||||
 | 
			
		||||
  // uncomment for even more debugging
 | 
			
		||||
#ifdef HELPER_LOG_PACKETS
 | 
			
		||||
  ESP_LOGVV(TAG, "Received frame: %s", hexencode(rx_buf_).c_str());
 | 
			
		||||
  ESP_LOGVV(TAG, "Received frame: %s", format_hex_pretty(rx_buf_).c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  frame->msg = std::move(rx_buf_);
 | 
			
		||||
  // consume msg
 | 
			
		||||
@@ -874,7 +874,6 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) {
 | 
			
		||||
  int err;
 | 
			
		||||
  APIError aerr;
 | 
			
		||||
 | 
			
		||||
  if (state_ != State::DATA) {
 | 
			
		||||
@@ -894,9 +893,6 @@ APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) {
 | 
			
		||||
}
 | 
			
		||||
bool APIPlaintextFrameHelper::can_write_without_blocking() { return state_ == State::DATA && tx_buf_.empty(); }
 | 
			
		||||
APIError APIPlaintextFrameHelper::write_packet(uint16_t type, const uint8_t *payload, size_t payload_len) {
 | 
			
		||||
  int err;
 | 
			
		||||
  APIError aerr;
 | 
			
		||||
 | 
			
		||||
  if (state_ != State::DATA) {
 | 
			
		||||
    return APIError::BAD_STATE;
 | 
			
		||||
  }
 | 
			
		||||
@@ -940,13 +936,13 @@ APIError APIPlaintextFrameHelper::try_send_tx_buf_() {
 | 
			
		||||
APIError APIPlaintextFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) {
 | 
			
		||||
  if (iovcnt == 0)
 | 
			
		||||
    return APIError::OK;
 | 
			
		||||
  int err;
 | 
			
		||||
  APIError aerr;
 | 
			
		||||
 | 
			
		||||
  size_t total_write_len = 0;
 | 
			
		||||
  for (int i = 0; i < iovcnt; i++) {
 | 
			
		||||
#ifdef HELPER_LOG_PACKETS
 | 
			
		||||
    ESP_LOGVV(TAG, "Sending raw: %s", hexencode(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len).c_str());
 | 
			
		||||
    ESP_LOGVV(TAG, "Sending raw: %s",
 | 
			
		||||
              format_hex_pretty(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len).c_str());
 | 
			
		||||
#endif
 | 
			
		||||
    total_write_len += iov[i].iov_len;
 | 
			
		||||
  }
 | 
			
		||||
@@ -980,7 +976,7 @@ APIError APIPlaintextFrameHelper::write_raw_(const struct iovec *iov, int iovcnt
 | 
			
		||||
    state_ = State::FAILED;
 | 
			
		||||
    HELPER_LOG("Socket write failed with errno %d", errno);
 | 
			
		||||
    return APIError::SOCKET_WRITE_FAILED;
 | 
			
		||||
  } else if (sent != total_write_len) {
 | 
			
		||||
  } else if ((size_t) sent != total_write_len) {
 | 
			
		||||
    // partially sent, add end to tx_buf
 | 
			
		||||
    size_t to_consume = sent;
 | 
			
		||||
    for (int i = 0; i < iovcnt; i++) {
 | 
			
		||||
 
 | 
			
		||||
@@ -266,6 +266,18 @@ template<> const char *proto_enum_to_string<enums::ClimatePreset>(enums::Climate
 | 
			
		||||
      return "UNKNOWN";
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
template<> const char *proto_enum_to_string<enums::NumberMode>(enums::NumberMode value) {
 | 
			
		||||
  switch (value) {
 | 
			
		||||
    case enums::NUMBER_MODE_AUTO:
 | 
			
		||||
      return "NUMBER_MODE_AUTO";
 | 
			
		||||
    case enums::NUMBER_MODE_BOX:
 | 
			
		||||
      return "NUMBER_MODE_BOX";
 | 
			
		||||
    case enums::NUMBER_MODE_SLIDER:
 | 
			
		||||
      return "NUMBER_MODE_SLIDER";
 | 
			
		||||
    default:
 | 
			
		||||
      return "UNKNOWN";
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
bool HelloRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 1: {
 | 
			
		||||
@@ -279,7 +291,7 @@ bool HelloRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value)
 | 
			
		||||
void HelloRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->client_info); }
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void HelloRequest::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("HelloRequest {\n");
 | 
			
		||||
  out.append("  client_info: ");
 | 
			
		||||
  out.append("'").append(this->client_info).append("'");
 | 
			
		||||
@@ -318,7 +330,7 @@ void HelloResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void HelloResponse::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("HelloResponse {\n");
 | 
			
		||||
  out.append("  api_version_major: ");
 | 
			
		||||
  sprintf(buffer, "%u", this->api_version_major);
 | 
			
		||||
@@ -349,7 +361,7 @@ bool ConnectRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value
 | 
			
		||||
void ConnectRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->password); }
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void ConnectRequest::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("ConnectRequest {\n");
 | 
			
		||||
  out.append("  password: ");
 | 
			
		||||
  out.append("'").append(this->password).append("'");
 | 
			
		||||
@@ -370,7 +382,7 @@ bool ConnectResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
			
		||||
void ConnectResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(1, this->invalid_password); }
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void ConnectResponse::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("ConnectResponse {\n");
 | 
			
		||||
  out.append("  invalid_password: ");
 | 
			
		||||
  out.append(YESNO(this->invalid_password));
 | 
			
		||||
@@ -464,7 +476,7 @@ void DeviceInfoResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void DeviceInfoResponse::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("DeviceInfoResponse {\n");
 | 
			
		||||
  out.append("  uses_password: ");
 | 
			
		||||
  out.append(YESNO(this->uses_password));
 | 
			
		||||
@@ -588,7 +600,7 @@ void ListEntitiesBinarySensorResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void ListEntitiesBinarySensorResponse::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("ListEntitiesBinarySensorResponse {\n");
 | 
			
		||||
  out.append("  object_id: ");
 | 
			
		||||
  out.append("'").append(this->object_id).append("'");
 | 
			
		||||
@@ -660,7 +672,7 @@ void BinarySensorStateResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void BinarySensorStateResponse::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("BinarySensorStateResponse {\n");
 | 
			
		||||
  out.append("  key: ");
 | 
			
		||||
  sprintf(buffer, "%u", this->key);
 | 
			
		||||
@@ -754,7 +766,7 @@ void ListEntitiesCoverResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void ListEntitiesCoverResponse::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("ListEntitiesCoverResponse {\n");
 | 
			
		||||
  out.append("  object_id: ");
 | 
			
		||||
  out.append("'").append(this->object_id).append("'");
 | 
			
		||||
@@ -844,7 +856,7 @@ void CoverStateResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void CoverStateResponse::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("CoverStateResponse {\n");
 | 
			
		||||
  out.append("  key: ");
 | 
			
		||||
  sprintf(buffer, "%u", this->key);
 | 
			
		||||
@@ -927,7 +939,7 @@ void CoverCommandRequest::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void CoverCommandRequest::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("CoverCommandRequest {\n");
 | 
			
		||||
  out.append("  key: ");
 | 
			
		||||
  sprintf(buffer, "%u", this->key);
 | 
			
		||||
@@ -1043,7 +1055,7 @@ void ListEntitiesFanResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void ListEntitiesFanResponse::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("ListEntitiesFanResponse {\n");
 | 
			
		||||
  out.append("  object_id: ");
 | 
			
		||||
  out.append("'").append(this->object_id).append("'");
 | 
			
		||||
@@ -1139,7 +1151,7 @@ void FanStateResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void FanStateResponse::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("FanStateResponse {\n");
 | 
			
		||||
  out.append("  key: ");
 | 
			
		||||
  sprintf(buffer, "%u", this->key);
 | 
			
		||||
@@ -1240,7 +1252,7 @@ void FanCommandRequest::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void FanCommandRequest::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("FanCommandRequest {\n");
 | 
			
		||||
  out.append("  key: ");
 | 
			
		||||
  sprintf(buffer, "%u", this->key);
 | 
			
		||||
@@ -1391,7 +1403,7 @@ void ListEntitiesLightResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void ListEntitiesLightResponse::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("ListEntitiesLightResponse {\n");
 | 
			
		||||
  out.append("  object_id: ");
 | 
			
		||||
  out.append("'").append(this->object_id).append("'");
 | 
			
		||||
@@ -1549,7 +1561,7 @@ void LightStateResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void LightStateResponse::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("LightStateResponse {\n");
 | 
			
		||||
  out.append("  key: ");
 | 
			
		||||
  sprintf(buffer, "%u", this->key);
 | 
			
		||||
@@ -1772,7 +1784,7 @@ void LightCommandRequest::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void LightCommandRequest::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("LightCommandRequest {\n");
 | 
			
		||||
  out.append("  key: ");
 | 
			
		||||
  sprintf(buffer, "%u", this->key);
 | 
			
		||||
@@ -1983,7 +1995,7 @@ void ListEntitiesSensorResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void ListEntitiesSensorResponse::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("ListEntitiesSensorResponse {\n");
 | 
			
		||||
  out.append("  object_id: ");
 | 
			
		||||
  out.append("'").append(this->object_id).append("'");
 | 
			
		||||
@@ -2072,7 +2084,7 @@ void SensorStateResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void SensorStateResponse::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("SensorStateResponse {\n");
 | 
			
		||||
  out.append("  key: ");
 | 
			
		||||
  sprintf(buffer, "%u", this->key);
 | 
			
		||||
@@ -2152,7 +2164,7 @@ void ListEntitiesSwitchResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void ListEntitiesSwitchResponse::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("ListEntitiesSwitchResponse {\n");
 | 
			
		||||
  out.append("  object_id: ");
 | 
			
		||||
  out.append("'").append(this->object_id).append("'");
 | 
			
		||||
@@ -2215,7 +2227,7 @@ void SwitchStateResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void SwitchStateResponse::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("SwitchStateResponse {\n");
 | 
			
		||||
  out.append("  key: ");
 | 
			
		||||
  sprintf(buffer, "%u", this->key);
 | 
			
		||||
@@ -2254,7 +2266,7 @@ void SwitchCommandRequest::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void SwitchCommandRequest::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("SwitchCommandRequest {\n");
 | 
			
		||||
  out.append("  key: ");
 | 
			
		||||
  sprintf(buffer, "%u", this->key);
 | 
			
		||||
@@ -2324,7 +2336,7 @@ void ListEntitiesTextSensorResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void ListEntitiesTextSensorResponse::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("ListEntitiesTextSensorResponse {\n");
 | 
			
		||||
  out.append("  object_id: ");
 | 
			
		||||
  out.append("'").append(this->object_id).append("'");
 | 
			
		||||
@@ -2394,7 +2406,7 @@ void TextSensorStateResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void TextSensorStateResponse::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("TextSensorStateResponse {\n");
 | 
			
		||||
  out.append("  key: ");
 | 
			
		||||
  sprintf(buffer, "%u", this->key);
 | 
			
		||||
@@ -2431,7 +2443,7 @@ void SubscribeLogsRequest::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void SubscribeLogsRequest::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("SubscribeLogsRequest {\n");
 | 
			
		||||
  out.append("  level: ");
 | 
			
		||||
  out.append(proto_enum_to_string<enums::LogLevel>(this->level));
 | 
			
		||||
@@ -2474,7 +2486,7 @@ void SubscribeLogsResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void SubscribeLogsResponse::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("SubscribeLogsResponse {\n");
 | 
			
		||||
  out.append("  level: ");
 | 
			
		||||
  out.append(proto_enum_to_string<enums::LogLevel>(this->level));
 | 
			
		||||
@@ -2516,7 +2528,7 @@ void HomeassistantServiceMap::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void HomeassistantServiceMap::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("HomeassistantServiceMap {\n");
 | 
			
		||||
  out.append("  key: ");
 | 
			
		||||
  out.append("'").append(this->key).append("'");
 | 
			
		||||
@@ -2575,7 +2587,7 @@ void HomeassistantServiceResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void HomeassistantServiceResponse::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("HomeassistantServiceResponse {\n");
 | 
			
		||||
  out.append("  service: ");
 | 
			
		||||
  out.append("'").append(this->service).append("'");
 | 
			
		||||
@@ -2631,7 +2643,7 @@ void SubscribeHomeAssistantStateResponse::encode(ProtoWriteBuffer buffer) const
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void SubscribeHomeAssistantStateResponse::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("SubscribeHomeAssistantStateResponse {\n");
 | 
			
		||||
  out.append("  entity_id: ");
 | 
			
		||||
  out.append("'").append(this->entity_id).append("'");
 | 
			
		||||
@@ -2668,7 +2680,7 @@ void HomeAssistantStateResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void HomeAssistantStateResponse::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("HomeAssistantStateResponse {\n");
 | 
			
		||||
  out.append("  entity_id: ");
 | 
			
		||||
  out.append("'").append(this->entity_id).append("'");
 | 
			
		||||
@@ -2701,7 +2713,7 @@ bool GetTimeResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
 | 
			
		||||
void GetTimeResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->epoch_seconds); }
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void GetTimeResponse::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("GetTimeResponse {\n");
 | 
			
		||||
  out.append("  epoch_seconds: ");
 | 
			
		||||
  sprintf(buffer, "%u", this->epoch_seconds);
 | 
			
		||||
@@ -2736,7 +2748,7 @@ void ListEntitiesServicesArgument::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void ListEntitiesServicesArgument::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("ListEntitiesServicesArgument {\n");
 | 
			
		||||
  out.append("  name: ");
 | 
			
		||||
  out.append("'").append(this->name).append("'");
 | 
			
		||||
@@ -2781,7 +2793,7 @@ void ListEntitiesServicesResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void ListEntitiesServicesResponse::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("ListEntitiesServicesResponse {\n");
 | 
			
		||||
  out.append("  name: ");
 | 
			
		||||
  out.append("'").append(this->name).append("'");
 | 
			
		||||
@@ -2875,7 +2887,7 @@ void ExecuteServiceArgument::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void ExecuteServiceArgument::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("ExecuteServiceArgument {\n");
 | 
			
		||||
  out.append("  bool_: ");
 | 
			
		||||
  out.append(YESNO(this->bool_));
 | 
			
		||||
@@ -2956,7 +2968,7 @@ void ExecuteServiceRequest::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void ExecuteServiceRequest::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("ExecuteServiceRequest {\n");
 | 
			
		||||
  out.append("  key: ");
 | 
			
		||||
  sprintf(buffer, "%u", this->key);
 | 
			
		||||
@@ -3028,7 +3040,7 @@ void ListEntitiesCameraResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void ListEntitiesCameraResponse::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("ListEntitiesCameraResponse {\n");
 | 
			
		||||
  out.append("  object_id: ");
 | 
			
		||||
  out.append("'").append(this->object_id).append("'");
 | 
			
		||||
@@ -3098,7 +3110,7 @@ void CameraImageResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void CameraImageResponse::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("CameraImageResponse {\n");
 | 
			
		||||
  out.append("  key: ");
 | 
			
		||||
  sprintf(buffer, "%u", this->key);
 | 
			
		||||
@@ -3135,7 +3147,7 @@ void CameraImageRequest::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void CameraImageRequest::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("CameraImageRequest {\n");
 | 
			
		||||
  out.append("  single: ");
 | 
			
		||||
  out.append(YESNO(this->single));
 | 
			
		||||
@@ -3281,7 +3293,7 @@ void ListEntitiesClimateResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void ListEntitiesClimateResponse::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("ListEntitiesClimateResponse {\n");
 | 
			
		||||
  out.append("  object_id: ");
 | 
			
		||||
  out.append("'").append(this->object_id).append("'");
 | 
			
		||||
@@ -3468,7 +3480,7 @@ void ClimateStateResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void ClimateStateResponse::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("ClimateStateResponse {\n");
 | 
			
		||||
  out.append("  key: ");
 | 
			
		||||
  sprintf(buffer, "%u", this->key);
 | 
			
		||||
@@ -3656,7 +3668,7 @@ void ClimateCommandRequest::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void ClimateCommandRequest::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("ClimateCommandRequest {\n");
 | 
			
		||||
  out.append("  key: ");
 | 
			
		||||
  sprintf(buffer, "%u", this->key);
 | 
			
		||||
@@ -3758,6 +3770,10 @@ bool ListEntitiesNumberResponse::decode_varint(uint32_t field_id, ProtoVarInt va
 | 
			
		||||
      this->entity_category = value.as_enum<enums::EntityCategory>();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 12: {
 | 
			
		||||
      this->mode = value.as_enum<enums::NumberMode>();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
@@ -3780,6 +3796,10 @@ bool ListEntitiesNumberResponse::decode_length(uint32_t field_id, ProtoLengthDel
 | 
			
		||||
      this->icon = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 11: {
 | 
			
		||||
      this->unit_of_measurement = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
@@ -3817,10 +3837,12 @@ void ListEntitiesNumberResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
  buffer.encode_float(8, this->step);
 | 
			
		||||
  buffer.encode_bool(9, this->disabled_by_default);
 | 
			
		||||
  buffer.encode_enum<enums::EntityCategory>(10, this->entity_category);
 | 
			
		||||
  buffer.encode_string(11, this->unit_of_measurement);
 | 
			
		||||
  buffer.encode_enum<enums::NumberMode>(12, this->mode);
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void ListEntitiesNumberResponse::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("ListEntitiesNumberResponse {\n");
 | 
			
		||||
  out.append("  object_id: ");
 | 
			
		||||
  out.append("'").append(this->object_id).append("'");
 | 
			
		||||
@@ -3865,6 +3887,14 @@ void ListEntitiesNumberResponse::dump_to(std::string &out) const {
 | 
			
		||||
  out.append("  entity_category: ");
 | 
			
		||||
  out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  unit_of_measurement: ");
 | 
			
		||||
  out.append("'").append(this->unit_of_measurement).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  mode: ");
 | 
			
		||||
  out.append(proto_enum_to_string<enums::NumberMode>(this->mode));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
@@ -3899,7 +3929,7 @@ void NumberStateResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void NumberStateResponse::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("NumberStateResponse {\n");
 | 
			
		||||
  out.append("  key: ");
 | 
			
		||||
  sprintf(buffer, "%u", this->key);
 | 
			
		||||
@@ -3937,7 +3967,7 @@ void NumberCommandRequest::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void NumberCommandRequest::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("NumberCommandRequest {\n");
 | 
			
		||||
  out.append("  key: ");
 | 
			
		||||
  sprintf(buffer, "%u", this->key);
 | 
			
		||||
@@ -4015,7 +4045,7 @@ void ListEntitiesSelectResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void ListEntitiesSelectResponse::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("ListEntitiesSelectResponse {\n");
 | 
			
		||||
  out.append("  object_id: ");
 | 
			
		||||
  out.append("'").append(this->object_id).append("'");
 | 
			
		||||
@@ -4091,7 +4121,7 @@ void SelectStateResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void SelectStateResponse::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("SelectStateResponse {\n");
 | 
			
		||||
  out.append("  key: ");
 | 
			
		||||
  sprintf(buffer, "%u", this->key);
 | 
			
		||||
@@ -4134,7 +4164,7 @@ void SelectCommandRequest::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void SelectCommandRequest::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  __attribute__((unused)) char buffer[64];
 | 
			
		||||
  out.append("SelectCommandRequest {\n");
 | 
			
		||||
  out.append("  key: ");
 | 
			
		||||
  sprintf(buffer, "%u", this->key);
 | 
			
		||||
@@ -4147,6 +4177,127 @@ void SelectCommandRequest::dump_to(std::string &out) const {
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
bool ListEntitiesButtonResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 6: {
 | 
			
		||||
      this->disabled_by_default = value.as_bool();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 7: {
 | 
			
		||||
      this->entity_category = value.as_enum<enums::EntityCategory>();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
bool ListEntitiesButtonResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 1: {
 | 
			
		||||
      this->object_id = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 3: {
 | 
			
		||||
      this->name = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 4: {
 | 
			
		||||
      this->unique_id = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 5: {
 | 
			
		||||
      this->icon = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case 8: {
 | 
			
		||||
      this->device_class = value.as_string();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
bool ListEntitiesButtonResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 2: {
 | 
			
		||||
      this->key = value.as_fixed32();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void ListEntitiesButtonResponse::encode(ProtoWriteBuffer buffer) const {
 | 
			
		||||
  buffer.encode_string(1, this->object_id);
 | 
			
		||||
  buffer.encode_fixed32(2, this->key);
 | 
			
		||||
  buffer.encode_string(3, this->name);
 | 
			
		||||
  buffer.encode_string(4, this->unique_id);
 | 
			
		||||
  buffer.encode_string(5, this->icon);
 | 
			
		||||
  buffer.encode_bool(6, this->disabled_by_default);
 | 
			
		||||
  buffer.encode_enum<enums::EntityCategory>(7, this->entity_category);
 | 
			
		||||
  buffer.encode_string(8, this->device_class);
 | 
			
		||||
}
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void ListEntitiesButtonResponse::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  out.append("ListEntitiesButtonResponse {\n");
 | 
			
		||||
  out.append("  object_id: ");
 | 
			
		||||
  out.append("'").append(this->object_id).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  key: ");
 | 
			
		||||
  sprintf(buffer, "%u", this->key);
 | 
			
		||||
  out.append(buffer);
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  name: ");
 | 
			
		||||
  out.append("'").append(this->name).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  unique_id: ");
 | 
			
		||||
  out.append("'").append(this->unique_id).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  icon: ");
 | 
			
		||||
  out.append("'").append(this->icon).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  disabled_by_default: ");
 | 
			
		||||
  out.append(YESNO(this->disabled_by_default));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  entity_category: ");
 | 
			
		||||
  out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
 | 
			
		||||
  out.append("  device_class: ");
 | 
			
		||||
  out.append("'").append(this->device_class).append("'");
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
bool ButtonCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
 | 
			
		||||
  switch (field_id) {
 | 
			
		||||
    case 1: {
 | 
			
		||||
      this->key = value.as_fixed32();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void ButtonCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); }
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
void ButtonCommandRequest::dump_to(std::string &out) const {
 | 
			
		||||
  char buffer[64];
 | 
			
		||||
  out.append("ButtonCommandRequest {\n");
 | 
			
		||||
  out.append("  key: ");
 | 
			
		||||
  sprintf(buffer, "%u", this->key);
 | 
			
		||||
  out.append(buffer);
 | 
			
		||||
  out.append("\n");
 | 
			
		||||
  out.append("}");
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -123,6 +123,11 @@ enum ClimatePreset : uint32_t {
 | 
			
		||||
  CLIMATE_PRESET_SLEEP = 6,
 | 
			
		||||
  CLIMATE_PRESET_ACTIVITY = 7,
 | 
			
		||||
};
 | 
			
		||||
enum NumberMode : uint32_t {
 | 
			
		||||
  NUMBER_MODE_AUTO = 0,
 | 
			
		||||
  NUMBER_MODE_BOX = 1,
 | 
			
		||||
  NUMBER_MODE_SLIDER = 2,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace enums
 | 
			
		||||
 | 
			
		||||
@@ -957,6 +962,8 @@ class ListEntitiesNumberResponse : public ProtoMessage {
 | 
			
		||||
  float step{0.0f};
 | 
			
		||||
  bool disabled_by_default{false};
 | 
			
		||||
  enums::EntityCategory entity_category{};
 | 
			
		||||
  std::string unit_of_measurement{};
 | 
			
		||||
  enums::NumberMode mode{};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
@@ -1041,6 +1048,37 @@ class SelectCommandRequest : public ProtoMessage {
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
};
 | 
			
		||||
class ListEntitiesButtonResponse : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  std::string object_id{};
 | 
			
		||||
  uint32_t key{0};
 | 
			
		||||
  std::string name{};
 | 
			
		||||
  std::string unique_id{};
 | 
			
		||||
  std::string icon{};
 | 
			
		||||
  bool disabled_by_default{false};
 | 
			
		||||
  enums::EntityCategory entity_category{};
 | 
			
		||||
  std::string device_class{};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
 | 
			
		||||
  bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
 | 
			
		||||
  bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
 | 
			
		||||
};
 | 
			
		||||
class ButtonCommandRequest : public ProtoMessage {
 | 
			
		||||
 public:
 | 
			
		||||
  uint32_t key{0};
 | 
			
		||||
  void encode(ProtoWriteBuffer buffer) const override;
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  void dump_to(std::string &out) const override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -282,6 +282,16 @@ bool APIServerConnectionBase::send_select_state_response(const SelectStateRespon
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SELECT
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BUTTON
 | 
			
		||||
bool APIServerConnectionBase::send_list_entities_button_response(const ListEntitiesButtonResponse &msg) {
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
  ESP_LOGVV(TAG, "send_list_entities_button_response: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
  return this->send_message_<ListEntitiesButtonResponse>(msg, 61);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BUTTON
 | 
			
		||||
#endif
 | 
			
		||||
bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {
 | 
			
		||||
  switch (msg_type) {
 | 
			
		||||
    case 1: {
 | 
			
		||||
@@ -513,6 +523,17 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
 | 
			
		||||
      ESP_LOGVV(TAG, "on_select_command_request: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
      this->on_select_command_request(msg);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case 62: {
 | 
			
		||||
#ifdef USE_BUTTON
 | 
			
		||||
      ButtonCommandRequest msg;
 | 
			
		||||
      msg.decode(msg_data, msg_size);
 | 
			
		||||
#ifdef HAS_PROTO_MESSAGE_DUMP
 | 
			
		||||
      ESP_LOGVV(TAG, "on_button_command_request: %s", msg.dump().c_str());
 | 
			
		||||
#endif
 | 
			
		||||
      this->on_button_command_request(msg);
 | 
			
		||||
#endif
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
@@ -737,6 +758,19 @@ void APIServerConnection::on_select_command_request(const SelectCommandRequest &
 | 
			
		||||
  this->select_command(msg);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BUTTON
 | 
			
		||||
void APIServerConnection::on_button_command_request(const ButtonCommandRequest &msg) {
 | 
			
		||||
  if (!this->is_connection_setup()) {
 | 
			
		||||
    this->on_no_setup_connection();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (!this->is_authenticated()) {
 | 
			
		||||
    this->on_unauthenticated_access();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->button_command(msg);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -129,6 +129,12 @@ class APIServerConnectionBase : public ProtoService {
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SELECT
 | 
			
		||||
  virtual void on_select_command_request(const SelectCommandRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BUTTON
 | 
			
		||||
  bool send_list_entities_button_response(const ListEntitiesButtonResponse &msg);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BUTTON
 | 
			
		||||
  virtual void on_button_command_request(const ButtonCommandRequest &value){};
 | 
			
		||||
#endif
 | 
			
		||||
 protected:
 | 
			
		||||
  bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;
 | 
			
		||||
@@ -171,6 +177,9 @@ class APIServerConnection : public APIServerConnectionBase {
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_SELECT
 | 
			
		||||
  virtual void select_command(const SelectCommandRequest &msg) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BUTTON
 | 
			
		||||
  virtual void button_command(const ButtonCommandRequest &msg) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
 protected:
 | 
			
		||||
  void on_hello_request(const HelloRequest &msg) override;
 | 
			
		||||
@@ -209,6 +218,9 @@ class APIServerConnection : public APIServerConnectionBase {
 | 
			
		||||
#ifdef USE_SELECT
 | 
			
		||||
  void on_select_command_request(const SelectCommandRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BUTTON
 | 
			
		||||
  void on_button_command_request(const ButtonCommandRequest &msg) override;
 | 
			
		||||
#endif
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace api
 | 
			
		||||
 
 | 
			
		||||
@@ -12,10 +12,10 @@ template<typename... X> class TemplatableStringValue : public TemplatableValue<s
 | 
			
		||||
 public:
 | 
			
		||||
  TemplatableStringValue() : TemplatableValue<std::string, X...>() {}
 | 
			
		||||
 | 
			
		||||
  template<typename F, enable_if_t<!is_callable<F, X...>::value, int> = 0>
 | 
			
		||||
  template<typename F, enable_if_t<!is_invocable<F, X...>::value, int> = 0>
 | 
			
		||||
  TemplatableStringValue(F value) : TemplatableValue<std::string, X...>(value) {}
 | 
			
		||||
 | 
			
		||||
  template<typename F, enable_if_t<is_callable<F, X...>::value, int> = 0>
 | 
			
		||||
  template<typename F, enable_if_t<is_invocable<F, X...>::value, int> = 0>
 | 
			
		||||
  TemplatableStringValue(F f)
 | 
			
		||||
      : TemplatableValue<std::string, X...>([f](X... x) -> std::string { return to_string(f(x...)); }) {}
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -27,6 +27,9 @@ bool ListEntitiesIterator::on_sensor(sensor::Sensor *sensor) { return this->clie
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
bool ListEntitiesIterator::on_switch(switch_::Switch *a_switch) { return this->client_->send_switch_info(a_switch); }
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BUTTON
 | 
			
		||||
bool ListEntitiesIterator::on_button(button::Button *button) { return this->client_->send_button_info(button); }
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_TEXT_SENSOR
 | 
			
		||||
bool ListEntitiesIterator::on_text_sensor(text_sensor::TextSensor *text_sensor) {
 | 
			
		||||
  return this->client_->send_text_sensor_info(text_sensor);
 | 
			
		||||
 
 | 
			
		||||
@@ -30,6 +30,9 @@ class ListEntitiesIterator : public ComponentIterator {
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
  bool on_switch(switch_::Switch *a_switch) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BUTTON
 | 
			
		||||
  bool on_button(button::Button *button) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_TEXT_SENSOR
 | 
			
		||||
  bool on_text_sensor(text_sensor::TextSensor *text_sensor) override;
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -31,6 +31,9 @@ class InitialStateIterator : public ComponentIterator {
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
  bool on_switch(switch_::Switch *a_switch) override;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BUTTON
 | 
			
		||||
  bool on_button(button::Button *button) override { return true; };
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_TEXT_SENSOR
 | 
			
		||||
  bool on_text_sensor(text_sensor::TextSensor *text_sensor) override;
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -116,6 +116,21 @@ void ComponentIterator::advance() {
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BUTTON
 | 
			
		||||
    case IteratorState::BUTTON:
 | 
			
		||||
      if (this->at_ >= App.get_buttons().size()) {
 | 
			
		||||
        advance_platform = true;
 | 
			
		||||
      } else {
 | 
			
		||||
        auto *button = App.get_buttons()[this->at_];
 | 
			
		||||
        if (button->is_internal()) {
 | 
			
		||||
          success = true;
 | 
			
		||||
          break;
 | 
			
		||||
        } else {
 | 
			
		||||
          success = this->on_button(button);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_TEXT_SENSOR
 | 
			
		||||
    case IteratorState::TEXT_SENSOR:
 | 
			
		||||
      if (this->at_ >= App.get_text_sensors().size()) {
 | 
			
		||||
 
 | 
			
		||||
@@ -38,6 +38,9 @@ class ComponentIterator {
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
  virtual bool on_switch(switch_::Switch *a_switch) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BUTTON
 | 
			
		||||
  virtual bool on_button(button::Button *button) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_TEXT_SENSOR
 | 
			
		||||
  virtual bool on_text_sensor(text_sensor::TextSensor *text_sensor) = 0;
 | 
			
		||||
#endif
 | 
			
		||||
@@ -78,6 +81,9 @@ class ComponentIterator {
 | 
			
		||||
#ifdef USE_SWITCH
 | 
			
		||||
    SWITCH,
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_BUTTON
 | 
			
		||||
    BUTTON,
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_TEXT_SENSOR
 | 
			
		||||
    TEXT_SENSOR,
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -45,6 +45,8 @@ bool ATCMiThermometer::parse_device(const esp32_ble_tracker::ESPBTDevice &device
 | 
			
		||||
      this->battery_voltage_->publish_state(*res->battery_voltage);
 | 
			
		||||
    success = true;
 | 
			
		||||
  }
 | 
			
		||||
  if (this->signal_strength_ != nullptr)
 | 
			
		||||
    this->signal_strength_->publish_state(device.get_rssi());
 | 
			
		||||
 | 
			
		||||
  return success;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -28,6 +28,7 @@ class ATCMiThermometer : public Component, public esp32_ble_tracker::ESPBTDevice
 | 
			
		||||
  void set_humidity(sensor::Sensor *humidity) { humidity_ = humidity; }
 | 
			
		||||
  void set_battery_level(sensor::Sensor *battery_level) { battery_level_ = battery_level; }
 | 
			
		||||
  void set_battery_voltage(sensor::Sensor *battery_voltage) { battery_voltage_ = battery_voltage; }
 | 
			
		||||
  void set_signal_strength(sensor::Sensor *signal_strength) { signal_strength_ = signal_strength; }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  uint64_t address_;
 | 
			
		||||
@@ -35,6 +36,7 @@ class ATCMiThermometer : public Component, public esp32_ble_tracker::ESPBTDevice
 | 
			
		||||
  sensor::Sensor *humidity_{nullptr};
 | 
			
		||||
  sensor::Sensor *battery_level_{nullptr};
 | 
			
		||||
  sensor::Sensor *battery_voltage_{nullptr};
 | 
			
		||||
  sensor::Sensor *signal_strength_{nullptr};
 | 
			
		||||
 | 
			
		||||
  optional<ParseResult> parse_header_(const esp32_ble_tracker::ServiceData &service_data);
 | 
			
		||||
  bool parse_message_(const std::vector<uint8_t> &message, ParseResult &result);
 | 
			
		||||
 
 | 
			
		||||
@@ -6,15 +6,18 @@ from esphome.const import (
 | 
			
		||||
    CONF_BATTERY_VOLTAGE,
 | 
			
		||||
    CONF_MAC_ADDRESS,
 | 
			
		||||
    CONF_HUMIDITY,
 | 
			
		||||
    CONF_SIGNAL_STRENGTH,
 | 
			
		||||
    CONF_TEMPERATURE,
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    DEVICE_CLASS_BATTERY,
 | 
			
		||||
    DEVICE_CLASS_HUMIDITY,
 | 
			
		||||
    DEVICE_CLASS_SIGNAL_STRENGTH,
 | 
			
		||||
    DEVICE_CLASS_TEMPERATURE,
 | 
			
		||||
    DEVICE_CLASS_VOLTAGE,
 | 
			
		||||
    ENTITY_CATEGORY_DIAGNOSTIC,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    UNIT_CELSIUS,
 | 
			
		||||
    UNIT_DECIBEL_MILLIWATT,
 | 
			
		||||
    UNIT_PERCENT,
 | 
			
		||||
    UNIT_VOLT,
 | 
			
		||||
)
 | 
			
		||||
@@ -59,6 +62,13 @@ CONFIG_SCHEMA = (
 | 
			
		||||
                state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
                entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_SIGNAL_STRENGTH): sensor.sensor_schema(
 | 
			
		||||
                unit_of_measurement=UNIT_DECIBEL_MILLIWATT,
 | 
			
		||||
                accuracy_decimals=0,
 | 
			
		||||
                device_class=DEVICE_CLASS_SIGNAL_STRENGTH,
 | 
			
		||||
                state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
                entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
    .extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
 | 
			
		||||
@@ -85,3 +95,6 @@ async def to_code(config):
 | 
			
		||||
    if CONF_BATTERY_VOLTAGE in config:
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_BATTERY_VOLTAGE])
 | 
			
		||||
        cg.add(var.set_battery_voltage(sens))
 | 
			
		||||
    if CONF_SIGNAL_STRENGTH in config:
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_SIGNAL_STRENGTH])
 | 
			
		||||
        cg.add(var.set_signal_strength(sens))
 | 
			
		||||
 
 | 
			
		||||
@@ -80,21 +80,23 @@ void BangBangClimate::compute_state_() {
 | 
			
		||||
 | 
			
		||||
  climate::ClimateAction target_action;
 | 
			
		||||
  if (too_cold) {
 | 
			
		||||
    // too cold -> enable heating if possible, else idle
 | 
			
		||||
    if (this->supports_heat_)
 | 
			
		||||
    // too cold -> enable heating if possible and enabled, else idle
 | 
			
		||||
    if (this->supports_heat_ &&
 | 
			
		||||
        (this->mode == climate::CLIMATE_MODE_HEAT_COOL || this->mode == climate::CLIMATE_MODE_HEAT))
 | 
			
		||||
      target_action = climate::CLIMATE_ACTION_HEATING;
 | 
			
		||||
    else
 | 
			
		||||
      target_action = climate::CLIMATE_ACTION_IDLE;
 | 
			
		||||
  } else if (too_hot) {
 | 
			
		||||
    // too hot -> enable cooling if possible, else idle
 | 
			
		||||
    if (this->supports_cool_)
 | 
			
		||||
    // too hot -> enable cooling if possible and enabled, else idle
 | 
			
		||||
    if (this->supports_cool_ &&
 | 
			
		||||
        (this->mode == climate::CLIMATE_MODE_HEAT_COOL || this->mode == climate::CLIMATE_MODE_COOL))
 | 
			
		||||
      target_action = climate::CLIMATE_ACTION_COOLING;
 | 
			
		||||
    else
 | 
			
		||||
      target_action = climate::CLIMATE_ACTION_IDLE;
 | 
			
		||||
  } else {
 | 
			
		||||
    // neither too hot nor too cold -> in range
 | 
			
		||||
    if (this->supports_cool_ && this->supports_heat_) {
 | 
			
		||||
      // if supports both ends, go to idle action
 | 
			
		||||
    if (this->supports_cool_ && this->supports_heat_ && this->mode == climate::CLIMATE_MODE_HEAT_COOL) {
 | 
			
		||||
      // if supports both ends and both cooling and heating enabled, go to idle action
 | 
			
		||||
      target_action = climate::CLIMATE_ACTION_IDLE;
 | 
			
		||||
    } else {
 | 
			
		||||
      // else use current mode and don't change (hysteresis)
 | 
			
		||||
 
 | 
			
		||||
@@ -48,7 +48,10 @@ void BinarySensor::set_device_class(const std::string &device_class) { this->dev
 | 
			
		||||
std::string BinarySensor::get_device_class() {
 | 
			
		||||
  if (this->device_class_.has_value())
 | 
			
		||||
    return *this->device_class_;
 | 
			
		||||
#pragma GCC diagnostic push
 | 
			
		||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
 | 
			
		||||
  return this->device_class();
 | 
			
		||||
#pragma GCC diagnostic pop
 | 
			
		||||
}
 | 
			
		||||
void BinarySensor::add_filter(Filter *filter) {
 | 
			
		||||
  filter->parent_ = this;
 | 
			
		||||
 
 | 
			
		||||
@@ -74,7 +74,10 @@ class BinarySensor : public EntityBase {
 | 
			
		||||
 | 
			
		||||
  // ========== OVERRIDE METHODS ==========
 | 
			
		||||
  // (You'll only need this when creating your own custom binary sensor)
 | 
			
		||||
  /// Get the default device class for this sensor, or empty string for no default.
 | 
			
		||||
  /** Override this to set the default device class.
 | 
			
		||||
   *
 | 
			
		||||
   * @deprecated This method is deprecated, set the property during config validation instead. (2022.1)
 | 
			
		||||
   */
 | 
			
		||||
  virtual std::string device_class();
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								esphome/components/bl0940/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								esphome/components/bl0940/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
CODEOWNERS = ["@tobias-"]
 | 
			
		||||
							
								
								
									
										137
									
								
								esphome/components/bl0940/bl0940.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								esphome/components/bl0940/bl0940.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,137 @@
 | 
			
		||||
#include "bl0940.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace bl0940 {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "bl0940";
 | 
			
		||||
 | 
			
		||||
static const uint8_t BL0940_READ_COMMAND = 0x50;  // 0x58 according to documentation
 | 
			
		||||
static const uint8_t BL0940_FULL_PACKET = 0xAA;
 | 
			
		||||
static const uint8_t BL0940_PACKET_HEADER = 0x55;  // 0x58 according to documentation
 | 
			
		||||
 | 
			
		||||
static const uint8_t BL0940_WRITE_COMMAND = 0xA0;  // 0xA8 according to documentation
 | 
			
		||||
static const uint8_t BL0940_REG_I_FAST_RMS_CTRL = 0x10;
 | 
			
		||||
static const uint8_t BL0940_REG_MODE = 0x18;
 | 
			
		||||
static const uint8_t BL0940_REG_SOFT_RESET = 0x19;
 | 
			
		||||
static const uint8_t BL0940_REG_USR_WRPROT = 0x1A;
 | 
			
		||||
static const uint8_t BL0940_REG_TPS_CTRL = 0x1B;
 | 
			
		||||
 | 
			
		||||
const uint8_t BL0940_INIT[5][6] = {
 | 
			
		||||
    // Reset to default
 | 
			
		||||
    {BL0940_WRITE_COMMAND, BL0940_REG_SOFT_RESET, 0x5A, 0x5A, 0x5A, 0x38},
 | 
			
		||||
    // Enable User Operation Write
 | 
			
		||||
    {BL0940_WRITE_COMMAND, BL0940_REG_USR_WRPROT, 0x55, 0x00, 0x00, 0xF0},
 | 
			
		||||
    // 0x0100 = CF_UNABLE energy pulse, AC_FREQ_SEL 50Hz, RMS_UPDATE_SEL 800mS
 | 
			
		||||
    {BL0940_WRITE_COMMAND, BL0940_REG_MODE, 0x00, 0x10, 0x00, 0x37},
 | 
			
		||||
    // 0x47FF = Over-current and leakage alarm on, Automatic temperature measurement, Interval 100mS
 | 
			
		||||
    {BL0940_WRITE_COMMAND, BL0940_REG_TPS_CTRL, 0xFF, 0x47, 0x00, 0xFE},
 | 
			
		||||
    // 0x181C = Half cycle, Fast RMS threshold 6172
 | 
			
		||||
    {BL0940_WRITE_COMMAND, BL0940_REG_I_FAST_RMS_CTRL, 0x1C, 0x18, 0x00, 0x1B}};
 | 
			
		||||
 | 
			
		||||
void BL0940::loop() {
 | 
			
		||||
  DataPacket buffer;
 | 
			
		||||
  if (!this->available()) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (read_array((uint8_t *) &buffer, sizeof(buffer))) {
 | 
			
		||||
    if (validate_checksum(&buffer)) {
 | 
			
		||||
      received_package_(&buffer);
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    ESP_LOGW(TAG, "Junk on wire. Throwing away partial message");
 | 
			
		||||
    while (read() >= 0)
 | 
			
		||||
      ;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool BL0940::validate_checksum(const DataPacket *data) {
 | 
			
		||||
  uint8_t checksum = BL0940_READ_COMMAND;
 | 
			
		||||
  // Whole package but checksum
 | 
			
		||||
  for (uint32_t i = 0; i < sizeof(data->raw) - 1; i++) {
 | 
			
		||||
    checksum += data->raw[i];
 | 
			
		||||
  }
 | 
			
		||||
  checksum ^= 0xFF;
 | 
			
		||||
  if (checksum != data->checksum) {
 | 
			
		||||
    ESP_LOGW(TAG, "BL0940 invalid checksum! 0x%02X != 0x%02X", checksum, data->checksum);
 | 
			
		||||
  }
 | 
			
		||||
  return checksum == data->checksum;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BL0940::update() {
 | 
			
		||||
  this->flush();
 | 
			
		||||
  this->write_byte(BL0940_READ_COMMAND);
 | 
			
		||||
  this->write_byte(BL0940_FULL_PACKET);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BL0940::setup() {
 | 
			
		||||
  for (auto i : BL0940_INIT) {
 | 
			
		||||
    this->write_array(i, 6);
 | 
			
		||||
    delay(1);
 | 
			
		||||
  }
 | 
			
		||||
  this->flush();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float BL0940::update_temp_(sensor::Sensor *sensor, ube16_t temperature) const {
 | 
			
		||||
  auto tb = (float) (temperature.h << 8 | temperature.l);
 | 
			
		||||
  float converted_temp = ((float) 170 / 448) * (tb / 2 - 32) - 45;
 | 
			
		||||
  if (sensor != nullptr) {
 | 
			
		||||
    if (sensor->has_state() && std::abs(converted_temp - sensor->get_state()) > max_temperature_diff_) {
 | 
			
		||||
      ESP_LOGD("bl0940", "Invalid temperature change. Sensor: '%s', Old temperature: %f, New temperature: %f",
 | 
			
		||||
               sensor->get_name().c_str(), sensor->get_state(), converted_temp);
 | 
			
		||||
      return 0.0f;
 | 
			
		||||
    }
 | 
			
		||||
    sensor->publish_state(converted_temp);
 | 
			
		||||
  }
 | 
			
		||||
  return converted_temp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BL0940::received_package_(const DataPacket *data) const {
 | 
			
		||||
  // Bad header
 | 
			
		||||
  if (data->frame_header != BL0940_PACKET_HEADER) {
 | 
			
		||||
    ESP_LOGI("bl0940", "Invalid data. Header mismatch: %d", data->frame_header);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  float v_rms = (float) to_uint32_t(data->v_rms) / voltage_reference_;
 | 
			
		||||
  float i_rms = (float) to_uint32_t(data->i_rms) / current_reference_;
 | 
			
		||||
  float watt = (float) to_int32_t(data->watt) / power_reference_;
 | 
			
		||||
  uint32_t cf_cnt = to_uint32_t(data->cf_cnt);
 | 
			
		||||
  float total_energy_consumption = (float) cf_cnt / energy_reference_;
 | 
			
		||||
 | 
			
		||||
  float tps1 = update_temp_(internal_temperature_sensor_, data->tps1);
 | 
			
		||||
  float tps2 = update_temp_(external_temperature_sensor_, data->tps2);
 | 
			
		||||
 | 
			
		||||
  if (voltage_sensor_ != nullptr) {
 | 
			
		||||
    voltage_sensor_->publish_state(v_rms);
 | 
			
		||||
  }
 | 
			
		||||
  if (current_sensor_ != nullptr) {
 | 
			
		||||
    current_sensor_->publish_state(i_rms);
 | 
			
		||||
  }
 | 
			
		||||
  if (power_sensor_ != nullptr) {
 | 
			
		||||
    power_sensor_->publish_state(watt);
 | 
			
		||||
  }
 | 
			
		||||
  if (energy_sensor_ != nullptr) {
 | 
			
		||||
    energy_sensor_->publish_state(total_energy_consumption);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ESP_LOGV("bl0940", "BL0940: U %fV, I %fA, P %fW, Cnt %d, ∫P %fkWh, T1 %f°C, T2 %f°C", v_rms, i_rms, watt, cf_cnt,
 | 
			
		||||
           total_energy_consumption, tps1, tps2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BL0940::dump_config() {  // NOLINT(readability-function-cognitive-complexity)
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "BL0940:");
 | 
			
		||||
  LOG_SENSOR("", "Voltage", this->voltage_sensor_);
 | 
			
		||||
  LOG_SENSOR("", "Current", this->current_sensor_);
 | 
			
		||||
  LOG_SENSOR("", "Power", this->power_sensor_);
 | 
			
		||||
  LOG_SENSOR("", "Energy", this->energy_sensor_);
 | 
			
		||||
  LOG_SENSOR("", "Internal temperature", this->internal_temperature_sensor_);
 | 
			
		||||
  LOG_SENSOR("", "External temperature", this->external_temperature_sensor_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t BL0940::to_uint32_t(ube24_t input) { return input.h << 16 | input.m << 8 | input.l; }
 | 
			
		||||
 | 
			
		||||
int32_t BL0940::to_int32_t(sbe24_t input) { return input.h << 16 | input.m << 8 | input.l; }
 | 
			
		||||
 | 
			
		||||
}  // namespace bl0940
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										109
									
								
								esphome/components/bl0940/bl0940.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								esphome/components/bl0940/bl0940.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,109 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/components/uart/uart.h"
 | 
			
		||||
#include "esphome/components/sensor/sensor.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace bl0940 {
 | 
			
		||||
 | 
			
		||||
static const float BL0940_PREF = 1430;
 | 
			
		||||
static const float BL0940_UREF = 33000;
 | 
			
		||||
static const float BL0940_IREF = 275000;  // 2750 from tasmota. Seems to generate values 100 times too high
 | 
			
		||||
 | 
			
		||||
// Measured to 297J  per click according to power consumption of 5 minutes
 | 
			
		||||
// Converted to kWh (3.6MJ per kwH). Used to be 256 * 1638.4
 | 
			
		||||
static const float BL0940_EREF = 3.6e6 / 297;
 | 
			
		||||
 | 
			
		||||
struct ube24_t {  // NOLINT(readability-identifier-naming,altera-struct-pack-align)
 | 
			
		||||
  uint8_t l;
 | 
			
		||||
  uint8_t m;
 | 
			
		||||
  uint8_t h;
 | 
			
		||||
} __attribute__((packed));
 | 
			
		||||
 | 
			
		||||
struct ube16_t {  // NOLINT(readability-identifier-naming,altera-struct-pack-align)
 | 
			
		||||
  uint8_t l;
 | 
			
		||||
  uint8_t h;
 | 
			
		||||
} __attribute__((packed));
 | 
			
		||||
 | 
			
		||||
struct sbe24_t {  // NOLINT(readability-identifier-naming,altera-struct-pack-align)
 | 
			
		||||
  uint8_t l;
 | 
			
		||||
  uint8_t m;
 | 
			
		||||
  int8_t h;
 | 
			
		||||
} __attribute__((packed));
 | 
			
		||||
 | 
			
		||||
// Caveat: All these values are big endian (low - middle - high)
 | 
			
		||||
 | 
			
		||||
union DataPacket {  // NOLINT(altera-struct-pack-align)
 | 
			
		||||
  uint8_t raw[35];
 | 
			
		||||
  struct {
 | 
			
		||||
    uint8_t frame_header;  // value of 0x58 according to docs. 0x55 according to Tasmota real world tests. Reality wins.
 | 
			
		||||
    ube24_t i_fast_rms;    // 0x00
 | 
			
		||||
    ube24_t i_rms;         // 0x04
 | 
			
		||||
    ube24_t RESERVED0;     // reserved
 | 
			
		||||
    ube24_t v_rms;         // 0x06
 | 
			
		||||
    ube24_t RESERVED1;     // reserved
 | 
			
		||||
    sbe24_t watt;          // 0x08
 | 
			
		||||
    ube24_t RESERVED2;     // reserved
 | 
			
		||||
    ube24_t cf_cnt;        // 0x0A
 | 
			
		||||
    ube24_t RESERVED3;     // reserved
 | 
			
		||||
    ube16_t tps1;          // 0x0c
 | 
			
		||||
    uint8_t RESERVED4;     // value of 0x00
 | 
			
		||||
    ube16_t tps2;          // 0x0c
 | 
			
		||||
    uint8_t RESERVED5;     // value of 0x00
 | 
			
		||||
    uint8_t checksum;      // checksum
 | 
			
		||||
  };
 | 
			
		||||
} __attribute__((packed));
 | 
			
		||||
 | 
			
		||||
class BL0940 : public PollingComponent, public uart::UARTDevice {
 | 
			
		||||
 public:
 | 
			
		||||
  void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; }
 | 
			
		||||
  void set_current_sensor(sensor::Sensor *current_sensor) { current_sensor_ = current_sensor; }
 | 
			
		||||
  void set_power_sensor(sensor::Sensor *power_sensor) { power_sensor_ = power_sensor; }
 | 
			
		||||
  void set_energy_sensor(sensor::Sensor *energy_sensor) { energy_sensor_ = energy_sensor; }
 | 
			
		||||
  void set_internal_temperature_sensor(sensor::Sensor *internal_temperature_sensor) {
 | 
			
		||||
    internal_temperature_sensor_ = internal_temperature_sensor;
 | 
			
		||||
  }
 | 
			
		||||
  void set_external_temperature_sensor(sensor::Sensor *external_temperature_sensor) {
 | 
			
		||||
    external_temperature_sensor_ = external_temperature_sensor;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void loop() override;
 | 
			
		||||
 | 
			
		||||
  void update() override;
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  sensor::Sensor *voltage_sensor_;
 | 
			
		||||
  sensor::Sensor *current_sensor_;
 | 
			
		||||
  // NB This may be negative as the circuits is seemingly able to measure
 | 
			
		||||
  // power in both directions
 | 
			
		||||
  sensor::Sensor *power_sensor_;
 | 
			
		||||
  sensor::Sensor *energy_sensor_;
 | 
			
		||||
  sensor::Sensor *internal_temperature_sensor_;
 | 
			
		||||
  sensor::Sensor *external_temperature_sensor_;
 | 
			
		||||
 | 
			
		||||
  // Max difference between two measurements of the temperature. Used to avoid noise.
 | 
			
		||||
  float max_temperature_diff_{0};
 | 
			
		||||
  // Divide by this to turn into Watt
 | 
			
		||||
  float power_reference_ = BL0940_PREF;
 | 
			
		||||
  // Divide by this to turn into Volt
 | 
			
		||||
  float voltage_reference_ = BL0940_UREF;
 | 
			
		||||
  // Divide by this to turn into Ampere
 | 
			
		||||
  float current_reference_ = BL0940_IREF;
 | 
			
		||||
  // Divide by this to turn into kWh
 | 
			
		||||
  float energy_reference_ = BL0940_EREF;
 | 
			
		||||
 | 
			
		||||
  float update_temp_(sensor::Sensor *sensor, ube16_t packed_temperature) const;
 | 
			
		||||
 | 
			
		||||
  static uint32_t to_uint32_t(ube24_t input);
 | 
			
		||||
 | 
			
		||||
  static int32_t to_int32_t(sbe24_t input);
 | 
			
		||||
 | 
			
		||||
  static bool validate_checksum(const DataPacket *data);
 | 
			
		||||
 | 
			
		||||
  void received_package_(const DataPacket *data) const;
 | 
			
		||||
};
 | 
			
		||||
}  // namespace bl0940
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										106
									
								
								esphome/components/bl0940/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								esphome/components/bl0940/sensor.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,106 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import sensor, uart
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_CURRENT,
 | 
			
		||||
    CONF_ENERGY,
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_POWER,
 | 
			
		||||
    CONF_VOLTAGE,
 | 
			
		||||
    DEVICE_CLASS_CURRENT,
 | 
			
		||||
    DEVICE_CLASS_ENERGY,
 | 
			
		||||
    DEVICE_CLASS_POWER,
 | 
			
		||||
    DEVICE_CLASS_VOLTAGE,
 | 
			
		||||
    DEVICE_CLASS_TEMPERATURE,
 | 
			
		||||
    ICON_EMPTY,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    STATE_CLASS_NONE,
 | 
			
		||||
    UNIT_AMPERE,
 | 
			
		||||
    UNIT_CELSIUS,
 | 
			
		||||
    UNIT_KILOWATT_HOURS,
 | 
			
		||||
    UNIT_VOLT,
 | 
			
		||||
    UNIT_WATT,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ["uart"]
 | 
			
		||||
 | 
			
		||||
CONF_INTERNAL_TEMPERATURE = "internal_temperature"
 | 
			
		||||
CONF_EXTERNAL_TEMPERATURE = "external_temperature"
 | 
			
		||||
 | 
			
		||||
bl0940_ns = cg.esphome_ns.namespace("bl0940")
 | 
			
		||||
BL0940 = bl0940_ns.class_("BL0940", cg.PollingComponent, uart.UARTDevice)
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = (
 | 
			
		||||
    cv.Schema(
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(BL0940),
 | 
			
		||||
            cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
 | 
			
		||||
                UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE, STATE_CLASS_MEASUREMENT
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_CURRENT): sensor.sensor_schema(
 | 
			
		||||
                UNIT_AMPERE,
 | 
			
		||||
                ICON_EMPTY,
 | 
			
		||||
                2,
 | 
			
		||||
                DEVICE_CLASS_CURRENT,
 | 
			
		||||
                STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_POWER): sensor.sensor_schema(
 | 
			
		||||
                UNIT_WATT, ICON_EMPTY, 0, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_ENERGY): sensor.sensor_schema(
 | 
			
		||||
                UNIT_KILOWATT_HOURS,
 | 
			
		||||
                ICON_EMPTY,
 | 
			
		||||
                0,
 | 
			
		||||
                DEVICE_CLASS_ENERGY,
 | 
			
		||||
                STATE_CLASS_NONE,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_INTERNAL_TEMPERATURE): sensor.sensor_schema(
 | 
			
		||||
                UNIT_CELSIUS,
 | 
			
		||||
                ICON_EMPTY,
 | 
			
		||||
                0,
 | 
			
		||||
                DEVICE_CLASS_TEMPERATURE,
 | 
			
		||||
                STATE_CLASS_NONE,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_EXTERNAL_TEMPERATURE): sensor.sensor_schema(
 | 
			
		||||
                UNIT_CELSIUS,
 | 
			
		||||
                ICON_EMPTY,
 | 
			
		||||
                0,
 | 
			
		||||
                DEVICE_CLASS_TEMPERATURE,
 | 
			
		||||
                STATE_CLASS_NONE,
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
    .extend(cv.polling_component_schema("60s"))
 | 
			
		||||
    .extend(uart.UART_DEVICE_SCHEMA)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await uart.register_uart_device(var, config)
 | 
			
		||||
 | 
			
		||||
    if CONF_VOLTAGE in config:
 | 
			
		||||
        conf = config[CONF_VOLTAGE]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
        cg.add(var.set_voltage_sensor(sens))
 | 
			
		||||
    if CONF_CURRENT in config:
 | 
			
		||||
        conf = config[CONF_CURRENT]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
        cg.add(var.set_current_sensor(sens))
 | 
			
		||||
    if CONF_POWER in config:
 | 
			
		||||
        conf = config[CONF_POWER]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
        cg.add(var.set_power_sensor(sens))
 | 
			
		||||
    if CONF_ENERGY in config:
 | 
			
		||||
        conf = config[CONF_ENERGY]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
        cg.add(var.set_energy_sensor(sens))
 | 
			
		||||
    if CONF_INTERNAL_TEMPERATURE in config:
 | 
			
		||||
        conf = config[CONF_INTERNAL_TEMPERATURE]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
        cg.add(var.set_internal_temperature_sensor(sens))
 | 
			
		||||
    if CONF_EXTERNAL_TEMPERATURE in config:
 | 
			
		||||
        conf = config[CONF_EXTERNAL_TEMPERATURE]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
        cg.add(var.set_external_temperature_sensor(sens))
 | 
			
		||||
@@ -33,6 +33,7 @@ static const uint8_t BME280_REGISTER_CONTROLHUMID = 0xF2;
 | 
			
		||||
static const uint8_t BME280_REGISTER_STATUS = 0xF3;
 | 
			
		||||
static const uint8_t BME280_REGISTER_CONTROL = 0xF4;
 | 
			
		||||
static const uint8_t BME280_REGISTER_CONFIG = 0xF5;
 | 
			
		||||
static const uint8_t BME280_REGISTER_MEASUREMENTS = 0xF7;
 | 
			
		||||
static const uint8_t BME280_REGISTER_PRESSUREDATA = 0xF7;
 | 
			
		||||
static const uint8_t BME280_REGISTER_TEMPDATA = 0xFA;
 | 
			
		||||
static const uint8_t BME280_REGISTER_HUMIDDATA = 0xFD;
 | 
			
		||||
@@ -178,23 +179,29 @@ void BME280Component::update() {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  float meas_time = 1.5;
 | 
			
		||||
  float meas_time = 1.5f;
 | 
			
		||||
  meas_time += 2.3f * oversampling_to_time(this->temperature_oversampling_);
 | 
			
		||||
  meas_time += 2.3f * oversampling_to_time(this->pressure_oversampling_) + 0.575f;
 | 
			
		||||
  meas_time += 2.3f * oversampling_to_time(this->humidity_oversampling_) + 0.575f;
 | 
			
		||||
 | 
			
		||||
  this->set_timeout("data", uint32_t(ceilf(meas_time)), [this]() {
 | 
			
		||||
    uint8_t data[8];
 | 
			
		||||
    if (!this->read_bytes(BME280_REGISTER_MEASUREMENTS, data, 8)) {
 | 
			
		||||
      ESP_LOGW(TAG, "Error reading registers.");
 | 
			
		||||
      this->status_set_warning();
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    int32_t t_fine = 0;
 | 
			
		||||
    float temperature = this->read_temperature_(&t_fine);
 | 
			
		||||
    float temperature = this->read_temperature_(data, &t_fine);
 | 
			
		||||
    if (std::isnan(temperature)) {
 | 
			
		||||
      ESP_LOGW(TAG, "Invalid temperature, cannot read pressure & humidity values.");
 | 
			
		||||
      this->status_set_warning();
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    float pressure = this->read_pressure_(t_fine);
 | 
			
		||||
    float humidity = this->read_humidity_(t_fine);
 | 
			
		||||
    float pressure = this->read_pressure_(data, t_fine);
 | 
			
		||||
    float humidity = this->read_humidity_(data, t_fine);
 | 
			
		||||
 | 
			
		||||
    ESP_LOGD(TAG, "Got temperature=%.1f°C pressure=%.1fhPa humidity=%.1f%%", temperature, pressure, humidity);
 | 
			
		||||
    ESP_LOGV(TAG, "Got temperature=%.1f°C pressure=%.1fhPa humidity=%.1f%%", temperature, pressure, humidity);
 | 
			
		||||
    if (this->temperature_sensor_ != nullptr)
 | 
			
		||||
      this->temperature_sensor_->publish_state(temperature);
 | 
			
		||||
    if (this->pressure_sensor_ != nullptr)
 | 
			
		||||
@@ -204,11 +211,8 @@ void BME280Component::update() {
 | 
			
		||||
    this->status_clear_warning();
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
float BME280Component::read_temperature_(int32_t *t_fine) {
 | 
			
		||||
  uint8_t data[3];
 | 
			
		||||
  if (!this->read_bytes(BME280_REGISTER_TEMPDATA, data, 3))
 | 
			
		||||
    return NAN;
 | 
			
		||||
  int32_t adc = ((data[0] & 0xFF) << 16) | ((data[1] & 0xFF) << 8) | (data[2] & 0xFF);
 | 
			
		||||
float BME280Component::read_temperature_(const uint8_t *data, int32_t *t_fine) {
 | 
			
		||||
  int32_t adc = ((data[3] & 0xFF) << 16) | ((data[4] & 0xFF) << 8) | (data[5] & 0xFF);
 | 
			
		||||
  adc >>= 4;
 | 
			
		||||
  if (adc == 0x80000)
 | 
			
		||||
    // temperature was disabled
 | 
			
		||||
@@ -226,10 +230,7 @@ float BME280Component::read_temperature_(int32_t *t_fine) {
 | 
			
		||||
  return temperature / 100.0f;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float BME280Component::read_pressure_(int32_t t_fine) {
 | 
			
		||||
  uint8_t data[3];
 | 
			
		||||
  if (!this->read_bytes(BME280_REGISTER_PRESSUREDATA, data, 3))
 | 
			
		||||
    return NAN;
 | 
			
		||||
float BME280Component::read_pressure_(const uint8_t *data, int32_t t_fine) {
 | 
			
		||||
  int32_t adc = ((data[0] & 0xFF) << 16) | ((data[1] & 0xFF) << 8) | (data[2] & 0xFF);
 | 
			
		||||
  adc >>= 4;
 | 
			
		||||
  if (adc == 0x80000)
 | 
			
		||||
@@ -265,9 +266,9 @@ float BME280Component::read_pressure_(int32_t t_fine) {
 | 
			
		||||
  return (p / 256.0f) / 100.0f;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float BME280Component::read_humidity_(int32_t t_fine) {
 | 
			
		||||
  uint16_t raw_adc;
 | 
			
		||||
  if (!this->read_byte_16(BME280_REGISTER_HUMIDDATA, &raw_adc) || raw_adc == 0x8000)
 | 
			
		||||
float BME280Component::read_humidity_(const uint8_t *data, int32_t t_fine) {
 | 
			
		||||
  uint16_t raw_adc = ((data[6] & 0xFF) << 8) | (data[7] & 0xFF);
 | 
			
		||||
  if (raw_adc == 0x8000)
 | 
			
		||||
    return NAN;
 | 
			
		||||
 | 
			
		||||
  int32_t adc = raw_adc;
 | 
			
		||||
 
 | 
			
		||||
@@ -82,11 +82,11 @@ class BME280Component : public PollingComponent, public i2c::I2CDevice {
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  /// Read the temperature value and store the calculated ambient temperature in t_fine.
 | 
			
		||||
  float read_temperature_(int32_t *t_fine);
 | 
			
		||||
  float read_temperature_(const uint8_t *data, int32_t *t_fine);
 | 
			
		||||
  /// Read the pressure value in hPa using the provided t_fine value.
 | 
			
		||||
  float read_pressure_(int32_t t_fine);
 | 
			
		||||
  float read_pressure_(const uint8_t *data, int32_t t_fine);
 | 
			
		||||
  /// Read the humidity value in % using the provided t_fine value.
 | 
			
		||||
  float read_humidity_(int32_t t_fine);
 | 
			
		||||
  float read_humidity_(const uint8_t *data, int32_t t_fine);
 | 
			
		||||
  uint8_t read_u8_(uint8_t a_register);
 | 
			
		||||
  uint16_t read_u16_le_(uint8_t a_register);
 | 
			
		||||
  int16_t read_s16_le_(uint8_t a_register);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										0
									
								
								esphome/components/bmp3xx/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								esphome/components/bmp3xx/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										388
									
								
								esphome/components/bmp3xx/bmp3xx.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										388
									
								
								esphome/components/bmp3xx/bmp3xx.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,388 @@
 | 
			
		||||
/*
 | 
			
		||||
  based on BMP388_DEV by Martin Lindupp
 | 
			
		||||
  under MIT License (MIT)
 | 
			
		||||
  Copyright (C) Martin Lindupp 2020
 | 
			
		||||
  http://github.com/MartinL1/BMP388_DEV
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#include "bmp3xx.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/core/hal.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace bmp3xx {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "bmp3xx.sensor";
 | 
			
		||||
 | 
			
		||||
static const LogString *chip_type_to_str(uint8_t chip_type) {
 | 
			
		||||
  switch (chip_type) {
 | 
			
		||||
    case BMP388_ID:
 | 
			
		||||
      return LOG_STR("BMP 388");
 | 
			
		||||
    case BMP390_ID:
 | 
			
		||||
      return LOG_STR("BMP 390");
 | 
			
		||||
    default:
 | 
			
		||||
      return LOG_STR("Unknown Chip Type");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const LogString *oversampling_to_str(Oversampling oversampling) {
 | 
			
		||||
  switch (oversampling) {
 | 
			
		||||
    case Oversampling::OVERSAMPLING_NONE:
 | 
			
		||||
      return LOG_STR("None");
 | 
			
		||||
    case Oversampling::OVERSAMPLING_X2:
 | 
			
		||||
      return LOG_STR("2x");
 | 
			
		||||
    case Oversampling::OVERSAMPLING_X4:
 | 
			
		||||
      return LOG_STR("4x");
 | 
			
		||||
    case Oversampling::OVERSAMPLING_X8:
 | 
			
		||||
      return LOG_STR("8x");
 | 
			
		||||
    case Oversampling::OVERSAMPLING_X16:
 | 
			
		||||
      return LOG_STR("16x");
 | 
			
		||||
    case Oversampling::OVERSAMPLING_X32:
 | 
			
		||||
      return LOG_STR("32x");
 | 
			
		||||
    default:
 | 
			
		||||
      return LOG_STR("");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const LogString *iir_filter_to_str(IIRFilter filter) {
 | 
			
		||||
  switch (filter) {
 | 
			
		||||
    case IIRFilter::IIR_FILTER_OFF:
 | 
			
		||||
      return LOG_STR("OFF");
 | 
			
		||||
    case IIRFilter::IIR_FILTER_2:
 | 
			
		||||
      return LOG_STR("2x");
 | 
			
		||||
    case IIRFilter::IIR_FILTER_4:
 | 
			
		||||
      return LOG_STR("4x");
 | 
			
		||||
    case IIRFilter::IIR_FILTER_8:
 | 
			
		||||
      return LOG_STR("8x");
 | 
			
		||||
    case IIRFilter::IIR_FILTER_16:
 | 
			
		||||
      return LOG_STR("16x");
 | 
			
		||||
    case IIRFilter::IIR_FILTER_32:
 | 
			
		||||
      return LOG_STR("32x");
 | 
			
		||||
    case IIRFilter::IIR_FILTER_64:
 | 
			
		||||
      return LOG_STR("64x");
 | 
			
		||||
    case IIRFilter::IIR_FILTER_128:
 | 
			
		||||
      return LOG_STR("128x");
 | 
			
		||||
    default:
 | 
			
		||||
      return LOG_STR("");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BMP3XXComponent::setup() {
 | 
			
		||||
  this->error_code_ = NONE;
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up BMP3XX...");
 | 
			
		||||
  // Call the Device base class "initialise" function
 | 
			
		||||
  if (!reset()) {
 | 
			
		||||
    ESP_LOGE(TAG, "Failed to reset BMP3XX...");
 | 
			
		||||
    this->error_code_ = ERROR_SENSOR_RESET;
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!read_byte(BMP388_CHIP_ID, &this->chip_id_.reg)) {
 | 
			
		||||
    ESP_LOGE(TAG, "Can't read chip id");
 | 
			
		||||
    this->error_code_ = ERROR_COMMUNICATION_FAILED;
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Chip %s Id 0x%X", LOG_STR_ARG(chip_type_to_str(this->chip_id_.reg)), this->chip_id_.reg);
 | 
			
		||||
 | 
			
		||||
  if (chip_id_.reg != BMP388_ID && chip_id_.reg != BMP390_ID) {
 | 
			
		||||
    ESP_LOGE(TAG, "Unknown chip id - is this really a BMP388 or BMP390?");
 | 
			
		||||
    this->error_code_ = ERROR_WRONG_CHIP_ID;
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  // set sensor in sleep mode
 | 
			
		||||
  stop_conversion();
 | 
			
		||||
  // Read the calibration parameters into the params structure
 | 
			
		||||
  if (!read_bytes(BMP388_TRIM_PARAMS, (uint8_t *) &compensation_params_, sizeof(compensation_params_))) {
 | 
			
		||||
    ESP_LOGE(TAG, "Can't read calibration data");
 | 
			
		||||
    this->error_code_ = ERROR_COMMUNICATION_FAILED;
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  compensation_float_params_.param_T1 =
 | 
			
		||||
      (float) compensation_params_.param_T1 / powf(2.0f, -8.0f);  // Calculate the floating point trim parameters
 | 
			
		||||
  compensation_float_params_.param_T2 = (float) compensation_params_.param_T2 / powf(2.0f, 30.0f);
 | 
			
		||||
  compensation_float_params_.param_T3 = (float) compensation_params_.param_T3 / powf(2.0f, 48.0f);
 | 
			
		||||
  compensation_float_params_.param_P1 = ((float) compensation_params_.param_P1 - powf(2.0f, 14.0f)) / powf(2.0f, 20.0f);
 | 
			
		||||
  compensation_float_params_.param_P2 = ((float) compensation_params_.param_P2 - powf(2.0f, 14.0f)) / powf(2.0f, 29.0f);
 | 
			
		||||
  compensation_float_params_.param_P3 = (float) compensation_params_.param_P3 / powf(2.0f, 32.0f);
 | 
			
		||||
  compensation_float_params_.param_P4 = (float) compensation_params_.param_P4 / powf(2.0f, 37.0f);
 | 
			
		||||
  compensation_float_params_.param_P5 = (float) compensation_params_.param_P5 / powf(2.0f, -3.0f);
 | 
			
		||||
  compensation_float_params_.param_P6 = (float) compensation_params_.param_P6 / powf(2.0f, 6.0f);
 | 
			
		||||
  compensation_float_params_.param_P7 = (float) compensation_params_.param_P7 / powf(2.0f, 8.0f);
 | 
			
		||||
  compensation_float_params_.param_P8 = (float) compensation_params_.param_P8 / powf(2.0f, 15.0f);
 | 
			
		||||
  compensation_float_params_.param_P9 = (float) compensation_params_.param_P9 / powf(2.0f, 48.0f);
 | 
			
		||||
  compensation_float_params_.param_P10 = (float) compensation_params_.param_P10 / powf(2.0f, 48.0f);
 | 
			
		||||
  compensation_float_params_.param_P11 = (float) compensation_params_.param_P11 / powf(2.0f, 65.0f);
 | 
			
		||||
 | 
			
		||||
  // Initialise the BMP388 IIR filter register
 | 
			
		||||
  if (!set_iir_filter(this->iir_filter_)) {
 | 
			
		||||
    ESP_LOGE(TAG, "Failed to set IIR filter");
 | 
			
		||||
    this->error_code_ = ERROR_COMMUNICATION_FAILED;
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Set power control registers
 | 
			
		||||
  pwr_ctrl_.bit.press_en = 1;
 | 
			
		||||
  pwr_ctrl_.bit.temp_en = 1;
 | 
			
		||||
  // Disable pressure if no sensor defined
 | 
			
		||||
  // keep temperature enabled since it's needed for compensation
 | 
			
		||||
  if (this->pressure_sensor_ == nullptr) {
 | 
			
		||||
    pwr_ctrl_.bit.press_en = 0;
 | 
			
		||||
    this->pressure_oversampling_ = OVERSAMPLING_NONE;
 | 
			
		||||
  }
 | 
			
		||||
  // just disable oeversampling for temp if not used
 | 
			
		||||
  if (this->temperature_sensor_ == nullptr) {
 | 
			
		||||
    this->temperature_oversampling_ = OVERSAMPLING_NONE;
 | 
			
		||||
  }
 | 
			
		||||
  // Initialise the BMP388 oversampling register
 | 
			
		||||
  if (!set_oversampling_register(this->pressure_oversampling_, this->temperature_oversampling_)) {
 | 
			
		||||
    ESP_LOGE(TAG, "Failed to set oversampling register");
 | 
			
		||||
    this->error_code_ = ERROR_COMMUNICATION_FAILED;
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BMP3XXComponent::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "BMP3XX:");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Type: %s (0x%X)", LOG_STR_ARG(chip_type_to_str(this->chip_id_.reg)), this->chip_id_.reg);
 | 
			
		||||
  LOG_I2C_DEVICE(this);
 | 
			
		||||
  switch (this->error_code_) {
 | 
			
		||||
    case NONE:
 | 
			
		||||
      break;
 | 
			
		||||
    case ERROR_COMMUNICATION_FAILED:
 | 
			
		||||
      ESP_LOGE(TAG, "Communication with BMP3XX failed!");
 | 
			
		||||
      break;
 | 
			
		||||
    case ERROR_WRONG_CHIP_ID:
 | 
			
		||||
      ESP_LOGE(
 | 
			
		||||
          TAG,
 | 
			
		||||
          "BMP3XX has wrong chip ID (reported id: 0x%X) - please check if you are really using a BMP 388 or BMP 390",
 | 
			
		||||
          this->chip_id_.reg);
 | 
			
		||||
      break;
 | 
			
		||||
    case ERROR_SENSOR_RESET:
 | 
			
		||||
      ESP_LOGE(TAG, "BMP3XX failed to reset");
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      ESP_LOGE(TAG, "BMP3XX error code %d", (int) this->error_code_);
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  IIR Filter: %s", LOG_STR_ARG(iir_filter_to_str(this->iir_filter_)));
 | 
			
		||||
  LOG_UPDATE_INTERVAL(this);
 | 
			
		||||
  if (this->temperature_sensor_) {
 | 
			
		||||
    LOG_SENSOR("  ", "Temperature", this->temperature_sensor_);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "    Oversampling: %s", LOG_STR_ARG(oversampling_to_str(this->temperature_oversampling_)));
 | 
			
		||||
  }
 | 
			
		||||
  if (this->pressure_sensor_) {
 | 
			
		||||
    LOG_SENSOR("  ", "Pressure", this->pressure_sensor_);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "    Oversampling: %s", LOG_STR_ARG(oversampling_to_str(this->pressure_oversampling_)));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
float BMP3XXComponent::get_setup_priority() const { return setup_priority::DATA; }
 | 
			
		||||
 | 
			
		||||
inline uint8_t oversampling_to_time(Oversampling over_sampling) { return (1 << uint8_t(over_sampling)); }
 | 
			
		||||
 | 
			
		||||
void BMP3XXComponent::update() {
 | 
			
		||||
  // Enable sensor
 | 
			
		||||
  ESP_LOGV(TAG, "Sending conversion request...");
 | 
			
		||||
  float meas_time = 1.0f;
 | 
			
		||||
  // Ref: https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp390-ds002.pdf 3.9.2
 | 
			
		||||
  meas_time += 2.02f * oversampling_to_time(this->temperature_oversampling_) + 0.163f;
 | 
			
		||||
  meas_time += 2.02f * oversampling_to_time(this->pressure_oversampling_) + 0.392f;
 | 
			
		||||
  meas_time += 0.234f;
 | 
			
		||||
  if (!set_mode(FORCED_MODE)) {
 | 
			
		||||
    ESP_LOGE(TAG, "Failed start forced mode");
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ESP_LOGVV(TAG, "measurement time %d", uint32_t(ceilf(meas_time)));
 | 
			
		||||
  this->set_timeout("data", uint32_t(ceilf(meas_time)), [this]() {
 | 
			
		||||
    float temperature = 0.0f;
 | 
			
		||||
    float pressure = 0.0f;
 | 
			
		||||
    if (this->pressure_sensor_ != nullptr) {
 | 
			
		||||
      if (!get_measurements(temperature, pressure)) {
 | 
			
		||||
        ESP_LOGW(TAG, "Failed to read pressure and temperature - skipping update");
 | 
			
		||||
        this->status_set_warning();
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      ESP_LOGD(TAG, "Got temperature=%.1f°C pressure=%.1fhPa", temperature, pressure);
 | 
			
		||||
    } else {
 | 
			
		||||
      if (!get_temperature(temperature)) {
 | 
			
		||||
        ESP_LOGW(TAG, "Failed to read temperature - skipping update");
 | 
			
		||||
        this->status_set_warning();
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      ESP_LOGD(TAG, "Got temperature=%.1f°C", temperature);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (this->temperature_sensor_ != nullptr)
 | 
			
		||||
      this->temperature_sensor_->publish_state(temperature);
 | 
			
		||||
    if (this->pressure_sensor_ != nullptr)
 | 
			
		||||
      this->pressure_sensor_->publish_state(pressure);
 | 
			
		||||
    this->status_clear_warning();
 | 
			
		||||
    set_mode(SLEEP_MODE);
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Reset the BMP3XX
 | 
			
		||||
uint8_t BMP3XXComponent::reset() {
 | 
			
		||||
  write_byte(BMP388_CMD, RESET_CODE);  // Write the reset code to the command register
 | 
			
		||||
  // Wait for 10ms
 | 
			
		||||
  delay(10);
 | 
			
		||||
  this->read_byte(BMP388_EVENT, &event_.reg);  // Read the BMP388's event register
 | 
			
		||||
  return event_.bit.por_detected;              // Return if device reset is complete
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Start a one shot measurement in FORCED_MODE
 | 
			
		||||
bool BMP3XXComponent::start_forced_conversion() {
 | 
			
		||||
  // Only set FORCED_MODE if we're already in SLEEP_MODE
 | 
			
		||||
  if (pwr_ctrl_.bit.mode == SLEEP_MODE) {
 | 
			
		||||
    return set_mode(FORCED_MODE);
 | 
			
		||||
  }
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Stop the conversion and return to SLEEP_MODE
 | 
			
		||||
bool BMP3XXComponent::stop_conversion() { return set_mode(SLEEP_MODE); }
 | 
			
		||||
 | 
			
		||||
// Set the pressure oversampling rate
 | 
			
		||||
bool BMP3XXComponent::set_pressure_oversampling(Oversampling oversampling) {
 | 
			
		||||
  osr_.bit.osr_p = oversampling;
 | 
			
		||||
  return this->write_byte(BMP388_OSR, osr_.reg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Set the temperature oversampling rate
 | 
			
		||||
bool BMP3XXComponent::set_temperature_oversampling(Oversampling oversampling) {
 | 
			
		||||
  osr_.bit.osr_t = oversampling;
 | 
			
		||||
  return this->write_byte(BMP388_OSR, osr_.reg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Set the IIR filter setting
 | 
			
		||||
bool BMP3XXComponent::set_iir_filter(IIRFilter iir_filter) {
 | 
			
		||||
  config_.bit.iir_filter = iir_filter;
 | 
			
		||||
  return this->write_byte(BMP388_CONFIG, config_.reg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get temperature
 | 
			
		||||
bool BMP3XXComponent::get_temperature(float &temperature) {
 | 
			
		||||
  // Check if a measurement is ready
 | 
			
		||||
  if (!data_ready()) {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  uint8_t data[3];
 | 
			
		||||
  // Read the temperature
 | 
			
		||||
  if (!this->read_bytes(BMP388_DATA_3, &data[0], 3)) {
 | 
			
		||||
    ESP_LOGE(TAG, "Failed to read temperature");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  // Copy the temperature data into the adc variables
 | 
			
		||||
  int32_t adc_temp = (int32_t) data[2] << 16 | (int32_t) data[1] << 8 | (int32_t) data[0];
 | 
			
		||||
  // Temperature compensation (function from BMP388 datasheet)
 | 
			
		||||
  temperature = bmp388_compensate_temperature_((float) adc_temp);
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get the pressure
 | 
			
		||||
bool BMP3XXComponent::get_pressure(float &pressure) {
 | 
			
		||||
  float temperature;
 | 
			
		||||
  return get_measurements(temperature, pressure);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get temperature and pressure
 | 
			
		||||
bool BMP3XXComponent::get_measurements(float &temperature, float &pressure) {
 | 
			
		||||
  // Check if a measurement is ready
 | 
			
		||||
  if (!data_ready()) {
 | 
			
		||||
    ESP_LOGD(TAG, "BMP3XX Get measurement - data not ready skipping update");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint8_t data[6];
 | 
			
		||||
  // Read the temperature and pressure data
 | 
			
		||||
  if (!this->read_bytes(BMP388_DATA_0, &data[0], 6)) {
 | 
			
		||||
    ESP_LOGE(TAG, "Failed to read measurements");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  // Copy the temperature and pressure data into the adc variables
 | 
			
		||||
  int32_t adc_pres = (int32_t) data[2] << 16 | (int32_t) data[1] << 8 | (int32_t) data[0];
 | 
			
		||||
  int32_t adc_temp = (int32_t) data[5] << 16 | (int32_t) data[4] << 8 | (int32_t) data[3];
 | 
			
		||||
 | 
			
		||||
  // Temperature compensation (function from BMP388 datasheet)
 | 
			
		||||
  temperature = bmp388_compensate_temperature_((float) adc_temp);
 | 
			
		||||
  // Pressure compensation (function from BMP388 datasheet)
 | 
			
		||||
  pressure = bmp388_compensate_pressure_((float) adc_pres, temperature);
 | 
			
		||||
  // Calculate the pressure in millibar/hPa
 | 
			
		||||
  pressure /= 100.0f;
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Set the BMP388's mode in the power control register
 | 
			
		||||
bool BMP3XXComponent::set_mode(OperationMode mode) {
 | 
			
		||||
  pwr_ctrl_.bit.mode = mode;
 | 
			
		||||
  return this->write_byte(BMP388_PWR_CTRL, pwr_ctrl_.reg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Set the BMP388 oversampling register
 | 
			
		||||
bool BMP3XXComponent::set_oversampling_register(Oversampling pressure_oversampling,
 | 
			
		||||
                                                Oversampling temperature_oversampling) {
 | 
			
		||||
  osr_.reg = temperature_oversampling << 3 | pressure_oversampling;
 | 
			
		||||
  return this->write_byte(BMP388_OSR, osr_.reg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Check if measurement data is ready
 | 
			
		||||
bool BMP3XXComponent::data_ready() {
 | 
			
		||||
  // If we're in SLEEP_MODE return immediately
 | 
			
		||||
  if (pwr_ctrl_.bit.mode == SLEEP_MODE) {
 | 
			
		||||
    ESP_LOGD(TAG, "Not ready - sensor is in sleep mode");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  // Read the interrupt status register
 | 
			
		||||
  uint8_t status;
 | 
			
		||||
  if (!this->read_byte(BMP388_INT_STATUS, &status)) {
 | 
			
		||||
    ESP_LOGE(TAG, "Failed to read status register");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  int_status_.reg = status;
 | 
			
		||||
  ESP_LOGVV(TAG, "data ready status %d", status);
 | 
			
		||||
  // If we're in FORCED_MODE switch back to SLEEP_MODE
 | 
			
		||||
  if (int_status_.bit.drdy) {
 | 
			
		||||
    if (pwr_ctrl_.bit.mode == FORCED_MODE) {
 | 
			
		||||
      pwr_ctrl_.bit.mode = SLEEP_MODE;
 | 
			
		||||
    }
 | 
			
		||||
    return true;  // The measurement is ready
 | 
			
		||||
  }
 | 
			
		||||
  return false;  // The measurement is still pending
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
// Bosch BMP3XXComponent (Private) Member Functions
 | 
			
		||||
////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
float BMP3XXComponent::bmp388_compensate_temperature_(float uncomp_temp) {
 | 
			
		||||
  float partial_data1 = uncomp_temp - compensation_float_params_.param_T1;
 | 
			
		||||
  float partial_data2 = partial_data1 * compensation_float_params_.param_T2;
 | 
			
		||||
  return partial_data2 + partial_data1 * partial_data1 * compensation_float_params_.param_T3;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float BMP3XXComponent::bmp388_compensate_pressure_(float uncomp_press, float t_lin) {
 | 
			
		||||
  float partial_data1 = compensation_float_params_.param_P6 * t_lin;
 | 
			
		||||
  float partial_data2 = compensation_float_params_.param_P7 * t_lin * t_lin;
 | 
			
		||||
  float partial_data3 = compensation_float_params_.param_P8 * t_lin * t_lin * t_lin;
 | 
			
		||||
  float partial_out1 = compensation_float_params_.param_P5 + partial_data1 + partial_data2 + partial_data3;
 | 
			
		||||
  partial_data1 = compensation_float_params_.param_P2 * t_lin;
 | 
			
		||||
  partial_data2 = compensation_float_params_.param_P3 * t_lin * t_lin;
 | 
			
		||||
  partial_data3 = compensation_float_params_.param_P4 * t_lin * t_lin * t_lin;
 | 
			
		||||
  float partial_out2 =
 | 
			
		||||
      uncomp_press * (compensation_float_params_.param_P1 + partial_data1 + partial_data2 + partial_data3);
 | 
			
		||||
  partial_data1 = uncomp_press * uncomp_press;
 | 
			
		||||
  partial_data2 = compensation_float_params_.param_P9 + compensation_float_params_.param_P10 * t_lin;
 | 
			
		||||
  partial_data3 = partial_data1 * partial_data2;
 | 
			
		||||
  float partial_data4 =
 | 
			
		||||
      partial_data3 + uncomp_press * uncomp_press * uncomp_press * compensation_float_params_.param_P11;
 | 
			
		||||
  return partial_out1 + partial_out2 + partial_data4;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace bmp3xx
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										237
									
								
								esphome/components/bmp3xx/bmp3xx.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										237
									
								
								esphome/components/bmp3xx/bmp3xx.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,237 @@
 | 
			
		||||
/*
 | 
			
		||||
  based on BMP388_DEV by Martin Lindupp
 | 
			
		||||
  under MIT License (MIT)
 | 
			
		||||
  Copyright (C) Martin Lindupp 2020
 | 
			
		||||
  http://github.com/MartinL1/BMP388_DEV
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/components/sensor/sensor.h"
 | 
			
		||||
#include "esphome/components/i2c/i2c.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace bmp3xx {
 | 
			
		||||
 | 
			
		||||
static const uint8_t BMP388_ID = 0x50;   // The BMP388 device ID
 | 
			
		||||
static const uint8_t BMP390_ID = 0x60;   // The BMP390 device ID
 | 
			
		||||
static const uint8_t RESET_CODE = 0xB6;  // The BMP388 reset code
 | 
			
		||||
 | 
			
		||||
/// BMP388_DEV Registers
 | 
			
		||||
enum {
 | 
			
		||||
  BMP388_CHIP_ID = 0x00,       // Chip ID register sub-address
 | 
			
		||||
  BMP388_ERR_REG = 0x02,       // Error register sub-address
 | 
			
		||||
  BMP388_STATUS = 0x03,        // Status register sub-address
 | 
			
		||||
  BMP388_DATA_0 = 0x04,        // Pressure eXtended Least Significant Byte (XLSB) register sub-address
 | 
			
		||||
  BMP388_DATA_1 = 0x05,        // Pressure Least Significant Byte (LSB) register sub-address
 | 
			
		||||
  BMP388_DATA_2 = 0x06,        // Pressure Most Significant Byte (MSB) register sub-address
 | 
			
		||||
  BMP388_DATA_3 = 0x07,        // Temperature eXtended Least Significant Byte (XLSB) register sub-address
 | 
			
		||||
  BMP388_DATA_4 = 0x08,        // Temperature Least Significant Byte (LSB) register sub-address
 | 
			
		||||
  BMP388_DATA_5 = 0x09,        // Temperature Most Significant Byte (MSB) register sub-address
 | 
			
		||||
  BMP388_SENSORTIME_0 = 0x0C,  // Sensor time register 0 sub-address
 | 
			
		||||
  BMP388_SENSORTIME_1 = 0x0D,  // Sensor time register 1 sub-address
 | 
			
		||||
  BMP388_SENSORTIME_2 = 0x0E,  // Sensor time register 2 sub-address
 | 
			
		||||
  BMP388_EVENT = 0x10,         // Event register sub-address
 | 
			
		||||
  BMP388_INT_STATUS = 0x11,    // Interrupt Status register sub-address
 | 
			
		||||
  BMP388_INT_CTRL = 0x19,      // Interrupt Control register sub-address
 | 
			
		||||
  BMP388_IF_CONFIG = 0x1A,     // Interface Configuration register sub-address
 | 
			
		||||
  BMP388_PWR_CTRL = 0x1B,      // Power Control register sub-address
 | 
			
		||||
  BMP388_OSR = 0x1C,           // Oversampling register sub-address
 | 
			
		||||
  BMP388_ODR = 0x1D,           // Output Data Rate register sub-address
 | 
			
		||||
  BMP388_CONFIG = 0x1F,        // Configuration register sub-address
 | 
			
		||||
  BMP388_TRIM_PARAMS = 0x31,   // Trim parameter registers' base sub-address
 | 
			
		||||
  BMP388_CMD = 0x7E            // Command register sub-address
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Device mode bitfield in the control and measurement register
 | 
			
		||||
enum OperationMode { SLEEP_MODE = 0x00, FORCED_MODE = 0x01, NORMAL_MODE = 0x03 };
 | 
			
		||||
 | 
			
		||||
/// Oversampling bit fields in the control and measurement register
 | 
			
		||||
enum Oversampling {
 | 
			
		||||
  OVERSAMPLING_NONE = 0x00,
 | 
			
		||||
  OVERSAMPLING_X2 = 0x01,
 | 
			
		||||
  OVERSAMPLING_X4 = 0x02,
 | 
			
		||||
  OVERSAMPLING_X8 = 0x03,
 | 
			
		||||
  OVERSAMPLING_X16 = 0x04,
 | 
			
		||||
  OVERSAMPLING_X32 = 0x05
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Infinite Impulse Response (IIR) filter bit field in the configuration register
 | 
			
		||||
enum IIRFilter {
 | 
			
		||||
  IIR_FILTER_OFF = 0x00,
 | 
			
		||||
  IIR_FILTER_2 = 0x01,
 | 
			
		||||
  IIR_FILTER_4 = 0x02,
 | 
			
		||||
  IIR_FILTER_8 = 0x03,
 | 
			
		||||
  IIR_FILTER_16 = 0x04,
 | 
			
		||||
  IIR_FILTER_32 = 0x05,
 | 
			
		||||
  IIR_FILTER_64 = 0x06,
 | 
			
		||||
  IIR_FILTER_128 = 0x07
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// This class implements support for the BMP3XX Temperature+Pressure i2c sensor.
 | 
			
		||||
class BMP3XXComponent : 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_pressure_sensor(sensor::Sensor *pressure_sensor) { pressure_sensor_ = pressure_sensor; }
 | 
			
		||||
 | 
			
		||||
  /// Set the oversampling value for the temperature sensor. Default is 16x.
 | 
			
		||||
  void set_temperature_oversampling_config(Oversampling temperature_oversampling) {
 | 
			
		||||
    this->temperature_oversampling_ = temperature_oversampling;
 | 
			
		||||
  }
 | 
			
		||||
  /// Set the oversampling value for the pressure sensor. Default is 16x.
 | 
			
		||||
  void set_pressure_oversampling_config(Oversampling pressure_oversampling) {
 | 
			
		||||
    this->pressure_oversampling_ = pressure_oversampling;
 | 
			
		||||
  }
 | 
			
		||||
  /// Set the IIR Filter used to increase accuracy, defaults to no IIR Filter.
 | 
			
		||||
  void set_iir_filter_config(IIRFilter iir_filter) { this->iir_filter_ = iir_filter; }
 | 
			
		||||
 | 
			
		||||
  /// Soft reset the sensor
 | 
			
		||||
  uint8_t reset();
 | 
			
		||||
  /// Start continuous measurement in NORMAL_MODE
 | 
			
		||||
  bool start_normal_conversion();
 | 
			
		||||
  /// Start a one shot measurement in FORCED_MODE
 | 
			
		||||
  bool start_forced_conversion();
 | 
			
		||||
  /// Stop the conversion and return to SLEEP_MODE
 | 
			
		||||
  bool stop_conversion();
 | 
			
		||||
  /// Set the pressure oversampling: OFF, X1, X2, X4, X8, X16, X32
 | 
			
		||||
  bool set_pressure_oversampling(Oversampling pressure_oversampling);
 | 
			
		||||
  /// Set the temperature oversampling: OFF, X1, X2, X4, X8, X16, X32
 | 
			
		||||
  bool set_temperature_oversampling(Oversampling temperature_oversampling);
 | 
			
		||||
  /// Set the IIR filter setting: OFF, 2, 3, 8, 16, 32
 | 
			
		||||
  bool set_iir_filter(IIRFilter iir_filter);
 | 
			
		||||
  /// Get a temperature measurement
 | 
			
		||||
  bool get_temperature(float &temperature);
 | 
			
		||||
  /// Get a pressure measurement
 | 
			
		||||
  bool get_pressure(float &pressure);
 | 
			
		||||
  /// Get a temperature and pressure measurement
 | 
			
		||||
  bool get_measurements(float &temperature, float &pressure);
 | 
			
		||||
  /// Get a temperature and pressure measurement
 | 
			
		||||
  bool get_measurement();
 | 
			
		||||
  /// Set the barometer mode
 | 
			
		||||
  bool set_mode(OperationMode mode);
 | 
			
		||||
  /// Set the BMP388 oversampling register
 | 
			
		||||
  bool set_oversampling_register(Oversampling pressure_oversampling, Oversampling temperature_oversampling);
 | 
			
		||||
  /// Checks if a measurement is ready
 | 
			
		||||
  bool data_ready();
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  Oversampling temperature_oversampling_{OVERSAMPLING_X16};
 | 
			
		||||
  Oversampling pressure_oversampling_{OVERSAMPLING_X16};
 | 
			
		||||
  IIRFilter iir_filter_{IIR_FILTER_OFF};
 | 
			
		||||
  OperationMode operation_mode_{FORCED_MODE};
 | 
			
		||||
  sensor::Sensor *temperature_sensor_;
 | 
			
		||||
  sensor::Sensor *pressure_sensor_;
 | 
			
		||||
  enum ErrorCode {
 | 
			
		||||
    NONE = 0,
 | 
			
		||||
    ERROR_COMMUNICATION_FAILED,
 | 
			
		||||
    ERROR_WRONG_CHIP_ID,
 | 
			
		||||
    ERROR_SENSOR_STATUS,
 | 
			
		||||
    ERROR_SENSOR_RESET,
 | 
			
		||||
  } error_code_{NONE};
 | 
			
		||||
 | 
			
		||||
  struct {  // The BMP388 compensation trim parameters (coefficients)
 | 
			
		||||
    uint16_t param_T1;
 | 
			
		||||
    uint16_t param_T2;
 | 
			
		||||
    int8_t param_T3;
 | 
			
		||||
    int16_t param_P1;
 | 
			
		||||
    int16_t param_P2;
 | 
			
		||||
    int8_t param_P3;
 | 
			
		||||
    int8_t param_P4;
 | 
			
		||||
    uint16_t param_P5;
 | 
			
		||||
    uint16_t param_P6;
 | 
			
		||||
    int8_t param_P7;
 | 
			
		||||
    int8_t param_P8;
 | 
			
		||||
    int16_t param_P9;
 | 
			
		||||
    int8_t param_P10;
 | 
			
		||||
    int8_t param_P11;
 | 
			
		||||
  } __attribute__((packed)) compensation_params_;
 | 
			
		||||
 | 
			
		||||
  struct FloatParams {  // The BMP388 float point compensation trim parameters
 | 
			
		||||
    float param_T1;
 | 
			
		||||
    float param_T2;
 | 
			
		||||
    float param_T3;
 | 
			
		||||
    float param_P1;
 | 
			
		||||
    float param_P2;
 | 
			
		||||
    float param_P3;
 | 
			
		||||
    float param_P4;
 | 
			
		||||
    float param_P5;
 | 
			
		||||
    float param_P6;
 | 
			
		||||
    float param_P7;
 | 
			
		||||
    float param_P8;
 | 
			
		||||
    float param_P9;
 | 
			
		||||
    float param_P10;
 | 
			
		||||
    float param_P11;
 | 
			
		||||
  } compensation_float_params_;
 | 
			
		||||
 | 
			
		||||
  union {  // Copy of the BMP388's chip id register
 | 
			
		||||
    struct {
 | 
			
		||||
      uint8_t chip_id_nvm : 4;
 | 
			
		||||
      uint8_t chip_id_fixed : 4;
 | 
			
		||||
    } bit;
 | 
			
		||||
    uint8_t reg;
 | 
			
		||||
  } chip_id_ = {.reg = 0};
 | 
			
		||||
 | 
			
		||||
  union {  // Copy of the BMP388's event register
 | 
			
		||||
    struct {
 | 
			
		||||
      uint8_t por_detected : 1;
 | 
			
		||||
    } bit;
 | 
			
		||||
    uint8_t reg;
 | 
			
		||||
  } event_ = {.reg = 0};
 | 
			
		||||
 | 
			
		||||
  union {  // Copy of the BMP388's interrupt status register
 | 
			
		||||
    struct {
 | 
			
		||||
      uint8_t fwm_int : 1;
 | 
			
		||||
      uint8_t ffull_int : 1;
 | 
			
		||||
      uint8_t : 1;
 | 
			
		||||
      uint8_t drdy : 1;
 | 
			
		||||
    } bit;
 | 
			
		||||
    uint8_t reg;
 | 
			
		||||
  } int_status_ = {.reg = 0};
 | 
			
		||||
 | 
			
		||||
  union {  // Copy of the BMP388's power control register
 | 
			
		||||
    struct {
 | 
			
		||||
      uint8_t press_en : 1;
 | 
			
		||||
      uint8_t temp_en : 1;
 | 
			
		||||
      uint8_t : 2;
 | 
			
		||||
      uint8_t mode : 2;
 | 
			
		||||
    } bit;
 | 
			
		||||
    uint8_t reg;
 | 
			
		||||
  } pwr_ctrl_ = {.reg = 0};
 | 
			
		||||
 | 
			
		||||
  union {  // Copy of the BMP388's oversampling register
 | 
			
		||||
    struct {
 | 
			
		||||
      uint8_t osr_p : 3;
 | 
			
		||||
      uint8_t osr_t : 3;
 | 
			
		||||
    } bit;
 | 
			
		||||
    uint8_t reg;
 | 
			
		||||
  } osr_ = {.reg = 0};
 | 
			
		||||
 | 
			
		||||
  union {  // Copy of the BMP388's output data rate register
 | 
			
		||||
    struct {
 | 
			
		||||
      uint8_t odr_sel : 5;
 | 
			
		||||
    } bit;
 | 
			
		||||
    uint8_t reg;
 | 
			
		||||
  } odr_ = {.reg = 0};
 | 
			
		||||
 | 
			
		||||
  union {  // Copy of the BMP388's configuration register
 | 
			
		||||
    struct {
 | 
			
		||||
      uint8_t : 1;
 | 
			
		||||
      uint8_t iir_filter : 3;
 | 
			
		||||
    } bit;
 | 
			
		||||
    uint8_t reg;
 | 
			
		||||
  } config_ = {.reg = 0};
 | 
			
		||||
 | 
			
		||||
  // Bosch temperature compensation function
 | 
			
		||||
  float bmp388_compensate_temperature_(float uncomp_temp);
 | 
			
		||||
  // Bosch pressure compensation function
 | 
			
		||||
  float bmp388_compensate_pressure_(float uncomp_press, float t_lin);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace bmp3xx
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										100
									
								
								esphome/components/bmp3xx/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								esphome/components/bmp3xx/sensor.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,100 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import i2c, sensor
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_IIR_FILTER,
 | 
			
		||||
    CONF_OVERSAMPLING,
 | 
			
		||||
    CONF_PRESSURE,
 | 
			
		||||
    CONF_TEMPERATURE,
 | 
			
		||||
    DEVICE_CLASS_PRESSURE,
 | 
			
		||||
    DEVICE_CLASS_TEMPERATURE,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    UNIT_CELSIUS,
 | 
			
		||||
    UNIT_HECTOPASCAL,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ["@martgras"]
 | 
			
		||||
DEPENDENCIES = ["i2c"]
 | 
			
		||||
 | 
			
		||||
bmp3xx_ns = cg.esphome_ns.namespace("bmp3xx")
 | 
			
		||||
Oversampling = bmp3xx_ns.enum("Oversampling")
 | 
			
		||||
OVERSAMPLING_OPTIONS = {
 | 
			
		||||
    "NONE": Oversampling.OVERSAMPLING_NONE,
 | 
			
		||||
    "2X": Oversampling.OVERSAMPLING_X2,
 | 
			
		||||
    "4X": Oversampling.OVERSAMPLING_X4,
 | 
			
		||||
    "8X": Oversampling.OVERSAMPLING_X8,
 | 
			
		||||
    "16X": Oversampling.OVERSAMPLING_X16,
 | 
			
		||||
    "32x": Oversampling.OVERSAMPLING_X32,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IIRFilter = bmp3xx_ns.enum("IIRFilter")
 | 
			
		||||
IIR_FILTER_OPTIONS = {
 | 
			
		||||
    "OFF": IIRFilter.IIR_FILTER_OFF,
 | 
			
		||||
    "2X": IIRFilter.IIR_FILTER_2,
 | 
			
		||||
    "4X": IIRFilter.IIR_FILTER_4,
 | 
			
		||||
    "8X": IIRFilter.IIR_FILTER_8,
 | 
			
		||||
    "16X": IIRFilter.IIR_FILTER_16,
 | 
			
		||||
    "32X": IIRFilter.IIR_FILTER_32,
 | 
			
		||||
    "64X": IIRFilter.IIR_FILTER_64,
 | 
			
		||||
    "128X": IIRFilter.IIR_FILTER_128,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BMP3XXComponent = bmp3xx_ns.class_(
 | 
			
		||||
    "BMP3XXComponent", cg.PollingComponent, i2c.I2CDevice
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = (
 | 
			
		||||
    cv.Schema(
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(BMP3XXComponent),
 | 
			
		||||
            cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
 | 
			
		||||
                unit_of_measurement=UNIT_CELSIUS,
 | 
			
		||||
                accuracy_decimals=1,
 | 
			
		||||
                device_class=DEVICE_CLASS_TEMPERATURE,
 | 
			
		||||
                state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ).extend(
 | 
			
		||||
                {
 | 
			
		||||
                    cv.Optional(CONF_OVERSAMPLING, default="2X"): cv.enum(
 | 
			
		||||
                        OVERSAMPLING_OPTIONS, upper=True
 | 
			
		||||
                    ),
 | 
			
		||||
                }
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
 | 
			
		||||
                unit_of_measurement=UNIT_HECTOPASCAL,
 | 
			
		||||
                accuracy_decimals=1,
 | 
			
		||||
                device_class=DEVICE_CLASS_PRESSURE,
 | 
			
		||||
                state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ).extend(
 | 
			
		||||
                {
 | 
			
		||||
                    cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
 | 
			
		||||
                        OVERSAMPLING_OPTIONS, upper=True
 | 
			
		||||
                    ),
 | 
			
		||||
                }
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_IIR_FILTER, default="OFF"): cv.enum(
 | 
			
		||||
                IIR_FILTER_OPTIONS, upper=True
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
    .extend(cv.polling_component_schema("60s"))
 | 
			
		||||
    .extend(i2c.i2c_device_schema(0x77))
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await i2c.register_i2c_device(var, config)
 | 
			
		||||
    cg.add(var.set_iir_filter_config(config[CONF_IIR_FILTER]))
 | 
			
		||||
    if CONF_TEMPERATURE in config:
 | 
			
		||||
        conf = config[CONF_TEMPERATURE]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
        cg.add(var.set_temperature_sensor(sens))
 | 
			
		||||
        cg.add(var.set_temperature_oversampling_config(conf[CONF_OVERSAMPLING]))
 | 
			
		||||
 | 
			
		||||
    if CONF_PRESSURE in config:
 | 
			
		||||
        conf = config[CONF_PRESSURE]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
        cg.add(var.set_pressure_sensor(sens))
 | 
			
		||||
        cg.add(var.set_pressure_oversampling_config(conf[CONF_OVERSAMPLING]))
 | 
			
		||||
							
								
								
									
										127
									
								
								esphome/components/button/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								esphome/components/button/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,127 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome import automation
 | 
			
		||||
from esphome.automation import maybe_simple_id
 | 
			
		||||
from esphome.components import mqtt
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_DEVICE_CLASS,
 | 
			
		||||
    CONF_ENTITY_CATEGORY,
 | 
			
		||||
    CONF_ICON,
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_ON_PRESS,
 | 
			
		||||
    CONF_TRIGGER_ID,
 | 
			
		||||
    CONF_MQTT_ID,
 | 
			
		||||
    DEVICE_CLASS_RESTART,
 | 
			
		||||
    DEVICE_CLASS_UPDATE,
 | 
			
		||||
)
 | 
			
		||||
from esphome.core import CORE, coroutine_with_priority
 | 
			
		||||
from esphome.cpp_helpers import setup_entity
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ["@esphome/core"]
 | 
			
		||||
IS_PLATFORM_COMPONENT = True
 | 
			
		||||
 | 
			
		||||
DEVICE_CLASSES = [
 | 
			
		||||
    DEVICE_CLASS_RESTART,
 | 
			
		||||
    DEVICE_CLASS_UPDATE,
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
button_ns = cg.esphome_ns.namespace("button")
 | 
			
		||||
Button = button_ns.class_("Button", cg.EntityBase)
 | 
			
		||||
ButtonPtr = Button.operator("ptr")
 | 
			
		||||
 | 
			
		||||
PressAction = button_ns.class_("PressAction", automation.Action)
 | 
			
		||||
 | 
			
		||||
ButtonPressTrigger = button_ns.class_(
 | 
			
		||||
    "ButtonPressTrigger", automation.Trigger.template()
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BUTTON_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
 | 
			
		||||
    {
 | 
			
		||||
        cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTButtonComponent),
 | 
			
		||||
        cv.Optional(CONF_DEVICE_CLASS): validate_device_class,
 | 
			
		||||
        cv.Optional(CONF_ON_PRESS): automation.validate_automation(
 | 
			
		||||
            {
 | 
			
		||||
                cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ButtonPressTrigger),
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
    }
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
_UNDEF = object()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def button_schema(
 | 
			
		||||
    icon: str = _UNDEF,
 | 
			
		||||
    entity_category: str = _UNDEF,
 | 
			
		||||
    device_class: str = _UNDEF,
 | 
			
		||||
) -> cv.Schema:
 | 
			
		||||
    schema = BUTTON_SCHEMA
 | 
			
		||||
    if icon is not _UNDEF:
 | 
			
		||||
        schema = schema.extend({cv.Optional(CONF_ICON, default=icon): cv.icon})
 | 
			
		||||
    if entity_category is not _UNDEF:
 | 
			
		||||
        schema = schema.extend(
 | 
			
		||||
            {
 | 
			
		||||
                cv.Optional(
 | 
			
		||||
                    CONF_ENTITY_CATEGORY, default=entity_category
 | 
			
		||||
                ): cv.entity_category
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
    if device_class is not _UNDEF:
 | 
			
		||||
        schema = schema.extend(
 | 
			
		||||
            {
 | 
			
		||||
                cv.Optional(
 | 
			
		||||
                    CONF_DEVICE_CLASS, default=device_class
 | 
			
		||||
                ): validate_device_class
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
    return schema
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def setup_button_core_(var, config):
 | 
			
		||||
    await setup_entity(var, config)
 | 
			
		||||
 | 
			
		||||
    for conf in config.get(CONF_ON_PRESS, []):
 | 
			
		||||
        trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
 | 
			
		||||
        await automation.build_automation(trigger, [], conf)
 | 
			
		||||
 | 
			
		||||
    if CONF_DEVICE_CLASS in config:
 | 
			
		||||
        cg.add(var.set_device_class(config[CONF_DEVICE_CLASS]))
 | 
			
		||||
 | 
			
		||||
    if CONF_MQTT_ID in config:
 | 
			
		||||
        mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var)
 | 
			
		||||
        await mqtt.register_mqtt_component(mqtt_, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def register_button(var, config):
 | 
			
		||||
    if not CORE.has_id(config[CONF_ID]):
 | 
			
		||||
        var = cg.Pvariable(config[CONF_ID], var)
 | 
			
		||||
    cg.add(cg.App.register_button(var))
 | 
			
		||||
    await setup_button_core_(var, config)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def new_button(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    await register_button(var, config)
 | 
			
		||||
    return var
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
BUTTON_PRESS_SCHEMA = maybe_simple_id(
 | 
			
		||||
    {
 | 
			
		||||
        cv.Required(CONF_ID): cv.use_id(Button),
 | 
			
		||||
    }
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@automation.register_action("button.press", PressAction, BUTTON_PRESS_SCHEMA)
 | 
			
		||||
async def button_press_to_code(config, action_id, template_arg, args):
 | 
			
		||||
    paren = await cg.get_variable(config[CONF_ID])
 | 
			
		||||
    return cg.new_Pvariable(action_id, template_arg, paren)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@coroutine_with_priority(100.0)
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    cg.add_global(button_ns.using)
 | 
			
		||||
    cg.add_define("USE_BUTTON")
 | 
			
		||||
							
								
								
									
										28
									
								
								esphome/components/button/automation.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								esphome/components/button/automation.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/components/button/button.h"
 | 
			
		||||
#include "esphome/core/automation.h"
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace button {
 | 
			
		||||
 | 
			
		||||
template<typename... Ts> class PressAction : public Action<Ts...> {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit PressAction(Button *button) : button_(button) {}
 | 
			
		||||
 | 
			
		||||
  void play(Ts... x) override { this->button_->press(); }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  Button *button_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ButtonPressTrigger : public Trigger<> {
 | 
			
		||||
 public:
 | 
			
		||||
  ButtonPressTrigger(Button *button) {
 | 
			
		||||
    button->add_on_press_callback([this]() { this->trigger(); });
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace button
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										28
									
								
								esphome/components/button/button.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								esphome/components/button/button.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
			
		||||
#include "button.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace button {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "button";
 | 
			
		||||
 | 
			
		||||
Button::Button(const std::string &name) : EntityBase(name) {}
 | 
			
		||||
Button::Button() : Button("") {}
 | 
			
		||||
 | 
			
		||||
void Button::press() {
 | 
			
		||||
  ESP_LOGD(TAG, "'%s' Pressed.", this->get_name().c_str());
 | 
			
		||||
  this->press_action();
 | 
			
		||||
  this->press_callback_.call();
 | 
			
		||||
}
 | 
			
		||||
void Button::add_on_press_callback(std::function<void()> &&callback) { this->press_callback_.add(std::move(callback)); }
 | 
			
		||||
uint32_t Button::hash_base() { return 1495763804UL; }
 | 
			
		||||
 | 
			
		||||
void Button::set_device_class(const std::string &device_class) { this->device_class_ = device_class; }
 | 
			
		||||
std::string Button::get_device_class() {
 | 
			
		||||
  if (this->device_class_.has_value())
 | 
			
		||||
    return *this->device_class_;
 | 
			
		||||
  return "";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace button
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										57
									
								
								esphome/components/button/button.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								esphome/components/button/button.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/entity_base.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace button {
 | 
			
		||||
 | 
			
		||||
#define LOG_BUTTON(prefix, type, obj) \
 | 
			
		||||
  if ((obj) != nullptr) { \
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \
 | 
			
		||||
    if (!(obj)->get_icon().empty()) { \
 | 
			
		||||
      ESP_LOGCONFIG(TAG, "%s  Icon: '%s'", prefix, (obj)->get_icon().c_str()); \
 | 
			
		||||
    } \
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
/** Base class for all buttons.
 | 
			
		||||
 *
 | 
			
		||||
 * A button is just a momentary switch that does not have a state, only a trigger.
 | 
			
		||||
 */
 | 
			
		||||
class Button : public EntityBase {
 | 
			
		||||
 public:
 | 
			
		||||
  explicit Button();
 | 
			
		||||
  explicit Button(const std::string &name);
 | 
			
		||||
 | 
			
		||||
  /** Press this button. This is called by the front-end.
 | 
			
		||||
   *
 | 
			
		||||
   * For implementing buttons, please override press_action.
 | 
			
		||||
   */
 | 
			
		||||
  void press();
 | 
			
		||||
 | 
			
		||||
  /** Set callback for state changes.
 | 
			
		||||
   *
 | 
			
		||||
   * @param callback The void() callback.
 | 
			
		||||
   */
 | 
			
		||||
  void add_on_press_callback(std::function<void()> &&callback);
 | 
			
		||||
 | 
			
		||||
  /// Set the Home Assistant device class (see button::device_class).
 | 
			
		||||
  void set_device_class(const std::string &device_class);
 | 
			
		||||
 | 
			
		||||
  /// Get the device class for this button.
 | 
			
		||||
  std::string get_device_class();
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  /** You should implement this virtual method if you want to create your own button.
 | 
			
		||||
   */
 | 
			
		||||
  virtual void press_action(){};
 | 
			
		||||
 | 
			
		||||
  uint32_t hash_base() override;
 | 
			
		||||
 | 
			
		||||
  CallbackManager<void()> press_callback_{};
 | 
			
		||||
  optional<std::string> device_class_{};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace button
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
@@ -85,14 +85,7 @@ void CaptivePortal::start() {
 | 
			
		||||
  this->dns_server_->start(53, "*", (uint32_t) ip);
 | 
			
		||||
 | 
			
		||||
  this->base_->get_server()->onNotFound([this](AsyncWebServerRequest *req) {
 | 
			
		||||
    bool not_found = false;
 | 
			
		||||
    if (!this->active_) {
 | 
			
		||||
      not_found = true;
 | 
			
		||||
    } else if (req->host().c_str() == wifi::global_wifi_component->wifi_soft_ap_ip().str()) {
 | 
			
		||||
      not_found = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (not_found) {
 | 
			
		||||
    if (!this->active_ || req->host().c_str() == wifi::global_wifi_component->wifi_soft_ap_ip().str()) {
 | 
			
		||||
      req->send(404, "text/html", "File not found");
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										53
									
								
								esphome/components/cd74hc4067/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								esphome/components/cd74hc4067/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,53 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
from esphome import pins
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_DELAY,
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ["@asoehlke"]
 | 
			
		||||
AUTO_LOAD = ["sensor", "voltage_sampler"]
 | 
			
		||||
 | 
			
		||||
cd74hc4067_ns = cg.esphome_ns.namespace("cd74hc4067")
 | 
			
		||||
 | 
			
		||||
CD74HC4067Component = cd74hc4067_ns.class_(
 | 
			
		||||
    "CD74HC4067Component", cg.Component, cg.PollingComponent
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CONF_PIN_S0 = "pin_s0"
 | 
			
		||||
CONF_PIN_S1 = "pin_s1"
 | 
			
		||||
CONF_PIN_S2 = "pin_s2"
 | 
			
		||||
CONF_PIN_S3 = "pin_s3"
 | 
			
		||||
 | 
			
		||||
DEFAULT_DELAY = "2ms"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(): cv.declare_id(CD74HC4067Component),
 | 
			
		||||
        cv.Required(CONF_PIN_S0): pins.internal_gpio_output_pin_schema,
 | 
			
		||||
        cv.Required(CONF_PIN_S1): pins.internal_gpio_output_pin_schema,
 | 
			
		||||
        cv.Required(CONF_PIN_S2): pins.internal_gpio_output_pin_schema,
 | 
			
		||||
        cv.Required(CONF_PIN_S3): pins.internal_gpio_output_pin_schema,
 | 
			
		||||
        cv.Optional(
 | 
			
		||||
            CONF_DELAY, default=DEFAULT_DELAY
 | 
			
		||||
        ): cv.positive_time_period_milliseconds,
 | 
			
		||||
    }
 | 
			
		||||
).extend(cv.COMPONENT_SCHEMA)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
 | 
			
		||||
    pin_s0 = await cg.gpio_pin_expression(config[CONF_PIN_S0])
 | 
			
		||||
    cg.add(var.set_pin_s0(pin_s0))
 | 
			
		||||
    pin_s1 = await cg.gpio_pin_expression(config[CONF_PIN_S1])
 | 
			
		||||
    cg.add(var.set_pin_s1(pin_s1))
 | 
			
		||||
    pin_s2 = await cg.gpio_pin_expression(config[CONF_PIN_S2])
 | 
			
		||||
    cg.add(var.set_pin_s2(pin_s2))
 | 
			
		||||
    pin_s3 = await cg.gpio_pin_expression(config[CONF_PIN_S3])
 | 
			
		||||
    cg.add(var.set_pin_s3(pin_s3))
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_switch_delay(config[CONF_DELAY]))
 | 
			
		||||
							
								
								
									
										86
									
								
								esphome/components/cd74hc4067/cd74hc4067.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								esphome/components/cd74hc4067/cd74hc4067.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,86 @@
 | 
			
		||||
#include "cd74hc4067.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace cd74hc4067 {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "cd74hc4067";
 | 
			
		||||
 | 
			
		||||
float CD74HC4067Component::get_setup_priority() const { return setup_priority::DATA; }
 | 
			
		||||
 | 
			
		||||
void CD74HC4067Component::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up CD74HC4067...");
 | 
			
		||||
 | 
			
		||||
  this->pin_s0_->setup();
 | 
			
		||||
  this->pin_s1_->setup();
 | 
			
		||||
  this->pin_s2_->setup();
 | 
			
		||||
  this->pin_s3_->setup();
 | 
			
		||||
 | 
			
		||||
  // set other pin, so that activate_pin will really switch
 | 
			
		||||
  this->active_pin_ = 1;
 | 
			
		||||
  this->activate_pin(0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CD74HC4067Component::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "CD74HC4067 Multiplexer:");
 | 
			
		||||
  LOG_PIN("  S0 Pin: ", this->pin_s0_);
 | 
			
		||||
  LOG_PIN("  S1 Pin: ", this->pin_s1_);
 | 
			
		||||
  LOG_PIN("  S2 Pin: ", this->pin_s2_);
 | 
			
		||||
  LOG_PIN("  S3 Pin: ", this->pin_s3_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "switch delay: %d", this->switch_delay_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CD74HC4067Component::activate_pin(uint8_t pin) {
 | 
			
		||||
  if (this->active_pin_ != pin) {
 | 
			
		||||
    ESP_LOGD(TAG, "switch to input %d", pin);
 | 
			
		||||
 | 
			
		||||
    static int mux_channel[16][4] = {
 | 
			
		||||
        {0, 0, 0, 0},  // channel 0
 | 
			
		||||
        {1, 0, 0, 0},  // channel 1
 | 
			
		||||
        {0, 1, 0, 0},  // channel 2
 | 
			
		||||
        {1, 1, 0, 0},  // channel 3
 | 
			
		||||
        {0, 0, 1, 0},  // channel 4
 | 
			
		||||
        {1, 0, 1, 0},  // channel 5
 | 
			
		||||
        {0, 1, 1, 0},  // channel 6
 | 
			
		||||
        {1, 1, 1, 0},  // channel 7
 | 
			
		||||
        {0, 0, 0, 1},  // channel 8
 | 
			
		||||
        {1, 0, 0, 1},  // channel 9
 | 
			
		||||
        {0, 1, 0, 1},  // channel 10
 | 
			
		||||
        {1, 1, 0, 1},  // channel 11
 | 
			
		||||
        {0, 0, 1, 1},  // channel 12
 | 
			
		||||
        {1, 0, 1, 1},  // channel 13
 | 
			
		||||
        {0, 1, 1, 1},  // channel 14
 | 
			
		||||
        {1, 1, 1, 1}   // channel 15
 | 
			
		||||
    };
 | 
			
		||||
    this->pin_s0_->digital_write(mux_channel[pin][0]);
 | 
			
		||||
    this->pin_s1_->digital_write(mux_channel[pin][1]);
 | 
			
		||||
    this->pin_s2_->digital_write(mux_channel[pin][2]);
 | 
			
		||||
    this->pin_s3_->digital_write(mux_channel[pin][3]);
 | 
			
		||||
    // small delay is needed to let the multiplexer switch
 | 
			
		||||
    delay(this->switch_delay_);
 | 
			
		||||
    this->active_pin_ = pin;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CD74HC4067Sensor::CD74HC4067Sensor(CD74HC4067Component *parent) : parent_(parent) {}
 | 
			
		||||
 | 
			
		||||
void CD74HC4067Sensor::update() {
 | 
			
		||||
  float value_v = this->sample();
 | 
			
		||||
  this->publish_state(value_v);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float CD74HC4067Sensor::get_setup_priority() const { return this->parent_->get_setup_priority() - 1.0f; }
 | 
			
		||||
 | 
			
		||||
float CD74HC4067Sensor::sample() {
 | 
			
		||||
  this->parent_->activate_pin(this->pin_);
 | 
			
		||||
  return this->source_->sample();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CD74HC4067Sensor::dump_config() {
 | 
			
		||||
  LOG_SENSOR(TAG, "CD74HC4067 Sensor", this);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Pin: %u", this->pin_);
 | 
			
		||||
  LOG_UPDATE_INTERVAL(this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace cd74hc4067
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										65
									
								
								esphome/components/cd74hc4067/cd74hc4067.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								esphome/components/cd74hc4067/cd74hc4067.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,65 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
#include "esphome/core/hal.h"
 | 
			
		||||
#include "esphome/components/sensor/sensor.h"
 | 
			
		||||
#include "esphome/components/voltage_sampler/voltage_sampler.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace cd74hc4067 {
 | 
			
		||||
 | 
			
		||||
class CD74HC4067Component : public Component {
 | 
			
		||||
 public:
 | 
			
		||||
  /// Set up the internal sensor array.
 | 
			
		||||
  void setup() override;
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
  float get_setup_priority() const override;
 | 
			
		||||
 | 
			
		||||
  /// setting pin active by setting the right combination of the four multiplexer input pins
 | 
			
		||||
  void activate_pin(uint8_t pin);
 | 
			
		||||
 | 
			
		||||
  /// set the pin connected to multiplexer control pin 0
 | 
			
		||||
  void set_pin_s0(InternalGPIOPin *pin) { this->pin_s0_ = pin; }
 | 
			
		||||
  /// set the pin connected to multiplexer control pin 1
 | 
			
		||||
  void set_pin_s1(InternalGPIOPin *pin) { this->pin_s1_ = pin; }
 | 
			
		||||
  /// set the pin connected to multiplexer control pin 2
 | 
			
		||||
  void set_pin_s2(InternalGPIOPin *pin) { this->pin_s2_ = pin; }
 | 
			
		||||
  /// set the pin connected to multiplexer control pin 3
 | 
			
		||||
  void set_pin_s3(InternalGPIOPin *pin) { this->pin_s3_ = pin; }
 | 
			
		||||
 | 
			
		||||
  /// set the delay needed after an input switch
 | 
			
		||||
  void set_switch_delay(uint32_t switch_delay) { this->switch_delay_ = switch_delay; }
 | 
			
		||||
 | 
			
		||||
 private:
 | 
			
		||||
  InternalGPIOPin *pin_s0_;
 | 
			
		||||
  InternalGPIOPin *pin_s1_;
 | 
			
		||||
  InternalGPIOPin *pin_s2_;
 | 
			
		||||
  InternalGPIOPin *pin_s3_;
 | 
			
		||||
  /// the currently active pin
 | 
			
		||||
  uint8_t active_pin_;
 | 
			
		||||
  uint32_t switch_delay_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class CD74HC4067Sensor : public sensor::Sensor, public PollingComponent, public voltage_sampler::VoltageSampler {
 | 
			
		||||
 public:
 | 
			
		||||
  CD74HC4067Sensor(CD74HC4067Component *parent);
 | 
			
		||||
 | 
			
		||||
  void update() override;
 | 
			
		||||
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
  /// `HARDWARE_LATE` setup priority.
 | 
			
		||||
  float get_setup_priority() const override;
 | 
			
		||||
  void set_pin(uint8_t pin) { this->pin_ = pin; }
 | 
			
		||||
  void set_source(voltage_sampler::VoltageSampler *source) { this->source_ = source; }
 | 
			
		||||
 | 
			
		||||
  float sample() override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  CD74HC4067Component *parent_;
 | 
			
		||||
  /// The sampling source to read values from.
 | 
			
		||||
  voltage_sampler::VoltageSampler *source_;
 | 
			
		||||
 | 
			
		||||
  uint8_t pin_;
 | 
			
		||||
};
 | 
			
		||||
}  // namespace cd74hc4067
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
							
								
								
									
										55
									
								
								esphome/components/cd74hc4067/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								esphome/components/cd74hc4067/sensor.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import sensor, voltage_sampler
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_SENSOR,
 | 
			
		||||
    CONF_NUMBER,
 | 
			
		||||
    ICON_FLASH,
 | 
			
		||||
    UNIT_VOLT,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    DEVICE_CLASS_VOLTAGE,
 | 
			
		||||
)
 | 
			
		||||
from . import cd74hc4067_ns, CD74HC4067Component
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ["cd74hc4067"]
 | 
			
		||||
 | 
			
		||||
CD74HC4067Sensor = cd74hc4067_ns.class_(
 | 
			
		||||
    "CD74HC4067Sensor",
 | 
			
		||||
    sensor.Sensor,
 | 
			
		||||
    cg.PollingComponent,
 | 
			
		||||
    voltage_sampler.VoltageSampler,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CONF_CD74HC4067_ID = "cd74hc4067_id"
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = (
 | 
			
		||||
    sensor.sensor_schema(
 | 
			
		||||
        unit_of_measurement=UNIT_VOLT,
 | 
			
		||||
        accuracy_decimals=3,
 | 
			
		||||
        device_class=DEVICE_CLASS_VOLTAGE,
 | 
			
		||||
        state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
        icon=ICON_FLASH,
 | 
			
		||||
    )
 | 
			
		||||
    .extend(
 | 
			
		||||
        {
 | 
			
		||||
            cv.GenerateID(): cv.declare_id(CD74HC4067Sensor),
 | 
			
		||||
            cv.GenerateID(CONF_CD74HC4067_ID): cv.use_id(CD74HC4067Component),
 | 
			
		||||
            cv.Required(CONF_NUMBER): cv.int_range(0, 15),
 | 
			
		||||
            cv.Required(CONF_SENSOR): cv.use_id(voltage_sampler.VoltageSampler),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
    .extend(cv.polling_component_schema("60s"))
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    parent = await cg.get_variable(config[CONF_CD74HC4067_ID])
 | 
			
		||||
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID], parent)
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
    await sensor.register_sensor(var, config)
 | 
			
		||||
    cg.add(var.set_pin(config[CONF_NUMBER]))
 | 
			
		||||
 | 
			
		||||
    sens = await cg.get_variable(config[CONF_SENSOR])
 | 
			
		||||
    cg.add(var.set_source(sens))
 | 
			
		||||
@@ -213,7 +213,7 @@ async def setup_climate_core_(var, config):
 | 
			
		||||
        if CONF_MODE_COMMAND_TOPIC in config:
 | 
			
		||||
            cg.add(mqtt_.set_custom_mode_command_topic(config[CONF_MODE_COMMAND_TOPIC]))
 | 
			
		||||
        if CONF_MODE_STATE_TOPIC in config:
 | 
			
		||||
            cg.add(mqtt_.set_custom_state_topic(config[CONF_MODE_STATE_TOPIC]))
 | 
			
		||||
            cg.add(mqtt_.set_custom_mode_state_topic(config[CONF_MODE_STATE_TOPIC]))
 | 
			
		||||
 | 
			
		||||
        if CONF_SWING_MODE_COMMAND_TOPIC in config:
 | 
			
		||||
            cg.add(
 | 
			
		||||
 
 | 
			
		||||
@@ -10,21 +10,22 @@ climate::ClimateTraits ClimateIR::traits() {
 | 
			
		||||
  auto traits = climate::ClimateTraits();
 | 
			
		||||
  traits.set_supports_current_temperature(this->sensor_ != nullptr);
 | 
			
		||||
  traits.set_supported_modes({climate::CLIMATE_MODE_OFF, climate::CLIMATE_MODE_HEAT_COOL});
 | 
			
		||||
  if (supports_cool_)
 | 
			
		||||
  if (this->supports_cool_)
 | 
			
		||||
    traits.add_supported_mode(climate::CLIMATE_MODE_COOL);
 | 
			
		||||
  if (supports_heat_)
 | 
			
		||||
  if (this->supports_heat_)
 | 
			
		||||
    traits.add_supported_mode(climate::CLIMATE_MODE_HEAT);
 | 
			
		||||
  if (supports_dry_)
 | 
			
		||||
  if (this->supports_dry_)
 | 
			
		||||
    traits.add_supported_mode(climate::CLIMATE_MODE_DRY);
 | 
			
		||||
  if (supports_fan_only_)
 | 
			
		||||
  if (this->supports_fan_only_)
 | 
			
		||||
    traits.add_supported_mode(climate::CLIMATE_MODE_FAN_ONLY);
 | 
			
		||||
 | 
			
		||||
  traits.set_supports_two_point_target_temperature(false);
 | 
			
		||||
  traits.set_visual_min_temperature(this->minimum_temperature_);
 | 
			
		||||
  traits.set_visual_max_temperature(this->maximum_temperature_);
 | 
			
		||||
  traits.set_visual_temperature_step(this->temperature_step_);
 | 
			
		||||
  traits.set_supported_fan_modes(fan_modes_);
 | 
			
		||||
  traits.set_supported_swing_modes(swing_modes_);
 | 
			
		||||
  traits.set_supported_fan_modes(this->fan_modes_);
 | 
			
		||||
  traits.set_supported_swing_modes(this->swing_modes_);
 | 
			
		||||
  traits.set_supported_presets(this->presets_);
 | 
			
		||||
  return traits;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -50,6 +51,7 @@ void ClimateIR::setup() {
 | 
			
		||||
        roundf(clamp(this->current_temperature, this->minimum_temperature_, this->maximum_temperature_));
 | 
			
		||||
    this->fan_mode = climate::CLIMATE_FAN_AUTO;
 | 
			
		||||
    this->swing_mode = climate::CLIMATE_SWING_OFF;
 | 
			
		||||
    this->preset = climate::CLIMATE_PRESET_NONE;
 | 
			
		||||
  }
 | 
			
		||||
  // Never send nan to HA
 | 
			
		||||
  if (std::isnan(this->target_temperature))
 | 
			
		||||
@@ -65,6 +67,8 @@ void ClimateIR::control(const climate::ClimateCall &call) {
 | 
			
		||||
    this->fan_mode = *call.get_fan_mode();
 | 
			
		||||
  if (call.get_swing_mode().has_value())
 | 
			
		||||
    this->swing_mode = *call.get_swing_mode();
 | 
			
		||||
  if (call.get_preset().has_value())
 | 
			
		||||
    this->preset = *call.get_preset();
 | 
			
		||||
  this->transmit_state();
 | 
			
		||||
  this->publish_state();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@ class ClimateIR : public climate::Climate, public Component, public remote_base:
 | 
			
		||||
 public:
 | 
			
		||||
  ClimateIR(float minimum_temperature, float maximum_temperature, float temperature_step = 1.0f,
 | 
			
		||||
            bool supports_dry = false, bool supports_fan_only = false, std::set<climate::ClimateFanMode> fan_modes = {},
 | 
			
		||||
            std::set<climate::ClimateSwingMode> swing_modes = {}) {
 | 
			
		||||
            std::set<climate::ClimateSwingMode> swing_modes = {}, std::set<climate::ClimatePreset> presets = {}) {
 | 
			
		||||
    this->minimum_temperature_ = minimum_temperature;
 | 
			
		||||
    this->maximum_temperature_ = maximum_temperature;
 | 
			
		||||
    this->temperature_step_ = temperature_step;
 | 
			
		||||
@@ -30,6 +30,7 @@ class ClimateIR : public climate::Climate, public Component, public remote_base:
 | 
			
		||||
    this->supports_fan_only_ = supports_fan_only;
 | 
			
		||||
    this->fan_modes_ = std::move(fan_modes);
 | 
			
		||||
    this->swing_modes_ = std::move(swing_modes);
 | 
			
		||||
    this->presets_ = std::move(presets);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void setup() override;
 | 
			
		||||
@@ -61,6 +62,7 @@ class ClimateIR : public climate::Climate, public Component, public remote_base:
 | 
			
		||||
  bool supports_fan_only_{false};
 | 
			
		||||
  std::set<climate::ClimateFanMode> fan_modes_ = {};
 | 
			
		||||
  std::set<climate::ClimateSwingMode> swing_modes_ = {};
 | 
			
		||||
  std::set<climate::ClimatePreset> presets_ = {};
 | 
			
		||||
 | 
			
		||||
  remote_transmitter::RemoteTransmitterComponent *transmitter_;
 | 
			
		||||
  sensor::Sensor *sensor_{nullptr};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
#include "coolix.h"
 | 
			
		||||
#include "esphome/components/remote_base/coolix_protocol.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
@@ -6,29 +7,29 @@ namespace coolix {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "coolix.climate";
 | 
			
		||||
 | 
			
		||||
const uint32_t COOLIX_OFF = 0xB27BE0;
 | 
			
		||||
const uint32_t COOLIX_SWING = 0xB26BE0;
 | 
			
		||||
const uint32_t COOLIX_LED = 0xB5F5A5;
 | 
			
		||||
const uint32_t COOLIX_SILENCE_FP = 0xB5F5B6;
 | 
			
		||||
static const uint32_t COOLIX_OFF = 0xB27BE0;
 | 
			
		||||
static const uint32_t COOLIX_SWING = 0xB26BE0;
 | 
			
		||||
static const uint32_t COOLIX_LED = 0xB5F5A5;
 | 
			
		||||
static const uint32_t COOLIX_SILENCE_FP = 0xB5F5B6;
 | 
			
		||||
 | 
			
		||||
// On, 25C, Mode: Auto, Fan: Auto, Zone Follow: Off, Sensor Temp: Ignore.
 | 
			
		||||
const uint8_t COOLIX_COOL = 0b0000;
 | 
			
		||||
const uint8_t COOLIX_DRY_FAN = 0b0100;
 | 
			
		||||
const uint8_t COOLIX_AUTO = 0b1000;
 | 
			
		||||
const uint8_t COOLIX_HEAT = 0b1100;
 | 
			
		||||
const uint32_t COOLIX_MODE_MASK = 0b1100;
 | 
			
		||||
const uint32_t COOLIX_FAN_MASK = 0xF000;
 | 
			
		||||
const uint32_t COOLIX_FAN_MODE_AUTO_DRY = 0x1000;
 | 
			
		||||
const uint32_t COOLIX_FAN_AUTO = 0xB000;
 | 
			
		||||
const uint32_t COOLIX_FAN_MIN = 0x9000;
 | 
			
		||||
const uint32_t COOLIX_FAN_MED = 0x5000;
 | 
			
		||||
const uint32_t COOLIX_FAN_MAX = 0x3000;
 | 
			
		||||
static const uint8_t COOLIX_COOL = 0b0000;
 | 
			
		||||
static const uint8_t COOLIX_DRY_FAN = 0b0100;
 | 
			
		||||
static const uint8_t COOLIX_AUTO = 0b1000;
 | 
			
		||||
static const uint8_t COOLIX_HEAT = 0b1100;
 | 
			
		||||
static const uint32_t COOLIX_MODE_MASK = 0b1100;
 | 
			
		||||
static const uint32_t COOLIX_FAN_MASK = 0xF000;
 | 
			
		||||
static const uint32_t COOLIX_FAN_MODE_AUTO_DRY = 0x1000;
 | 
			
		||||
static const uint32_t COOLIX_FAN_AUTO = 0xB000;
 | 
			
		||||
static const uint32_t COOLIX_FAN_MIN = 0x9000;
 | 
			
		||||
static const uint32_t COOLIX_FAN_MED = 0x5000;
 | 
			
		||||
static const uint32_t COOLIX_FAN_MAX = 0x3000;
 | 
			
		||||
 | 
			
		||||
// Temperature
 | 
			
		||||
const uint8_t COOLIX_TEMP_RANGE = COOLIX_TEMP_MAX - COOLIX_TEMP_MIN + 1;
 | 
			
		||||
const uint8_t COOLIX_FAN_TEMP_CODE = 0b11100000;  // Part of Fan Mode.
 | 
			
		||||
const uint32_t COOLIX_TEMP_MASK = 0b11110000;
 | 
			
		||||
const uint8_t COOLIX_TEMP_MAP[COOLIX_TEMP_RANGE] = {
 | 
			
		||||
static const uint8_t COOLIX_TEMP_RANGE = COOLIX_TEMP_MAX - COOLIX_TEMP_MIN + 1;
 | 
			
		||||
static const uint8_t COOLIX_FAN_TEMP_CODE = 0b11100000;  // Part of Fan Mode.
 | 
			
		||||
static const uint32_t COOLIX_TEMP_MASK = 0b11110000;
 | 
			
		||||
static const uint8_t COOLIX_TEMP_MAP[COOLIX_TEMP_RANGE] = {
 | 
			
		||||
    0b00000000,  // 17C
 | 
			
		||||
    0b00010000,  // 18c
 | 
			
		||||
    0b00110000,  // 19C
 | 
			
		||||
@@ -45,17 +46,6 @@ const uint8_t COOLIX_TEMP_MAP[COOLIX_TEMP_RANGE] = {
 | 
			
		||||
    0b10110000   // 30C
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Constants
 | 
			
		||||
static const uint32_t BIT_MARK_US = 660;
 | 
			
		||||
static const uint32_t HEADER_MARK_US = 560 * 8;
 | 
			
		||||
static const uint32_t HEADER_SPACE_US = 560 * 8;
 | 
			
		||||
static const uint32_t BIT_ONE_SPACE_US = 1500;
 | 
			
		||||
static const uint32_t BIT_ZERO_SPACE_US = 450;
 | 
			
		||||
static const uint32_t FOOTER_MARK_US = BIT_MARK_US;
 | 
			
		||||
static const uint32_t FOOTER_SPACE_US = HEADER_SPACE_US;
 | 
			
		||||
 | 
			
		||||
const uint16_t COOLIX_BITS = 24;
 | 
			
		||||
 | 
			
		||||
void CoolixClimate::transmit_state() {
 | 
			
		||||
  uint32_t remote_state = 0xB20F00;
 | 
			
		||||
 | 
			
		||||
@@ -111,119 +101,60 @@ void CoolixClimate::transmit_state() {
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  ESP_LOGV(TAG, "Sending coolix code: 0x%02X", remote_state);
 | 
			
		||||
  ESP_LOGV(TAG, "Sending coolix code: 0x%06X", remote_state);
 | 
			
		||||
 | 
			
		||||
  auto transmit = this->transmitter_->transmit();
 | 
			
		||||
  auto data = transmit.get_data();
 | 
			
		||||
 | 
			
		||||
  data->set_carrier_frequency(38000);
 | 
			
		||||
  uint16_t repeat = 1;
 | 
			
		||||
  for (uint16_t r = 0; r <= repeat; r++) {
 | 
			
		||||
    // Header
 | 
			
		||||
    data->mark(HEADER_MARK_US);
 | 
			
		||||
    data->space(HEADER_SPACE_US);
 | 
			
		||||
    // Data
 | 
			
		||||
    //   Break data into bytes, starting at the Most Significant
 | 
			
		||||
    //   Byte. Each byte then being sent normal, then followed inverted.
 | 
			
		||||
    for (uint16_t i = 8; i <= COOLIX_BITS; i += 8) {
 | 
			
		||||
      // Grab a bytes worth of data.
 | 
			
		||||
      uint8_t byte = (remote_state >> (COOLIX_BITS - i)) & 0xFF;
 | 
			
		||||
      // Normal
 | 
			
		||||
      for (uint64_t mask = 1ULL << 7; mask; mask >>= 1) {
 | 
			
		||||
        data->mark(BIT_MARK_US);
 | 
			
		||||
        data->space((byte & mask) ? BIT_ONE_SPACE_US : BIT_ZERO_SPACE_US);
 | 
			
		||||
      }
 | 
			
		||||
      // Inverted
 | 
			
		||||
      for (uint64_t mask = 1ULL << 7; mask; mask >>= 1) {
 | 
			
		||||
        data->mark(BIT_MARK_US);
 | 
			
		||||
        data->space(!(byte & mask) ? BIT_ONE_SPACE_US : BIT_ZERO_SPACE_US);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    // Footer
 | 
			
		||||
    data->mark(BIT_MARK_US);
 | 
			
		||||
    data->space(FOOTER_SPACE_US);  // Pause before repeating
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  remote_base::CoolixProtocol().encode(data, remote_state);
 | 
			
		||||
  transmit.perform();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool CoolixClimate::on_receive(remote_base::RemoteReceiveData data) {
 | 
			
		||||
bool CoolixClimate::on_coolix(climate::Climate *parent, remote_base::RemoteReceiveData data) {
 | 
			
		||||
  auto decoded = remote_base::CoolixProtocol().decode(data);
 | 
			
		||||
  if (!decoded.has_value())
 | 
			
		||||
    return false;
 | 
			
		||||
  // Decoded remote state y 3 bytes long code.
 | 
			
		||||
  uint32_t remote_state = 0;
 | 
			
		||||
  // The protocol sends the data twice, read here
 | 
			
		||||
  uint32_t loop_read;
 | 
			
		||||
  for (uint16_t loop = 1; loop <= 2; loop++) {
 | 
			
		||||
    if (!data.expect_item(HEADER_MARK_US, HEADER_SPACE_US))
 | 
			
		||||
      return false;
 | 
			
		||||
    loop_read = 0;
 | 
			
		||||
    for (uint8_t a_byte = 0; a_byte < 3; a_byte++) {
 | 
			
		||||
      uint8_t byte = 0;
 | 
			
		||||
      for (int8_t a_bit = 7; a_bit >= 0; a_bit--) {
 | 
			
		||||
        if (data.expect_item(BIT_MARK_US, BIT_ONE_SPACE_US))
 | 
			
		||||
          byte |= 1 << a_bit;
 | 
			
		||||
        else if (!data.expect_item(BIT_MARK_US, BIT_ZERO_SPACE_US))
 | 
			
		||||
          return false;
 | 
			
		||||
      }
 | 
			
		||||
      // Need to see this segment inverted
 | 
			
		||||
      for (int8_t a_bit = 7; a_bit >= 0; a_bit--) {
 | 
			
		||||
        bool bit = byte & (1 << a_bit);
 | 
			
		||||
        if (!data.expect_item(BIT_MARK_US, bit ? BIT_ZERO_SPACE_US : BIT_ONE_SPACE_US))
 | 
			
		||||
          return false;
 | 
			
		||||
      }
 | 
			
		||||
      // Receiving MSB first: reorder bytes
 | 
			
		||||
      loop_read |= byte << ((2 - a_byte) * 8);
 | 
			
		||||
    }
 | 
			
		||||
    // Footer Mark
 | 
			
		||||
    if (!data.expect_mark(BIT_MARK_US))
 | 
			
		||||
      return false;
 | 
			
		||||
    if (loop == 1) {
 | 
			
		||||
      // Back up state on first loop
 | 
			
		||||
      remote_state = loop_read;
 | 
			
		||||
      if (!data.expect_space(FOOTER_SPACE_US))
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ESP_LOGV(TAG, "Decoded 0x%02X", remote_state);
 | 
			
		||||
  if (remote_state != loop_read || (remote_state & 0xFF0000) != 0xB20000)
 | 
			
		||||
  uint32_t remote_state = *decoded;
 | 
			
		||||
  ESP_LOGV(TAG, "Decoded 0x%06X", remote_state);
 | 
			
		||||
  if ((remote_state & 0xFF0000) != 0xB20000)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  if (remote_state == COOLIX_OFF) {
 | 
			
		||||
    this->mode = climate::CLIMATE_MODE_OFF;
 | 
			
		||||
    parent->mode = climate::CLIMATE_MODE_OFF;
 | 
			
		||||
  } else if (remote_state == COOLIX_SWING) {
 | 
			
		||||
    this->swing_mode =
 | 
			
		||||
        this->swing_mode == climate::CLIMATE_SWING_OFF ? climate::CLIMATE_SWING_VERTICAL : climate::CLIMATE_SWING_OFF;
 | 
			
		||||
    parent->swing_mode =
 | 
			
		||||
        parent->swing_mode == climate::CLIMATE_SWING_OFF ? climate::CLIMATE_SWING_VERTICAL : climate::CLIMATE_SWING_OFF;
 | 
			
		||||
  } else {
 | 
			
		||||
    if ((remote_state & COOLIX_MODE_MASK) == COOLIX_HEAT)
 | 
			
		||||
      this->mode = climate::CLIMATE_MODE_HEAT;
 | 
			
		||||
      parent->mode = climate::CLIMATE_MODE_HEAT;
 | 
			
		||||
    else if ((remote_state & COOLIX_MODE_MASK) == COOLIX_AUTO)
 | 
			
		||||
      this->mode = climate::CLIMATE_MODE_HEAT_COOL;
 | 
			
		||||
      parent->mode = climate::CLIMATE_MODE_HEAT_COOL;
 | 
			
		||||
    else if ((remote_state & COOLIX_MODE_MASK) == COOLIX_DRY_FAN) {
 | 
			
		||||
      if ((remote_state & COOLIX_FAN_MASK) == COOLIX_FAN_MODE_AUTO_DRY)
 | 
			
		||||
        this->mode = climate::CLIMATE_MODE_DRY;
 | 
			
		||||
        parent->mode = climate::CLIMATE_MODE_DRY;
 | 
			
		||||
      else
 | 
			
		||||
        this->mode = climate::CLIMATE_MODE_FAN_ONLY;
 | 
			
		||||
        parent->mode = climate::CLIMATE_MODE_FAN_ONLY;
 | 
			
		||||
    } else
 | 
			
		||||
      this->mode = climate::CLIMATE_MODE_COOL;
 | 
			
		||||
      parent->mode = climate::CLIMATE_MODE_COOL;
 | 
			
		||||
 | 
			
		||||
    // Fan Speed
 | 
			
		||||
    if ((remote_state & COOLIX_FAN_AUTO) == COOLIX_FAN_AUTO || this->mode == climate::CLIMATE_MODE_HEAT_COOL ||
 | 
			
		||||
        this->mode == climate::CLIMATE_MODE_DRY)
 | 
			
		||||
      this->fan_mode = climate::CLIMATE_FAN_AUTO;
 | 
			
		||||
    if ((remote_state & COOLIX_FAN_AUTO) == COOLIX_FAN_AUTO || parent->mode == climate::CLIMATE_MODE_HEAT_COOL ||
 | 
			
		||||
        parent->mode == climate::CLIMATE_MODE_DRY)
 | 
			
		||||
      parent->fan_mode = climate::CLIMATE_FAN_AUTO;
 | 
			
		||||
    else if ((remote_state & COOLIX_FAN_MIN) == COOLIX_FAN_MIN)
 | 
			
		||||
      this->fan_mode = climate::CLIMATE_FAN_LOW;
 | 
			
		||||
      parent->fan_mode = climate::CLIMATE_FAN_LOW;
 | 
			
		||||
    else if ((remote_state & COOLIX_FAN_MED) == COOLIX_FAN_MED)
 | 
			
		||||
      this->fan_mode = climate::CLIMATE_FAN_MEDIUM;
 | 
			
		||||
      parent->fan_mode = climate::CLIMATE_FAN_MEDIUM;
 | 
			
		||||
    else if ((remote_state & COOLIX_FAN_MAX) == COOLIX_FAN_MAX)
 | 
			
		||||
      this->fan_mode = climate::CLIMATE_FAN_HIGH;
 | 
			
		||||
      parent->fan_mode = climate::CLIMATE_FAN_HIGH;
 | 
			
		||||
 | 
			
		||||
    // Temperature
 | 
			
		||||
    uint8_t temperature_code = remote_state & COOLIX_TEMP_MASK;
 | 
			
		||||
    for (uint8_t i = 0; i < COOLIX_TEMP_RANGE; i++)
 | 
			
		||||
      if (COOLIX_TEMP_MAP[i] == temperature_code)
 | 
			
		||||
        this->target_temperature = i + COOLIX_TEMP_MIN;
 | 
			
		||||
        parent->target_temperature = i + COOLIX_TEMP_MIN;
 | 
			
		||||
  }
 | 
			
		||||
  this->publish_state();
 | 
			
		||||
  parent->publish_state();
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -26,11 +26,15 @@ class CoolixClimate : public climate_ir::ClimateIR {
 | 
			
		||||
    climate_ir::ClimateIR::control(call);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// This static method can be used in other climate components that accept the Coolix protocol. See midea_ir for
 | 
			
		||||
  /// example.
 | 
			
		||||
  static bool on_coolix(climate::Climate *parent, remote_base::RemoteReceiveData data);
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  /// Transmit via IR the state of this climate controller.
 | 
			
		||||
  void transmit_state() override;
 | 
			
		||||
  /// Handle received IR Buffer
 | 
			
		||||
  bool on_receive(remote_base::RemoteReceiveData data) override;
 | 
			
		||||
  bool on_receive(remote_base::RemoteReceiveData data) override { return CoolixClimate::on_coolix(this, data); }
 | 
			
		||||
 | 
			
		||||
  bool send_swing_cmd_{false};
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -210,7 +210,10 @@ Cover::Cover() : Cover("") {}
 | 
			
		||||
std::string Cover::get_device_class() {
 | 
			
		||||
  if (this->device_class_override_.has_value())
 | 
			
		||||
    return *this->device_class_override_;
 | 
			
		||||
#pragma GCC diagnostic push
 | 
			
		||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
 | 
			
		||||
  return this->device_class();
 | 
			
		||||
#pragma GCC diagnostic pop
 | 
			
		||||
}
 | 
			
		||||
bool Cover::is_fully_open() const { return this->position == COVER_OPEN; }
 | 
			
		||||
bool Cover::is_fully_closed() const { return this->position == COVER_CLOSED; }
 | 
			
		||||
 
 | 
			
		||||
@@ -169,6 +169,11 @@ class Cover : public EntityBase {
 | 
			
		||||
  friend CoverCall;
 | 
			
		||||
 | 
			
		||||
  virtual void control(const CoverCall &call) = 0;
 | 
			
		||||
 | 
			
		||||
  /** Override this to set the default device class.
 | 
			
		||||
   *
 | 
			
		||||
   * @deprecated This method is deprecated, set the property during config validation instead. (2022.1)
 | 
			
		||||
   */
 | 
			
		||||
  virtual std::string device_class();
 | 
			
		||||
 | 
			
		||||
  optional<CoverRestoreState> restore_state_();
 | 
			
		||||
 
 | 
			
		||||
@@ -102,8 +102,6 @@ void CS5460AComponent::hw_init_() {
 | 
			
		||||
 | 
			
		||||
/* Doesn't reset the register values etc., just restarts the "computation cycle" */
 | 
			
		||||
void CS5460AComponent::restart_() {
 | 
			
		||||
  int cnt;
 | 
			
		||||
 | 
			
		||||
  this->enable();
 | 
			
		||||
  /* Stop running conversion, wake up if needed */
 | 
			
		||||
  this->write_byte(CMD_POWER_UP);
 | 
			
		||||
 
 | 
			
		||||
@@ -90,6 +90,7 @@ void CSE7766Component::parse_data_() {
 | 
			
		||||
  uint32_t power_cycle = this->get_24_bit_uint_(17);
 | 
			
		||||
 | 
			
		||||
  uint8_t adj = this->raw_data_[20];
 | 
			
		||||
  uint32_t cf_pulses = (this->raw_data_[21] << 8) + this->raw_data_[22];
 | 
			
		||||
 | 
			
		||||
  bool power_ok = true;
 | 
			
		||||
  bool voltage_ok = true;
 | 
			
		||||
@@ -127,6 +128,18 @@ void CSE7766Component::parse_data_() {
 | 
			
		||||
    power = power_calib / float(power_cycle);
 | 
			
		||||
    this->power_acc_ += power;
 | 
			
		||||
    this->power_counts_ += 1;
 | 
			
		||||
 | 
			
		||||
    uint32_t difference;
 | 
			
		||||
    if (this->cf_pulses_last_ == 0)
 | 
			
		||||
      this->cf_pulses_last_ = cf_pulses;
 | 
			
		||||
 | 
			
		||||
    if (cf_pulses < this->cf_pulses_last_) {
 | 
			
		||||
      difference = cf_pulses + (0x10000 - this->cf_pulses_last_);
 | 
			
		||||
    } else {
 | 
			
		||||
      difference = cf_pulses - this->cf_pulses_last_;
 | 
			
		||||
    }
 | 
			
		||||
    this->cf_pulses_last_ = cf_pulses;
 | 
			
		||||
    this->energy_total_ += difference * float(power_calib) / 1000000.0 / 3600.0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if ((adj & 0x20) == 0x20 && current_ok && voltage_ok && power != 0.0) {
 | 
			
		||||
@@ -136,9 +149,9 @@ void CSE7766Component::parse_data_() {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void CSE7766Component::update() {
 | 
			
		||||
  float voltage = this->voltage_counts_ > 0 ? this->voltage_acc_ / this->voltage_counts_ : 0.0;
 | 
			
		||||
  float current = this->current_counts_ > 0 ? this->current_acc_ / this->current_counts_ : 0.0;
 | 
			
		||||
  float power = this->power_counts_ > 0 ? this->power_acc_ / this->power_counts_ : 0.0;
 | 
			
		||||
  float voltage = this->voltage_counts_ > 0 ? this->voltage_acc_ / this->voltage_counts_ : 0.0f;
 | 
			
		||||
  float current = this->current_counts_ > 0 ? this->current_acc_ / this->current_counts_ : 0.0f;
 | 
			
		||||
  float power = this->power_counts_ > 0 ? this->power_acc_ / this->power_counts_ : 0.0f;
 | 
			
		||||
 | 
			
		||||
  ESP_LOGV(TAG, "Got voltage_acc=%.2f current_acc=%.2f power_acc=%.2f", this->voltage_acc_, this->current_acc_,
 | 
			
		||||
           this->power_acc_);
 | 
			
		||||
@@ -152,6 +165,8 @@ void CSE7766Component::update() {
 | 
			
		||||
    this->current_sensor_->publish_state(current);
 | 
			
		||||
  if (this->power_sensor_ != nullptr)
 | 
			
		||||
    this->power_sensor_->publish_state(power);
 | 
			
		||||
  if (this->energy_sensor_ != nullptr)
 | 
			
		||||
    this->energy_sensor_->publish_state(this->energy_total_);
 | 
			
		||||
 | 
			
		||||
  this->voltage_acc_ = 0.0f;
 | 
			
		||||
  this->current_acc_ = 0.0f;
 | 
			
		||||
@@ -172,6 +187,7 @@ void CSE7766Component::dump_config() {
 | 
			
		||||
  LOG_SENSOR("  ", "Voltage", this->voltage_sensor_);
 | 
			
		||||
  LOG_SENSOR("  ", "Current", this->current_sensor_);
 | 
			
		||||
  LOG_SENSOR("  ", "Power", this->power_sensor_);
 | 
			
		||||
  LOG_SENSOR("  ", "Energy", this->energy_sensor_);
 | 
			
		||||
  this->check_uart_settings(4800);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@ class CSE7766Component : public PollingComponent, public uart::UARTDevice {
 | 
			
		||||
  void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; }
 | 
			
		||||
  void set_current_sensor(sensor::Sensor *current_sensor) { current_sensor_ = current_sensor; }
 | 
			
		||||
  void set_power_sensor(sensor::Sensor *power_sensor) { power_sensor_ = power_sensor; }
 | 
			
		||||
  void set_energy_sensor(sensor::Sensor *energy_sensor) { energy_sensor_ = energy_sensor; }
 | 
			
		||||
 | 
			
		||||
  void loop() override;
 | 
			
		||||
  float get_setup_priority() const override;
 | 
			
		||||
@@ -29,9 +30,12 @@ class CSE7766Component : public PollingComponent, public uart::UARTDevice {
 | 
			
		||||
  sensor::Sensor *voltage_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *current_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *power_sensor_{nullptr};
 | 
			
		||||
  sensor::Sensor *energy_sensor_{nullptr};
 | 
			
		||||
  float voltage_acc_{0.0f};
 | 
			
		||||
  float current_acc_{0.0f};
 | 
			
		||||
  float power_acc_{0.0f};
 | 
			
		||||
  float energy_total_{0.0f};
 | 
			
		||||
  uint32_t cf_pulses_last_{0};
 | 
			
		||||
  uint32_t voltage_counts_{0};
 | 
			
		||||
  uint32_t current_counts_{0};
 | 
			
		||||
  uint32_t power_counts_{0};
 | 
			
		||||
 
 | 
			
		||||
@@ -3,16 +3,20 @@ import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import sensor, uart
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_CURRENT,
 | 
			
		||||
    CONF_ENERGY,
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_POWER,
 | 
			
		||||
    CONF_VOLTAGE,
 | 
			
		||||
    DEVICE_CLASS_CURRENT,
 | 
			
		||||
    DEVICE_CLASS_ENERGY,
 | 
			
		||||
    DEVICE_CLASS_POWER,
 | 
			
		||||
    DEVICE_CLASS_VOLTAGE,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
    STATE_CLASS_TOTAL_INCREASING,
 | 
			
		||||
    UNIT_VOLT,
 | 
			
		||||
    UNIT_AMPERE,
 | 
			
		||||
    UNIT_WATT,
 | 
			
		||||
    UNIT_WATT_HOURS,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ["uart"]
 | 
			
		||||
@@ -44,6 +48,12 @@ CONFIG_SCHEMA = (
 | 
			
		||||
                device_class=DEVICE_CLASS_POWER,
 | 
			
		||||
                state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_ENERGY): sensor.sensor_schema(
 | 
			
		||||
                unit_of_measurement=UNIT_WATT_HOURS,
 | 
			
		||||
                accuracy_decimals=3,
 | 
			
		||||
                device_class=DEVICE_CLASS_ENERGY,
 | 
			
		||||
                state_class=STATE_CLASS_TOTAL_INCREASING,
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
    .extend(cv.polling_component_schema("60s"))
 | 
			
		||||
@@ -71,3 +81,7 @@ async def to_code(config):
 | 
			
		||||
        conf = config[CONF_POWER]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
        cg.add(var.set_power_sensor(sens))
 | 
			
		||||
    if CONF_ENERGY in config:
 | 
			
		||||
        conf = config[CONF_ENERGY]
 | 
			
		||||
        sens = await sensor.new_sensor(conf)
 | 
			
		||||
        cg.add(var.set_energy_sensor(sens))
 | 
			
		||||
 
 | 
			
		||||
@@ -231,7 +231,7 @@ bool DaikinClimate::on_receive(remote_base::RemoteReceiveData data) {
 | 
			
		||||
      // frame header
 | 
			
		||||
      if (byte != 0x27)
 | 
			
		||||
        return false;
 | 
			
		||||
    } else if (pos == 3) {
 | 
			
		||||
    } else if (pos == 3) {  // NOLINT(bugprone-branch-clone)
 | 
			
		||||
      // frame header
 | 
			
		||||
      if (byte != 0x00)
 | 
			
		||||
        return false;
 | 
			
		||||
 
 | 
			
		||||
@@ -38,10 +38,9 @@ void DallasComponent::setup() {
 | 
			
		||||
  raw_sensors = this->one_wire_->search_vec();
 | 
			
		||||
 | 
			
		||||
  for (auto &address : raw_sensors) {
 | 
			
		||||
    std::string s = uint64_to_string(address);
 | 
			
		||||
    auto *address8 = reinterpret_cast<uint8_t *>(&address);
 | 
			
		||||
    if (crc8(address8, 7) != address8[7]) {
 | 
			
		||||
      ESP_LOGW(TAG, "Dallas device 0x%s has invalid CRC.", s.c_str());
 | 
			
		||||
      ESP_LOGW(TAG, "Dallas device 0x%s has invalid CRC.", format_hex(address).c_str());
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    if (address8[0] != DALLAS_MODEL_DS18S20 && address8[0] != DALLAS_MODEL_DS1822 &&
 | 
			
		||||
@@ -77,8 +76,7 @@ void DallasComponent::dump_config() {
 | 
			
		||||
  } else {
 | 
			
		||||
    ESP_LOGD(TAG, "  Found sensors:");
 | 
			
		||||
    for (auto &address : this->found_sensors_) {
 | 
			
		||||
      std::string s = uint64_to_string(address);
 | 
			
		||||
      ESP_LOGD(TAG, "    0x%s", s.c_str());
 | 
			
		||||
      ESP_LOGD(TAG, "    0x%s", format_hex(address).c_str());
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -147,7 +145,7 @@ void DallasTemperatureSensor::set_index(uint8_t index) { this->index_ = index; }
 | 
			
		||||
uint8_t *DallasTemperatureSensor::get_address8() { return reinterpret_cast<uint8_t *>(&this->address_); }
 | 
			
		||||
const std::string &DallasTemperatureSensor::get_address_name() {
 | 
			
		||||
  if (this->address_name_.empty()) {
 | 
			
		||||
    this->address_name_ = std::string("0x") + uint64_to_string(this->address_);
 | 
			
		||||
    this->address_name_ = std::string("0x") + format_hex(this->address_);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return this->address_name_;
 | 
			
		||||
@@ -237,7 +235,7 @@ float DallasTemperatureSensor::get_temp_c() {
 | 
			
		||||
 | 
			
		||||
  return temp / 128.0f;
 | 
			
		||||
}
 | 
			
		||||
std::string DallasTemperatureSensor::unique_id() { return "dallas-" + uint64_to_string(this->address_); }
 | 
			
		||||
std::string DallasTemperatureSensor::unique_id() { return "dallas-" + str_upper_case(format_hex(this->address_)); }
 | 
			
		||||
 | 
			
		||||
}  // namespace dallas
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@ from pathlib import Path
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components.packages import validate_source_shorthand
 | 
			
		||||
from esphome.wizard import wizard_file
 | 
			
		||||
from esphome.yaml_util import dump
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -29,12 +30,11 @@ CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
    }
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
WIFI_MESSAGE = """
 | 
			
		||||
WIFI_CONFIG = """
 | 
			
		||||
 | 
			
		||||
# Do not forget to add your own wifi configuration before installing this configuration
 | 
			
		||||
# wifi:
 | 
			
		||||
#   ssid: !secret wifi_ssid
 | 
			
		||||
#   password: !secret wifi_password
 | 
			
		||||
wifi:
 | 
			
		||||
  ssid: !secret wifi_ssid
 | 
			
		||||
  password: !secret wifi_password
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -49,12 +49,24 @@ def import_config(path: str, name: str, project_name: str, import_url: str) -> N
 | 
			
		||||
    if p.exists():
 | 
			
		||||
        raise FileExistsError
 | 
			
		||||
 | 
			
		||||
    config = {
 | 
			
		||||
        "substitutions": {"name": name},
 | 
			
		||||
        "packages": {project_name: import_url},
 | 
			
		||||
        "esphome": {"name_add_mac_suffix": False},
 | 
			
		||||
    }
 | 
			
		||||
    p.write_text(
 | 
			
		||||
        dump(config) + WIFI_MESSAGE,
 | 
			
		||||
        encoding="utf8",
 | 
			
		||||
    )
 | 
			
		||||
    if project_name == "esphome.web":
 | 
			
		||||
        p.write_text(
 | 
			
		||||
            wizard_file(
 | 
			
		||||
                name=name,
 | 
			
		||||
                platform="ESP32" if "esp32" in import_url else "ESP8266",
 | 
			
		||||
                board="esp32dev" if "esp32" in import_url else "esp01_1m",
 | 
			
		||||
                ssid="!secret wifi_ssid",
 | 
			
		||||
                psk="!secret wifi_password",
 | 
			
		||||
            ),
 | 
			
		||||
            encoding="utf8",
 | 
			
		||||
        )
 | 
			
		||||
    else:
 | 
			
		||||
        config = {
 | 
			
		||||
            "substitutions": {"name": name},
 | 
			
		||||
            "packages": {project_name: import_url},
 | 
			
		||||
            "esphome": {"name_add_mac_suffix": False},
 | 
			
		||||
        }
 | 
			
		||||
        p.write_text(
 | 
			
		||||
            dump(config) + WIFI_CONFIG,
 | 
			
		||||
            encoding="utf8",
 | 
			
		||||
        )
 | 
			
		||||
 
 | 
			
		||||
@@ -101,7 +101,7 @@ void DebugComponent::dump_config() {
 | 
			
		||||
    info.features &= ~CHIP_FEATURE_BT;
 | 
			
		||||
  }
 | 
			
		||||
  if (info.features)
 | 
			
		||||
    features += "Other:" + uint64_to_string(info.features);
 | 
			
		||||
    features += "Other:" + format_hex(info.features);
 | 
			
		||||
  ESP_LOGD(TAG, "Chip: Model=%s, Features=%s Cores=%u, Revision=%u", model, features.c_str(), info.cores,
 | 
			
		||||
           info.revision);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -41,15 +41,30 @@ EXT1_WAKEUP_MODES = {
 | 
			
		||||
    "ALL_LOW": esp_sleep_ext1_wakeup_mode_t.ESP_EXT1_WAKEUP_ALL_LOW,
 | 
			
		||||
    "ANY_HIGH": esp_sleep_ext1_wakeup_mode_t.ESP_EXT1_WAKEUP_ANY_HIGH,
 | 
			
		||||
}
 | 
			
		||||
WakeupCauseToRunDuration = deep_sleep_ns.struct("WakeupCauseToRunDuration")
 | 
			
		||||
 | 
			
		||||
CONF_WAKEUP_PIN_MODE = "wakeup_pin_mode"
 | 
			
		||||
CONF_ESP32_EXT1_WAKEUP = "esp32_ext1_wakeup"
 | 
			
		||||
CONF_TOUCH_WAKEUP = "touch_wakeup"
 | 
			
		||||
CONF_DEFAULT = "default"
 | 
			
		||||
CONF_GPIO_WAKEUP_REASON = "gpio_wakeup_reason"
 | 
			
		||||
CONF_TOUCH_WAKEUP_REASON = "touch_wakeup_reason"
 | 
			
		||||
 | 
			
		||||
WAKEUP_CAUSES_SCHEMA = cv.Schema(
 | 
			
		||||
    {
 | 
			
		||||
        cv.Required(CONF_DEFAULT): cv.positive_time_period_milliseconds,
 | 
			
		||||
        cv.Optional(CONF_TOUCH_WAKEUP_REASON): cv.positive_time_period_milliseconds,
 | 
			
		||||
        cv.Optional(CONF_GPIO_WAKEUP_REASON): cv.positive_time_period_milliseconds,
 | 
			
		||||
    }
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(): cv.declare_id(DeepSleepComponent),
 | 
			
		||||
        cv.Optional(CONF_RUN_DURATION): cv.positive_time_period_milliseconds,
 | 
			
		||||
        cv.Optional(CONF_RUN_DURATION): cv.Any(
 | 
			
		||||
            cv.All(cv.only_on_esp32, WAKEUP_CAUSES_SCHEMA),
 | 
			
		||||
            cv.positive_time_period_milliseconds,
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_SLEEP_DURATION): cv.positive_time_period_milliseconds,
 | 
			
		||||
        cv.Optional(CONF_WAKEUP_PIN): cv.All(
 | 
			
		||||
            cv.only_on_esp32, pins.internal_gpio_input_pin_schema, validate_pin_number
 | 
			
		||||
@@ -85,7 +100,28 @@ async def to_code(config):
 | 
			
		||||
    if CONF_WAKEUP_PIN_MODE in config:
 | 
			
		||||
        cg.add(var.set_wakeup_pin_mode(config[CONF_WAKEUP_PIN_MODE]))
 | 
			
		||||
    if CONF_RUN_DURATION in config:
 | 
			
		||||
        cg.add(var.set_run_duration(config[CONF_RUN_DURATION]))
 | 
			
		||||
        run_duration_config = config[CONF_RUN_DURATION]
 | 
			
		||||
        if not isinstance(run_duration_config, dict):
 | 
			
		||||
            cg.add(var.set_run_duration(config[CONF_RUN_DURATION]))
 | 
			
		||||
        else:
 | 
			
		||||
            default_run_duration = run_duration_config[CONF_DEFAULT]
 | 
			
		||||
            wakeup_cause_to_run_duration = cg.StructInitializer(
 | 
			
		||||
                WakeupCauseToRunDuration,
 | 
			
		||||
                ("default_cause", default_run_duration),
 | 
			
		||||
                (
 | 
			
		||||
                    "touch_cause",
 | 
			
		||||
                    run_duration_config.get(
 | 
			
		||||
                        CONF_TOUCH_WAKEUP_REASON, default_run_duration
 | 
			
		||||
                    ),
 | 
			
		||||
                ),
 | 
			
		||||
                (
 | 
			
		||||
                    "gpio_cause",
 | 
			
		||||
                    run_duration_config.get(
 | 
			
		||||
                        CONF_GPIO_WAKEUP_REASON, default_run_duration
 | 
			
		||||
                    ),
 | 
			
		||||
                ),
 | 
			
		||||
            )
 | 
			
		||||
            cg.add(var.set_run_duration(wakeup_cause_to_run_duration))
 | 
			
		||||
 | 
			
		||||
    if CONF_ESP32_EXT1_WAKEUP in config:
 | 
			
		||||
        conf = config[CONF_ESP32_EXT1_WAKEUP]
 | 
			
		||||
 
 | 
			
		||||
@@ -13,12 +13,35 @@ static const char *const TAG = "deep_sleep";
 | 
			
		||||
 | 
			
		||||
bool global_has_deep_sleep = false;  // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
 | 
			
		||||
 | 
			
		||||
optional<uint32_t> DeepSleepComponent::get_run_duration_() const {
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
  if (this->wakeup_cause_to_run_duration_.has_value()) {
 | 
			
		||||
    esp_sleep_wakeup_cause_t wakeup_cause = esp_sleep_get_wakeup_cause();
 | 
			
		||||
    switch (wakeup_cause) {
 | 
			
		||||
      case ESP_SLEEP_WAKEUP_EXT0:
 | 
			
		||||
      case ESP_SLEEP_WAKEUP_EXT1:
 | 
			
		||||
        return this->wakeup_cause_to_run_duration_->gpio_cause;
 | 
			
		||||
      case ESP_SLEEP_WAKEUP_TOUCHPAD:
 | 
			
		||||
        return this->wakeup_cause_to_run_duration_->touch_cause;
 | 
			
		||||
      default:
 | 
			
		||||
        return this->wakeup_cause_to_run_duration_->default_cause;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
  return this->run_duration_;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DeepSleepComponent::setup() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up Deep Sleep...");
 | 
			
		||||
  global_has_deep_sleep = true;
 | 
			
		||||
 | 
			
		||||
  if (this->run_duration_.has_value())
 | 
			
		||||
    this->set_timeout(*this->run_duration_, [this]() { this->begin_sleep(); });
 | 
			
		||||
  const optional<uint32_t> run_duration = get_run_duration_();
 | 
			
		||||
  if (run_duration.has_value()) {
 | 
			
		||||
    ESP_LOGI(TAG, "Scheduling Deep Sleep to start in %u ms", *run_duration);
 | 
			
		||||
    this->set_timeout(*run_duration, [this]() { this->begin_sleep(); });
 | 
			
		||||
  } else {
 | 
			
		||||
    ESP_LOGD(TAG, "Not scheduling Deep Sleep, as no run duration is configured.");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void DeepSleepComponent::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "Setting up Deep Sleep...");
 | 
			
		||||
@@ -33,6 +56,11 @@ void DeepSleepComponent::dump_config() {
 | 
			
		||||
  if (wakeup_pin_ != nullptr) {
 | 
			
		||||
    LOG_PIN("  Wakeup Pin: ", this->wakeup_pin_);
 | 
			
		||||
  }
 | 
			
		||||
  if (this->wakeup_cause_to_run_duration_.has_value()) {
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  Default Wakeup Run Duration: %u ms", this->wakeup_cause_to_run_duration_->default_cause);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  Touch Wakeup Run Duration: %u ms", this->wakeup_cause_to_run_duration_->touch_cause);
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  GPIO Wakeup Run Duration: %u ms", this->wakeup_cause_to_run_duration_->gpio_cause);
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
void DeepSleepComponent::loop() {
 | 
			
		||||
@@ -49,6 +77,9 @@ void DeepSleepComponent::set_wakeup_pin_mode(WakeupPinMode wakeup_pin_mode) {
 | 
			
		||||
}
 | 
			
		||||
void DeepSleepComponent::set_ext1_wakeup(Ext1Wakeup ext1_wakeup) { this->ext1_wakeup_ = ext1_wakeup; }
 | 
			
		||||
void DeepSleepComponent::set_touch_wakeup(bool touch_wakeup) { this->touch_wakeup_ = touch_wakeup; }
 | 
			
		||||
void DeepSleepComponent::set_run_duration(WakeupCauseToRunDuration wakeup_cause_to_run_duration) {
 | 
			
		||||
  wakeup_cause_to_run_duration_ = wakeup_cause_to_run_duration;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
void DeepSleepComponent::set_run_duration(uint32_t time_ms) { this->run_duration_ = time_ms; }
 | 
			
		||||
void DeepSleepComponent::begin_sleep(bool manual) {
 | 
			
		||||
@@ -77,8 +108,8 @@ void DeepSleepComponent::begin_sleep(bool manual) {
 | 
			
		||||
  if (this->sleep_duration_.has_value())
 | 
			
		||||
    esp_sleep_enable_timer_wakeup(*this->sleep_duration_);
 | 
			
		||||
  if (this->wakeup_pin_ != nullptr) {
 | 
			
		||||
    bool level = this->wakeup_pin_->is_inverted();
 | 
			
		||||
    if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_INVERT_WAKEUP && !this->wakeup_pin_->digital_read()) {
 | 
			
		||||
    bool level = !this->wakeup_pin_->is_inverted();
 | 
			
		||||
    if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_INVERT_WAKEUP && this->wakeup_pin_->digital_read()) {
 | 
			
		||||
      level = !level;
 | 
			
		||||
    }
 | 
			
		||||
    esp_sleep_enable_ext0_wakeup(gpio_num_t(this->wakeup_pin_->get_pin()), level);
 | 
			
		||||
 
 | 
			
		||||
@@ -32,6 +32,15 @@ struct Ext1Wakeup {
 | 
			
		||||
  esp_sleep_ext1_wakeup_mode_t wakeup_mode;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct WakeupCauseToRunDuration {
 | 
			
		||||
  // Run duration if woken up by timer or any other reason besides those below.
 | 
			
		||||
  uint32_t default_cause;
 | 
			
		||||
  // Run duration if woken up by touch pads.
 | 
			
		||||
  uint32_t touch_cause;
 | 
			
		||||
  // Run duration if woken up by GPIO pins.
 | 
			
		||||
  uint32_t gpio_cause;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
template<typename... Ts> class EnterDeepSleepAction;
 | 
			
		||||
@@ -59,6 +68,11 @@ class DeepSleepComponent : public Component {
 | 
			
		||||
  void set_ext1_wakeup(Ext1Wakeup ext1_wakeup);
 | 
			
		||||
 | 
			
		||||
  void set_touch_wakeup(bool touch_wakeup);
 | 
			
		||||
 | 
			
		||||
  // Set the duration in ms for how long the code should run before entering
 | 
			
		||||
  // deep sleep mode, according to the cause the ESP32 has woken.
 | 
			
		||||
  void set_run_duration(WakeupCauseToRunDuration wakeup_cause_to_run_duration);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
  /// Set a duration in ms for how long the code should run before entering deep sleep mode.
 | 
			
		||||
  void set_run_duration(uint32_t time_ms);
 | 
			
		||||
@@ -75,12 +89,17 @@ class DeepSleepComponent : public Component {
 | 
			
		||||
  void prevent_deep_sleep();
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  // Returns nullopt if no run duration is set. Otherwise, returns the run
 | 
			
		||||
  // duration before entering deep sleep.
 | 
			
		||||
  optional<uint32_t> get_run_duration_() const;
 | 
			
		||||
 | 
			
		||||
  optional<uint64_t> sleep_duration_;
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
  InternalGPIOPin *wakeup_pin_;
 | 
			
		||||
  WakeupPinMode wakeup_pin_mode_{WAKEUP_PIN_MODE_IGNORE};
 | 
			
		||||
  optional<Ext1Wakeup> ext1_wakeup_;
 | 
			
		||||
  optional<bool> touch_wakeup_;
 | 
			
		||||
  optional<WakeupCauseToRunDuration> wakeup_cause_to_run_duration_;
 | 
			
		||||
#endif
 | 
			
		||||
  optional<uint32_t> run_duration_;
 | 
			
		||||
  bool next_enter_deep_sleep_{false};
 | 
			
		||||
 
 | 
			
		||||
@@ -33,7 +33,7 @@ DHT = dht_ns.class_("DHT", cg.PollingComponent)
 | 
			
		||||
CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(): cv.declare_id(DHT),
 | 
			
		||||
        cv.Required(CONF_PIN): pins.gpio_input_pin_schema,
 | 
			
		||||
        cv.Required(CONF_PIN): pins.internal_gpio_input_pin_schema,
 | 
			
		||||
        cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
 | 
			
		||||
            unit_of_measurement=UNIT_CELSIUS,
 | 
			
		||||
            accuracy_decimals=1,
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@
 | 
			
		||||
#include "esphome/core/color.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/core/hal.h"
 | 
			
		||||
#include "esphome/core/helpers.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace display {
 | 
			
		||||
@@ -15,7 +16,8 @@ const Color COLOR_OFF(0, 0, 0, 0);
 | 
			
		||||
const Color COLOR_ON(255, 255, 255, 255);
 | 
			
		||||
 | 
			
		||||
void DisplayBuffer::init_internal_(uint32_t buffer_length) {
 | 
			
		||||
  this->buffer_ = new (std::nothrow) uint8_t[buffer_length];  // NOLINT
 | 
			
		||||
  ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
 | 
			
		||||
  this->buffer_ = allocator.allocate(buffer_length);
 | 
			
		||||
  if (this->buffer_ == nullptr) {
 | 
			
		||||
    ESP_LOGE(TAG, "Could not allocate buffer for display!");
 | 
			
		||||
    return;
 | 
			
		||||
@@ -496,7 +498,7 @@ bool Animation::get_pixel(int x, int y) const {
 | 
			
		||||
    return false;
 | 
			
		||||
  const uint32_t width_8 = ((this->width_ + 7u) / 8u) * 8u;
 | 
			
		||||
  const uint32_t frame_index = this->height_ * width_8 * this->current_frame_;
 | 
			
		||||
  if (frame_index >= this->width_ * this->height_ * this->animation_frame_count_)
 | 
			
		||||
  if (frame_index >= (uint32_t)(this->width_ * this->height_ * this->animation_frame_count_))
 | 
			
		||||
    return false;
 | 
			
		||||
  const uint32_t pos = x + y * width_8 + frame_index;
 | 
			
		||||
  return progmem_read_byte(this->data_start_ + (pos / 8u)) & (0x80 >> (pos % 8u));
 | 
			
		||||
@@ -505,7 +507,7 @@ Color Animation::get_color_pixel(int x, int y) const {
 | 
			
		||||
  if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_)
 | 
			
		||||
    return Color::BLACK;
 | 
			
		||||
  const uint32_t frame_index = this->width_ * this->height_ * this->current_frame_;
 | 
			
		||||
  if (frame_index >= this->width_ * this->height_ * this->animation_frame_count_)
 | 
			
		||||
  if (frame_index >= (uint32_t)(this->width_ * this->height_ * this->animation_frame_count_))
 | 
			
		||||
    return Color::BLACK;
 | 
			
		||||
  const uint32_t pos = (x + y * this->width_ + frame_index) * 3;
 | 
			
		||||
  const uint32_t color32 = (progmem_read_byte(this->data_start_ + pos + 2) << 0) |
 | 
			
		||||
@@ -517,7 +519,7 @@ Color Animation::get_grayscale_pixel(int x, int y) const {
 | 
			
		||||
  if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_)
 | 
			
		||||
    return Color::BLACK;
 | 
			
		||||
  const uint32_t frame_index = this->width_ * this->height_ * this->current_frame_;
 | 
			
		||||
  if (frame_index >= this->width_ * this->height_ * this->animation_frame_count_)
 | 
			
		||||
  if (frame_index >= (uint32_t)(this->width_ * this->height_ * this->animation_frame_count_))
 | 
			
		||||
    return Color::BLACK;
 | 
			
		||||
  const uint32_t pos = (x + y * this->width_ + frame_index);
 | 
			
		||||
  const uint8_t gray = progmem_read_byte(this->data_start_ + pos);
 | 
			
		||||
 
 | 
			
		||||
@@ -42,7 +42,7 @@ class ColorUtil {
 | 
			
		||||
                       ? esp_scale(((colorcode >> third_bits) & ((1 << second_bits) - 1)), ((1 << second_bits) - 1))
 | 
			
		||||
                       : esp_scale(((colorcode >> 8) & 0xFF), ((1 << second_bits) - 1));
 | 
			
		||||
 | 
			
		||||
    third_color = (right_bit_aligned ? esp_scale(((colorcode >> 0) & 0xFF), ((1 << third_bits) - 1))
 | 
			
		||||
    third_color = (right_bit_aligned ? esp_scale(((colorcode >> 0) & ((1 << third_bits) - 1)), ((1 << third_bits) - 1))
 | 
			
		||||
                                     : esp_scale(((colorcode >> 0) & 0xFF), (1 << third_bits) - 1));
 | 
			
		||||
 | 
			
		||||
    Color color_return;
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ from esphome.components import uart
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_UART_ID,
 | 
			
		||||
    CONF_RECEIVE_TIMEOUT,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ["@glmnet", "@zuidwijk"]
 | 
			
		||||
@@ -52,7 +53,12 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
            cv.Optional(CONF_GAS_MBUS_ID, default=1): cv.int_,
 | 
			
		||||
            cv.Optional(CONF_MAX_TELEGRAM_LENGTH, default=1500): cv.int_,
 | 
			
		||||
            cv.Optional(CONF_REQUEST_PIN): pins.gpio_output_pin_schema,
 | 
			
		||||
            cv.Optional(CONF_REQUEST_INTERVAL): cv.positive_time_period_milliseconds,
 | 
			
		||||
            cv.Optional(
 | 
			
		||||
                CONF_REQUEST_INTERVAL, default="0ms"
 | 
			
		||||
            ): cv.positive_time_period_milliseconds,
 | 
			
		||||
            cv.Optional(
 | 
			
		||||
                CONF_RECEIVE_TIMEOUT, default="200ms"
 | 
			
		||||
            ): cv.positive_time_period_milliseconds,
 | 
			
		||||
        }
 | 
			
		||||
    ).extend(uart.UART_DEVICE_SCHEMA),
 | 
			
		||||
    cv.only_with_arduino,
 | 
			
		||||
@@ -70,10 +76,8 @@ async def to_code(config):
 | 
			
		||||
    if CONF_REQUEST_PIN in config:
 | 
			
		||||
        request_pin = await cg.gpio_pin_expression(config[CONF_REQUEST_PIN])
 | 
			
		||||
        cg.add(var.set_request_pin(request_pin))
 | 
			
		||||
    if CONF_REQUEST_INTERVAL in config:
 | 
			
		||||
        cg.add(
 | 
			
		||||
            var.set_request_interval(config[CONF_REQUEST_INTERVAL].total_milliseconds)
 | 
			
		||||
        )
 | 
			
		||||
    cg.add(var.set_request_interval(config[CONF_REQUEST_INTERVAL].total_milliseconds))
 | 
			
		||||
    cg.add(var.set_receive_timeout(config[CONF_RECEIVE_TIMEOUT].total_milliseconds))
 | 
			
		||||
 | 
			
		||||
    cg.add_define("DSMR_GAS_MBUS_ID", config[CONF_GAS_MBUS_ID])
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,7 @@ void Dsmr::loop() {
 | 
			
		||||
    if (this->decryption_key_.empty()) {
 | 
			
		||||
      this->receive_telegram_();
 | 
			
		||||
    } else {
 | 
			
		||||
      this->receive_encrypted_();
 | 
			
		||||
      this->receive_encrypted_telegram_();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -57,14 +57,42 @@ bool Dsmr::request_interval_reached_() {
 | 
			
		||||
  return millis() - this->last_request_time_ > this->request_interval_;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Dsmr::receive_timeout_reached_() { return millis() - this->last_read_time_ > this->receive_timeout_; }
 | 
			
		||||
 | 
			
		||||
bool Dsmr::available_within_timeout_() {
 | 
			
		||||
  uint8_t tries = READ_TIMEOUT_MS / 5;
 | 
			
		||||
  while (tries--) {
 | 
			
		||||
    delay(5);
 | 
			
		||||
    if (this->available()) {
 | 
			
		||||
      return true;
 | 
			
		||||
  // Data are available for reading on the UART bus?
 | 
			
		||||
  // Then we can start reading right away.
 | 
			
		||||
  if (this->available()) {
 | 
			
		||||
    this->last_read_time_ = millis();
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
  // When we're not in the process of reading a telegram, then there is
 | 
			
		||||
  // no need to actively wait for new data to come in.
 | 
			
		||||
  if (!header_found_) {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  // A telegram is being read. The smart meter might not deliver a telegram
 | 
			
		||||
  // in one go, but instead send it in chunks with small pauses in between.
 | 
			
		||||
  // When the UART RX buffer cannot hold a full telegram, then make sure
 | 
			
		||||
  // that the UART read buffer does not overflow while other components
 | 
			
		||||
  // perform their work in their loop. Do this by not returning control to
 | 
			
		||||
  // the main loop, until the read timeout is reached.
 | 
			
		||||
  if (this->parent_->get_rx_buffer_size() < this->max_telegram_len_) {
 | 
			
		||||
    while (!this->receive_timeout_reached_()) {
 | 
			
		||||
      delay(5);
 | 
			
		||||
      if (this->available()) {
 | 
			
		||||
        this->last_read_time_ = millis();
 | 
			
		||||
        return true;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  // No new data has come in during the read timeout? Then stop reading the
 | 
			
		||||
  // telegram and start waiting for the next one to arrive.
 | 
			
		||||
  if (this->receive_timeout_reached_()) {
 | 
			
		||||
    ESP_LOGW(TAG, "Timeout while reading data for telegram");
 | 
			
		||||
    this->reset_telegram_();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -96,30 +124,31 @@ void Dsmr::stop_requesting_data_() {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Dsmr::receive_telegram_() {
 | 
			
		||||
  while (true) {
 | 
			
		||||
    if (!this->available()) {
 | 
			
		||||
      if (!this->header_found_ || !this->available_within_timeout_()) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
void Dsmr::reset_telegram_() {
 | 
			
		||||
  this->header_found_ = false;
 | 
			
		||||
  this->footer_found_ = false;
 | 
			
		||||
  this->bytes_read_ = 0;
 | 
			
		||||
  this->crypt_bytes_read_ = 0;
 | 
			
		||||
  this->crypt_telegram_len_ = 0;
 | 
			
		||||
  this->last_read_time_ = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Dsmr::receive_telegram_() {
 | 
			
		||||
  while (this->available_within_timeout_()) {
 | 
			
		||||
    const char c = this->read();
 | 
			
		||||
 | 
			
		||||
    // Find a new telegram header, i.e. forward slash.
 | 
			
		||||
    if (c == '/') {
 | 
			
		||||
      ESP_LOGV(TAG, "Header of telegram found");
 | 
			
		||||
      this->reset_telegram_();
 | 
			
		||||
      this->header_found_ = true;
 | 
			
		||||
      this->footer_found_ = false;
 | 
			
		||||
      this->telegram_len_ = 0;
 | 
			
		||||
    }
 | 
			
		||||
    if (!this->header_found_)
 | 
			
		||||
      continue;
 | 
			
		||||
 | 
			
		||||
    // Check for buffer overflow.
 | 
			
		||||
    if (this->telegram_len_ >= this->max_telegram_len_) {
 | 
			
		||||
      this->header_found_ = false;
 | 
			
		||||
      this->footer_found_ = false;
 | 
			
		||||
    if (this->bytes_read_ >= this->max_telegram_len_) {
 | 
			
		||||
      this->reset_telegram_();
 | 
			
		||||
      ESP_LOGE(TAG, "Error: telegram larger than buffer (%d bytes)", this->max_telegram_len_);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
@@ -129,9 +158,9 @@ void Dsmr::receive_telegram_() {
 | 
			
		||||
    // proper parsing, remove these new line characters.
 | 
			
		||||
    if (c == '(') {
 | 
			
		||||
      while (true) {
 | 
			
		||||
        auto previous_char = this->telegram_[this->telegram_len_ - 1];
 | 
			
		||||
        auto previous_char = this->telegram_[this->bytes_read_ - 1];
 | 
			
		||||
        if (previous_char == '\n' || previous_char == '\r') {
 | 
			
		||||
          this->telegram_len_--;
 | 
			
		||||
          this->bytes_read_--;
 | 
			
		||||
        } else {
 | 
			
		||||
          break;
 | 
			
		||||
        }
 | 
			
		||||
@@ -139,8 +168,8 @@ void Dsmr::receive_telegram_() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Store the byte in the buffer.
 | 
			
		||||
    this->telegram_[this->telegram_len_] = c;
 | 
			
		||||
    this->telegram_len_++;
 | 
			
		||||
    this->telegram_[this->bytes_read_] = c;
 | 
			
		||||
    this->bytes_read_++;
 | 
			
		||||
 | 
			
		||||
    // Check for a footer, i.e. exlamation mark, followed by a hex checksum.
 | 
			
		||||
    if (c == '!') {
 | 
			
		||||
@@ -152,28 +181,14 @@ void Dsmr::receive_telegram_() {
 | 
			
		||||
    if (this->footer_found_ && c == '\n') {
 | 
			
		||||
      // Parse the telegram and publish sensor values.
 | 
			
		||||
      this->parse_telegram();
 | 
			
		||||
 | 
			
		||||
      this->header_found_ = false;
 | 
			
		||||
      this->reset_telegram_();
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Dsmr::receive_encrypted_() {
 | 
			
		||||
  this->encrypted_telegram_len_ = 0;
 | 
			
		||||
  size_t packet_size = 0;
 | 
			
		||||
 | 
			
		||||
  while (true) {
 | 
			
		||||
    if (!this->available()) {
 | 
			
		||||
      if (!this->header_found_) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      if (!this->available_within_timeout_()) {
 | 
			
		||||
        ESP_LOGW(TAG, "Timeout while reading data for encrypted telegram");
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
void Dsmr::receive_encrypted_telegram_() {
 | 
			
		||||
  while (this->available_within_timeout_()) {
 | 
			
		||||
    const char c = this->read();
 | 
			
		||||
 | 
			
		||||
    // Find a new telegram start byte.
 | 
			
		||||
@@ -182,50 +197,58 @@ void Dsmr::receive_encrypted_() {
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
      ESP_LOGV(TAG, "Start byte 0xDB of encrypted telegram found");
 | 
			
		||||
      this->reset_telegram_();
 | 
			
		||||
      this->header_found_ = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Check for buffer overflow.
 | 
			
		||||
    if (this->encrypted_telegram_len_ >= this->max_telegram_len_) {
 | 
			
		||||
      this->header_found_ = false;
 | 
			
		||||
    if (this->crypt_bytes_read_ >= this->max_telegram_len_) {
 | 
			
		||||
      this->reset_telegram_();
 | 
			
		||||
      ESP_LOGE(TAG, "Error: encrypted telegram larger than buffer (%d bytes)", this->max_telegram_len_);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this->encrypted_telegram_[this->encrypted_telegram_len_++] = c;
 | 
			
		||||
    // Store the byte in the buffer.
 | 
			
		||||
    this->crypt_telegram_[this->crypt_bytes_read_] = c;
 | 
			
		||||
    this->crypt_bytes_read_++;
 | 
			
		||||
 | 
			
		||||
    if (packet_size == 0 && this->encrypted_telegram_len_ > 20) {
 | 
			
		||||
    // Read the length of the incoming encrypted telegram.
 | 
			
		||||
    if (this->crypt_telegram_len_ == 0 && this->crypt_bytes_read_ > 20) {
 | 
			
		||||
      // Complete header + data bytes
 | 
			
		||||
      packet_size = 13 + (this->encrypted_telegram_[11] << 8 | this->encrypted_telegram_[12]);
 | 
			
		||||
      ESP_LOGV(TAG, "Encrypted telegram size: %d bytes", packet_size);
 | 
			
		||||
      this->crypt_telegram_len_ = 13 + (this->crypt_telegram_[11] << 8 | this->crypt_telegram_[12]);
 | 
			
		||||
      ESP_LOGV(TAG, "Encrypted telegram length: %d bytes", this->crypt_telegram_len_);
 | 
			
		||||
    }
 | 
			
		||||
    if (this->encrypted_telegram_len_ == packet_size && packet_size > 0) {
 | 
			
		||||
      ESP_LOGV(TAG, "End of encrypted telegram found");
 | 
			
		||||
      GCM<AES128> *gcmaes128{new GCM<AES128>()};
 | 
			
		||||
      gcmaes128->setKey(this->decryption_key_.data(), gcmaes128->keySize());
 | 
			
		||||
      // the iv is 8 bytes of the system title + 4 bytes frame counter
 | 
			
		||||
      // system title is at byte 2 and frame counter at byte 15
 | 
			
		||||
      for (int i = 10; i < 14; i++)
 | 
			
		||||
        this->encrypted_telegram_[i] = this->encrypted_telegram_[i + 4];
 | 
			
		||||
      constexpr uint16_t iv_size{12};
 | 
			
		||||
      gcmaes128->setIV(&this->encrypted_telegram_[2], iv_size);
 | 
			
		||||
      gcmaes128->decrypt(reinterpret_cast<uint8_t *>(this->telegram_),
 | 
			
		||||
                         // the ciphertext start at byte 18
 | 
			
		||||
                         &this->encrypted_telegram_[18],
 | 
			
		||||
                         // cipher size
 | 
			
		||||
                         this->encrypted_telegram_len_ - 17);
 | 
			
		||||
      delete gcmaes128;  // NOLINT(cppcoreguidelines-owning-memory)
 | 
			
		||||
 | 
			
		||||
      this->telegram_len_ = strnlen(this->telegram_, this->max_telegram_len_);
 | 
			
		||||
      ESP_LOGV(TAG, "Decrypted telegram size: %d bytes", this->telegram_len_);
 | 
			
		||||
      ESP_LOGVV(TAG, "Decrypted telegram: %s", this->telegram_);
 | 
			
		||||
 | 
			
		||||
      this->parse_telegram();
 | 
			
		||||
 | 
			
		||||
      this->header_found_ = false;
 | 
			
		||||
      this->telegram_len_ = 0;
 | 
			
		||||
      return;
 | 
			
		||||
    // Check for the end of the encrypted telegram.
 | 
			
		||||
    if (this->crypt_telegram_len_ == 0 || this->crypt_bytes_read_ != this->crypt_telegram_len_) {
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    ESP_LOGV(TAG, "End of encrypted telegram found");
 | 
			
		||||
 | 
			
		||||
    // Decrypt the encrypted telegram.
 | 
			
		||||
    GCM<AES128> *gcmaes128{new GCM<AES128>()};
 | 
			
		||||
    gcmaes128->setKey(this->decryption_key_.data(), gcmaes128->keySize());
 | 
			
		||||
    // the iv is 8 bytes of the system title + 4 bytes frame counter
 | 
			
		||||
    // system title is at byte 2 and frame counter at byte 15
 | 
			
		||||
    for (int i = 10; i < 14; i++)
 | 
			
		||||
      this->crypt_telegram_[i] = this->crypt_telegram_[i + 4];
 | 
			
		||||
    constexpr uint16_t iv_size{12};
 | 
			
		||||
    gcmaes128->setIV(&this->crypt_telegram_[2], iv_size);
 | 
			
		||||
    gcmaes128->decrypt(reinterpret_cast<uint8_t *>(this->telegram_),
 | 
			
		||||
                       // the ciphertext start at byte 18
 | 
			
		||||
                       &this->crypt_telegram_[18],
 | 
			
		||||
                       // cipher size
 | 
			
		||||
                       this->crypt_bytes_read_ - 17);
 | 
			
		||||
    delete gcmaes128;  // NOLINT(cppcoreguidelines-owning-memory)
 | 
			
		||||
 | 
			
		||||
    this->bytes_read_ = strnlen(this->telegram_, this->max_telegram_len_);
 | 
			
		||||
    ESP_LOGV(TAG, "Decrypted telegram size: %d bytes", this->bytes_read_);
 | 
			
		||||
    ESP_LOGVV(TAG, "Decrypted telegram: %s", this->telegram_);
 | 
			
		||||
 | 
			
		||||
    // Parse the decrypted telegram and publish sensor values.
 | 
			
		||||
    this->parse_telegram();
 | 
			
		||||
    this->reset_telegram_();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -234,11 +257,11 @@ bool Dsmr::parse_telegram() {
 | 
			
		||||
  ESP_LOGV(TAG, "Trying to parse telegram");
 | 
			
		||||
  this->stop_requesting_data_();
 | 
			
		||||
  ::dsmr::ParseResult<void> res =
 | 
			
		||||
      ::dsmr::P1Parser::parse(&data, this->telegram_, this->telegram_len_, false,
 | 
			
		||||
      ::dsmr::P1Parser::parse(&data, this->telegram_, this->bytes_read_, false,
 | 
			
		||||
                              this->crc_check_);  // Parse telegram according to data definition. Ignore unknown values.
 | 
			
		||||
  if (res.err) {
 | 
			
		||||
    // Parsing error, show it
 | 
			
		||||
    auto err_str = res.fullError(this->telegram_, this->telegram_ + this->telegram_len_);
 | 
			
		||||
    auto err_str = res.fullError(this->telegram_, this->telegram_ + this->bytes_read_);
 | 
			
		||||
    ESP_LOGE(TAG, "%s", err_str.c_str());
 | 
			
		||||
    return false;
 | 
			
		||||
  } else {
 | 
			
		||||
@@ -251,7 +274,7 @@ bool Dsmr::parse_telegram() {
 | 
			
		||||
void Dsmr::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "DSMR:");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Max telegram length: %d", this->max_telegram_len_);
 | 
			
		||||
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Receive timeout: %.1fs", this->receive_timeout_ / 1e3f);
 | 
			
		||||
  if (this->request_pin_ != nullptr) {
 | 
			
		||||
    LOG_PIN("  Request Pin: ", this->request_pin_);
 | 
			
		||||
  }
 | 
			
		||||
@@ -270,9 +293,9 @@ void Dsmr::set_decryption_key(const std::string &decryption_key) {
 | 
			
		||||
  if (decryption_key.length() == 0) {
 | 
			
		||||
    ESP_LOGI(TAG, "Disabling decryption");
 | 
			
		||||
    this->decryption_key_.clear();
 | 
			
		||||
    if (this->encrypted_telegram_ != nullptr) {
 | 
			
		||||
      delete[] this->encrypted_telegram_;
 | 
			
		||||
      this->encrypted_telegram_ = nullptr;
 | 
			
		||||
    if (this->crypt_telegram_ != nullptr) {
 | 
			
		||||
      delete[] this->crypt_telegram_;
 | 
			
		||||
      this->crypt_telegram_ = nullptr;
 | 
			
		||||
    }
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
@@ -293,13 +316,11 @@ void Dsmr::set_decryption_key(const std::string &decryption_key) {
 | 
			
		||||
    this->decryption_key_.push_back(std::strtoul(temp, nullptr, 16));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (this->encrypted_telegram_ == nullptr) {
 | 
			
		||||
    this->encrypted_telegram_ = new uint8_t[this->max_telegram_len_];  // NOLINT
 | 
			
		||||
  if (this->crypt_telegram_ == nullptr) {
 | 
			
		||||
    this->crypt_telegram_ = new uint8_t[this->max_telegram_len_];  // NOLINT
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Dsmr::set_max_telegram_length(size_t length) { max_telegram_len_ = length; }
 | 
			
		||||
 | 
			
		||||
}  // namespace dsmr
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -16,8 +16,6 @@
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace dsmr {
 | 
			
		||||
 | 
			
		||||
static constexpr uint32_t READ_TIMEOUT_MS = 200;
 | 
			
		||||
 | 
			
		||||
using namespace ::dsmr::fields;
 | 
			
		||||
 | 
			
		||||
// DSMR_**_LIST generated by ESPHome and written in esphome/core/defines
 | 
			
		||||
@@ -71,11 +69,10 @@ class Dsmr : public Component, public uart::UARTDevice {
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
 | 
			
		||||
  void set_decryption_key(const std::string &decryption_key);
 | 
			
		||||
 | 
			
		||||
  void set_max_telegram_length(size_t length);
 | 
			
		||||
 | 
			
		||||
  void set_max_telegram_length(size_t length) { this->max_telegram_len_ = length; }
 | 
			
		||||
  void set_request_pin(GPIOPin *request_pin) { this->request_pin_ = request_pin; }
 | 
			
		||||
  void set_request_interval(uint32_t interval) { this->request_interval_ = interval; }
 | 
			
		||||
  void set_receive_timeout(uint32_t timeout) { this->receive_timeout_ = timeout; }
 | 
			
		||||
 | 
			
		||||
// Sensor setters
 | 
			
		||||
#define DSMR_SET_SENSOR(s) \
 | 
			
		||||
@@ -88,7 +85,8 @@ class Dsmr : public Component, public uart::UARTDevice {
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  void receive_telegram_();
 | 
			
		||||
  void receive_encrypted_();
 | 
			
		||||
  void receive_encrypted_telegram_();
 | 
			
		||||
  void reset_telegram_();
 | 
			
		||||
 | 
			
		||||
  /// Wait for UART data to become available within the read timeout.
 | 
			
		||||
  ///
 | 
			
		||||
@@ -101,24 +99,26 @@ class Dsmr : public Component, public uart::UARTDevice {
 | 
			
		||||
  /// lost in the process.
 | 
			
		||||
  bool available_within_timeout_();
 | 
			
		||||
 | 
			
		||||
  // Data request
 | 
			
		||||
  // Request telegram
 | 
			
		||||
  uint32_t request_interval_;
 | 
			
		||||
  bool request_interval_reached_();
 | 
			
		||||
  GPIOPin *request_pin_{nullptr};
 | 
			
		||||
  uint32_t request_interval_{0};
 | 
			
		||||
  uint32_t last_request_time_{0};
 | 
			
		||||
  bool requesting_data_{false};
 | 
			
		||||
  bool ready_to_request_data_();
 | 
			
		||||
  bool request_interval_reached_();
 | 
			
		||||
  void start_requesting_data_();
 | 
			
		||||
  void stop_requesting_data_();
 | 
			
		||||
 | 
			
		||||
  // Telegram buffer
 | 
			
		||||
  // Read telegram
 | 
			
		||||
  uint32_t receive_timeout_;
 | 
			
		||||
  bool receive_timeout_reached_();
 | 
			
		||||
  size_t max_telegram_len_;
 | 
			
		||||
  char *telegram_{nullptr};
 | 
			
		||||
  int telegram_len_{0};
 | 
			
		||||
  uint8_t *encrypted_telegram_{nullptr};
 | 
			
		||||
  int encrypted_telegram_len_{0};
 | 
			
		||||
 | 
			
		||||
  // Serial parser
 | 
			
		||||
  size_t bytes_read_{0};
 | 
			
		||||
  uint8_t *crypt_telegram_{nullptr};
 | 
			
		||||
  size_t crypt_telegram_len_{0};
 | 
			
		||||
  size_t crypt_bytes_read_{0};
 | 
			
		||||
  uint32_t last_read_time_{0};
 | 
			
		||||
  bool header_found_{false};
 | 
			
		||||
  bool footer_found_{false};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -23,22 +23,24 @@ void DutyCycleSensor::dump_config() {
 | 
			
		||||
}
 | 
			
		||||
void DutyCycleSensor::update() {
 | 
			
		||||
  const uint32_t now = micros();
 | 
			
		||||
  const uint32_t last_interrupt = this->store_.last_interrupt;  // Read the measurement taken by the interrupt
 | 
			
		||||
  uint32_t on_time = this->store_.on_time;
 | 
			
		||||
 | 
			
		||||
  this->store_.on_time = 0;  // Start new measurement, exactly aligned with the micros() reading
 | 
			
		||||
  this->store_.last_interrupt = now;
 | 
			
		||||
 | 
			
		||||
  if (this->last_update_ != 0) {
 | 
			
		||||
    const bool level = this->store_.last_level;
 | 
			
		||||
    const uint32_t last_interrupt = this->store_.last_interrupt;
 | 
			
		||||
    uint32_t on_time = this->store_.on_time;
 | 
			
		||||
 | 
			
		||||
    if (level)
 | 
			
		||||
      on_time += now - last_interrupt;
 | 
			
		||||
 | 
			
		||||
    const float total_time = float(now - this->last_update_);
 | 
			
		||||
 | 
			
		||||
    const float value = (on_time / total_time) * 100.0f;
 | 
			
		||||
    const float value = (on_time * 100.0f) / total_time;
 | 
			
		||||
    ESP_LOGD(TAG, "'%s' Got duty cycle=%.1f%%", this->get_name().c_str(), value);
 | 
			
		||||
    this->publish_state(value);
 | 
			
		||||
  }
 | 
			
		||||
  this->store_.on_time = 0;
 | 
			
		||||
  this->store_.last_interrupt = now;
 | 
			
		||||
  this->last_update_ = now;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2,8 +2,9 @@ from dataclasses import dataclass
 | 
			
		||||
from typing import Union
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
import logging
 | 
			
		||||
import os
 | 
			
		||||
 | 
			
		||||
from esphome.helpers import write_file_if_changed
 | 
			
		||||
from esphome.helpers import copy_file_if_changed, write_file_if_changed
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_BOARD,
 | 
			
		||||
    CONF_FRAMEWORK,
 | 
			
		||||
@@ -295,6 +296,8 @@ async def to_code(config):
 | 
			
		||||
    conf = config[CONF_FRAMEWORK]
 | 
			
		||||
    cg.add_platformio_option("platform", conf[CONF_PLATFORM_VERSION])
 | 
			
		||||
 | 
			
		||||
    cg.add_platformio_option("extra_scripts", ["post:post_build.py"])
 | 
			
		||||
 | 
			
		||||
    if conf[CONF_TYPE] == FRAMEWORK_ESP_IDF:
 | 
			
		||||
        cg.add_platformio_option("framework", "espidf")
 | 
			
		||||
        cg.add_build_flag("-DUSE_ESP_IDF")
 | 
			
		||||
@@ -311,9 +314,16 @@ async def to_code(config):
 | 
			
		||||
        )
 | 
			
		||||
        add_idf_sdkconfig_option("CONFIG_COMPILER_OPTIMIZATION_DEFAULT", False)
 | 
			
		||||
        add_idf_sdkconfig_option("CONFIG_COMPILER_OPTIMIZATION_SIZE", True)
 | 
			
		||||
 | 
			
		||||
        # Increase freertos tick speed from 100Hz to 1kHz so that delay() resolution is 1ms
 | 
			
		||||
        add_idf_sdkconfig_option("CONFIG_FREERTOS_HZ", 1000)
 | 
			
		||||
 | 
			
		||||
        # Setup watchdog
 | 
			
		||||
        add_idf_sdkconfig_option("CONFIG_ESP_TASK_WDT", True)
 | 
			
		||||
        add_idf_sdkconfig_option("CONFIG_ESP_TASK_WDT_PANIC", True)
 | 
			
		||||
        add_idf_sdkconfig_option("CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0", False)
 | 
			
		||||
        add_idf_sdkconfig_option("CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1", False)
 | 
			
		||||
 | 
			
		||||
        cg.add_platformio_option("board_build.partitions", "partitions.csv")
 | 
			
		||||
 | 
			
		||||
        for name, value in conf[CONF_SDKCONFIG_OPTIONS].items():
 | 
			
		||||
@@ -405,3 +415,10 @@ def copy_files():
 | 
			
		||||
            CORE.relative_build_path("partitions.csv"),
 | 
			
		||||
            IDF_PARTITIONS_CSV,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    dir = os.path.dirname(__file__)
 | 
			
		||||
    post_build_file = os.path.join(dir, "post_build.py.script")
 | 
			
		||||
    copy_file_if_changed(
 | 
			
		||||
        post_build_file,
 | 
			
		||||
        CORE.relative_build_path("post_build.py"),
 | 
			
		||||
    )
 | 
			
		||||
 
 | 
			
		||||
@@ -261,6 +261,37 @@ ESP32_BOARD_PINS = {
 | 
			
		||||
        "SS": 33,
 | 
			
		||||
        "TX": 17,
 | 
			
		||||
    },
 | 
			
		||||
    "featheresp32-s2": {
 | 
			
		||||
        "SDA": 3,
 | 
			
		||||
        "SCL": 4,
 | 
			
		||||
        "SS": 42,
 | 
			
		||||
        "MOSI": 35,
 | 
			
		||||
        "SCK": 36,
 | 
			
		||||
        "MISO": 37,
 | 
			
		||||
        "A0": 18,
 | 
			
		||||
        "A1": 17,
 | 
			
		||||
        "A10": 27,
 | 
			
		||||
        "A11": 12,
 | 
			
		||||
        "A12": 13,
 | 
			
		||||
        "A13": 35,
 | 
			
		||||
        "A2": 16,
 | 
			
		||||
        "A3": 15,
 | 
			
		||||
        "A4": 14,
 | 
			
		||||
        "A5": 8,
 | 
			
		||||
        "LED": 13,
 | 
			
		||||
        "TX": 39,
 | 
			
		||||
        "RX": 38,
 | 
			
		||||
        "T5": 5,
 | 
			
		||||
        "T8": 8,
 | 
			
		||||
        "T9": 9,
 | 
			
		||||
        "T10": 10,
 | 
			
		||||
        "T11": 11,
 | 
			
		||||
        "T12": 12,
 | 
			
		||||
        "T13": 13,
 | 
			
		||||
        "T14": 14,
 | 
			
		||||
        "DAC1": 17,
 | 
			
		||||
        "DAC2": 18,
 | 
			
		||||
    },
 | 
			
		||||
    "firebeetle32": {"LED": 2},
 | 
			
		||||
    "fm-devkit": {
 | 
			
		||||
        "D0": 34,
 | 
			
		||||
 
 | 
			
		||||
@@ -6,12 +6,17 @@
 | 
			
		||||
#include <freertos/FreeRTOS.h>
 | 
			
		||||
#include <freertos/task.h>
 | 
			
		||||
#include <esp_idf_version.h>
 | 
			
		||||
#include <esp_task_wdt.h>
 | 
			
		||||
#include <soc/rtc.h>
 | 
			
		||||
 | 
			
		||||
#if ESP_IDF_VERSION_MAJOR >= 4
 | 
			
		||||
#include <hal/cpu_hal.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ARDUINO
 | 
			
		||||
#include <esp32-hal.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void setup();
 | 
			
		||||
void loop();
 | 
			
		||||
 | 
			
		||||
@@ -29,24 +34,24 @@ void arch_restart() {
 | 
			
		||||
    yield();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void IRAM_ATTR HOT arch_feed_wdt() {
 | 
			
		||||
#ifdef USE_ARDUINO
 | 
			
		||||
#if CONFIG_ARDUINO_RUNNING_CORE == 0
 | 
			
		||||
#ifdef CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0
 | 
			
		||||
  // ESP32 uses "Task Watchdog" which is hooked to the FreeRTOS idle task.
 | 
			
		||||
  // To cause the Watchdog to be triggered we need to put the current task
 | 
			
		||||
  // to sleep to get the idle task scheduled.
 | 
			
		||||
  delay(1);
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
#endif  // USE_ARDUINO
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP_IDF
 | 
			
		||||
#ifdef CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0
 | 
			
		||||
  delay(1);
 | 
			
		||||
void arch_init() {
 | 
			
		||||
  // Enable the task watchdog only on the loop task (from which we're currently running)
 | 
			
		||||
#if defined(USE_ESP_IDF)
 | 
			
		||||
  esp_task_wdt_add(nullptr);
 | 
			
		||||
  // Idle task watchdog is disabled on ESP-IDF
 | 
			
		||||
#elif defined(USE_ARDUINO)
 | 
			
		||||
  enableLoopWDT();
 | 
			
		||||
  // Disable idle task watchdog on the core we're using (Arduino pins the task to a core)
 | 
			
		||||
#if defined(CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0) && CONFIG_ARDUINO_RUNNING_CORE == 0
 | 
			
		||||
  disableCore0WDT();
 | 
			
		||||
#endif
 | 
			
		||||
#if defined(CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1) && CONFIG_ARDUINO_RUNNING_CORE == 1
 | 
			
		||||
  disableCore1WDT();
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
#endif  // USE_ESP_IDF
 | 
			
		||||
}
 | 
			
		||||
void IRAM_ATTR HOT arch_feed_wdt() { esp_task_wdt_reset(); }
 | 
			
		||||
 | 
			
		||||
uint8_t progmem_read_byte(const uint8_t *addr) { return *addr; }
 | 
			
		||||
uint32_t arch_get_cpu_cycle_count() {
 | 
			
		||||
 
 | 
			
		||||
@@ -18,7 +18,7 @@ _ESP_SDIO_PINS = {
 | 
			
		||||
    11: "Flash Command",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_ESP32_STRAPPING_PINS = {0, 2, 4, 15}
 | 
			
		||||
_ESP32_STRAPPING_PINS = {0, 2, 4, 12, 15}
 | 
			
		||||
_LOGGER = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										43
									
								
								esphome/components/esp32/post_build.py.script
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								esphome/components/esp32/post_build.py.script
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
			
		||||
# Source https://github.com/letscontrolit/ESPEasy/pull/3845#issuecomment-1005864664
 | 
			
		||||
 | 
			
		||||
import esptool
 | 
			
		||||
 | 
			
		||||
# pylint: disable=E0602
 | 
			
		||||
Import("env")  # noqa
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def esp32_create_combined_bin(source, target, env):
 | 
			
		||||
    print("Generating combined binary for serial flashing")
 | 
			
		||||
    app_offset = 0x10000
 | 
			
		||||
 | 
			
		||||
    new_file_name = env.subst("$BUILD_DIR/${PROGNAME}-factory.bin")
 | 
			
		||||
    sections = env.subst(env.get("FLASH_EXTRA_IMAGES"))
 | 
			
		||||
    firmware_name = env.subst("$BUILD_DIR/${PROGNAME}.bin")
 | 
			
		||||
    chip = env.get("BOARD_MCU")
 | 
			
		||||
    flash_size = env.BoardConfig().get("upload.flash_size")
 | 
			
		||||
    cmd = [
 | 
			
		||||
        "--chip",
 | 
			
		||||
        chip,
 | 
			
		||||
        "merge_bin",
 | 
			
		||||
        "-o",
 | 
			
		||||
        new_file_name,
 | 
			
		||||
        "--flash_size",
 | 
			
		||||
        flash_size,
 | 
			
		||||
    ]
 | 
			
		||||
    print("    Offset | File")
 | 
			
		||||
    for section in sections:
 | 
			
		||||
        sect_adr, sect_file = section.split(" ", 1)
 | 
			
		||||
        print(f" -  {sect_adr} | {sect_file}")
 | 
			
		||||
        cmd += [sect_adr, sect_file]
 | 
			
		||||
 | 
			
		||||
    print(f" - {hex(app_offset)} | {firmware_name}")
 | 
			
		||||
    cmd += [hex(app_offset), firmware_name]
 | 
			
		||||
 | 
			
		||||
    print()
 | 
			
		||||
    print(f"Using esptool.py arguments: {' '.join(cmd)}")
 | 
			
		||||
    print()
 | 
			
		||||
    esptool.main(cmd)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# pylint: disable=E0602
 | 
			
		||||
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", esp32_create_combined_bin)  # noqa
 | 
			
		||||
@@ -76,6 +76,7 @@ class ESP32Preferences : public ESPPreferences {
 | 
			
		||||
  uint32_t current_offset = 0;
 | 
			
		||||
 | 
			
		||||
  void open() {
 | 
			
		||||
    nvs_flash_init();
 | 
			
		||||
    esp_err_t err = nvs_open("esphome", NVS_READWRITE, &nvs_handle);
 | 
			
		||||
    if (err == 0)
 | 
			
		||||
      return;
 | 
			
		||||
 
 | 
			
		||||
@@ -483,6 +483,7 @@ optional<ESPBLEiBeacon> ESPBLEiBeacon::from_manufacturer_data(const ServiceData
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ESPBTDevice::parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m) {
 | 
			
		||||
  this->scan_result_ = param;
 | 
			
		||||
  for (uint8_t i = 0; i < ESP_BD_ADDR_LEN; i++)
 | 
			
		||||
    this->address_[i] = param.bda[i];
 | 
			
		||||
  this->address_type_ = param.ble_addr_type;
 | 
			
		||||
@@ -524,7 +525,7 @@ void ESPBTDevice::parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_e
 | 
			
		||||
    ESP_LOGVV(TAG, "  Service UUID: %s", uuid.to_string().c_str());
 | 
			
		||||
  }
 | 
			
		||||
  for (auto &data : this->manufacturer_datas_) {
 | 
			
		||||
    ESP_LOGVV(TAG, "  Manufacturer data: %s", hexencode(data.data).c_str());
 | 
			
		||||
    ESP_LOGVV(TAG, "  Manufacturer data: %s", format_hex_pretty(data.data).c_str());
 | 
			
		||||
    if (this->get_ibeacon().has_value()) {
 | 
			
		||||
      auto ibeacon = this->get_ibeacon().value();
 | 
			
		||||
      ESP_LOGVV(TAG, "    iBeacon data:");
 | 
			
		||||
@@ -537,10 +538,10 @@ void ESPBTDevice::parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_e
 | 
			
		||||
  for (auto &data : this->service_datas_) {
 | 
			
		||||
    ESP_LOGVV(TAG, "  Service data:");
 | 
			
		||||
    ESP_LOGVV(TAG, "    UUID: %s", data.uuid.to_string().c_str());
 | 
			
		||||
    ESP_LOGVV(TAG, "    Data: %s", hexencode(data.data).c_str());
 | 
			
		||||
    ESP_LOGVV(TAG, "    Data: %s", format_hex_pretty(data.data).c_str());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ESP_LOGVV(TAG, "Adv data: %s", hexencode(param.ble_adv, param.adv_data_len + param.scan_rsp_len).c_str());
 | 
			
		||||
  ESP_LOGVV(TAG, "Adv data: %s", format_hex_pretty(param.ble_adv, param.adv_data_len + param.scan_rsp_len).c_str());
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
void ESPBTDevice::parse_adv_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m) {
 | 
			
		||||
 
 | 
			
		||||
@@ -97,6 +97,8 @@ class ESPBTDevice {
 | 
			
		||||
 | 
			
		||||
  const std::vector<ServiceData> &get_service_datas() const { return service_datas_; }
 | 
			
		||||
 | 
			
		||||
  const esp_ble_gap_cb_param_t::ble_scan_result_evt_param &get_scan_result() const { return scan_result_; }
 | 
			
		||||
 | 
			
		||||
  optional<ESPBLEiBeacon> get_ibeacon() const {
 | 
			
		||||
    for (auto &it : this->manufacturer_datas_) {
 | 
			
		||||
      auto res = ESPBLEiBeacon::from_manufacturer_data(it);
 | 
			
		||||
@@ -121,6 +123,7 @@ class ESPBTDevice {
 | 
			
		||||
  std::vector<ESPBTUUID> service_uuids_;
 | 
			
		||||
  std::vector<ServiceData> manufacturer_datas_{};
 | 
			
		||||
  std::vector<ServiceData> service_datas_{};
 | 
			
		||||
  esp_ble_gap_cb_param_t::ble_scan_result_evt_param scan_result_{};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ESP32BLETracker;
 | 
			
		||||
 
 | 
			
		||||
@@ -19,6 +19,8 @@ from esphome.cpp_helpers import setup_entity
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ["esp32"]
 | 
			
		||||
 | 
			
		||||
AUTO_LOAD = ["psram"]
 | 
			
		||||
 | 
			
		||||
esp32_camera_ns = cg.esphome_ns.namespace("esp32_camera")
 | 
			
		||||
ESP32Camera = esp32_camera_ns.class_("ESP32Camera", cg.PollingComponent, cg.EntityBase)
 | 
			
		||||
ESP32CameraFrameSize = esp32_camera_ns.enum("ESP32CameraFrameSize")
 | 
			
		||||
@@ -57,6 +59,9 @@ CONF_IDLE_FRAMERATE = "idle_framerate"
 | 
			
		||||
CONF_JPEG_QUALITY = "jpeg_quality"
 | 
			
		||||
CONF_VERTICAL_FLIP = "vertical_flip"
 | 
			
		||||
CONF_HORIZONTAL_MIRROR = "horizontal_mirror"
 | 
			
		||||
CONF_AEC2 = "aec2"
 | 
			
		||||
CONF_AE_LEVEL = "ae_level"
 | 
			
		||||
CONF_AEC_VALUE = "aec_value"
 | 
			
		||||
CONF_SATURATION = "saturation"
 | 
			
		||||
CONF_TEST_PATTERN = "test_pattern"
 | 
			
		||||
 | 
			
		||||
@@ -102,6 +107,9 @@ CONFIG_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(
 | 
			
		||||
        cv.Optional(CONF_SATURATION, default=0): camera_range_param,
 | 
			
		||||
        cv.Optional(CONF_VERTICAL_FLIP, default=True): cv.boolean,
 | 
			
		||||
        cv.Optional(CONF_HORIZONTAL_MIRROR, default=True): cv.boolean,
 | 
			
		||||
        cv.Optional(CONF_AEC2, default=False): cv.boolean,
 | 
			
		||||
        cv.Optional(CONF_AE_LEVEL, default=0): camera_range_param,
 | 
			
		||||
        cv.Optional(CONF_AEC_VALUE, default=300): cv.int_range(min=0, max=1200),
 | 
			
		||||
        cv.Optional(CONF_TEST_PATTERN, default=False): cv.boolean,
 | 
			
		||||
    }
 | 
			
		||||
).extend(cv.COMPONENT_SCHEMA)
 | 
			
		||||
@@ -116,6 +124,9 @@ SETTERS = {
 | 
			
		||||
    CONF_JPEG_QUALITY: "set_jpeg_quality",
 | 
			
		||||
    CONF_VERTICAL_FLIP: "set_vertical_flip",
 | 
			
		||||
    CONF_HORIZONTAL_MIRROR: "set_horizontal_mirror",
 | 
			
		||||
    CONF_AEC2: "set_aec2",
 | 
			
		||||
    CONF_AE_LEVEL: "set_ae_level",
 | 
			
		||||
    CONF_AEC_VALUE: "set_aec_value",
 | 
			
		||||
    CONF_CONTRAST: "set_contrast",
 | 
			
		||||
    CONF_BRIGHTNESS: "set_brightness",
 | 
			
		||||
    CONF_SATURATION: "set_saturation",
 | 
			
		||||
@@ -144,9 +155,7 @@ async def to_code(config):
 | 
			
		||||
    cg.add(var.set_frame_size(config[CONF_RESOLUTION]))
 | 
			
		||||
 | 
			
		||||
    cg.add_define("USE_ESP32_CAMERA")
 | 
			
		||||
    cg.add_build_flag("-DBOARD_HAS_PSRAM")
 | 
			
		||||
 | 
			
		||||
    if CORE.using_esp_idf:
 | 
			
		||||
        cg.add_library("espressif/esp32-camera", "1.0.0")
 | 
			
		||||
        add_idf_sdkconfig_option("CONFIG_RTCIO_SUPPORT_RTC_GPIO_DESC", True)
 | 
			
		||||
        add_idf_sdkconfig_option("CONFIG_ESP32_SPIRAM_SUPPORT", True)
 | 
			
		||||
 
 | 
			
		||||
@@ -26,6 +26,9 @@ void ESP32Camera::setup() {
 | 
			
		||||
  sensor_t *s = esp_camera_sensor_get();
 | 
			
		||||
  s->set_vflip(s, this->vertical_flip_);
 | 
			
		||||
  s->set_hmirror(s, this->horizontal_mirror_);
 | 
			
		||||
  s->set_aec2(s, this->aec2_);            // 0 = disable , 1 = enable
 | 
			
		||||
  s->set_ae_level(s, this->ae_level_);    // -2 to 2
 | 
			
		||||
  s->set_aec_value(s, this->aec_value_);  // 0 to 1200
 | 
			
		||||
  s->set_contrast(s, this->contrast_);
 | 
			
		||||
  s->set_brightness(s, this->brightness_);
 | 
			
		||||
  s->set_saturation(s, this->saturation_);
 | 
			
		||||
@@ -46,9 +49,6 @@ void ESP32Camera::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "ESP32 Camera:");
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Name: %s", this->name_.c_str());
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Internal: %s", YESNO(this->internal_));
 | 
			
		||||
#ifdef USE_ARDUINO
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Board Has PSRAM: %s", YESNO(psramFound()));
 | 
			
		||||
#endif  // USE_ARDUINO
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Data Pins: D0:%d D1:%d D2:%d D3:%d D4:%d D5:%d D6:%d D7:%d", conf.pin_d0, conf.pin_d1,
 | 
			
		||||
                conf.pin_d2, conf.pin_d3, conf.pin_d4, conf.pin_d5, conf.pin_d6, conf.pin_d7);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  VSYNC Pin: %d", conf.pin_vsync);
 | 
			
		||||
@@ -111,9 +111,9 @@ void ESP32Camera::dump_config() {
 | 
			
		||||
  // ESP_LOGCONFIG(TAG, "  Auto White Balance: %u", st.awb);
 | 
			
		||||
  // ESP_LOGCONFIG(TAG, "  Auto White Balance Gain: %u", st.awb_gain);
 | 
			
		||||
  // ESP_LOGCONFIG(TAG, "  Auto Exposure Control: %u", st.aec);
 | 
			
		||||
  // ESP_LOGCONFIG(TAG, "  Auto Exposure Control 2: %u", st.aec2);
 | 
			
		||||
  // ESP_LOGCONFIG(TAG, "  Auto Exposure Level: %d", st.ae_level);
 | 
			
		||||
  // ESP_LOGCONFIG(TAG, "  Auto Exposure Value: %u", st.aec_value);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Auto Exposure Control 2: %u", st.aec2);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Auto Exposure Level: %d", st.ae_level);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Auto Exposure Value: %u", st.aec_value);
 | 
			
		||||
  // ESP_LOGCONFIG(TAG, "  AGC: %u", st.agc);
 | 
			
		||||
  // ESP_LOGCONFIG(TAG, "  AGC Gain: %u", st.agc_gain);
 | 
			
		||||
  // ESP_LOGCONFIG(TAG, "  Gain Ceiling: %u", st.gainceiling);
 | 
			
		||||
@@ -133,6 +133,13 @@ void ESP32Camera::loop() {
 | 
			
		||||
    this->current_image_.reset();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // request idle image every idle_update_interval
 | 
			
		||||
  const uint32_t now = millis();
 | 
			
		||||
  if (this->idle_update_interval_ != 0 && now - this->last_idle_request_ > this->idle_update_interval_) {
 | 
			
		||||
    this->last_idle_request_ = now;
 | 
			
		||||
    this->request_image(IDLE);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Check if we should fetch a new image
 | 
			
		||||
  if (!this->has_requested_image_())
 | 
			
		||||
    return;
 | 
			
		||||
@@ -140,7 +147,6 @@ void ESP32Camera::loop() {
 | 
			
		||||
    // image is still in use
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  const uint32_t now = millis();
 | 
			
		||||
  if (now - this->last_update_ <= this->max_update_interval_)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
@@ -157,12 +163,12 @@ void ESP32Camera::loop() {
 | 
			
		||||
    xQueueSend(this->framebuffer_return_queue_, &fb, portMAX_DELAY);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  this->current_image_ = std::make_shared<CameraImage>(fb);
 | 
			
		||||
  this->current_image_ = std::make_shared<CameraImage>(fb, this->single_requesters_ | this->stream_requesters_);
 | 
			
		||||
 | 
			
		||||
  ESP_LOGD(TAG, "Got Image: len=%u", fb->len);
 | 
			
		||||
  this->new_image_callback_.call(this->current_image_);
 | 
			
		||||
  this->last_update_ = now;
 | 
			
		||||
  this->single_requester_ = false;
 | 
			
		||||
  this->single_requesters_ = 0;
 | 
			
		||||
}
 | 
			
		||||
void ESP32Camera::framebuffer_task(void *pv) {
 | 
			
		||||
  while (true) {
 | 
			
		||||
@@ -250,29 +256,18 @@ void ESP32Camera::add_image_callback(std::function<void(std::shared_ptr<CameraIm
 | 
			
		||||
}
 | 
			
		||||
void ESP32Camera::set_vertical_flip(bool vertical_flip) { this->vertical_flip_ = vertical_flip; }
 | 
			
		||||
void ESP32Camera::set_horizontal_mirror(bool horizontal_mirror) { this->horizontal_mirror_ = horizontal_mirror; }
 | 
			
		||||
void ESP32Camera::set_aec2(bool aec2) { this->aec2_ = aec2; }
 | 
			
		||||
void ESP32Camera::set_ae_level(int ae_level) { this->ae_level_ = ae_level; }
 | 
			
		||||
void ESP32Camera::set_aec_value(uint32_t aec_value) { this->aec_value_ = aec_value; }
 | 
			
		||||
void ESP32Camera::set_contrast(int contrast) { this->contrast_ = contrast; }
 | 
			
		||||
void ESP32Camera::set_brightness(int brightness) { this->brightness_ = brightness; }
 | 
			
		||||
void ESP32Camera::set_saturation(int saturation) { this->saturation_ = saturation; }
 | 
			
		||||
float ESP32Camera::get_setup_priority() const { return setup_priority::DATA; }
 | 
			
		||||
uint32_t ESP32Camera::hash_base() { return 3010542557UL; }
 | 
			
		||||
void ESP32Camera::request_image() { this->single_requester_ = true; }
 | 
			
		||||
void ESP32Camera::request_stream() { this->last_stream_request_ = millis(); }
 | 
			
		||||
bool ESP32Camera::has_requested_image_() const {
 | 
			
		||||
  if (this->single_requester_)
 | 
			
		||||
    // single request
 | 
			
		||||
    return true;
 | 
			
		||||
 | 
			
		||||
  uint32_t now = millis();
 | 
			
		||||
  if (now - this->last_stream_request_ < 5000)
 | 
			
		||||
    // stream request
 | 
			
		||||
    return true;
 | 
			
		||||
 | 
			
		||||
  if (this->idle_update_interval_ != 0 && now - this->last_update_ > this->idle_update_interval_)
 | 
			
		||||
    // idle update
 | 
			
		||||
    return true;
 | 
			
		||||
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
void ESP32Camera::request_image(CameraRequester requester) { this->single_requesters_ |= 1 << requester; }
 | 
			
		||||
void ESP32Camera::start_stream(CameraRequester requester) { this->stream_requesters_ |= 1 << requester; }
 | 
			
		||||
void ESP32Camera::stop_stream(CameraRequester requester) { this->stream_requesters_ &= ~(1 << requester); }
 | 
			
		||||
bool ESP32Camera::has_requested_image_() const { return this->single_requesters_ || this->stream_requesters_; }
 | 
			
		||||
bool ESP32Camera::can_return_image_() const { return this->current_image_.use_count() == 1; }
 | 
			
		||||
void ESP32Camera::set_max_update_interval(uint32_t max_update_interval) {
 | 
			
		||||
  this->max_update_interval_ = max_update_interval;
 | 
			
		||||
@@ -301,7 +296,10 @@ uint8_t *CameraImageReader::peek_data_buffer() { return this->image_->get_data_b
 | 
			
		||||
camera_fb_t *CameraImage::get_raw_buffer() { return this->buffer_; }
 | 
			
		||||
uint8_t *CameraImage::get_data_buffer() { return this->buffer_->buf; }
 | 
			
		||||
size_t CameraImage::get_data_length() { return this->buffer_->len; }
 | 
			
		||||
CameraImage::CameraImage(camera_fb_t *buffer) : buffer_(buffer) {}
 | 
			
		||||
bool CameraImage::was_requested_by(CameraRequester requester) const {
 | 
			
		||||
  return (this->requesters_ & (1 << requester)) != 0;
 | 
			
		||||
}
 | 
			
		||||
CameraImage::CameraImage(camera_fb_t *buffer, uint8_t requesters) : buffer_(buffer), requesters_(requesters) {}
 | 
			
		||||
 | 
			
		||||
}  // namespace esp32_camera
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -14,15 +14,19 @@ namespace esp32_camera {
 | 
			
		||||
 | 
			
		||||
class ESP32Camera;
 | 
			
		||||
 | 
			
		||||
enum CameraRequester { IDLE, API_REQUESTER, WEB_REQUESTER };
 | 
			
		||||
 | 
			
		||||
class CameraImage {
 | 
			
		||||
 public:
 | 
			
		||||
  CameraImage(camera_fb_t *buffer);
 | 
			
		||||
  CameraImage(camera_fb_t *buffer, uint8_t requester);
 | 
			
		||||
  camera_fb_t *get_raw_buffer();
 | 
			
		||||
  uint8_t *get_data_buffer();
 | 
			
		||||
  size_t get_data_length();
 | 
			
		||||
  bool was_requested_by(CameraRequester requester) const;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  camera_fb_t *buffer_;
 | 
			
		||||
  uint8_t requesters_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class CameraImageReader {
 | 
			
		||||
@@ -67,6 +71,9 @@ class ESP32Camera : public Component, public EntityBase {
 | 
			
		||||
  void set_power_down_pin(uint8_t pin);
 | 
			
		||||
  void set_vertical_flip(bool vertical_flip);
 | 
			
		||||
  void set_horizontal_mirror(bool horizontal_mirror);
 | 
			
		||||
  void set_aec2(bool aec2);
 | 
			
		||||
  void set_ae_level(int ae_level);
 | 
			
		||||
  void set_aec_value(uint32_t aec_value);
 | 
			
		||||
  void set_contrast(int contrast);
 | 
			
		||||
  void set_brightness(int brightness);
 | 
			
		||||
  void set_saturation(int saturation);
 | 
			
		||||
@@ -78,8 +85,9 @@ class ESP32Camera : public Component, public EntityBase {
 | 
			
		||||
  void dump_config() override;
 | 
			
		||||
  void add_image_callback(std::function<void(std::shared_ptr<CameraImage>)> &&f);
 | 
			
		||||
  float get_setup_priority() const override;
 | 
			
		||||
  void request_stream();
 | 
			
		||||
  void request_image();
 | 
			
		||||
  void start_stream(CameraRequester requester);
 | 
			
		||||
  void stop_stream(CameraRequester requester);
 | 
			
		||||
  void request_image(CameraRequester requester);
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  uint32_t hash_base() override;
 | 
			
		||||
@@ -91,6 +99,9 @@ class ESP32Camera : public Component, public EntityBase {
 | 
			
		||||
  camera_config_t config_{};
 | 
			
		||||
  bool vertical_flip_{true};
 | 
			
		||||
  bool horizontal_mirror_{true};
 | 
			
		||||
  bool aec2_{false};
 | 
			
		||||
  int ae_level_{0};
 | 
			
		||||
  uint32_t aec_value_{300};
 | 
			
		||||
  int contrast_{0};
 | 
			
		||||
  int brightness_{0};
 | 
			
		||||
  int saturation_{0};
 | 
			
		||||
@@ -98,13 +109,14 @@ class ESP32Camera : public Component, public EntityBase {
 | 
			
		||||
 | 
			
		||||
  esp_err_t init_error_{ESP_OK};
 | 
			
		||||
  std::shared_ptr<CameraImage> current_image_;
 | 
			
		||||
  uint32_t last_stream_request_{0};
 | 
			
		||||
  bool single_requester_{false};
 | 
			
		||||
  uint8_t single_requesters_{0};
 | 
			
		||||
  uint8_t stream_requesters_{0};
 | 
			
		||||
  QueueHandle_t framebuffer_get_queue_;
 | 
			
		||||
  QueueHandle_t framebuffer_return_queue_;
 | 
			
		||||
  CallbackManager<void(std::shared_ptr<CameraImage>)> new_image_callback_;
 | 
			
		||||
  uint32_t max_update_interval_{1000};
 | 
			
		||||
  uint32_t idle_update_interval_{15000};
 | 
			
		||||
  uint32_t last_idle_request_{0};
 | 
			
		||||
  uint32_t last_update_{0};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace esp32_camera_web_server {
 | 
			
		||||
 | 
			
		||||
static const int IMAGE_REQUEST_TIMEOUT = 2000;
 | 
			
		||||
static const int IMAGE_REQUEST_TIMEOUT = 5000;
 | 
			
		||||
static const char *const TAG = "esp32_camera_web_server";
 | 
			
		||||
 | 
			
		||||
#define PART_BOUNDARY "123456789000000000000987654321"
 | 
			
		||||
@@ -68,7 +68,7 @@ void CameraWebServer::setup() {
 | 
			
		||||
  httpd_register_uri_handler(this->httpd_, &uri);
 | 
			
		||||
 | 
			
		||||
  esp32_camera::global_esp32_camera->add_image_callback([this](std::shared_ptr<esp32_camera::CameraImage> image) {
 | 
			
		||||
    if (this->running_) {
 | 
			
		||||
    if (this->running_ && image->was_requested_by(esp32_camera::WEB_REQUESTER)) {
 | 
			
		||||
      this->image_ = std::move(image);
 | 
			
		||||
      xSemaphoreGive(this->semaphore_);
 | 
			
		||||
    }
 | 
			
		||||
@@ -169,11 +169,9 @@ esp_err_t CameraWebServer::streaming_handler_(struct httpd_req *req) {
 | 
			
		||||
  uint32_t last_frame = millis();
 | 
			
		||||
  uint32_t frames = 0;
 | 
			
		||||
 | 
			
		||||
  while (res == ESP_OK && this->running_) {
 | 
			
		||||
    if (esp32_camera::global_esp32_camera != nullptr) {
 | 
			
		||||
      esp32_camera::global_esp32_camera->request_stream();
 | 
			
		||||
    }
 | 
			
		||||
  esp32_camera::global_esp32_camera->start_stream(esphome::esp32_camera::WEB_REQUESTER);
 | 
			
		||||
 | 
			
		||||
  while (res == ESP_OK && this->running_) {
 | 
			
		||||
    auto image = this->wait_for_image_();
 | 
			
		||||
 | 
			
		||||
    if (!image) {
 | 
			
		||||
@@ -204,6 +202,8 @@ esp_err_t CameraWebServer::streaming_handler_(struct httpd_req *req) {
 | 
			
		||||
    res = httpd_send_all(req, STREAM_ERROR, strlen(STREAM_ERROR));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  esp32_camera::global_esp32_camera->stop_stream(esphome::esp32_camera::WEB_REQUESTER);
 | 
			
		||||
 | 
			
		||||
  ESP_LOGI(TAG, "STREAM: closed. Frames: %u", frames);
 | 
			
		||||
 | 
			
		||||
  return res;
 | 
			
		||||
@@ -212,9 +212,7 @@ esp_err_t CameraWebServer::streaming_handler_(struct httpd_req *req) {
 | 
			
		||||
esp_err_t CameraWebServer::snapshot_handler_(struct httpd_req *req) {
 | 
			
		||||
  esp_err_t res = ESP_OK;
 | 
			
		||||
 | 
			
		||||
  if (esp32_camera::global_esp32_camera != nullptr) {
 | 
			
		||||
    esp32_camera::global_esp32_camera->request_image();
 | 
			
		||||
  }
 | 
			
		||||
  esp32_camera::global_esp32_camera->request_image(esphome::esp32_camera::WEB_REQUESTER);
 | 
			
		||||
 | 
			
		||||
  auto image = this->wait_for_image_();
 | 
			
		||||
 | 
			
		||||
@@ -233,9 +231,6 @@ esp_err_t CameraWebServer::snapshot_handler_(struct httpd_req *req) {
 | 
			
		||||
 | 
			
		||||
  httpd_resp_set_hdr(req, "Content-Disposition", "inline; filename=capture.jpg");
 | 
			
		||||
 | 
			
		||||
  if (res == ESP_OK) {
 | 
			
		||||
    res = httpd_resp_set_hdr(req, CONTENT_LENGTH, esphome::to_string(image->get_data_length()).c_str());
 | 
			
		||||
  }
 | 
			
		||||
  if (res == ESP_OK) {
 | 
			
		||||
    res = httpd_resp_send(req, (const char *) image->get_data_buffer(), image->get_data_length());
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										0
									
								
								esphome/components/esp32_can/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								esphome/components/esp32_can/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										39
									
								
								esphome/components/esp32_can/canbus.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								esphome/components/esp32_can/canbus.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome import pins
 | 
			
		||||
from esphome.components import canbus
 | 
			
		||||
from esphome.const import CONF_ID, CONF_RX_PIN, CONF_TX_PIN
 | 
			
		||||
from esphome.components.canbus import CanbusComponent, CanSpeed, CONF_BIT_RATE
 | 
			
		||||
 | 
			
		||||
CODEOWNERS = ["@Sympatron"]
 | 
			
		||||
DEPENDENCIES = ["esp32"]
 | 
			
		||||
 | 
			
		||||
esp32_can_ns = cg.esphome_ns.namespace("esp32_can")
 | 
			
		||||
esp32_can = esp32_can_ns.class_("ESP32Can", CanbusComponent)
 | 
			
		||||
 | 
			
		||||
# Currently the driver only supports a subset of the bit rates defined in canbus
 | 
			
		||||
CAN_SPEEDS = {
 | 
			
		||||
    "50KBPS": CanSpeed.CAN_50KBPS,
 | 
			
		||||
    "100KBPS": CanSpeed.CAN_100KBPS,
 | 
			
		||||
    "125KBPS": CanSpeed.CAN_125KBPS,
 | 
			
		||||
    "250KBPS": CanSpeed.CAN_250KBPS,
 | 
			
		||||
    "500KBPS": CanSpeed.CAN_500KBPS,
 | 
			
		||||
    "1000KBPS": CanSpeed.CAN_1000KBPS,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = canbus.CANBUS_SCHEMA.extend(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(): cv.declare_id(esp32_can),
 | 
			
		||||
        cv.Optional(CONF_BIT_RATE, default="125KBPS"): cv.enum(CAN_SPEEDS, upper=True),
 | 
			
		||||
        cv.Required(CONF_RX_PIN): pins.internal_gpio_input_pin_number,
 | 
			
		||||
        cv.Required(CONF_TX_PIN): pins.internal_gpio_output_pin_number,
 | 
			
		||||
    }
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def to_code(config):
 | 
			
		||||
    var = cg.new_Pvariable(config[CONF_ID])
 | 
			
		||||
    await canbus.register_canbus(var, config)
 | 
			
		||||
 | 
			
		||||
    cg.add(var.set_rx(config[CONF_RX_PIN]))
 | 
			
		||||
    cg.add(var.set_tx(config[CONF_TX_PIN]))
 | 
			
		||||
							
								
								
									
										123
									
								
								esphome/components/esp32_can/esp32_can.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								esphome/components/esp32_can/esp32_can.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,123 @@
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
#include "esp32_can.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
#include <driver/can.h>
 | 
			
		||||
 | 
			
		||||
// WORKAROUND, because CAN_IO_UNUSED is just defined as (-1) in this version
 | 
			
		||||
// of the framework which does not work with -fpermissive
 | 
			
		||||
#undef CAN_IO_UNUSED
 | 
			
		||||
#define CAN_IO_UNUSED ((gpio_num_t) -1)
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace esp32_can {
 | 
			
		||||
 | 
			
		||||
static const char *const TAG = "esp32_can";
 | 
			
		||||
 | 
			
		||||
static bool get_bitrate(canbus::CanSpeed bitrate, can_timing_config_t *t_config) {
 | 
			
		||||
  switch (bitrate) {
 | 
			
		||||
    case canbus::CAN_50KBPS:
 | 
			
		||||
      *t_config = (can_timing_config_t) CAN_TIMING_CONFIG_50KBITS();
 | 
			
		||||
      return true;
 | 
			
		||||
    case canbus::CAN_100KBPS:
 | 
			
		||||
      *t_config = (can_timing_config_t) CAN_TIMING_CONFIG_100KBITS();
 | 
			
		||||
      return true;
 | 
			
		||||
    case canbus::CAN_125KBPS:
 | 
			
		||||
      *t_config = (can_timing_config_t) CAN_TIMING_CONFIG_125KBITS();
 | 
			
		||||
      return true;
 | 
			
		||||
    case canbus::CAN_250KBPS:
 | 
			
		||||
      *t_config = (can_timing_config_t) CAN_TIMING_CONFIG_250KBITS();
 | 
			
		||||
      return true;
 | 
			
		||||
    case canbus::CAN_500KBPS:
 | 
			
		||||
      *t_config = (can_timing_config_t) CAN_TIMING_CONFIG_500KBITS();
 | 
			
		||||
      return true;
 | 
			
		||||
    case canbus::CAN_1000KBPS:
 | 
			
		||||
      *t_config = (can_timing_config_t) CAN_TIMING_CONFIG_1MBITS();
 | 
			
		||||
      return true;
 | 
			
		||||
    default:
 | 
			
		||||
      return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ESP32Can::setup_internal() {
 | 
			
		||||
  can_general_config_t g_config =
 | 
			
		||||
      CAN_GENERAL_CONFIG_DEFAULT((gpio_num_t) this->tx_, (gpio_num_t) this->rx_, CAN_MODE_NORMAL);
 | 
			
		||||
  can_filter_config_t f_config = CAN_FILTER_CONFIG_ACCEPT_ALL();
 | 
			
		||||
  can_timing_config_t t_config;
 | 
			
		||||
 | 
			
		||||
  if (!get_bitrate(this->bit_rate_, &t_config)) {
 | 
			
		||||
    // invalid bit rate
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Install CAN driver
 | 
			
		||||
  if (can_driver_install(&g_config, &t_config, &f_config) != ESP_OK) {
 | 
			
		||||
    // Failed to install driver
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Start CAN driver
 | 
			
		||||
  if (can_start() != ESP_OK) {
 | 
			
		||||
    // Failed to start driver
 | 
			
		||||
    this->mark_failed();
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
canbus::Error ESP32Can::send_message(struct canbus::CanFrame *frame) {
 | 
			
		||||
  if (frame->can_data_length_code > canbus::CAN_MAX_DATA_LENGTH) {
 | 
			
		||||
    return canbus::ERROR_FAILTX;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint32_t flags = CAN_MSG_FLAG_NONE;
 | 
			
		||||
  if (frame->use_extended_id) {
 | 
			
		||||
    flags |= CAN_MSG_FLAG_EXTD;
 | 
			
		||||
  }
 | 
			
		||||
  if (frame->remote_transmission_request) {
 | 
			
		||||
    flags |= CAN_MSG_FLAG_RTR;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  can_message_t message = {
 | 
			
		||||
      .flags = flags,
 | 
			
		||||
      .identifier = frame->can_id,
 | 
			
		||||
      .data_length_code = frame->can_data_length_code,
 | 
			
		||||
  };
 | 
			
		||||
  if (!frame->remote_transmission_request) {
 | 
			
		||||
    memcpy(message.data, frame->data, frame->can_data_length_code);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (can_transmit(&message, pdMS_TO_TICKS(1000)) == ESP_OK) {
 | 
			
		||||
    return canbus::ERROR_OK;
 | 
			
		||||
  } else {
 | 
			
		||||
    return canbus::ERROR_ALLTXBUSY;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
canbus::Error ESP32Can::read_message(struct canbus::CanFrame *frame) {
 | 
			
		||||
  can_message_t message;
 | 
			
		||||
 | 
			
		||||
  if (can_receive(&message, 0) != ESP_OK) {
 | 
			
		||||
    return canbus::ERROR_NOMSG;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  frame->can_id = message.identifier;
 | 
			
		||||
  frame->use_extended_id = message.flags & CAN_MSG_FLAG_EXTD;
 | 
			
		||||
  frame->remote_transmission_request = message.flags & CAN_MSG_FLAG_RTR;
 | 
			
		||||
  frame->can_data_length_code = message.data_length_code;
 | 
			
		||||
 | 
			
		||||
  if (!frame->remote_transmission_request) {
 | 
			
		||||
    size_t dlc =
 | 
			
		||||
        message.data_length_code < canbus::CAN_MAX_DATA_LENGTH ? message.data_length_code : canbus::CAN_MAX_DATA_LENGTH;
 | 
			
		||||
    memcpy(frame->data, message.data, dlc);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return canbus::ERROR_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace esp32_can
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										29
									
								
								esphome/components/esp32_can/esp32_can.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								esphome/components/esp32_can/esp32_can.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
 | 
			
		||||
#include "esphome/components/canbus/canbus.h"
 | 
			
		||||
#include "esphome/core/component.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace esp32_can {
 | 
			
		||||
 | 
			
		||||
class ESP32Can : public canbus::Canbus {
 | 
			
		||||
 public:
 | 
			
		||||
  void set_rx(int rx) { rx_ = rx; }
 | 
			
		||||
  void set_tx(int tx) { tx_ = tx; }
 | 
			
		||||
  ESP32Can(){};
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  bool setup_internal() override;
 | 
			
		||||
  canbus::Error send_message(struct canbus::CanFrame *frame) override;
 | 
			
		||||
  canbus::Error read_message(struct canbus::CanFrame *frame) override;
 | 
			
		||||
 | 
			
		||||
  int rx_{-1};
 | 
			
		||||
  int tx_{-1};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace esp32_can
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
@@ -219,7 +219,7 @@ void ESP32ImprovComponent::dump_config() {
 | 
			
		||||
void ESP32ImprovComponent::process_incoming_data_() {
 | 
			
		||||
  uint8_t length = this->incoming_data_[1];
 | 
			
		||||
 | 
			
		||||
  ESP_LOGD(TAG, "Processing bytes - %s", hexencode(this->incoming_data_).c_str());
 | 
			
		||||
  ESP_LOGD(TAG, "Processing bytes - %s", format_hex_pretty(this->incoming_data_).c_str());
 | 
			
		||||
  if (this->incoming_data_.size() - 3 == length) {
 | 
			
		||||
    this->set_error_(improv::ERROR_NONE);
 | 
			
		||||
    improv::ImprovCommand command = improv::parse_improv_data(this->incoming_data_);
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user