mirror of
				https://github.com/home-assistant/frontend.git
				synced 2025-10-31 06:29:43 +00:00 
			
		
		
		
	Compare commits
	
		
			1389 Commits
		
	
	
		
			20210407.0
			...
			fix-search
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 21e441b682 | ||
|  | 737f7ba6b9 | ||
|  | a5862b86ca | ||
|  | 099fa706a0 | ||
|  | ed84ce9692 | ||
|  | 9912d427f2 | ||
|  | 76f574f875 | ||
|  | ac90bb7088 | ||
|  | ce9f83e9a2 | ||
|  | fca7d2c5b0 | ||
|  | d7a5921e7b | ||
|  | cefa2ee183 | ||
|  | 0eeed85193 | ||
|  | fd80408de2 | ||
|  | 467a5169c0 | ||
|  | b0b3222b33 | ||
|  | b053881cef | ||
|  | 92a9ed7080 | ||
|  | 830b449006 | ||
|  | d38a8a317e | ||
|  | a0aed9112c | ||
|  | ce3b8544b9 | ||
|  | 134ed7d303 | ||
|  | dc27871189 | ||
|  | 9c9bfa2b77 | ||
|  | f02dd39619 | ||
|  | d37d99223d | ||
|  | 4db943c5ff | ||
|  | ed001fb10b | ||
|  | 5f43715dd8 | ||
|  | 5435218187 | ||
|  | 4ef5f3af89 | ||
|  | 9eea17b793 | ||
|  | 6a51e2aaad | ||
|  | 2d33327d88 | ||
|  | e9ec2da917 | ||
|  | 09d46dac61 | ||
|  | 236fa14ec3 | ||
|  | 2cb37820df | ||
|  | 869fa91ae5 | ||
|  | 22df03427f | ||
|  | e72a4e4a20 | ||
|  | ca8d31c6bb | ||
|  | 354ea88984 | ||
|  | 76af6e48cd | ||
|  | d05f807b9d | ||
|  | 4092f7f75d | ||
|  | 04668ad809 | ||
|  | 9be5a15c77 | ||
|  | 21d86f4797 | ||
|  | 45e6ec1ee2 | ||
|  | 9b97faa5e3 | ||
|  | 8730c122fd | ||
|  | 0046252e32 | ||
|  | f47440083e | ||
|  | bfaf44f9d1 | ||
|  | deba6a0db4 | ||
|  | 890ad9a1c8 | ||
|  | 8466ef371a | ||
|  | 4e55460799 | ||
|  | 5fde6e659d | ||
|  | 148bb99d89 | ||
|  | 0540bae707 | ||
|  | 0c6f647f53 | ||
|  | 3aca67d511 | ||
|  | 0e41a408e7 | ||
|  | 19e1eaf2d7 | ||
|  | 5e80a2b465 | ||
|  | 866a57cde4 | ||
|  | a88da0e39a | ||
|  | 21a8fac477 | ||
|  | ca5ce04a38 | ||
|  | 7c4b9a0410 | ||
|  | de6f06ea6d | ||
|  | bbc8e323e8 | ||
|  | 89b6863ae3 | ||
|  | 3f1850e9eb | ||
|  | 54d6b5b6f3 | ||
|  | fb55ab197f | ||
|  | cc2db9a761 | ||
|  | 58ba3e5c22 | ||
|  | 182ffccd0c | ||
|  | ce99d14ee0 | ||
|  | 8ce160b9ce | ||
|  | fe33714c8b | ||
|  | afbe85625c | ||
|  | cb47ee7721 | ||
|  | 5caa256f1b | ||
|  | c66dfb84f9 | ||
|  | df1d703e4e | ||
|  | ce0ced0b6a | ||
|  | 730e9b144d | ||
|  | 69ff8dd0c4 | ||
|  | 8d2c716fbe | ||
|  | 389a100b46 | ||
|  | 9fee7a2829 | ||
|  | a91897821a | ||
|  | 815a2a07ff | ||
|  | b8d3eb76ac | ||
|  | ba75c2e7af | ||
|  | f04b844223 | ||
|  | 242bad0a29 | ||
|  | 8b20b2b63c | ||
|  | e0c8efc5e6 | ||
|  | f59c30ac04 | ||
|  | e4b9c08b45 | ||
|  | 04e63eefe2 | ||
|  | a064ca0856 | ||
|  | 6044ea92ad | ||
|  | 17e8215420 | ||
|  | a4ae1bee79 | ||
|  | 7d335d7d85 | ||
|  | 7c194d8910 | ||
|  | a92100bb0a | ||
|  | 303af611d1 | ||
|  | 559b6e9d5b | ||
|  | 75a95ff675 | ||
|  | 3024ee43f9 | ||
|  | b34b92fa87 | ||
|  | 1832ed0a48 | ||
|  | f398692e75 | ||
|  | 68bee4dd58 | ||
|  | f1297e1f36 | ||
|  | 953e3e060b | ||
|  | c37f660718 | ||
|  | 02754369a6 | ||
|  | 0df9e9932f | ||
|  | eddb392ad0 | ||
|  | e8ba349447 | ||
|  | 5be22d46ab | ||
|  | ffaff30b46 | ||
|  | c4cad5bccd | ||
|  | e4085fe1f6 | ||
|  | 8bfef92c86 | ||
|  | 0c07178c0a | ||
|  | 1010777139 | ||
|  | e57477c16a | ||
|  | 30fa92c120 | ||
|  | b32438dc18 | ||
|  | 614bd2f451 | ||
|  | 6c12a5a4b1 | ||
|  | bbcec38450 | ||
|  | 416e2e26c0 | ||
|  | 1a7164b466 | ||
|  | 3ddcd2d0f6 | ||
|  | 648c02e622 | ||
|  | b0b953bfac | ||
|  | abeaa63005 | ||
|  | 9cd23374f4 | ||
|  | 72bd5f84d6 | ||
|  | 22b4550fdf | ||
|  | 87c22229e0 | ||
|  | 971fd8dc60 | ||
|  | 049c3caadd | ||
|  | fb2a24d11e | ||
|  | d4646bac01 | ||
|  | 14e5b2a7a5 | ||
|  | 734a733a4c | ||
|  | 8f31c182f6 | ||
|  | e51a819bfd | ||
|  | 05d7e85aa3 | ||
|  | cf527e4bc2 | ||
|  | 197b581e8e | ||
|  | f75bf1f676 | ||
|  | 28df79cfda | ||
|  | 3bf19883a8 | ||
|  | 303e065433 | ||
|  | 7ad0b37a9e | ||
|  | 930c7e4afa | ||
|  | 81faae6f74 | ||
|  | f7fc83ac12 | ||
|  | 21a099ee9f | ||
|  | 7d1ce1b240 | ||
|  | d1f1309198 | ||
|  | 68dd818f7a | ||
|  | 50bea33a19 | ||
|  | 27cae037ce | ||
|  | dbb5bf7550 | ||
|  | 9ef743a695 | ||
|  | f3642a1677 | ||
|  | 2d651c2a66 | ||
|  | ef39317019 | ||
|  | 441f1fbcb5 | ||
|  | 09a27a6791 | ||
|  | 32bbdc194a | ||
|  | 52588a3915 | ||
|  | 9fffc93e5d | ||
|  | effec839af | ||
|  | 884ed561a1 | ||
|  | 4165e64ce0 | ||
|  | 6053b64b2e | ||
|  | ed462dc257 | ||
|  | 74a05929be | ||
|  | 5e388b1f02 | ||
|  | ff2fa9a78c | ||
|  | 6d1be9e73f | ||
|  | ba570f4004 | ||
|  | 6ab497edf8 | ||
|  | 8c1cd273df | ||
|  | 8f68bcbba9 | ||
|  | 8291cf9daa | ||
|  | bb40e66833 | ||
|  | f852208eff | ||
|  | 3bbe1603eb | ||
|  | 25d60e11da | ||
|  | 78d06426cf | ||
|  | 320b2bb48b | ||
|  | a7b8382617 | ||
|  | 77fe687ec2 | ||
|  | 069f08b55e | ||
|  | 204ccf8b40 | ||
|  | 4b9ff641ba | ||
|  | 1520b5832a | ||
|  | 04f2e2e70c | ||
|  | 920d2972ea | ||
|  | e94fc493b8 | ||
|  | 3e22270c2c | ||
|  | 27fa34e24e | ||
|  | 4e0cebaf32 | ||
|  | f021480bc5 | ||
|  | 34c3374d84 | ||
|  | 4cb7154917 | ||
|  | 08863348dc | ||
|  | 2bcf816b77 | ||
|  | d2b99e6963 | ||
|  | 48a800882e | ||
|  | 595e13ecac | ||
|  | 5261d583a8 | ||
|  | 5c488f8298 | ||
|  | 6fc87a6f66 | ||
|  | 3133f9b01f | ||
|  | 2c0d330f1f | ||
|  | fb9ea981ed | ||
|  | 63c113f78d | ||
|  | a67799a670 | ||
|  | e3d78d6dc5 | ||
|  | 76a4b1efbd | ||
|  | 882e79524b | ||
|  | 0ab8f8fd7c | ||
|  | 86b9eb0bd7 | ||
|  | 011cbe7d22 | ||
|  | 9b0b2c5b71 | ||
|  | be72bf7b3c | ||
|  | 3e062ba673 | ||
|  | 322d965539 | ||
|  | 7b840527b5 | ||
|  | dced053ba2 | ||
|  | fe4322e64b | ||
|  | 0800c702fb | ||
|  | b6d6e2fd4b | ||
|  | 2bbb1bfa7e | ||
|  | e2af8ac3cc | ||
|  | 25ff5fef14 | ||
|  | 2f9c088091 | ||
|  | 50c397901b | ||
|  | 1f7d4c25d4 | ||
|  | 29819fac23 | ||
|  | cc301df57d | ||
|  | 7d5b566312 | ||
|  | 07cd68f5d0 | ||
|  | 99bf6fa781 | ||
|  | bfad1eb5ac | ||
|  | 6f9b2ee569 | ||
|  | b7bd7c1065 | ||
|  | 4ebdca2a46 | ||
|  | fc700fdaf0 | ||
|  | d8e12f4280 | ||
|  | 86114758c3 | ||
|  | 792278cf17 | ||
|  | b8832f2121 | ||
|  | 76339c90f7 | ||
|  | b3d4451035 | ||
|  | dc58481918 | ||
|  | 14af735507 | ||
|  | a7b558b64a | ||
|  | b7665bef6f | ||
|  | 5ec37a35f1 | ||
|  | 91bb2ddcc4 | ||
|  | 61bae5da64 | ||
|  | 85168b3a35 | ||
|  | 942150cda2 | ||
|  | 2606d55895 | ||
|  | 1f671198aa | ||
|  | deb65e7108 | ||
|  | cd00f7f874 | ||
|  | bdd13db8cf | ||
|  | 2b0359edba | ||
|  | 35e9687170 | ||
|  | b730676914 | ||
|  | 2890192c05 | ||
|  | bfb84a834f | ||
|  | ca6fd6c770 | ||
|  | 585648ac4c | ||
|  | bec5c564b6 | ||
|  | 48c66e6349 | ||
|  | cea40610c0 | ||
|  | 0c3fd8f3ad | ||
|  | cdc3d11181 | ||
|  | 02bdeebc82 | ||
|  | 60c7669d8f | ||
|  | 919bf94a03 | ||
|  | ead5e288eb | ||
|  | add8a702cc | ||
|  | 39774c0e02 | ||
|  | 149f381bc3 | ||
|  | faccb12430 | ||
|  | 7039bae9be | ||
|  | 0a7b703d57 | ||
|  | 24e8028e8f | ||
|  | 8f729e2a95 | ||
|  | 8412cd71cb | ||
|  | 5c78b74005 | ||
|  | 2459477ec4 | ||
|  | a065740c91 | ||
|  | f3104d3c93 | ||
|  | 1916c179b4 | ||
|  | e8b9766eb6 | ||
|  | ff7a2c8cb7 | ||
|  | 7ccde2cb41 | ||
|  | d6b9b16f02 | ||
|  | 66df15007a | ||
|  | f164d21c44 | ||
|  | 911d322aac | ||
|  | 419879ee7a | ||
|  | c3e1a2edf0 | ||
|  | 8f5751d5bb | ||
|  | 4095450476 | ||
|  | bc9195f7d5 | ||
|  | e61f587c51 | ||
|  | d43d19190e | ||
|  | a283acaabf | ||
|  | ea18fc0078 | ||
|  | 1df11e9bf1 | ||
|  | c71b2e6b9d | ||
|  | db4aa05bf4 | ||
|  | a54a2a54f8 | ||
|  | 0bcb4d0e09 | ||
|  | 95dbc811d3 | ||
|  | e28a11964e | ||
|  | 46a9e36516 | ||
|  | e99f20c4f3 | ||
|  | 2100603cdc | ||
|  | da4942aca3 | ||
|  | 7c78fb314e | ||
|  | 5bc2468cbc | ||
|  | a580904c52 | ||
|  | 48d12ceafe | ||
|  | 60ce805b3b | ||
|  | 251416b51d | ||
|  | c41c6eedd8 | ||
|  | 6877fd9e00 | ||
|  | 4cc104a99f | ||
|  | 6494177821 | ||
|  | cea1a62867 | ||
|  | a6b5262d02 | ||
|  | 2a5fc5181e | ||
|  | 2fe8f5ff27 | ||
|  | 0c75d5afc9 | ||
|  | cf062bf0f4 | ||
|  | acf4d59fde | ||
|  | 05333ac2d9 | ||
|  | 4b49da58b1 | ||
|  | 68373e6372 | ||
|  | 01049e8eb8 | ||
|  | 87f7981144 | ||
|  | ceac9834b9 | ||
|  | ac8f748656 | ||
|  | 1d97d8dca9 | ||
|  | fd6785b593 | ||
|  | d5fc751da6 | ||
|  | 933fd72629 | ||
|  | 0611133065 | ||
|  | 02644b923f | ||
|  | 67f06112c6 | ||
|  | 49e39644f3 | ||
|  | 990ad1bb67 | ||
|  | dbbf246060 | ||
|  | d2c20837a5 | ||
|  | e91d1777d0 | ||
|  | a5be143c3b | ||
|  | 0ef07e4835 | ||
|  | 9361e4cf9c | ||
|  | e7fd75703f | ||
|  | 2c0b2f4bc5 | ||
|  | faec09f0d1 | ||
|  | b79c06ad71 | ||
|  | 5614e0d29c | ||
|  | 0b7fc177f9 | ||
|  | 367322415e | ||
|  | 117b50f3ea | ||
|  | 366aa8aed1 | ||
|  | 43011179eb | ||
|  | 6177d2b416 | ||
|  | f70485bc49 | ||
|  | 921763b5f1 | ||
|  | 5fd4315789 | ||
|  | ed291b57d0 | ||
|  | f833701e7c | ||
|  | 8533b90957 | ||
|  | c95a54c6f3 | ||
|  | a991640f52 | ||
|  | 3d99b92c07 | ||
|  | d28ad17135 | ||
|  | 3c67fc96b1 | ||
|  | 4719636176 | ||
|  | 45efee28b8 | ||
|  | 3bcf225380 | ||
|  | 2e81f843ce | ||
|  | a430142296 | ||
|  | 6335b13c5e | ||
|  | 6c4e987a24 | ||
|  | 1a5c43d72a | ||
|  | 91dbfca899 | ||
|  | 96f103644a | ||
|  | 5304e5a670 | ||
|  | 390e5b3881 | ||
|  | 9f5756c9fa | ||
|  | 0ca35d7012 | ||
|  | 0d19f4792f | ||
|  | 91b009af79 | ||
|  | 1ebd2fb9f1 | ||
|  | 4684979ae7 | ||
|  | a567312bdb | ||
|  | 1e851e0e8c | ||
|  | 7d94615f47 | ||
|  | 582fab7ea1 | ||
|  | 822590ec8a | ||
|  | e9f0967578 | ||
|  | 481da19c74 | ||
|  | b969db0c0f | ||
|  | a6b98fc3c3 | ||
|  | 87c2046ab5 | ||
|  | 4b992fb0c4 | ||
|  | 3154011c65 | ||
|  | 4e68383cf7 | ||
|  | db6ef22ebb | ||
|  | c238c7dbbc | ||
|  | d04823b4c5 | ||
|  | 4cb45d6313 | ||
|  | 6623e5f017 | ||
|  | 7f1a321075 | ||
|  | 6518aefb7f | ||
|  | d5600b7c08 | ||
|  | 4789295d32 | ||
|  | 70d54aa855 | ||
|  | 77549efc47 | ||
|  | 00299bc74d | ||
|  | b74fc5578d | ||
|  | 9018d4cc18 | ||
|  | 72b9f8636d | ||
|  | fcdceba09d | ||
|  | 06d4ccf344 | ||
|  | a268040ae7 | ||
|  | 67d79d618a | ||
|  | c9cd316c0c | ||
|  | 0e8a06e24d | ||
|  | d7732ee850 | ||
|  | 729a928cfe | ||
|  | fe5a582a74 | ||
|  | c26a59d805 | ||
|  | ea331dbe0b | ||
|  | b97d6d7059 | ||
|  | 9425b943dd | ||
|  | 3fd0becfd4 | ||
|  | 12ef191a0f | ||
|  | 2bbb4acf3d | ||
|  | 77d54df007 | ||
|  | 1c35571ef0 | ||
|  | c8804160bf | ||
|  | 0a6ffb6bc8 | ||
|  | 6cf3580fb4 | ||
|  | 6984f19aa0 | ||
|  | cb8de53d74 | ||
|  | 93680b9764 | ||
|  | 3cf9b745b5 | ||
|  | 5851fe26ff | ||
|  | b188c4ec81 | ||
|  | 4624c3d75b | ||
|  | 5d91aefb55 | ||
|  | 7d196b4b95 | ||
|  | 6347e44d94 | ||
|  | 719d9386c5 | ||
|  | bb734be4bc | ||
|  | 7cadaf1dc3 | ||
|  | c30453a86f | ||
|  | c2e3d0188e | ||
|  | aabb8ea16f | ||
|  | df572d59c5 | ||
|  | 5ef7a37c20 | ||
|  | 4b44e197ae | ||
|  | 8b5b21ae69 | ||
|  | f5417fad6f | ||
|  | 7fa6317f5c | ||
|  | 74533cebc6 | ||
|  | e3c0530941 | ||
|  | 10986db7c6 | ||
|  | 67648baca7 | ||
|  | dc9182e9ab | ||
|  | 4a7a81ffdb | ||
|  | 09ef72647e | ||
|  | da38e6f986 | ||
|  | bd1a9f2cb0 | ||
|  | 2c9223ed80 | ||
|  | 171eddd779 | ||
|  | 7acc2f9e08 | ||
|  | 27a6341137 | ||
|  | 6c5e15e707 | ||
|  | 06b1718ade | ||
|  | e50d2e16a7 | ||
|  | 0b2404a0f2 | ||
|  | 371804591d | ||
|  | 54c64c15f3 | ||
|  | 0e1124cd4f | ||
|  | 70fd759e18 | ||
|  | 8e383b2bec | ||
|  | 63cd576d56 | ||
|  | 32ac04ea78 | ||
|  | 5d6bacb0bd | ||
|  | 398d777681 | ||
|  | 549a360d98 | ||
|  | 1140e6026c | ||
|  | 29a1167782 | ||
|  | d61a77f2d9 | ||
|  | b9bde1960b | ||
|  | a12c2eea5d | ||
|  | b5c717a559 | ||
|  | 3adbc4cfaf | ||
|  | dd11fb1b99 | ||
|  | bf0d102c86 | ||
|  | dad2b92d2e | ||
|  | d027ec0018 | ||
|  | 0c038398aa | ||
|  | 5c3e0cc016 | ||
|  | 9bcd26ce57 | ||
|  | 3e8a6c418c | ||
|  | 279f3e1183 | ||
|  | f77339ad85 | ||
|  | da73b316ff | ||
|  | 82a49d2cbf | ||
|  | 05711b4636 | ||
|  | 2c2809573f | ||
|  | bbbeafcc92 | ||
|  | 95c6adc739 | ||
|  | 7c2e0aea92 | ||
|  | d05c76356f | ||
|  | f1a0623447 | ||
|  | 41d02fdb72 | ||
|  | 52d45d482c | ||
|  | a0fea94db2 | ||
|  | c3975e48d9 | ||
|  | f062e13921 | ||
|  | 08ca9c9064 | ||
|  | 667fd39147 | ||
|  | b760e543b0 | ||
|  | 760ead4860 | ||
|  | 9a4cce74f0 | ||
|  | 7488eb782d | ||
|  | b1e6935df9 | ||
|  | df53364d16 | ||
|  | 777e6c4c72 | ||
|  | e47a5effe6 | ||
|  | 62d3f74513 | ||
|  | 21e1fef0fb | ||
|  | b3f8daa758 | ||
|  | 04f586721f | ||
|  | 8e22e41605 | ||
|  | 2770d1f36b | ||
|  | 403c042235 | ||
|  | bdb3c04037 | ||
|  | f1cb21e7fc | ||
|  | a8486eda9f | ||
|  | d5b98d306d | ||
|  | bb2fe650ac | ||
|  | b576c3de40 | ||
|  | 84533b8843 | ||
|  | a8ff98b808 | ||
|  | f0062b1e67 | ||
|  | 93f64de875 | ||
|  | ec47e320d2 | ||
|  | 816d5ee594 | ||
|  | 588f5bd6b7 | ||
|  | 825ea93dba | ||
|  | a690a1d7bf | ||
|  | 9fe4c79782 | ||
|  | 42613d6519 | ||
|  | 4b77910e4f | ||
|  | 3f2cce936c | ||
|  | 6e8e9824f9 | ||
|  | 33e1d34cb1 | ||
|  | 48948d5854 | ||
|  | 7fc00ce1cb | ||
|  | 0c940be5fb | ||
|  | bddb505b7f | ||
|  | a91d25b27d | ||
|  | 4ad005f0bf | ||
|  | 7472545204 | ||
|  | 164c9c8e73 | ||
|  | e52118db93 | ||
|  | 9e7acacb06 | ||
|  | 56deb15bca | ||
|  | a3d4969d7b | ||
|  | 4a00957b71 | ||
|  | 56bd731361 | ||
|  | b6c470edf1 | ||
|  | 5bc0feacf0 | ||
|  | dc8d837e88 | ||
|  | cddf6ce1f4 | ||
|  | 8abb212ae7 | ||
|  | 0056d75127 | ||
|  | 5be475ea17 | ||
|  | b157cf5294 | ||
|  | 48c9c89e3d | ||
|  | 83f405b695 | ||
|  | 9bf41a37b4 | ||
|  | 774f22b7e7 | ||
|  | aaa3964bb3 | ||
|  | 6f6fc759cc | ||
|  | 4358b7f924 | ||
|  | 2841369d3d | ||
|  | ad031d4bda | ||
|  | 588ee2c3b1 | ||
|  | 038033cf27 | ||
|  | 84c4bbd380 | ||
|  | 807ce468d6 | ||
|  | a839494a1e | ||
|  | 80bbc9990a | ||
|  | fa52442c1c | ||
|  | 919ce2afb1 | ||
|  | db55be6d33 | ||
|  | 2dc7c1afed | ||
|  | 85956dc7fd | ||
|  | 910cd98a38 | ||
|  | 8022bd2868 | ||
|  | d5ca7e1719 | ||
|  | 066a0771b3 | ||
|  | 9e35c1ab68 | ||
|  | fb1deb838c | ||
|  | 8e010618bb | ||
|  | 365cf1f7ef | ||
|  | b226b20e3d | ||
|  | ec21f4c2c6 | ||
|  | a696d849b2 | ||
|  | ea3fae2ce4 | ||
|  | 736e117eca | ||
|  | 2fb3ac74eb | ||
|  | 2d5c8ec3e9 | ||
|  | 25c1156c88 | ||
|  | c44624282c | ||
|  | 370f2eb9e4 | ||
|  | 1793c68aae | ||
|  | 5e52bd905d | ||
|  | cba6bbdc74 | ||
|  | 6f4593508b | ||
|  | dc3bad56f2 | ||
|  | 784e5e6e39 | ||
|  | 13fe62975d | ||
|  | b97fd9918a | ||
|  | dc56c2de52 | ||
|  | 375a5323d5 | ||
|  | 31b69147f4 | ||
|  | 8e3011807d | ||
|  | ec7c6ab96c | ||
|  | 8a4097a366 | ||
|  | 792a736e48 | ||
|  | cce0a02ebb | ||
|  | 2ddab4eecc | ||
|  | f66755cbf1 | ||
|  | 257e60a2b1 | ||
|  | 75a3566760 | ||
|  | 7a9f17e059 | ||
|  | abbfe7200a | ||
|  | 419942112b | ||
|  | 597d4a0426 | ||
|  | e023d60be7 | ||
|  | bc5010a953 | ||
|  | 41a7b42037 | ||
|  | 2936865c55 | ||
|  | ff2bf1f3c1 | ||
|  | 1bccbd4173 | ||
|  | d7f00df391 | ||
|  | 22f88c59c7 | ||
|  | 8721776839 | ||
|  | a89da0dac0 | ||
|  | e4b4dc4ae9 | ||
|  | b26c44b2b9 | ||
|  | 68095417b9 | ||
|  | b6344eb6e8 | ||
|  | 224302cfef | ||
|  | abc4816888 | ||
|  | 21e14bd644 | ||
|  | a89caccd32 | ||
|  | 03dc3e52b7 | ||
|  | f04be8efa6 | ||
|  | 2c32f6bcb3 | ||
|  | a3a08ff5c7 | ||
|  | ea51186767 | ||
|  | 49494c572b | ||
|  | fcac3fa164 | ||
|  | 9c1153ef37 | ||
|  | 0adc4b33ef | ||
|  | c0f3215340 | ||
|  | bab1e6a95f | ||
|  | 53b26a43c0 | ||
|  | 2240d019f5 | ||
|  | cb11c6b3ea | ||
|  | 5893559951 | ||
|  | 8408d25cef | ||
|  | 1ac2ffcf02 | ||
|  | 6b6c38c2c8 | ||
|  | e55df73a91 | ||
|  | 360c2cbfa3 | ||
|  | aba96674f3 | ||
|  | 5c3d85fc90 | ||
|  | 6486b7fd4c | ||
|  | 5f3e980de0 | ||
|  | d0edbec5fb | ||
|  | 5d46963e8a | ||
|  | 321f441b63 | ||
|  | d55bade070 | ||
|  | 6ba6b821f5 | ||
|  | b3dedae115 | ||
|  | 5a1070c30f | ||
|  | 40664997e1 | ||
|  | c6e83cb7c0 | ||
|  | e7e27e794c | ||
|  | 1073dbe6ab | ||
|  | 2bd9b5a015 | ||
|  | bc09febd2c | ||
|  | b2a87c90a2 | ||
|  | d6dbbcb0de | ||
|  | 9ccb5360b3 | ||
|  | 0187c4faff | ||
|  | 605172a0bc | ||
|  | 8565a0d911 | ||
|  | 61c8d23a7e | ||
|  | 5e3487ed59 | ||
|  | d5a161769c | ||
|  | 1692f9c2dd | ||
|  | 0cbac8bb44 | ||
|  | 35a81e7f11 | ||
|  | ac64d293e7 | ||
|  | 708b8787c5 | ||
|  | 49947f3337 | ||
|  | 2bddd151eb | ||
|  | 43a585187c | ||
|  | 324658a36b | ||
|  | dd9a9b34d1 | ||
|  | 2ab0e40952 | ||
|  | dfea80ae96 | ||
|  | 6e38f5accf | ||
|  | 7c952d92bf | ||
|  | 2fae0d2d95 | ||
|  | 67ab63f00e | ||
|  | 719f9c28af | ||
|  | 035d621109 | ||
|  | 791f3b896d | ||
|  | fe2172a660 | ||
|  | 640fbd616b | ||
|  | 900efe8a36 | ||
|  | 5bd92d04d9 | ||
|  | b15684bcbd | ||
|  | a93222dbb2 | ||
|  | 20744e90a0 | ||
|  | 32777b4259 | ||
|  | 271120999c | ||
|  | 68fe13a67d | ||
|  | f3606014c6 | ||
|  | efbf4482b2 | ||
|  | 21a3b4f8e2 | ||
|  | de23b2d046 | ||
|  | bd8f436c1d | ||
|  | e963735dba | ||
|  | 46c981103d | ||
|  | f6d02d8fc6 | ||
|  | e08f691510 | ||
|  | af9199aaff | ||
|  | 8576b13f74 | ||
|  | 2270d8a795 | ||
|  | f4dcce6d6c | ||
|  | b802a410b9 | ||
|  | 9e3d339ec5 | ||
|  | fb97a98b97 | ||
|  | 72773f3bc8 | ||
|  | b0fd93e0c3 | ||
|  | 7aa2ec78f2 | ||
|  | 047e856a61 | ||
|  | dbe209e3f2 | ||
|  | e0d23ee6cf | ||
|  | 7a35f46370 | ||
|  | 4a4465efb6 | ||
|  | 3a112531cc | ||
|  | 456209dded | ||
|  | 2556b0d157 | ||
|  | 1e8903fd76 | ||
|  | ad9f18c231 | ||
|  | 63e3de00cb | ||
|  | d06ffeeede | ||
|  | 3479fb9d94 | ||
|  | 304bd002ae | ||
|  | 5dad18c85f | ||
|  | 19e4c0657a | ||
|  | 44548fdc33 | ||
|  | d8929074b5 | ||
|  | e11532ae92 | ||
|  | eff48acdf4 | ||
|  | a9850f9641 | ||
|  | aab0b8a3ce | ||
|  | b12e062d94 | ||
|  | b36e342f15 | ||
|  | f686816c86 | ||
|  | dc50e54afc | ||
|  | 3897e3d452 | ||
|  | 2557b03b11 | ||
|  | 29d29a337f | ||
|  | d3ce4af541 | ||
|  | 34f8e5e28d | ||
|  | afd1a68c62 | ||
|  | dbcf1cb907 | ||
|  | 9ca64f9789 | ||
|  | d45f47d908 | ||
|  | 4c247ac49d | ||
|  | e8a406526b | ||
|  | 7fcea16c6b | ||
|  | 028b799d2c | ||
|  | 3485296e23 | ||
|  | 03078cdd45 | ||
|  | 740310800d | ||
|  | 6d7c558482 | ||
|  | fdb10515c3 | ||
|  | 5a0f13c485 | ||
|  | 80b330ad7b | ||
|  | d929e1c134 | ||
|  | 5e40dcdc38 | ||
|  | 1dd3e2a83b | ||
|  | a62742fad9 | ||
|  | 1f9c45b11c | ||
|  | 68bec5e158 | ||
|  | 37f1bd7d63 | ||
|  | 5ba24211e2 | ||
|  | d699647418 | ||
|  | b246502cb6 | ||
|  | 9b33ead8aa | ||
|  | 32e8c1dc6d | ||
|  | e09ef7862e | ||
|  | 85420304d0 | ||
|  | 1c097a669d | ||
|  | 4e1497c5da | ||
|  | 49d426675f | ||
|  | dc6ac668b4 | ||
|  | 4ee24b0845 | ||
|  | ba20aef206 | ||
|  | 41ef6133c1 | ||
|  | 50bd5ee8f7 | ||
|  | 285f3fe330 | ||
|  | 4d01199986 | ||
|  | bcc0052fe0 | ||
|  | 4b592d81bd | ||
|  | 884e323288 | ||
|  | 78b799dd05 | ||
|  | 847fa2e700 | ||
|  | 481a79e311 | ||
|  | f19f2ff321 | ||
|  | 6dc4d7bb70 | ||
|  | 83460a34f4 | ||
|  | 2adbb47373 | ||
|  | 2159a5419a | ||
|  | 044d6a15d9 | ||
|  | b6055062c6 | ||
|  | 6fd85e043b | ||
|  | e07ac52356 | ||
|  | 0f16ba9325 | ||
|  | 378e6d28bc | ||
|  | 539d2b880c | ||
|  | 2982adbfa7 | ||
|  | 5147dff670 | ||
|  | 2cdf78c504 | ||
|  | cfad45b7c2 | ||
|  | 5234e9bce5 | ||
|  | 0ed5454d02 | ||
|  | 03080973be | ||
|  | a4f51b0cb3 | ||
|  | 749079c1c3 | ||
|  | ae10ff42e1 | ||
|  | d4cbdab4a3 | ||
|  | 1bd6392a4c | ||
|  | 1531e99528 | ||
|  | 6e7af18494 | ||
|  | e1bae65aeb | ||
|  | 65bfdf94c9 | ||
|  | 9a92825954 | ||
|  | 469faf509b | ||
|  | f87d4a5ab6 | ||
|  | 9c31b749d7 | ||
|  | 521d5df064 | ||
|  | 4d330fba8a | ||
|  | 2e04a55d5c | ||
|  | 0d9d0aa18b | ||
|  | f8dd1795bc | ||
|  | 1d7007584c | ||
|  | 8cd9f891fb | ||
|  | 6ab0f1db57 | ||
|  | 37d754d069 | ||
|  | e12b194d41 | ||
|  | 07d7fa26fe | ||
|  | 73b9b87ef3 | ||
|  | 0c0091375c | ||
|  | 21a29ed3a5 | ||
|  | a6312f4279 | ||
|  | f459abdf85 | ||
|  | 586fa1d0f0 | ||
|  | bf4192a1f0 | ||
|  | ac31eedf65 | ||
|  | b05dc5141c | ||
|  | 32c6fb14dd | ||
|  | 982c940381 | ||
|  | a7a8aaa887 | ||
|  | bf83a9980e | ||
|  | 11be603ed3 | ||
|  | a432cf8405 | ||
|  | 9dd6b3b72d | ||
|  | faca62b55f | ||
|  | a339de89f5 | ||
|  | e40c90e9c0 | ||
|  | 3f447bb8a7 | ||
|  | 21dca3fbf8 | ||
|  | 1078bb4287 | ||
|  | daeed06e70 | ||
|  | 1206e2d75f | ||
|  | cc81239b9d | ||
|  | e797c01761 | ||
|  | 12f7366968 | ||
|  | b7fd7abe85 | ||
|  | a5e1f3d165 | ||
|  | 212d047ada | ||
|  | 2e16127fde | ||
|  | e8b53a619d | ||
|  | df1ca1fd96 | ||
|  | 8f85132d48 | ||
|  | 349144599c | ||
|  | 979093923b | ||
|  | 137f8ad4cb | ||
|  | c1f462b8f8 | ||
|  | 6701c4c371 | ||
|  | fc6e459c09 | ||
|  | 9e28b3447e | ||
|  | 5c737e1969 | ||
|  | 569765e77e | ||
|  | bc0d63ed12 | ||
|  | 02f9893522 | ||
|  | b4bbe63f0f | ||
|  | fabbcac99f | ||
|  | b1b5ab6949 | ||
|  | 4b9487183b | ||
|  | de5a817953 | ||
|  | 4970f640fa | ||
|  | 18996535b7 | ||
|  | 2a1e31b5e9 | ||
|  | 8ca9a0f409 | ||
|  | fcc89a67ba | ||
|  | 1f377d43c5 | ||
|  | 30d6c68908 | ||
|  | dc781da93a | ||
|  | 36c20e4348 | ||
|  | 4466950bb8 | ||
|  | be29828454 | ||
|  | 7bab245073 | ||
|  | f5dcf0b760 | ||
|  | 8141f78a92 | ||
|  | be244b3d00 | ||
|  | 805f5ff9b6 | ||
|  | 76daeb7e55 | ||
|  | 9594c8106e | ||
|  | ed4052365c | ||
|  | 377ebadc10 | ||
|  | ed4809b71e | ||
|  | db37dffdbb | ||
|  | 13cab7e301 | ||
|  | 0a50fc66e5 | ||
|  | a3d1a3566d | ||
|  | ba0be927ed | ||
|  | 4260606267 | ||
|  | 4665db4f27 | ||
|  | 43503ba085 | ||
|  | 0a83a704f1 | ||
|  | 08de941c90 | ||
|  | 62228ef144 | ||
|  | 9731257782 | ||
|  | 4ec9c9c16e | ||
|  | 45436731e2 | ||
|  | 27730e65e7 | ||
|  | a4aba93d57 | ||
|  | d93db16963 | ||
|  | c327fe11b8 | ||
|  | 4fbc31d0b0 | ||
|  | 9a4a1cb4ec | ||
|  | 202d6957bc | ||
|  | 14fcff7774 | ||
|  | 2c9aa1cab4 | ||
|  | 7745c10d07 | ||
|  | c1d571de42 | ||
|  | cecb66451c | ||
|  | 0ef3421fa2 | ||
|  | f88e238d41 | ||
|  | ce3c8f264d | ||
|  | 9fbd594f37 | ||
|  | 5ad95cad90 | ||
|  | 7e507b40c4 | ||
|  | 446a9b5c02 | ||
|  | e02e61384e | ||
|  | 5deb570fdf | ||
|  | 915c46f144 | ||
|  | 30d6c5eaf3 | ||
|  | 6e50d1166a | ||
|  | 0e3eed0563 | ||
|  | 1b1676cecc | ||
|  | d911fe6a0b | ||
|  | 22253a3385 | ||
|  | 38640c99e3 | ||
|  | d6df8bddea | ||
|  | ddfc4bd98e | ||
|  | 3d6674325c | ||
|  | 194829f5b1 | ||
|  | 11a77253f4 | ||
|  | 67be2343f8 | ||
|  | e9b1b3d853 | ||
|  | 8a33d174d7 | ||
|  | 226d6216b7 | ||
|  | 1925bb01be | ||
|  | 82a4806e01 | ||
|  | ce419fae7b | ||
|  | c68b76e2da | ||
|  | 342020b420 | ||
|  | 1e6e99e3c7 | ||
|  | 2e9aafc377 | ||
|  | 299c863f49 | ||
|  | c2792a28ba | ||
|  | 635a027a8e | ||
|  | a45b8ca8e7 | ||
|  | 1e6e945a07 | ||
|  | 8666e6baae | ||
|  | f71157c24d | ||
|  | e87a2b36cf | ||
|  | 5418474f64 | ||
|  | 8836ba6ceb | ||
|  | 509c5b497a | ||
|  | e00bcc9f48 | ||
|  | bdef9fd040 | ||
|  | c956491ec5 | ||
|  | 68bc549d6a | ||
|  | 9c64eafc21 | ||
|  | b05e86d442 | ||
|  | fe5f9576c6 | ||
|  | 1b282b65b7 | ||
|  | e49664bad3 | ||
|  | 2db9f33c41 | ||
|  | 2a30b55a43 | ||
|  | 9d0b20adce | ||
|  | acd5e1c081 | ||
|  | cc1c5e45b2 | ||
|  | 038199c447 | ||
|  | 8a1eab7ceb | ||
|  | bc5bd35448 | ||
|  | 1795fd56b7 | ||
|  | 3d788d6056 | ||
|  | 4a7c33edad | ||
|  | 797f60d725 | ||
|  | 2427d68aa1 | ||
|  | 00c6b0f8ed | ||
|  | 7560f98d70 | ||
|  | 7b8d4ab3d6 | ||
|  | 07a1a805f6 | ||
|  | d8bab6aba9 | ||
|  | a930e2dc75 | ||
|  | 2eb35668fa | ||
|  | 07f4e5ac5c | ||
|  | 1533c22d5c | ||
|  | db82a90414 | ||
|  | 51a693badf | ||
|  | 2aa8f5b352 | ||
|  | 93b3b8f985 | ||
|  | 92c8bd80b5 | ||
|  | 528af0157d | ||
|  | 10a77b6278 | ||
|  | 03bbf6a582 | ||
|  | 63fcb649d2 | ||
|  | 4f60a92b92 | ||
|  | 0419c1a41f | ||
|  | 2d5ae78521 | ||
|  | 959134df02 | ||
|  | cf03f103ab | ||
|  | a9f9fc4ce2 | ||
|  | cfb370a3c8 | ||
|  | 353435c8d5 | ||
|  | c8c85d096b | ||
|  | 19c9c8f227 | ||
|  | 6ea2a29eea | ||
|  | 59f3f819a6 | ||
|  | 93e8f52880 | ||
|  | 02810efcc4 | ||
|  | 4a8a7c997e | ||
|  | 4b9be7ce16 | ||
|  | f3ec09e480 | ||
|  | 8291a84e3e | ||
|  | b0e1f0f73a | ||
|  | a66b966e7d | ||
|  | 5f56040c64 | ||
|  | eaccd22267 | ||
|  | 27845a7345 | ||
|  | f7ef8180e4 | ||
|  | 5958eb9a55 | ||
|  | 3ef2912b60 | ||
|  | fa9c6a765a | ||
|  | c4a8899780 | ||
|  | 3cc4628d03 | ||
|  | b6c5223221 | ||
|  | cbd6d4251c | ||
|  | fdcbb5b432 | ||
|  | de09e31815 | ||
|  | f55e911313 | ||
|  | 465a91dbf3 | ||
|  | 835a7833ae | ||
|  | 179717d40c | ||
|  | 3d4d789f7f | ||
|  | d97fb19f05 | ||
|  | 0dd3757df2 | ||
|  | c32a4546f3 | ||
|  | 1bb025ccd0 | ||
|  | 2b8033a97f | ||
|  | 21a3a8c594 | ||
|  | 1026e90296 | ||
|  | 0eca602e61 | ||
|  | 7f75ca81f1 | ||
|  | 8af05e2726 | ||
|  | 0a478ee1da | ||
|  | a4bdc5a05f | ||
|  | d425767dae | ||
|  | c78382c119 | ||
|  | ee15ddfbc3 | ||
|  | 0af14eb77e | ||
|  | 583cc4bc8a | ||
|  | 2ee92f48e6 | ||
|  | d05e02ab3e | ||
|  | abb9f8e233 | ||
|  | f873ef9b59 | ||
|  | 1255b56522 | ||
|  | fd9bb4d8cc | ||
|  | 9328576b55 | ||
|  | 70a1edd1dd | ||
|  | 87e4c209f4 | ||
|  | 3d0a5642cc | ||
|  | e211d812ad | ||
|  | 0dcf673b87 | ||
|  | cb14e1f20c | ||
|  | 52087c0e30 | ||
|  | 1b9286db76 | ||
|  | bc92c0b052 | ||
|  | 245bb639f2 | ||
|  | 8d81ed58c8 | ||
|  | 7890ca85a8 | ||
|  | 07bab7b264 | ||
|  | 5730c14dc1 | ||
|  | f8e8b5ad18 | ||
|  | fd2728c02c | ||
|  | 7e2bf920e1 | ||
|  | 1f65328f2d | ||
|  | 4f731baa00 | ||
|  | 5abb3dd8c1 | ||
|  | 0a672c55c5 | ||
|  | a6b2299c74 | ||
|  | 37cc6709d4 | ||
|  | f4ffbe67e2 | ||
|  | 9f32d72a41 | ||
|  | 64a117d8ac | ||
|  | ebf0bdc840 | ||
|  | cc0a120bf6 | ||
|  | fe2fe7468f | ||
|  | b12a10ccb5 | ||
|  | 2ad2a4b198 | ||
|  | 6a62f05657 | ||
|  | 4910f60ec4 | ||
|  | d35168e88f | ||
|  | 01b3d2aca9 | ||
|  | 29e8d1cff0 | ||
|  | 4e1d10cc08 | ||
|  | 3575d94ca1 | ||
|  | d91546b532 | ||
|  | 9f554f4917 | ||
|  | d4720a9244 | ||
|  | 5c466712db | ||
|  | 6dc7e852ae | ||
|  | 785f614bd9 | ||
|  | 0a8e27249d | ||
|  | 15ee87ee67 | ||
|  | 12612a16df | ||
|  | 4f449e2600 | ||
|  | 7f49f039fd | ||
|  | 88dc65bc4e | ||
|  | 6edebe18ad | ||
|  | 38b3a9205d | ||
|  | 4b796b4929 | ||
|  | 83cabcac28 | ||
|  | d308c5d9b9 | ||
|  | 9f032a61a9 | ||
|  | 0f58214ba1 | ||
|  | c48a60cce6 | ||
|  | cd3ffceeff | ||
|  | 9be4a00169 | ||
|  | a9c7a39a47 | ||
|  | abcdd60a21 | ||
|  | a94f85a100 | ||
|  | 9755bf723f | ||
|  | a71ebcf47e | ||
|  | 72695631cd | ||
|  | 2af211b543 | ||
|  | 6e5e2625d6 | ||
|  | 23c1c2f5eb | ||
|  | da85ee5d01 | ||
|  | 9612bc78fe | ||
|  | d408e8653c | ||
|  | cc76ccc3c9 | ||
|  | 105a00d3e4 | ||
|  | 2c08cba8cc | ||
|  | 344b11a204 | ||
|  | 1ff5bf0fd5 | ||
|  | 2b86137388 | ||
|  | c29cf7f77c | ||
|  | 193cb46d60 | ||
|  | 9dc864d486 | ||
|  | cee166839a | ||
|  | 1a60a3c728 | ||
|  | 5d946778cb | ||
|  | ac5f85820f | ||
|  | 716e100a28 | ||
|  | 7b8cb16c12 | ||
|  | 00d46424a3 | ||
|  | 2a5f940744 | ||
|  | 13cc016b36 | ||
|  | a8d49c27c8 | ||
|  | 8fdbe447c1 | ||
|  | a8522e91b5 | ||
|  | 5754f4463d | ||
|  | d4118ade0f | ||
|  | 6d80f15a98 | ||
|  | f8aa472409 | ||
|  | df22fd00ca | ||
|  | ce2743a982 | ||
|  | 92b32458ad | ||
|  | d57e8a45d3 | ||
|  | 551d3ffdf3 | ||
|  | 7add6eb736 | ||
|  | a28616d535 | ||
|  | a288fd370f | ||
|  | acd335e249 | ||
|  | 764ae7e0b6 | ||
|  | da0bfa1945 | ||
|  | 3c61d709b5 | ||
|  | ffc92a7b63 | ||
|  | af0c7b5a50 | ||
|  | 1904c4d057 | ||
|  | 542f169b36 | ||
|  | 65a30bf60c | ||
|  | 2e51da32f0 | ||
|  | 0562242043 | ||
|  | debcdefc21 | ||
|  | 6b7e78320d | ||
|  | 0de3f3a332 | ||
|  | 4fcb4d449e | ||
|  | 408fe25abd | ||
|  | 236e5e0b25 | ||
|  | ebe0caba83 | ||
|  | 9d33c0cfaf | ||
|  | 7962130a0c | ||
|  | 9690434cac | ||
|  | 7304544c37 | ||
|  | 5a3408c242 | ||
|  | 16996f25af | ||
|  | 0c12586019 | ||
|  | 93a1adaa56 | ||
|  | 83e65e2cc6 | ||
|  | 36586b798e | ||
|  | 20c351949f | ||
|  | b63bd92d81 | ||
|  | 9f3bb7f4d6 | ||
|  | 73bb346c00 | ||
|  | 33703a3b53 | ||
|  | b7a4f97eca | ||
|  | dd4efe0f51 | ||
|  | 7e0522c3b3 | ||
|  | e682abfb75 | ||
|  | 24e202a3d7 | ||
|  | ac9a881ab5 | ||
|  | 4d287a1f83 | ||
|  | b8d6b1ebdd | ||
|  | 8ca1b9320d | ||
|  | cba3992d2b | ||
|  | 96d6e337be | ||
|  | 959f7ae046 | ||
|  | 9572a58764 | ||
|  | 393ae9e5dc | ||
|  | 63e10314bd | ||
|  | b599417a37 | ||
|  | 899eab4e5c | ||
|  | 3f21c87a3d | ||
|  | c296a60bab | ||
|  | 5f78f18cb4 | ||
|  | 0b8d356865 | ||
|  | e8d1318a5b | ||
|  | 07ce07c4a5 | ||
|  | a07220f383 | ||
|  | f21ed24a49 | ||
|  | e3c38b93f4 | ||
|  | b398727413 | ||
|  | 9bc2ab29a1 | ||
|  | 51f1ff26f1 | ||
|  | 97d5e6512d | ||
|  | b76c67fc9b | ||
|  | b96a70cd55 | ||
|  | 982ab93cdb | ||
|  | c7f4e1152d | ||
|  | 519988326b | ||
|  | b518f4b03c | ||
|  | 5493fdfcb7 | ||
|  | 179767e9f8 | ||
|  | 25b3bb1285 | ||
|  | 841c8ab1f1 | ||
|  | 1ce17e2847 | ||
|  | a09b206b0e | ||
|  | bb4617c53b | ||
|  | cfd18bfb74 | ||
|  | e225d6f546 | ||
|  | 60fe48d355 | ||
|  | 2dcd0d2b0a | ||
|  | 8e11aa9130 | ||
|  | f6e223c18d | ||
|  | 9d29b55bee | ||
|  | 92aa8580db | ||
|  | 538028a003 | ||
|  | c53575a74f | ||
|  | 193016a46a | ||
|  | aaa50b4d1d | ||
|  | a43120320e | ||
|  | b8bb0c038d | ||
|  | dc79fc2919 | ||
|  | 30787fef60 | ||
|  | 445ae156ef | ||
|  | 62a0cfb0f6 | ||
|  | 96bc3ef99a | ||
|  | 1d3b95d24f | ||
|  | 56fe4b07f3 | ||
|  | ea60f7005b | ||
|  | 9eb59062aa | ||
|  | d00927c31f | ||
|  | c03017208d | ||
|  | 73f945458a | ||
|  | db12234611 | ||
|  | ed1cd4632f | ||
|  | 17d3755152 | ||
|  | d7c0c2ea72 | ||
|  | 7c823c98ae | ||
|  | 97508a6f31 | ||
|  | 2507a41b6e | ||
|  | 9833accc79 | ||
|  | d46123771a | ||
|  | 87fe84b1ac | ||
|  | 21140f437e | ||
|  | ba9e410393 | ||
|  | 9b628546c1 | ||
|  | d0837fada8 | ||
|  | 520647d72f | ||
|  | 51c888845c | ||
|  | e4606219bc | ||
|  | 716335df2c | ||
|  | ad4f90c502 | ||
|  | a1bdfa7560 | ||
|  | 587fb2a170 | ||
|  | 7d801ff84c | ||
|  | d69accd9a5 | ||
|  | 1127750c5e | ||
|  | 7758bd89c1 | ||
|  | de7264327a | ||
|  | c3f0932794 | ||
|  | 367907e037 | ||
|  | 2d15bd651e | ||
|  | 4b1d7863f8 | ||
|  | e425d768dd | ||
|  | 34ca807044 | ||
|  | 9075146b47 | ||
|  | 26c4591baa | ||
|  | 2aac8c55e7 | 
							
								
								
									
										123
									
								
								.eslintrc.json
									
									
									
									
									
								
							
							
						
						
									
										123
									
								
								.eslintrc.json
									
									
									
									
									
								
							| @@ -1,11 +1,11 @@ | |||||||
| { | { | ||||||
|   "extends": [ |   "extends": [ | ||||||
|  |     "airbnb-base", | ||||||
|     "airbnb-typescript/base", |     "airbnb-typescript/base", | ||||||
|     "plugin:@typescript-eslint/recommended", |     "plugin:@typescript-eslint/recommended", | ||||||
|     "plugin:wc/recommended", |     "plugin:wc/recommended", | ||||||
|     "plugin:lit/recommended", |     "plugin:lit/all", | ||||||
|     "prettier", |     "prettier" | ||||||
|     "prettier/@typescript-eslint" |  | ||||||
|   ], |   ], | ||||||
|   "parser": "@typescript-eslint/parser", |   "parser": "@typescript-eslint/parser", | ||||||
|   "parserOptions": { |   "parserOptions": { | ||||||
| @@ -29,64 +29,91 @@ | |||||||
|     "__BUILD__": false, |     "__BUILD__": false, | ||||||
|     "__VERSION__": false, |     "__VERSION__": false, | ||||||
|     "__STATIC_PATH__": false, |     "__STATIC_PATH__": false, | ||||||
|     "Polymer": true, |     "__SUPERVISOR__": false, | ||||||
|     "webkitSpeechRecognition": false, |     "Polymer": true | ||||||
|     "ResizeObserver": false |  | ||||||
|   }, |   }, | ||||||
|   "env": { |   "env": { | ||||||
|     "browser": true, |     "browser": true, | ||||||
|     "es6": true |     "es6": true | ||||||
|   }, |   }, | ||||||
|   "rules": { |   "rules": { | ||||||
|     "class-methods-use-this": 0, |     "class-methods-use-this": "off", | ||||||
|     "new-cap": 0, |     "new-cap": "off", | ||||||
|     "prefer-template": 0, |     "prefer-template": "off", | ||||||
|     "object-shorthand": 0, |     "object-shorthand": "off", | ||||||
|     "func-names": 0, |     "func-names": "off", | ||||||
|     "prefer-arrow-callback": 0, |     "no-underscore-dangle": "off", | ||||||
|     "no-underscore-dangle": 0, |     "strict": "off", | ||||||
|     "strict": 0, |     "no-plusplus": "off", | ||||||
|     "prefer-spread": 0, |     "no-bitwise": "error", | ||||||
|     "no-plusplus": 0, |     "comma-dangle": "off", | ||||||
|     "no-bitwise": 2, |     "vars-on-top": "off", | ||||||
|     "comma-dangle": 0, |     "no-continue": "off", | ||||||
|     "vars-on-top": 0, |     "no-param-reassign": "off", | ||||||
|     "no-continue": 0, |     "no-multi-assign": "off", | ||||||
|     "no-param-reassign": 0, |     "no-console": "error", | ||||||
|     "no-multi-assign": 0, |     "radix": "off", | ||||||
|     "no-console": 2, |     "no-alert": "off", | ||||||
|     "radix": 0, |     "no-nested-ternary": "off", | ||||||
|     "no-alert": 0, |     "prefer-destructuring": "off", | ||||||
|     "no-return-await": 0, |  | ||||||
|     "no-nested-ternary": 0, |  | ||||||
|     "prefer-destructuring": 0, |  | ||||||
|     "no-restricted-globals": [2, "event"], |     "no-restricted-globals": [2, "event"], | ||||||
|     "prefer-promise-reject-errors": 0, |     "prefer-promise-reject-errors": "off", | ||||||
|     "import/order": 0, |     "import/prefer-default-export": "off", | ||||||
|     "import/prefer-default-export": 0, |     "import/no-default-export": "off", | ||||||
|     "import/no-unresolved": 0, |     "import/no-unresolved": "off", | ||||||
|     "import/no-cycle": 0, |     "import/no-cycle": "off", | ||||||
|     "import/extensions": [ |     "import/extensions": [ | ||||||
|       2, |       "error", | ||||||
|       "ignorePackages", |       "ignorePackages", | ||||||
|       { "ts": "never", "js": "never" } |       { "ts": "never", "js": "never" } | ||||||
|     ], |     ], | ||||||
|     "no-restricted-syntax": ["error", "LabeledStatement", "WithStatement"], |     "no-restricted-syntax": ["error", "LabeledStatement", "WithStatement"], | ||||||
|     "object-curly-newline": 0, |     "object-curly-newline": "off", | ||||||
|     "default-case": 0, |     "default-case": "off", | ||||||
|     "wc/no-self-class": 0, |     "wc/no-self-class": "off", | ||||||
|     "no-shadow": 0, |     "no-shadow": "off", | ||||||
|     "@typescript-eslint/camelcase": 0, |     "@typescript-eslint/camelcase": "off", | ||||||
|     "@typescript-eslint/ban-ts-comment": 0, |     "@typescript-eslint/ban-ts-comment": "off", | ||||||
|     "@typescript-eslint/no-use-before-define": 0, |     "@typescript-eslint/no-use-before-define": "off", | ||||||
|     "@typescript-eslint/no-non-null-assertion": 0, |     "@typescript-eslint/no-non-null-assertion": "off", | ||||||
|     "@typescript-eslint/no-explicit-any": 0, |     "@typescript-eslint/no-explicit-any": "off", | ||||||
|     "@typescript-eslint/no-unused-vars": 0, |     "@typescript-eslint/explicit-function-return-type": "off", | ||||||
|     "@typescript-eslint/explicit-function-return-type": 0, |     "@typescript-eslint/explicit-module-boundary-types": "off", | ||||||
|     "@typescript-eslint/explicit-module-boundary-types": 0, |  | ||||||
|     "@typescript-eslint/no-shadow": ["error"], |     "@typescript-eslint/no-shadow": ["error"], | ||||||
|     "lit/attribute-value-entities": 0 |     "@typescript-eslint/naming-convention": [ | ||||||
|  |       "off", | ||||||
|  |       { | ||||||
|  |         "selector": "default", | ||||||
|  |         "format": ["camelCase", "snake_case"], | ||||||
|  |         "leadingUnderscore": "allow", | ||||||
|  |         "trailingUnderscore": "allow" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "selector": ["variable"], | ||||||
|  |         "format": ["camelCase", "snake_case", "UPPER_CASE"], | ||||||
|  |         "leadingUnderscore": "allow", | ||||||
|  |         "trailingUnderscore": "allow" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "selector": "typeLike", | ||||||
|  |         "format": ["PascalCase"] | ||||||
|  |       } | ||||||
|  |     ], | ||||||
|  |     "@typescript-eslint/no-unused-vars": "off", | ||||||
|  |     "unused-imports/no-unused-vars": [ | ||||||
|  |       "error", | ||||||
|  |       { | ||||||
|  |         "vars": "all", | ||||||
|  |         "varsIgnorePattern": "^_", | ||||||
|  |         "args": "after-used", | ||||||
|  |         "argsIgnorePattern": "^_", | ||||||
|  |         "ignoreRestSiblings": true | ||||||
|  |       } | ||||||
|  |     ], | ||||||
|  |     "unused-imports/no-unused-imports": "error", | ||||||
|  |     "lit/attribute-value-entities": "off", | ||||||
|  |     "lit/no-template-map": "off" | ||||||
|   }, |   }, | ||||||
|   "plugins": ["disable", "import", "lit", "prettier", "@typescript-eslint"], |   "plugins": ["disable", "unused-imports"], | ||||||
|   "processor": "disable/disable" |   "processor": "disable/disable" | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										35
									
								
								.github/ISSUE_TEMPLATE/bug_report.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										35
									
								
								.github/ISSUE_TEMPLATE/bug_report.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,8 +1,6 @@ | |||||||
| name: Report a bug with the UI, Frontend or Lovelace | name: Report a bug with the UI, Frontend or Lovelace | ||||||
| about: Report an issue related to the Home Assistant frontend. | description: Report an issue related to the Home Assistant frontend. | ||||||
| labels: bug | labels: bug | ||||||
| title: "" |  | ||||||
| issue_body: true |  | ||||||
| body: | body: | ||||||
|   - type: markdown |   - type: markdown | ||||||
|     attributes: |     attributes: | ||||||
| @@ -97,11 +95,7 @@ body: | |||||||
|         If your issue is about how an entity is shown in the UI, please add the |         If your issue is about how an entity is shown in the UI, please add the | ||||||
|         state and attributes for all situations. You can find this information |         state and attributes for all situations. You can find this information | ||||||
|         at Developer Tools -> States. |         at Developer Tools -> States. | ||||||
|       value: | |       render: txt | ||||||
|         ```yaml |  | ||||||
|         # Paste your state here. |  | ||||||
|  |  | ||||||
|         ``` |  | ||||||
|   - type: textarea |   - type: textarea | ||||||
|     attributes: |     attributes: | ||||||
|       label: Problem-relevant frontend configuration |       label: Problem-relevant frontend configuration | ||||||
| @@ -110,29 +104,18 @@ body: | |||||||
|         configuration of the used cards. Fill this out even if it seems |         configuration of the used cards. Fill this out even if it seems | ||||||
|         unimportant to you. Please be sure to remove personal information like |         unimportant to you. Please be sure to remove personal information like | ||||||
|         passwords, private URLs and other credentials. |         passwords, private URLs and other credentials. | ||||||
|       value: | |       render: yaml | ||||||
|         ```yaml |  | ||||||
|         # Paste your YAML here. |  | ||||||
|  |  | ||||||
|         ``` |  | ||||||
|   - type: textarea |   - type: textarea | ||||||
|     attributes: |     attributes: | ||||||
|       label: Javascript errors shown in your browser console/inspector |       label: Javascript errors shown in your browser console/inspector | ||||||
|       description: > |       description: > | ||||||
|         If you come across any Javascript or other error logs, e.g., in your |         If you come across any Javascript or other error logs, e.g., in your | ||||||
|         browser console/inspector please provide them. |         browser console/inspector please provide them. | ||||||
|       value: | |       render: txt | ||||||
|         ```txt |   - type: textarea | ||||||
|         # Paste your logs here. |  | ||||||
|  |  | ||||||
|         ``` |  | ||||||
|   - type: markdown |  | ||||||
|     attributes: |     attributes: | ||||||
|       value: | |       label: Additional information | ||||||
|         ## Additional information |       description: > | ||||||
|   - type: markdown |  | ||||||
|     attributes: |  | ||||||
|       value: | |  | ||||||
|         If you have any additional information for us, use the field below. |         If you have any additional information for us, use the field below. | ||||||
|         Please note, you can attach screenshots or screen recordings here, |         Please note, you can attach screenshots or screen recordings here, by | ||||||
|         by dragging and dropping files in the field below. |         dragging and dropping files in the field below. | ||||||
|   | |||||||
							
								
								
									
										88
									
								
								.github/workflows/ci.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										88
									
								
								.github/workflows/ci.yaml
									
									
									
									
										vendored
									
									
								
							| @@ -10,81 +10,64 @@ on: | |||||||
|       - dev |       - dev | ||||||
|       - master |       - master | ||||||
|  |  | ||||||
|  | env: | ||||||
|  |   NODE_VERSION: 14 | ||||||
|  |   NODE_OPTIONS: --max_old_space_size=6144 | ||||||
|  |  | ||||||
| jobs: | jobs: | ||||||
|   lint: |   lint: | ||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|     steps: |     steps: | ||||||
|       - name: Check out files from GitHub |       - name: Check out files from GitHub | ||||||
|         uses: actions/checkout@v2 |         uses: actions/checkout@v2 | ||||||
|       - name: Setting up Node.js |       - name: Set up Node ${{ env.NODE_VERSION }} | ||||||
|         uses: actions/setup-node@v1 |         uses: actions/setup-node@v2 | ||||||
|         with: |         with: | ||||||
|           node-version: 12.x |           node-version: ${{ env.NODE_VERSION }} | ||||||
|       - name: Get yarn cache path |           cache: yarn | ||||||
|         id: yarn-cache-dir-path |  | ||||||
|         run: echo "::set-output name=dir::$(yarn cache dir)" |  | ||||||
|       - name: Fetching Yarn cache |  | ||||||
|         uses: actions/cache@v1 |  | ||||||
|         with: |  | ||||||
|           path: ${{ steps.yarn-cache-dir-path.outputs.dir }} |  | ||||||
|           key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} |  | ||||||
|           restore-keys: | |  | ||||||
|             ${{ runner.os }}-yarn- |  | ||||||
|       - name: Install dependencies |       - name: Install dependencies | ||||||
|         run: yarn install |         run: yarn install | ||||||
|         env: |         env: | ||||||
|           CI: true |           CI: true | ||||||
|       - name: Build resources |       - name: Build resources | ||||||
|         run: ./node_modules/.bin/gulp gen-icons-json build-translations gather-gallery-demos |         run: ./node_modules/.bin/gulp gen-icons-json build-translations build-locale-data gather-gallery-pages | ||||||
|       - name: Run eslint |       - name: Run eslint | ||||||
|         run: ./node_modules/.bin/eslint '{**/src,src}/**/*.{js,ts,html}' --ignore-path .gitignore |         run: yarn run lint:eslint | ||||||
|       - name: Run tsc |       - name: Run tsc | ||||||
|         run: ./node_modules/.bin/tsc |         run: yarn run lint:types | ||||||
|  |       - name: Run prettier | ||||||
|  |         run: yarn run lint:prettier | ||||||
|  |       - name: Check for duplicate dependencies | ||||||
|  |         run: yarn dedupe --check | ||||||
|   test: |   test: | ||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|     steps: |     steps: | ||||||
|       - name: Check out files from GitHub |       - name: Check out files from GitHub | ||||||
|         uses: actions/checkout@v2 |         uses: actions/checkout@v2 | ||||||
|       - name: Setting up Node.js |       - name: Set up Node ${{ env.NODE_VERSION }} | ||||||
|         uses: actions/setup-node@v1 |         uses: actions/setup-node@v2 | ||||||
|         with: |         with: | ||||||
|           node-version: 12.x |           node-version: ${{ env.NODE_VERSION }} | ||||||
|       - name: Get yarn cache path |           cache: yarn | ||||||
|         id: yarn-cache-dir-path |  | ||||||
|         run: echo "::set-output name=dir::$(yarn cache dir)" |  | ||||||
|       - name: Fetching Yarn cache |  | ||||||
|         uses: actions/cache@v1 |  | ||||||
|         with: |  | ||||||
|           path: ${{ steps.yarn-cache-dir-path.outputs.dir }} |  | ||||||
|           key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} |  | ||||||
|           restore-keys: | |  | ||||||
|             ${{ runner.os }}-yarn- |  | ||||||
|       - name: Install dependencies |       - name: Install dependencies | ||||||
|         run: yarn install |         run: yarn install | ||||||
|         env: |         env: | ||||||
|           CI: true |           CI: true | ||||||
|       - name: Run Mocha |       - name: Build resources | ||||||
|         run: npm run mocha |         run: ./node_modules/.bin/gulp build-translations build-locale-data | ||||||
|  |       - name: Run Tests | ||||||
|  |         run: yarn run test | ||||||
|   build: |   build: | ||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|     needs: [lint, test] |     needs: [lint, test] | ||||||
|     steps: |     steps: | ||||||
|       - name: Check out files from GitHub |       - name: Check out files from GitHub | ||||||
|         uses: actions/checkout@v2 |         uses: actions/checkout@v2 | ||||||
|       - name: Setting up Node.js |       - name: Set up Node ${{ env.NODE_VERSION }} | ||||||
|         uses: actions/setup-node@v1 |         uses: actions/setup-node@v2 | ||||||
|         with: |         with: | ||||||
|           node-version: 12.x |           node-version: ${{ env.NODE_VERSION }} | ||||||
|       - name: Get yarn cache path |           cache: yarn | ||||||
|         id: yarn-cache-dir-path |  | ||||||
|         run: echo "::set-output name=dir::$(yarn cache dir)" |  | ||||||
|       - name: Fetching Yarn cache |  | ||||||
|         uses: actions/cache@v1 |  | ||||||
|         with: |  | ||||||
|           path: ${{ steps.yarn-cache-dir-path.outputs.dir }} |  | ||||||
|           key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} |  | ||||||
|           restore-keys: | |  | ||||||
|             ${{ runner.os }}-yarn- |  | ||||||
|       - name: Install dependencies |       - name: Install dependencies | ||||||
|         run: yarn install |         run: yarn install | ||||||
|         env: |         env: | ||||||
| @@ -99,20 +82,11 @@ jobs: | |||||||
|     steps: |     steps: | ||||||
|       - name: Check out files from GitHub |       - name: Check out files from GitHub | ||||||
|         uses: actions/checkout@v2 |         uses: actions/checkout@v2 | ||||||
|       - name: Setting up Node.js |       - name: Set up Node ${{ env.NODE_VERSION }} | ||||||
|         uses: actions/setup-node@v1 |         uses: actions/setup-node@v2 | ||||||
|         with: |         with: | ||||||
|           node-version: 12.x |           node-version: ${{ env.NODE_VERSION }} | ||||||
|       - name: Get yarn cache path |           cache: yarn | ||||||
|         id: yarn-cache-dir-path |  | ||||||
|         run: echo "::set-output name=dir::$(yarn cache dir)" |  | ||||||
|       - name: Fetching Yarn cache |  | ||||||
|         uses: actions/cache@v1 |  | ||||||
|         with: |  | ||||||
|           path: ${{ steps.yarn-cache-dir-path.outputs.dir }} |  | ||||||
|           key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} |  | ||||||
|           restore-keys: | |  | ||||||
|             ${{ runner.os }}-yarn- |  | ||||||
|       - name: Install dependencies |       - name: Install dependencies | ||||||
|         run: yarn install |         run: yarn install | ||||||
|         env: |         env: | ||||||
|   | |||||||
							
								
								
									
										22
									
								
								.github/workflows/demo.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										22
									
								
								.github/workflows/demo.yaml
									
									
									
									
										vendored
									
									
								
							| @@ -4,26 +4,22 @@ on: | |||||||
|   push: |   push: | ||||||
|     branches: |     branches: | ||||||
|       - dev |       - dev | ||||||
|  |  | ||||||
|  | env: | ||||||
|  |   NODE_VERSION: 14 | ||||||
|  |   NODE_OPTIONS: --max_old_space_size=6144 | ||||||
|  |  | ||||||
| jobs: | jobs: | ||||||
|   deploy: |   deploy: | ||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|     steps: |     steps: | ||||||
|       - name: Check out files from GitHub |       - name: Check out files from GitHub | ||||||
|         uses: actions/checkout@v2 |         uses: actions/checkout@v2 | ||||||
|       - name: Setting up Node.js |       - name: Set up Node ${{ env.NODE_VERSION }} | ||||||
|         uses: actions/setup-node@v1 |         uses: actions/setup-node@v2 | ||||||
|         with: |         with: | ||||||
|           node-version: 12.x |           node-version: ${{ env.NODE_VERSION }} | ||||||
|       - name: Get yarn cache path |           cache: yarn | ||||||
|         id: yarn-cache-dir-path |  | ||||||
|         run: echo "::set-output name=dir::$(yarn cache dir)" |  | ||||||
|       - name: Fetching Yarn cache |  | ||||||
|         uses: actions/cache@v1 |  | ||||||
|         with: |  | ||||||
|           path: ${{ steps.yarn-cache-dir-path.outputs.dir }} |  | ||||||
|           key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} |  | ||||||
|           restore-keys: | |  | ||||||
|             ${{ runner.os }}-yarn- |  | ||||||
|       - name: Install dependencies |       - name: Install dependencies | ||||||
|         run: yarn install |         run: yarn install | ||||||
|         env: |         env: | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								.github/workflows/netflify.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/netflify.yml
									
									
									
									
										vendored
									
									
								
							| @@ -15,5 +15,5 @@ jobs: | |||||||
|       - name: Trigger Demo build |       - name: Trigger Demo build | ||||||
|         run: curl -X POST -d {} https://api.netlify.com/build_hooks/${{ secrets.NETLIFY_DEMO_DEV_BUILD_HOOK }} |         run: curl -X POST -d {} https://api.netlify.com/build_hooks/${{ secrets.NETLIFY_DEMO_DEV_BUILD_HOOK }} | ||||||
|  |  | ||||||
|       - name: Trigger Gallery build |       - name: Trigger Design build | ||||||
|         run: curl -X POST -d {} https://api.netlify.com/build_hooks/${{ secrets.NETLIFY_GALLERY_DEV_BUILD_HOOK }} |         run: curl -X POST -d "NIGHTLY" https://api.netlify.com/build_hooks/${{ secrets.NETLIFY_GALLERY_DEV_BUILD_HOOK }} | ||||||
|   | |||||||
							
								
								
									
										20
									
								
								.github/workflows/release.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										20
									
								
								.github/workflows/release.yaml
									
									
									
									
										vendored
									
									
								
							| @@ -6,9 +6,9 @@ on: | |||||||
|       - published |       - published | ||||||
|  |  | ||||||
| env: | env: | ||||||
|   WHEELS_TAG: 3.7-alpine3.11 |   PYTHON_VERSION: 3.8 | ||||||
|   PYTHON_VERSION: 3.7 |   NODE_VERSION: 14 | ||||||
|   NODE_VERSION: 12.1 |   NODE_OPTIONS: --max_old_space_size=6144 | ||||||
|  |  | ||||||
| jobs: | jobs: | ||||||
|   release: |   release: | ||||||
| @@ -30,10 +30,18 @@ jobs: | |||||||
|         uses: actions/setup-node@v2 |         uses: actions/setup-node@v2 | ||||||
|         with: |         with: | ||||||
|           node-version: ${{ env.NODE_VERSION }} |           node-version: ${{ env.NODE_VERSION }} | ||||||
|  |           cache: yarn | ||||||
|  |  | ||||||
|  |       - name: Install dependencies | ||||||
|  |         run: yarn install | ||||||
|  |  | ||||||
|  |       - name: Download Translations | ||||||
|  |         run: ./script/translations_download | ||||||
|  |         env: | ||||||
|  |           LOKALISE_TOKEN: ${{ secrets.LOKALISE_TOKEN }} | ||||||
|       - name: Build and release package |       - name: Build and release package | ||||||
|         run: | |         run: | | ||||||
|           python3 -m pip install twine |           python3 -m pip install twine build | ||||||
|           export TWINE_USERNAME="__token__" |           export TWINE_USERNAME="__token__" | ||||||
|           export TWINE_PASSWORD="${{ secrets.TWINE_TOKEN }}" |           export TWINE_PASSWORD="${{ secrets.TWINE_TOKEN }}" | ||||||
|  |  | ||||||
| @@ -64,6 +72,8 @@ jobs: | |||||||
|     strategy: |     strategy: | ||||||
|       matrix: |       matrix: | ||||||
|         arch: ["aarch64", "armhf", "armv7", "amd64", "i386"] |         arch: ["aarch64", "armhf", "armv7", "amd64", "i386"] | ||||||
|  |         tag: | ||||||
|  |           - "3.9-alpine3.14" | ||||||
|     steps: |     steps: | ||||||
|       - name: Download requirements.txt |       - name: Download requirements.txt | ||||||
|         uses: actions/download-artifact@v2 |         uses: actions/download-artifact@v2 | ||||||
| @@ -73,7 +83,7 @@ jobs: | |||||||
|       - name: Build wheels |       - name: Build wheels | ||||||
|         uses: home-assistant/wheels@master |         uses: home-assistant/wheels@master | ||||||
|         with: |         with: | ||||||
|           tag: ${{ env.WHEELS_TAG }} |           tag: ${{ matrix.tag }} | ||||||
|           arch: ${{ matrix.arch }} |           arch: ${{ matrix.arch }} | ||||||
|           wheels-host: ${{ secrets.WHEELS_HOST }} |           wheels-host: ${{ secrets.WHEELS_HOST }} | ||||||
|           wheels-key: ${{ secrets.WHEELS_KEY }} |           wheels-key: ${{ secrets.WHEELS_KEY }} | ||||||
|   | |||||||
							
								
								
									
										42
									
								
								.github/workflows/translations.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										42
									
								
								.github/workflows/translations.yaml
									
									
									
									
										vendored
									
									
								
							| @@ -1,8 +1,6 @@ | |||||||
| name: Translations | name: Translations | ||||||
|  |  | ||||||
| on: | on: | ||||||
|   schedule: |  | ||||||
|     - cron: "30 0 * * *" |  | ||||||
|   push: |   push: | ||||||
|     branches: |     branches: | ||||||
|       - dev |       - dev | ||||||
| @@ -10,7 +8,7 @@ on: | |||||||
|       - src/translations/en.json |       - src/translations/en.json | ||||||
|  |  | ||||||
| env: | env: | ||||||
|   NODE_VERSION: 12 |   NODE_VERSION: 14 | ||||||
|  |  | ||||||
| jobs: | jobs: | ||||||
|   upload: |   upload: | ||||||
| @@ -20,46 +18,8 @@ jobs: | |||||||
|       - name: Checkout the repository |       - name: Checkout the repository | ||||||
|         uses: actions/checkout@v2 |         uses: actions/checkout@v2 | ||||||
|  |  | ||||||
|       - name: Set up Node ${{ env.NODE_VERSION }} |  | ||||||
|         uses: actions/setup-node@v2 |  | ||||||
|         with: |  | ||||||
|           node-version: ${{ env.NODE_VERSION }} |  | ||||||
|  |  | ||||||
|       - name: Upload Translations |       - name: Upload Translations | ||||||
|         run: | |         run: | | ||||||
|           export LOKALISE_TOKEN="${{ secrets.LOKALISE_TOKEN }}" |           export LOKALISE_TOKEN="${{ secrets.LOKALISE_TOKEN }}" | ||||||
|  |  | ||||||
|           ./script/translations_upload_base |           ./script/translations_upload_base | ||||||
|  |  | ||||||
|   download: |  | ||||||
|     name: Download |  | ||||||
|     needs: upload |  | ||||||
|     if: github.event_name == 'schedule' |  | ||||||
|     runs-on: ubuntu-latest |  | ||||||
|     steps: |  | ||||||
|       - name: Checkout the repository |  | ||||||
|         uses: actions/checkout@v2 |  | ||||||
|  |  | ||||||
|       - name: Set up Node ${{ env.NODE_VERSION }} |  | ||||||
|         uses: actions/setup-node@v2 |  | ||||||
|         with: |  | ||||||
|           node-version: ${{ env.NODE_VERSION }} |  | ||||||
|  |  | ||||||
|       - name: Download Translations |  | ||||||
|         run: | |  | ||||||
|           export LOKALISE_TOKEN="${{ secrets.LOKALISE_TOKEN }}" |  | ||||||
|  |  | ||||||
|           npm install |  | ||||||
|           ./script/translations_download |  | ||||||
|  |  | ||||||
|       - name: Initialize git |  | ||||||
|         uses: home-assistant/actions/helpers/git-init@master |  | ||||||
|         with: |  | ||||||
|           name: GitHub Action |  | ||||||
|           email: github-action@users.noreply.github.com |  | ||||||
|  |  | ||||||
|       - name: Update translation |  | ||||||
|         run: | |  | ||||||
|           git add translations |  | ||||||
|           git commit -am "Translation update" |  | ||||||
|           git push |  | ||||||
|   | |||||||
							
								
								
									
										30
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										30
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,11 +1,23 @@ | |||||||
| build |  | ||||||
| build-translations/* |  | ||||||
| node_modules/* |  | ||||||
| npm-debug.log |  | ||||||
| .DS_Store | .DS_Store | ||||||
| hass_frontend/* |  | ||||||
| .reify-cache | .reify-cache | ||||||
|  |  | ||||||
|  | # build | ||||||
|  | build | ||||||
|  | hass_frontend/* | ||||||
|  | dist | ||||||
|  |  | ||||||
|  | # yarn | ||||||
|  | .yarn/* | ||||||
|  | !.yarn/patches | ||||||
|  | !.yarn/releases | ||||||
|  | !.yarn/plugins | ||||||
|  | !.yarn/sdks | ||||||
|  | !.yarn/versions | ||||||
|  | .pnp.* | ||||||
|  | node_modules/* | ||||||
|  | yarn-error.log | ||||||
|  | npm-debug.log | ||||||
|  |  | ||||||
| # Python stuff | # Python stuff | ||||||
| *.py[cod] | *.py[cod] | ||||||
| *.egg | *.egg | ||||||
| @@ -14,11 +26,8 @@ hass_frontend/* | |||||||
| # venv stuff | # venv stuff | ||||||
| pyvenv.cfg | pyvenv.cfg | ||||||
| pip-selfcheck.json | pip-selfcheck.json | ||||||
| venv | venv/* | ||||||
| .venv | .venv | ||||||
| lib |  | ||||||
| bin |  | ||||||
| dist |  | ||||||
|  |  | ||||||
| # vscode | # vscode | ||||||
| .vscode/* | .vscode/* | ||||||
| @@ -31,9 +40,8 @@ src/cast/dev_const.ts | |||||||
|  |  | ||||||
| # Secrets | # Secrets | ||||||
| .lokalise_token | .lokalise_token | ||||||
| yarn-error.log |  | ||||||
|  |  | ||||||
| #asdf | # asdf | ||||||
| .tool-versions | .tool-versions | ||||||
|  |  | ||||||
| # Home Assistant config | # Home Assistant config | ||||||
|   | |||||||
| @@ -1,5 +1,4 @@ | |||||||
| build | build | ||||||
| build-translations/* |  | ||||||
| translations/* | translations/* | ||||||
| node_modules/* | node_modules/* | ||||||
| hass_frontend/* | hass_frontend/* | ||||||
|   | |||||||
							
								
								
									
										29
									
								
								.yarn/patches/@lit-labs/virtualizer/event-target-shim.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								.yarn/patches/@lit-labs/virtualizer/event-target-shim.patch
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | |||||||
|  | diff --git a/polyfillLoaders/EventTarget.js b/polyfillLoaders/EventTarget.js | ||||||
|  | index 4e18ade7ba485849f17f28c94c42f0e0e01ac387..8f34f4f646c7f7becc208fb5a546c96034fc74dc 100644 | ||||||
|  | --- a/polyfillLoaders/EventTarget.js | ||||||
|  | +++ b/polyfillLoaders/EventTarget.js | ||||||
|  | @@ -6,16 +6,15 @@ | ||||||
|  |  let _ET; | ||||||
|  |  let ET; | ||||||
|  |  export default async function EventTarget() { | ||||||
|  | -    return ET || init(); | ||||||
|  | +  return ET || init(); | ||||||
|  |  } | ||||||
|  |  async function init() { | ||||||
|  | -    _ET = window.EventTarget; | ||||||
|  | -    try { | ||||||
|  | -        new _ET(); | ||||||
|  | -    } | ||||||
|  | -    catch (_a) { | ||||||
|  | -        _ET = (await import('event-target-shim')).EventTarget; | ||||||
|  | -    } | ||||||
|  | -    return (ET = _ET); | ||||||
|  | +  _ET = window.EventTarget; | ||||||
|  | +  try { | ||||||
|  | +    new _ET(); | ||||||
|  | +  } catch (_a) { | ||||||
|  | +    _ET = (await import("event-target-shim")).default.EventTarget; | ||||||
|  | +  } | ||||||
|  | +  return (ET = _ET); | ||||||
|  |  } | ||||||
|  |  //# sourceMappingURL=EventTarget.js.map | ||||||
							
								
								
									
										12
									
								
								.yarn/patches/@material/mwc-icon-button/remove-icon.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								.yarn/patches/@material/mwc-icon-button/remove-icon.patch
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | |||||||
|  | diff --git a/mwc-icon-button-base.js b/mwc-icon-button-base.js | ||||||
|  | index 45cdaab93ccc0a6daaaaabc01266dcdc32e46bfd..b3ea5b541597308d85f86ce6c23fd00785fda835 100644 | ||||||
|  | --- a/mwc-icon-button-base.js | ||||||
|  | +++ b/mwc-icon-button-base.js | ||||||
|  | @@ -63,7 +63,6 @@ export class IconButtonBase extends LitElement { | ||||||
|  |          @touchend="${this.handleRippleDeactivate}" | ||||||
|  |          @touchcancel="${this.handleRippleDeactivate}" | ||||||
|  |      >${this.renderRipple()} | ||||||
|  | -    <i class="material-icons">${this.icon}</i> | ||||||
|  |      <span | ||||||
|  |        ><slot></slot | ||||||
|  |      ></span> | ||||||
							
								
								
									
										34
									
								
								.yarn/patches/@polymer/polymer/pr-5569.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								.yarn/patches/@polymer/polymer/pr-5569.patch
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | |||||||
|  | diff --git a/lib/legacy/class.js b/lib/legacy/class.js | ||||||
|  | index aee2511be1cd9bf900ee552bc98190c1631c57c0..f2f499d68bf52034cac9c28307c99e8ce6b8417d 100644 | ||||||
|  | --- a/lib/legacy/class.js | ||||||
|  | +++ b/lib/legacy/class.js | ||||||
|  | @@ -304,17 +304,23 @@ function GenerateClassFromInfo(info, Base, behaviors) { | ||||||
|  |        // only proceed if the generated class' prototype has not been registered. | ||||||
|  |        const generatedProto = PolymerGenerated.prototype; | ||||||
|  |        if (!generatedProto.hasOwnProperty(JSCompiler_renameProperty('__hasRegisterFinished', generatedProto))) { | ||||||
|  | -        generatedProto.__hasRegisterFinished = true; | ||||||
|  | +        // make sure legacy lifecycle is called on the *element*'s prototype | ||||||
|  | +        // and not the generated class prototype; if the element has been | ||||||
|  | +        // extended, these are *not* the same. | ||||||
|  | +        const proto = Object.getPrototypeOf(this); | ||||||
|  | +        // Only set flag when generated prototype itself is registered, | ||||||
|  | +        // as this element may be extended from, and needs to run `registered` | ||||||
|  | +        // on all behaviors on the subclass as well. | ||||||
|  | +        if (proto === generatedProto) { | ||||||
|  | +          generatedProto.__hasRegisterFinished = true; | ||||||
|  | +        } | ||||||
|  |          // ensure superclass is registered first. | ||||||
|  |          super._registered(); | ||||||
|  |          // copy properties onto the generated class lazily if we're optimizing, | ||||||
|  | -        if (legacyOptimizations) { | ||||||
|  | +        if (legacyOptimizations && !Object.hasOwnProperty(generatedProto, '__hasCopiedProperties')) { | ||||||
|  | +          generatedProto.__hasCopiedProperties = true; | ||||||
|  |            copyPropertiesToProto(generatedProto); | ||||||
|  |          } | ||||||
|  | -        // make sure legacy lifecycle is called on the *element*'s prototype | ||||||
|  | -        // and not the generated class prototype; if the element has been | ||||||
|  | -        // extended, these are *not* the same. | ||||||
|  | -        const proto = Object.getPrototypeOf(this); | ||||||
|  |          let list = lifecycle.beforeRegister; | ||||||
|  |          if (list) { | ||||||
|  |            for (let i=0; i < list.length; i++) { | ||||||
							
								
								
									
										77
									
								
								.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										8
									
								
								.yarn/plugins/@yarnpkg/plugin-typescript.cjs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								.yarn/plugins/@yarnpkg/plugin-typescript.cjs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										631
									
								
								.yarn/releases/yarn-3.0.2.cjs
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										631
									
								
								.yarn/releases/yarn-3.0.2.cjs
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										9
									
								
								.yarnrc.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								.yarnrc.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | nodeLinker: node-modules | ||||||
|  |  | ||||||
|  | plugins: | ||||||
|  |   - path: .yarn/plugins/@yarnpkg/plugin-typescript.cjs | ||||||
|  |     spec: "@yarnpkg/plugin-typescript" | ||||||
|  |   - path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs | ||||||
|  |     spec: "@yarnpkg/plugin-interactive-tools" | ||||||
|  |  | ||||||
|  | yarnPath: .yarn/releases/yarn-3.0.2.cjs | ||||||
| @@ -1,5 +1,4 @@ | |||||||
| include README.md | include README.md | ||||||
| include LICENSE.md |  | ||||||
| graft hass_frontend | graft hass_frontend | ||||||
| graft hass_frontend_es5 | graft hass_frontend_es5 | ||||||
| recursive-exclude * *.py[co] | recursive-exclude * *.py[co] | ||||||
|   | |||||||
							
								
								
									
										170
									
								
								build-scripts/babel-plugins/inline-constants-plugin.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										170
									
								
								build-scripts/babel-plugins/inline-constants-plugin.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,170 @@ | |||||||
|  | /* eslint-disable @typescript-eslint/no-var-requires */ | ||||||
|  | const path = require("path"); | ||||||
|  |  | ||||||
|  | // Currently only supports CommonJS modules, as require is synchronous. `import` would need babel running asynchronous. | ||||||
|  | module.exports = function inlineConstants(babel, options, cwd) { | ||||||
|  |   const t = babel.types; | ||||||
|  |  | ||||||
|  |   if (!Array.isArray(options.modules)) { | ||||||
|  |     throw new TypeError( | ||||||
|  |       "babel-plugin-inline-constants: expected a `modules` array to be passed" | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (options.resolveExtensions && !Array.isArray(options.resolveExtensions)) { | ||||||
|  |     throw new TypeError( | ||||||
|  |       "babel-plugin-inline-constants: expected `resolveExtensions` to be an array" | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   const ignoreModuleNotFound = options.ignoreModuleNotFound; | ||||||
|  |   const resolveExtensions = options.resolveExtensions; | ||||||
|  |  | ||||||
|  |   const hasRelativeModules = options.modules.some( | ||||||
|  |     (module) => module.startsWith(".") || module.startsWith("/") | ||||||
|  |   ); | ||||||
|  |  | ||||||
|  |   const modules = Object.fromEntries( | ||||||
|  |     options.modules.map((module) => { | ||||||
|  |       const absolute = module.startsWith(".") | ||||||
|  |         ? require.resolve(module, { paths: [cwd] }) | ||||||
|  |         : module; | ||||||
|  |       // eslint-disable-next-line import/no-dynamic-require | ||||||
|  |       return [absolute, require(absolute)]; | ||||||
|  |     }) | ||||||
|  |   ); | ||||||
|  |  | ||||||
|  |   const toLiteral = (value) => { | ||||||
|  |     if (typeof value === "string") { | ||||||
|  |       return t.stringLiteral(value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (typeof value === "number") { | ||||||
|  |       return t.numericLiteral(value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (typeof value === "boolean") { | ||||||
|  |       return t.booleanLiteral(value); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (value === null) { | ||||||
|  |       return t.nullLiteral(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     throw new Error( | ||||||
|  |       "babel-plugin-inline-constants: cannot handle non-literal `" + value + "`" | ||||||
|  |     ); | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   const resolveAbsolute = (value, state, resolveExtensionIndex) => { | ||||||
|  |     if (!state.filename) { | ||||||
|  |       throw new TypeError( | ||||||
|  |         "babel-plugin-inline-constants: expected a `filename` to be set for files" | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (resolveExtensions && resolveExtensionIndex !== undefined) { | ||||||
|  |       value += resolveExtensions[resolveExtensionIndex]; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     try { | ||||||
|  |       return require.resolve(value, { paths: [path.dirname(state.filename)] }); | ||||||
|  |     } catch (error) { | ||||||
|  |       if ( | ||||||
|  |         error.code === "MODULE_NOT_FOUND" && | ||||||
|  |         resolveExtensions && | ||||||
|  |         (resolveExtensionIndex === undefined || | ||||||
|  |           resolveExtensionIndex < resolveExtensions.length - 1) | ||||||
|  |       ) { | ||||||
|  |         const resolveExtensionIdx = (resolveExtensionIndex || -1) + 1; | ||||||
|  |         return resolveAbsolute(value, state, resolveExtensionIdx); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       if (error.code === "MODULE_NOT_FOUND" && ignoreModuleNotFound) { | ||||||
|  |         return undefined; | ||||||
|  |       } | ||||||
|  |       throw error; | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   const importDeclaration = (p, state) => { | ||||||
|  |     if (p.node.type !== "ImportDeclaration") { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     const absolute = | ||||||
|  |       hasRelativeModules && p.node.source.value.startsWith(".") | ||||||
|  |         ? resolveAbsolute(p.node.source.value, state) | ||||||
|  |         : p.node.source.value; | ||||||
|  |  | ||||||
|  |     if (!absolute || !(absolute in modules)) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const module = modules[absolute]; | ||||||
|  |  | ||||||
|  |     for (const specifier of p.node.specifiers) { | ||||||
|  |       if ( | ||||||
|  |         specifier.type === "ImportDefaultSpecifier" && | ||||||
|  |         specifier.local && | ||||||
|  |         specifier.local.type === "Identifier" | ||||||
|  |       ) { | ||||||
|  |         if (!("default" in module)) { | ||||||
|  |           throw new Error( | ||||||
|  |             "babel-plugin-inline-constants: cannot access default export from `" + | ||||||
|  |               p.node.source.value + | ||||||
|  |               "`" | ||||||
|  |           ); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         const variableValue = toLiteral(module.default); | ||||||
|  |         const variable = t.variableDeclarator( | ||||||
|  |           t.identifier(specifier.local.name), | ||||||
|  |           variableValue | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |         p.insertBefore({ | ||||||
|  |           type: "VariableDeclaration", | ||||||
|  |           kind: "const", | ||||||
|  |           declarations: [variable], | ||||||
|  |         }); | ||||||
|  |       } else if ( | ||||||
|  |         specifier.type === "ImportSpecifier" && | ||||||
|  |         specifier.imported && | ||||||
|  |         specifier.imported.type === "Identifier" && | ||||||
|  |         specifier.local && | ||||||
|  |         specifier.local.type === "Identifier" | ||||||
|  |       ) { | ||||||
|  |         if (!(specifier.imported.name in module)) { | ||||||
|  |           throw new Error( | ||||||
|  |             "babel-plugin-inline-constants: cannot access `" + | ||||||
|  |               specifier.imported.name + | ||||||
|  |               "` from `" + | ||||||
|  |               p.node.source.value + | ||||||
|  |               "`" | ||||||
|  |           ); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         const variableValue = toLiteral(module[specifier.imported.name]); | ||||||
|  |         const variable = t.variableDeclarator( | ||||||
|  |           t.identifier(specifier.local.name), | ||||||
|  |           variableValue | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |         p.insertBefore({ | ||||||
|  |           type: "VariableDeclaration", | ||||||
|  |           kind: "const", | ||||||
|  |           declarations: [variable], | ||||||
|  |         }); | ||||||
|  |       } else { | ||||||
|  |         throw new Error("Cannot handle specifier `" + specifier.type + "`"); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     p.remove(); | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   return { | ||||||
|  |     visitor: { | ||||||
|  |       ImportDeclaration: importDeclaration, | ||||||
|  |     }, | ||||||
|  |   }; | ||||||
|  | }; | ||||||
| @@ -1,17 +1,16 @@ | |||||||
|  | /* eslint-disable @typescript-eslint/no-var-requires */ | ||||||
| const path = require("path"); | const path = require("path"); | ||||||
| const env = require("./env.js"); | const env = require("./env.js"); | ||||||
| const paths = require("./paths.js"); | const paths = require("./paths.js"); | ||||||
|  |  | ||||||
| // Files from NPM Packages that should not be imported | // Files from NPM Packages that should not be imported | ||||||
| module.exports.ignorePackages = ({ latestBuild }) => [ | module.exports.ignorePackages = ({ latestBuild }) => [ | ||||||
|   // Bloats bundle and it's not used. |  | ||||||
|   path.resolve(require.resolve("moment"), "../locale"), |  | ||||||
|   // Part of yaml.js and only used for !!js functions that we don't use |   // Part of yaml.js and only used for !!js functions that we don't use | ||||||
|   require.resolve("esprima"), |   require.resolve("esprima"), | ||||||
| ]; | ]; | ||||||
|  |  | ||||||
| // Files from NPM packages that we should replace with empty file | // Files from NPM packages that we should replace with empty file | ||||||
| module.exports.emptyPackages = ({ latestBuild }) => | module.exports.emptyPackages = ({ latestBuild, isHassioBuild }) => | ||||||
|   [ |   [ | ||||||
|     // Contains all color definitions for all material color sets. |     // Contains all color definitions for all material color sets. | ||||||
|     // We don't use it |     // We don't use it | ||||||
| @@ -19,7 +18,8 @@ module.exports.emptyPackages = ({ latestBuild }) => | |||||||
|     require.resolve("@polymer/paper-styles/default-theme.js"), |     require.resolve("@polymer/paper-styles/default-theme.js"), | ||||||
|     // Loads stuff from a CDN |     // Loads stuff from a CDN | ||||||
|     require.resolve("@polymer/font-roboto/roboto.js"), |     require.resolve("@polymer/font-roboto/roboto.js"), | ||||||
|     require.resolve("@vaadin/vaadin-material-styles/font-roboto.js"), |     require.resolve("@vaadin/vaadin-material-styles/typography.js"), | ||||||
|  |     require.resolve("@vaadin/vaadin-material-styles/font-icons.js"), | ||||||
|     // Compatibility not needed for latest builds |     // Compatibility not needed for latest builds | ||||||
|     latestBuild && |     latestBuild && | ||||||
|       // wrapped in require.resolve so it blows up if file no longer exists |       // wrapped in require.resolve so it blows up if file no longer exists | ||||||
| @@ -28,6 +28,11 @@ module.exports.emptyPackages = ({ latestBuild }) => | |||||||
|       ), |       ), | ||||||
|     // This polyfill is loaded in workers to support ES5, filter it out. |     // This polyfill is loaded in workers to support ES5, filter it out. | ||||||
|     latestBuild && require.resolve("proxy-polyfill/src/index.js"), |     latestBuild && require.resolve("proxy-polyfill/src/index.js"), | ||||||
|  |     // Icons in supervisor conflict with icons in HA so we don't load. | ||||||
|  |     isHassioBuild && | ||||||
|  |       require.resolve( | ||||||
|  |         path.resolve(paths.polymer_dir, "src/components/ha-icon.ts") | ||||||
|  |       ), | ||||||
|   ].filter(Boolean); |   ].filter(Boolean); | ||||||
|  |  | ||||||
| module.exports.definedVars = ({ isProdBuild, latestBuild, defineOverlay }) => ({ | module.exports.definedVars = ({ isProdBuild, latestBuild, defineOverlay }) => ({ | ||||||
| @@ -35,6 +40,7 @@ module.exports.definedVars = ({ isProdBuild, latestBuild, defineOverlay }) => ({ | |||||||
|   __BUILD__: JSON.stringify(latestBuild ? "latest" : "es5"), |   __BUILD__: JSON.stringify(latestBuild ? "latest" : "es5"), | ||||||
|   __VERSION__: JSON.stringify(env.version()), |   __VERSION__: JSON.stringify(env.version()), | ||||||
|   __DEMO__: false, |   __DEMO__: false, | ||||||
|  |   __SUPERVISOR__: false, | ||||||
|   __BACKWARDS_COMPAT__: false, |   __BACKWARDS_COMPAT__: false, | ||||||
|   __STATIC_PATH__: "/static/", |   __STATIC_PATH__: "/static/", | ||||||
|   "process.env.NODE_ENV": JSON.stringify( |   "process.env.NODE_ENV": JSON.stringify( | ||||||
| @@ -51,17 +57,29 @@ module.exports.terserOptions = (latestBuild) => ({ | |||||||
|  |  | ||||||
| module.exports.babelOptions = ({ latestBuild }) => ({ | module.exports.babelOptions = ({ latestBuild }) => ({ | ||||||
|   babelrc: false, |   babelrc: false, | ||||||
|  |   compact: false, | ||||||
|   presets: [ |   presets: [ | ||||||
|     !latestBuild && [ |     !latestBuild && [ | ||||||
|       require("@babel/preset-env").default, |       "@babel/preset-env", | ||||||
|       { |       { | ||||||
|         useBuiltIns: "entry", |         useBuiltIns: "entry", | ||||||
|         corejs: "3.6", |         corejs: "3.15", | ||||||
|  |         bugfixes: true, | ||||||
|       }, |       }, | ||||||
|     ], |     ], | ||||||
|     require("@babel/preset-typescript").default, |     "@babel/preset-typescript", | ||||||
|   ].filter(Boolean), |   ].filter(Boolean), | ||||||
|   plugins: [ |   plugins: [ | ||||||
|  |     [ | ||||||
|  |       path.resolve( | ||||||
|  |         paths.polymer_dir, | ||||||
|  |         "build-scripts/babel-plugins/inline-constants-plugin.js" | ||||||
|  |       ), | ||||||
|  |       { | ||||||
|  |         modules: ["@mdi/js"], | ||||||
|  |         ignoreModuleNotFound: true, | ||||||
|  |       }, | ||||||
|  |     ], | ||||||
|     // Part of ES2018. Converts {...a, b: 2} to Object.assign({}, a, {b: 2}) |     // Part of ES2018. Converts {...a, b: 2} to Object.assign({}, a, {b: 2}) | ||||||
|     !latestBuild && [ |     !latestBuild && [ | ||||||
|       "@babel/plugin-proposal-object-rest-spread", |       "@babel/plugin-proposal-object-rest-spread", | ||||||
| @@ -70,25 +88,21 @@ module.exports.babelOptions = ({ latestBuild }) => ({ | |||||||
|     // Only support the syntax, Webpack will handle it. |     // Only support the syntax, Webpack will handle it. | ||||||
|     "@babel/plugin-syntax-import-meta", |     "@babel/plugin-syntax-import-meta", | ||||||
|     "@babel/plugin-syntax-dynamic-import", |     "@babel/plugin-syntax-dynamic-import", | ||||||
|  |     "@babel/plugin-syntax-top-level-await", | ||||||
|     "@babel/plugin-proposal-optional-chaining", |     "@babel/plugin-proposal-optional-chaining", | ||||||
|     "@babel/plugin-proposal-nullish-coalescing-operator", |     "@babel/plugin-proposal-nullish-coalescing-operator", | ||||||
|     [ |     ["@babel/plugin-proposal-decorators", { decoratorsBeforeExport: true }], | ||||||
|       require("@babel/plugin-proposal-decorators").default, |     ["@babel/plugin-proposal-private-methods", { loose: true }], | ||||||
|       { decoratorsBeforeExport: true }, |     ["@babel/plugin-proposal-private-property-in-object", { loose: true }], | ||||||
|     ], |     ["@babel/plugin-proposal-class-properties", { loose: true }], | ||||||
|     [ |  | ||||||
|       require("@babel/plugin-proposal-class-properties").default, |  | ||||||
|       { loose: true }, |  | ||||||
|     ], |  | ||||||
|   ].filter(Boolean), |   ].filter(Boolean), | ||||||
|  |   exclude: [ | ||||||
|  |     // \\ for Windows, / for Mac OS and Linux | ||||||
|  |     /node_modules[\\/]core-js/, | ||||||
|  |     /node_modules[\\/]webpack[\\/]buildin/, | ||||||
|  |   ], | ||||||
| }); | }); | ||||||
|  |  | ||||||
| // Are already ES5, cause warnings when babelified. |  | ||||||
| module.exports.babelExclude = () => [ |  | ||||||
|   require.resolve("@mdi/js/mdi.js"), |  | ||||||
|   require.resolve("hls.js"), |  | ||||||
| ]; |  | ||||||
|  |  | ||||||
| const outputPath = (outputRoot, latestBuild) => | const outputPath = (outputRoot, latestBuild) => | ||||||
|   path.resolve(outputRoot, latestBuild ? "frontend_latest" : "frontend_es5"); |   path.resolve(outputRoot, latestBuild ? "frontend_latest" : "frontend_es5"); | ||||||
|  |  | ||||||
| @@ -156,6 +170,7 @@ module.exports.config = { | |||||||
|   cast({ isProdBuild, latestBuild }) { |   cast({ isProdBuild, latestBuild }) { | ||||||
|     const entry = { |     const entry = { | ||||||
|       launcher: path.resolve(paths.cast_dir, "src/launcher/entrypoint.ts"), |       launcher: path.resolve(paths.cast_dir, "src/launcher/entrypoint.ts"), | ||||||
|  |       media: path.resolve(paths.cast_dir, "src/media/entrypoint.ts"), | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     if (latestBuild) { |     if (latestBuild) { | ||||||
| @@ -186,6 +201,10 @@ module.exports.config = { | |||||||
|       publicPath: publicPath(latestBuild, paths.hassio_publicPath), |       publicPath: publicPath(latestBuild, paths.hassio_publicPath), | ||||||
|       isProdBuild, |       isProdBuild, | ||||||
|       latestBuild, |       latestBuild, | ||||||
|  |       isHassioBuild: true, | ||||||
|  |       defineOverlay: { | ||||||
|  |         __SUPERVISOR__: true, | ||||||
|  |       }, | ||||||
|     }; |     }; | ||||||
|   }, |   }, | ||||||
|  |  | ||||||
| @@ -198,6 +217,9 @@ module.exports.config = { | |||||||
|       publicPath: publicPath(latestBuild), |       publicPath: publicPath(latestBuild), | ||||||
|       isProdBuild, |       isProdBuild, | ||||||
|       latestBuild, |       latestBuild, | ||||||
|  |       defineOverlay: { | ||||||
|  |         __DEMO__: true, | ||||||
|  |       }, | ||||||
|     }; |     }; | ||||||
|   }, |   }, | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -1,3 +1,4 @@ | |||||||
|  | /* eslint-disable @typescript-eslint/no-var-requires */ | ||||||
| const fs = require("fs"); | const fs = require("fs"); | ||||||
| const path = require("path"); | const path = require("path"); | ||||||
| const paths = require("./paths.js"); | const paths = require("./paths.js"); | ||||||
| @@ -25,11 +26,11 @@ module.exports = { | |||||||
|   }, |   }, | ||||||
|   version() { |   version() { | ||||||
|     const version = fs |     const version = fs | ||||||
|       .readFileSync(path.resolve(paths.polymer_dir, "setup.py"), "utf8") |       .readFileSync(path.resolve(paths.polymer_dir, "setup.cfg"), "utf8") | ||||||
|       .match(/\d{8}\.\d+/); |       .match(/version\W+=\W(\d{8}\.\d)/); | ||||||
|     if (!version) { |     if (!version) { | ||||||
|       throw Error("Version not found"); |       throw Error("Version not found"); | ||||||
|     } |     } | ||||||
|     return version[0]; |     return version[1]; | ||||||
|   }, |   }, | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -5,6 +5,7 @@ const env = require("../env"); | |||||||
|  |  | ||||||
| require("./clean.js"); | require("./clean.js"); | ||||||
| require("./translations.js"); | require("./translations.js"); | ||||||
|  | require("./locale-data.js"); | ||||||
| require("./gen-icons-json.js"); | require("./gen-icons-json.js"); | ||||||
| require("./gather-static.js"); | require("./gather-static.js"); | ||||||
| require("./compress.js"); | require("./compress.js"); | ||||||
| @@ -26,7 +27,8 @@ gulp.task( | |||||||
|       "gen-icons-json", |       "gen-icons-json", | ||||||
|       "gen-pages-dev", |       "gen-pages-dev", | ||||||
|       "gen-index-app-dev", |       "gen-index-app-dev", | ||||||
|       "build-translations" |       "build-translations", | ||||||
|  |       "build-locale-data" | ||||||
|     ), |     ), | ||||||
|     "copy-static-app", |     "copy-static-app", | ||||||
|     env.useWDS() |     env.useWDS() | ||||||
| @@ -44,11 +46,11 @@ gulp.task( | |||||||
|       process.env.NODE_ENV = "production"; |       process.env.NODE_ENV = "production"; | ||||||
|     }, |     }, | ||||||
|     "clean", |     "clean", | ||||||
|     gulp.parallel("gen-icons-json", "build-translations"), |     gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"), | ||||||
|     "copy-static-app", |     "copy-static-app", | ||||||
|     env.useRollup() ? "rollup-prod-app" : "webpack-prod-app", |     env.useRollup() ? "rollup-prod-app" : "webpack-prod-app", | ||||||
|     ...// Don't compress running tests |     // Don't compress running tests | ||||||
|     (env.isTest() ? [] : ["compress-app"]), |     ...(env.isTest() ? [] : ["compress-app"]), | ||||||
|     gulp.parallel( |     gulp.parallel( | ||||||
|       "gen-pages-prod", |       "gen-pages-prod", | ||||||
|       "gen-index-app-prod", |       "gen-index-app-prod", | ||||||
|   | |||||||
| @@ -18,7 +18,7 @@ gulp.task( | |||||||
|     }, |     }, | ||||||
|     "clean-cast", |     "clean-cast", | ||||||
|     "translations-enable-merge-backend", |     "translations-enable-merge-backend", | ||||||
|     gulp.parallel("gen-icons-json", "build-translations"), |     gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"), | ||||||
|     "copy-static-cast", |     "copy-static-cast", | ||||||
|     "gen-index-cast-dev", |     "gen-index-cast-dev", | ||||||
|     env.useRollup() ? "rollup-dev-server-cast" : "webpack-dev-server-cast" |     env.useRollup() ? "rollup-dev-server-cast" : "webpack-dev-server-cast" | ||||||
| @@ -33,7 +33,7 @@ gulp.task( | |||||||
|     }, |     }, | ||||||
|     "clean-cast", |     "clean-cast", | ||||||
|     "translations-enable-merge-backend", |     "translations-enable-merge-backend", | ||||||
|     gulp.parallel("gen-icons-json", "build-translations"), |     gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"), | ||||||
|     "copy-static-cast", |     "copy-static-cast", | ||||||
|     env.useRollup() ? "rollup-prod-cast" : "webpack-prod-cast", |     env.useRollup() ? "rollup-prod-cast" : "webpack-prod-cast", | ||||||
|     "gen-index-cast-prod" |     "gen-index-cast-prod" | ||||||
|   | |||||||
| @@ -5,32 +5,32 @@ require("./translations"); | |||||||
|  |  | ||||||
| gulp.task( | gulp.task( | ||||||
|   "clean", |   "clean", | ||||||
|   gulp.parallel("clean-translations", function cleanOutputAndBuildDir() { |   gulp.parallel("clean-translations", () => | ||||||
|     return del([paths.app_output_root, paths.build_dir]); |     del([paths.app_output_root, paths.build_dir]) | ||||||
|   }) |   ) | ||||||
| ); | ); | ||||||
|  |  | ||||||
| gulp.task( | gulp.task( | ||||||
|   "clean-demo", |   "clean-demo", | ||||||
|   gulp.parallel("clean-translations", function cleanOutputAndBuildDir() { |   gulp.parallel("clean-translations", () => | ||||||
|     return del([paths.demo_output_root, paths.build_dir]); |     del([paths.demo_output_root, paths.build_dir]) | ||||||
|   }) |   ) | ||||||
| ); | ); | ||||||
|  |  | ||||||
| gulp.task( | gulp.task( | ||||||
|   "clean-cast", |   "clean-cast", | ||||||
|   gulp.parallel("clean-translations", function cleanOutputAndBuildDir() { |   gulp.parallel("clean-translations", () => | ||||||
|     return del([paths.cast_output_root, paths.build_dir]); |     del([paths.cast_output_root, paths.build_dir]) | ||||||
|   }) |   ) | ||||||
| ); | ); | ||||||
|  |  | ||||||
| gulp.task("clean-hassio", function cleanOutputAndBuildDir() { | gulp.task("clean-hassio", () => | ||||||
|   return del([paths.hassio_output_root, paths.build_dir]); |   del([paths.hassio_output_root, paths.build_dir]) | ||||||
| }); | ); | ||||||
|  |  | ||||||
| gulp.task( | gulp.task( | ||||||
|   "clean-gallery", |   "clean-gallery", | ||||||
|   gulp.parallel("clean-translations", function cleanOutputAndBuildDir() { |   gulp.parallel("clean-translations", () => | ||||||
|     return del([paths.gallery_output_root, paths.build_dir]); |     del([paths.gallery_output_root, paths.gallery_build, paths.build_dir]) | ||||||
|   }) |   ) | ||||||
| ); | ); | ||||||
|   | |||||||
| @@ -20,7 +20,12 @@ gulp.task( | |||||||
|     }, |     }, | ||||||
|     "clean-demo", |     "clean-demo", | ||||||
|     "translations-enable-merge-backend", |     "translations-enable-merge-backend", | ||||||
|     gulp.parallel("gen-icons-json", "gen-index-demo-dev", "build-translations"), |     gulp.parallel( | ||||||
|  |       "gen-icons-json", | ||||||
|  |       "gen-index-demo-dev", | ||||||
|  |       "build-translations", | ||||||
|  |       "build-locale-data" | ||||||
|  |     ), | ||||||
|     "copy-static-demo", |     "copy-static-demo", | ||||||
|     env.useRollup() ? "rollup-dev-server-demo" : "webpack-dev-server-demo" |     env.useRollup() ? "rollup-dev-server-demo" : "webpack-dev-server-demo" | ||||||
|   ) |   ) | ||||||
| @@ -35,7 +40,7 @@ gulp.task( | |||||||
|     "clean-demo", |     "clean-demo", | ||||||
|     // Cast needs to be backwards compatible and older HA has no translations |     // Cast needs to be backwards compatible and older HA has no translations | ||||||
|     "translations-enable-merge-backend", |     "translations-enable-merge-backend", | ||||||
|     gulp.parallel("gen-icons-json", "build-translations"), |     gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"), | ||||||
|     "copy-static-demo", |     "copy-static-demo", | ||||||
|     env.useRollup() ? "rollup-prod-demo" : "webpack-prod-demo", |     env.useRollup() ? "rollup-prod-demo" : "webpack-prod-demo", | ||||||
|     "gen-index-demo-prod" |     "gen-index-demo-prod" | ||||||
|   | |||||||
| @@ -154,6 +154,15 @@ gulp.task("gen-index-cast-dev", (done) => { | |||||||
|     contentReceiver |     contentReceiver | ||||||
|   ); |   ); | ||||||
|  |  | ||||||
|  |   const contentMedia = renderCastTemplate("media", { | ||||||
|  |     latestMediaJS: "/frontend_latest/media.js", | ||||||
|  |     es5MediaJS: "/frontend_es5/media.js", | ||||||
|  |   }); | ||||||
|  |   fs.outputFileSync( | ||||||
|  |     path.resolve(paths.cast_output_root, "media.html"), | ||||||
|  |     contentMedia | ||||||
|  |   ); | ||||||
|  |  | ||||||
|   const contentFAQ = renderCastTemplate("launcher-faq", { |   const contentFAQ = renderCastTemplate("launcher-faq", { | ||||||
|     latestLauncherJS: "/frontend_latest/launcher.js", |     latestLauncherJS: "/frontend_latest/launcher.js", | ||||||
|     es5LauncherJS: "/frontend_es5/launcher.js", |     es5LauncherJS: "/frontend_es5/launcher.js", | ||||||
| @@ -192,6 +201,15 @@ gulp.task("gen-index-cast-prod", (done) => { | |||||||
|     contentReceiver |     contentReceiver | ||||||
|   ); |   ); | ||||||
|  |  | ||||||
|  |   const contentMedia = renderCastTemplate("media", { | ||||||
|  |     latestMediaJS: latestManifest["media.js"], | ||||||
|  |     es5MediaJS: es5Manifest["media.js"], | ||||||
|  |   }); | ||||||
|  |   fs.outputFileSync( | ||||||
|  |     path.resolve(paths.cast_output_root, "media.html"), | ||||||
|  |     contentMedia | ||||||
|  |   ); | ||||||
|  |  | ||||||
|   const contentFAQ = renderCastTemplate("launcher-faq", { |   const contentFAQ = renderCastTemplate("launcher-faq", { | ||||||
|     latestLauncherJS: latestManifest["launcher.js"], |     latestLauncherJS: latestManifest["launcher.js"], | ||||||
|     es5LauncherJS: es5Manifest["launcher.js"], |     es5LauncherJS: es5Manifest["launcher.js"], | ||||||
| @@ -302,15 +320,23 @@ gulp.task("gen-index-hassio-prod", async () => { | |||||||
|  |  | ||||||
| function writeHassioEntrypoint(latestEntrypoint, es5Entrypoint) { | function writeHassioEntrypoint(latestEntrypoint, es5Entrypoint) { | ||||||
|   fs.mkdirSync(paths.hassio_output_root, { recursive: true }); |   fs.mkdirSync(paths.hassio_output_root, { recursive: true }); | ||||||
|  |   // Safari 12 and below does not have a compliant ES2015 implementation of template literals, so we ship ES5 | ||||||
|   fs.writeFileSync( |   fs.writeFileSync( | ||||||
|     path.resolve(paths.hassio_output_root, "entrypoint.js"), |     path.resolve(paths.hassio_output_root, "entrypoint.js"), | ||||||
|     ` |     ` | ||||||
| try { | function loadES5() { | ||||||
|   new Function("import('${latestEntrypoint}')")(); |  | ||||||
| } catch (err) { |  | ||||||
|   var el = document.createElement('script'); |   var el = document.createElement('script'); | ||||||
|   el.src = '${es5Entrypoint}'; |   el.src = '${es5Entrypoint}'; | ||||||
|   document.body.appendChild(el); |   document.body.appendChild(el); | ||||||
|  | } | ||||||
|  | if (/.*Version\\/(?:11|12)(?:\\.\\d+)*.*Safari\\//.test(navigator.userAgent)) { | ||||||
|  |     loadES5(); | ||||||
|  | } else { | ||||||
|  |   try { | ||||||
|  |     new Function("import('${latestEntrypoint}')")(); | ||||||
|  |   } catch (err) { | ||||||
|  |     loadES5(); | ||||||
|  |   } | ||||||
| } | } | ||||||
|   `, |   `, | ||||||
|     { encoding: "utf-8" } |     { encoding: "utf-8" } | ||||||
|   | |||||||
| @@ -1,7 +1,11 @@ | |||||||
|  | /* eslint-disable */ | ||||||
| // Run demo develop mode | // Run demo develop mode | ||||||
| const gulp = require("gulp"); | const gulp = require("gulp"); | ||||||
| const fs = require("fs"); | const fs = require("fs"); | ||||||
| const path = require("path"); | const path = require("path"); | ||||||
|  | const marked = require("marked"); | ||||||
|  | const glob = require("glob"); | ||||||
|  | const yaml = require("js-yaml"); | ||||||
|  |  | ||||||
| const env = require("../env"); | const env = require("../env"); | ||||||
| const paths = require("../paths"); | const paths = require("../paths"); | ||||||
| @@ -15,26 +19,129 @@ require("./service-worker.js"); | |||||||
| require("./entry-html.js"); | require("./entry-html.js"); | ||||||
| require("./rollup.js"); | require("./rollup.js"); | ||||||
|  |  | ||||||
| gulp.task("gather-gallery-demos", async function gatherDemos() { | gulp.task("gather-gallery-pages", async function gatherPages() { | ||||||
|   const files = await fs.promises.readdir( |   const pageDir = path.resolve(paths.gallery_dir, "src/pages"); | ||||||
|     path.resolve(paths.gallery_dir, "src/demos") |   const files = glob.sync(path.resolve(pageDir, "**/*")); | ||||||
|   ); |  | ||||||
|  |  | ||||||
|   let content = "export const DEMOS = {\n"; |  | ||||||
|  |  | ||||||
|   for (const file of files) { |  | ||||||
|     const demoId = path.basename(file, ".ts"); |  | ||||||
|     const demoPath = "../src/demos/" + demoId; |  | ||||||
|     content += `  "${demoId}": () => import("${demoPath}"),\n`; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   content += "};"; |  | ||||||
|  |  | ||||||
|   const galleryBuild = path.resolve(paths.gallery_dir, "build"); |   const galleryBuild = path.resolve(paths.gallery_dir, "build"); | ||||||
|  |  | ||||||
|   fs.mkdirSync(galleryBuild, { recursive: true }); |   fs.mkdirSync(galleryBuild, { recursive: true }); | ||||||
|  |  | ||||||
|  |   let content = "export const PAGES = {\n"; | ||||||
|  |  | ||||||
|  |   const processed = new Set(); | ||||||
|  |  | ||||||
|  |   for (const file of files) { | ||||||
|  |     if (fs.lstatSync(file).isDirectory()) { | ||||||
|  |       continue; | ||||||
|  |     } | ||||||
|  |     const pageId = file.substring(pageDir.length + 1, file.lastIndexOf(".")); | ||||||
|  |  | ||||||
|  |     if (processed.has(pageId)) { | ||||||
|  |       continue; | ||||||
|  |     } | ||||||
|  |     processed.add(pageId); | ||||||
|  |  | ||||||
|  |     const [category, name] = pageId.split("/", 2); | ||||||
|  |  | ||||||
|  |     const demoFile = path.resolve(pageDir, `${pageId}.ts`); | ||||||
|  |     const descriptionFile = path.resolve(pageDir, `${pageId}.markdown`); | ||||||
|  |     const hasDemo = fs.existsSync(demoFile); | ||||||
|  |     let hasDescription = fs.existsSync(descriptionFile); | ||||||
|  |     let metadata = {}; | ||||||
|  |     if (hasDescription) { | ||||||
|  |       let descriptionContent = fs.readFileSync(descriptionFile, "utf-8"); | ||||||
|  |  | ||||||
|  |       if (descriptionContent.startsWith("---")) { | ||||||
|  |         const metadataEnd = descriptionContent.indexOf("---", 3); | ||||||
|  |         metadata = yaml.load(descriptionContent.substring(3, metadataEnd)); | ||||||
|  |         descriptionContent = descriptionContent | ||||||
|  |           .substring(metadataEnd + 3) | ||||||
|  |           .trim(); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       // If description is just metadata | ||||||
|  |       if (descriptionContent === "") { | ||||||
|  |         hasDescription = false; | ||||||
|  |       } else { | ||||||
|  |         descriptionContent = marked(descriptionContent).replace(/`/g, "\\`"); | ||||||
|  |         fs.mkdirSync(path.resolve(galleryBuild, category), { recursive: true }); | ||||||
|  |         fs.writeFileSync( | ||||||
|  |           path.resolve(galleryBuild, `${pageId}-description.ts`), | ||||||
|  |           ` | ||||||
|  |           import {html} from "lit"; | ||||||
|  |           export default html\`${descriptionContent}\` | ||||||
|  |           ` | ||||||
|  |         ); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     content += `  "${pageId}": { | ||||||
|  |       metadata: ${JSON.stringify(metadata)}, | ||||||
|  |       ${ | ||||||
|  |         hasDescription | ||||||
|  |           ? `description: () => import("./${pageId}-description").then(m => m.default),` | ||||||
|  |           : "" | ||||||
|  |       } | ||||||
|  |       ${hasDemo ? `demo: () => import("../src/pages/${pageId}")` : ""} | ||||||
|  |  | ||||||
|  |     },\n`; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   content += "};\n"; | ||||||
|  |  | ||||||
|  |   // Generate sidebar | ||||||
|  |   const sidebarPath = path.resolve(paths.gallery_dir, "sidebar.js"); | ||||||
|  |   // To make watch work during development | ||||||
|  |   delete require.cache[sidebarPath]; | ||||||
|  |   const sidebar = require(sidebarPath); | ||||||
|  |  | ||||||
|  |   const pagesToProcess = {}; | ||||||
|  |   for (const key of processed) { | ||||||
|  |     const [category, page] = key.split("/", 2); | ||||||
|  |     if (!(category in pagesToProcess)) { | ||||||
|  |       pagesToProcess[category] = new Set(); | ||||||
|  |     } | ||||||
|  |     pagesToProcess[category].add(page); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   for (const group of Object.values(sidebar)) { | ||||||
|  |     const toProcess = pagesToProcess[group.category]; | ||||||
|  |     delete pagesToProcess[group.category]; | ||||||
|  |  | ||||||
|  |     if (!toProcess) { | ||||||
|  |       console.error("Unknown category", group.category); | ||||||
|  |       if (!group.pages) { | ||||||
|  |         group.pages = []; | ||||||
|  |       } | ||||||
|  |       continue; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Any pre-defined groups will not be sorted. | ||||||
|  |     if (group.pages) { | ||||||
|  |       for (const page of group.pages) { | ||||||
|  |         if (!toProcess.delete(page)) { | ||||||
|  |           console.error("Found unreferenced demo", page); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } else { | ||||||
|  |       group.pages = []; | ||||||
|  |     } | ||||||
|  |     for (const page of Array.from(toProcess).sort()) { | ||||||
|  |       group.pages.push(page); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   for (const [category, pages] of Object.entries(pagesToProcess)) { | ||||||
|  |     sidebar.push({ | ||||||
|  |       category, | ||||||
|  |       header: category, | ||||||
|  |       pages: Array.from(pages).sort(), | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   content += `export const SIDEBAR = ${JSON.stringify(sidebar, null, 2)};\n`; | ||||||
|  |  | ||||||
|   fs.writeFileSync( |   fs.writeFileSync( | ||||||
|     path.resolve(galleryBuild, "import-demos.ts"), |     path.resolve(galleryBuild, "import-pages.ts"), | ||||||
|     content, |     content, | ||||||
|     "utf-8" |     "utf-8" | ||||||
|   ); |   ); | ||||||
| @@ -51,11 +158,25 @@ gulp.task( | |||||||
|     gulp.parallel( |     gulp.parallel( | ||||||
|       "gen-icons-json", |       "gen-icons-json", | ||||||
|       "build-translations", |       "build-translations", | ||||||
|       "gather-gallery-demos" |       "build-locale-data", | ||||||
|  |       "gather-gallery-pages" | ||||||
|     ), |     ), | ||||||
|     "copy-static-gallery", |     "copy-static-gallery", | ||||||
|     "gen-index-gallery-dev", |     "gen-index-gallery-dev", | ||||||
|     env.useRollup() ? "rollup-dev-server-gallery" : "webpack-dev-server-gallery" |     gulp.parallel( | ||||||
|  |       env.useRollup() | ||||||
|  |         ? "rollup-dev-server-gallery" | ||||||
|  |         : "webpack-dev-server-gallery", | ||||||
|  |       async function watchMarkdownFiles() { | ||||||
|  |         gulp.watch( | ||||||
|  |           [ | ||||||
|  |             path.resolve(paths.gallery_dir, "src/pages/**/*.markdown"), | ||||||
|  |             path.resolve(paths.gallery_dir, "sidebar.js"), | ||||||
|  |           ], | ||||||
|  |           gulp.series("gather-gallery-pages") | ||||||
|  |         ); | ||||||
|  |       } | ||||||
|  |     ) | ||||||
|   ) |   ) | ||||||
| ); | ); | ||||||
|  |  | ||||||
| @@ -70,7 +191,8 @@ gulp.task( | |||||||
|     gulp.parallel( |     gulp.parallel( | ||||||
|       "gen-icons-json", |       "gen-icons-json", | ||||||
|       "build-translations", |       "build-translations", | ||||||
|       "gather-gallery-demos" |       "build-locale-data", | ||||||
|  |       "gather-gallery-pages" | ||||||
|     ), |     ), | ||||||
|     "copy-static-gallery", |     "copy-static-gallery", | ||||||
|     env.useRollup() ? "rollup-prod-gallery" : "webpack-prod-gallery", |     env.useRollup() ? "rollup-prod-gallery" : "webpack-prod-gallery", | ||||||
|   | |||||||
| @@ -2,7 +2,6 @@ | |||||||
|  |  | ||||||
| const gulp = require("gulp"); | const gulp = require("gulp"); | ||||||
| const path = require("path"); | const path = require("path"); | ||||||
| const cpx = require("cpx"); |  | ||||||
| const fs = require("fs-extra"); | const fs = require("fs-extra"); | ||||||
| const paths = require("../paths"); | const paths = require("../paths"); | ||||||
|  |  | ||||||
| @@ -13,19 +12,28 @@ const polyPath = (...parts) => path.resolve(paths.polymer_dir, ...parts); | |||||||
| const copyFileDir = (fromFile, toDir) => | const copyFileDir = (fromFile, toDir) => | ||||||
|   fs.copySync(fromFile, path.join(toDir, path.basename(fromFile))); |   fs.copySync(fromFile, path.join(toDir, path.basename(fromFile))); | ||||||
|  |  | ||||||
| const genStaticPath = (staticDir) => (...parts) => | const genStaticPath = | ||||||
|   path.resolve(staticDir, ...parts); |   (staticDir) => | ||||||
|  |   (...parts) => | ||||||
|  |     path.resolve(staticDir, ...parts); | ||||||
|  |  | ||||||
| function copyTranslations(staticDir) { | function copyTranslations(staticDir) { | ||||||
|   const staticPath = genStaticPath(staticDir); |   const staticPath = genStaticPath(staticDir); | ||||||
|  |  | ||||||
|   // Translation output |   // Translation output | ||||||
|   fs.copySync( |   fs.copySync( | ||||||
|     polyPath("build-translations/output"), |     polyPath("build/translations/output"), | ||||||
|     staticPath("translations") |     staticPath("translations") | ||||||
|   ); |   ); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | function copyLocaleData(staticDir) { | ||||||
|  |   const staticPath = genStaticPath(staticDir); | ||||||
|  |  | ||||||
|  |   // Locale data output | ||||||
|  |   fs.copySync(polyPath("build/locale-data"), staticPath("locale-data")); | ||||||
|  | } | ||||||
|  |  | ||||||
| function copyMdiIcons(staticDir) { | function copyMdiIcons(staticDir) { | ||||||
|   const staticPath = genStaticPath(staticDir); |   const staticPath = genStaticPath(staticDir); | ||||||
|  |  | ||||||
| @@ -62,12 +70,20 @@ function copyLoaderJS(staticDir) { | |||||||
| function copyFonts(staticDir) { | function copyFonts(staticDir) { | ||||||
|   const staticPath = genStaticPath(staticDir); |   const staticPath = genStaticPath(staticDir); | ||||||
|   // Local fonts |   // Local fonts | ||||||
|   cpx.copySync( |   fs.copySync( | ||||||
|     npmPath("roboto-fontface/fonts/roboto/*.woff2"), |     npmPath("roboto-fontface/fonts/roboto/"), | ||||||
|     staticPath("fonts/roboto") |     staticPath("fonts/roboto/"), | ||||||
|  |     { | ||||||
|  |       filter: (src) => !src.includes(".") || src.endsWith(".woff2"), | ||||||
|  |     } | ||||||
|   ); |   ); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | function copyQrScannerWorker(staticDir) { | ||||||
|  |   const staticPath = genStaticPath(staticDir); | ||||||
|  |   copyFileDir(npmPath("qr-scanner/qr-scanner-worker.min.js"), staticPath("js")); | ||||||
|  | } | ||||||
|  |  | ||||||
| function copyMapPanel(staticDir) { | function copyMapPanel(staticDir) { | ||||||
|   const staticPath = genStaticPath(staticDir); |   const staticPath = genStaticPath(staticDir); | ||||||
|   copyFileDir( |   copyFileDir( | ||||||
| @@ -80,6 +96,11 @@ function copyMapPanel(staticDir) { | |||||||
|   ); |   ); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | gulp.task("copy-locale-data", async () => { | ||||||
|  |   const staticDir = paths.app_output_static; | ||||||
|  |   copyLocaleData(staticDir); | ||||||
|  | }); | ||||||
|  |  | ||||||
| gulp.task("copy-translations-app", async () => { | gulp.task("copy-translations-app", async () => { | ||||||
|   const staticDir = paths.app_output_static; |   const staticDir = paths.app_output_static; | ||||||
|   copyTranslations(staticDir); |   copyTranslations(staticDir); | ||||||
| @@ -90,6 +111,11 @@ gulp.task("copy-translations-supervisor", async () => { | |||||||
|   copyTranslations(staticDir); |   copyTranslations(staticDir); | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | gulp.task("copy-locale-data-supervisor", async () => { | ||||||
|  |   const staticDir = paths.hassio_output_static; | ||||||
|  |   copyLocaleData(staticDir); | ||||||
|  | }); | ||||||
|  |  | ||||||
| gulp.task("copy-static-app", async () => { | gulp.task("copy-static-app", async () => { | ||||||
|   const staticDir = paths.app_output_static; |   const staticDir = paths.app_output_static; | ||||||
|   // Basic static files |   // Basic static files | ||||||
| @@ -99,10 +125,14 @@ gulp.task("copy-static-app", async () => { | |||||||
|   copyPolyfills(staticDir); |   copyPolyfills(staticDir); | ||||||
|   copyFonts(staticDir); |   copyFonts(staticDir); | ||||||
|   copyTranslations(staticDir); |   copyTranslations(staticDir); | ||||||
|  |   copyLocaleData(staticDir); | ||||||
|   copyMdiIcons(staticDir); |   copyMdiIcons(staticDir); | ||||||
|  |  | ||||||
|   // Panel assets |   // Panel assets | ||||||
|   copyMapPanel(staticDir); |   copyMapPanel(staticDir); | ||||||
|  |  | ||||||
|  |   // Qr Scanner assets | ||||||
|  |   copyQrScannerWorker(staticDir); | ||||||
| }); | }); | ||||||
|  |  | ||||||
| gulp.task("copy-static-demo", async () => { | gulp.task("copy-static-demo", async () => { | ||||||
| @@ -119,6 +149,7 @@ gulp.task("copy-static-demo", async () => { | |||||||
|   copyMapPanel(paths.demo_output_static); |   copyMapPanel(paths.demo_output_static); | ||||||
|   copyFonts(paths.demo_output_static); |   copyFonts(paths.demo_output_static); | ||||||
|   copyTranslations(paths.demo_output_static); |   copyTranslations(paths.demo_output_static); | ||||||
|  |   copyLocaleData(paths.demo_output_static); | ||||||
|   copyMdiIcons(paths.demo_output_static); |   copyMdiIcons(paths.demo_output_static); | ||||||
| }); | }); | ||||||
|  |  | ||||||
| @@ -133,6 +164,7 @@ gulp.task("copy-static-cast", async () => { | |||||||
|   copyMapPanel(paths.cast_output_static); |   copyMapPanel(paths.cast_output_static); | ||||||
|   copyFonts(paths.cast_output_static); |   copyFonts(paths.cast_output_static); | ||||||
|   copyTranslations(paths.cast_output_static); |   copyTranslations(paths.cast_output_static); | ||||||
|  |   copyLocaleData(paths.cast_output_static); | ||||||
|   copyMdiIcons(paths.cast_output_static); |   copyMdiIcons(paths.cast_output_static); | ||||||
| }); | }); | ||||||
|  |  | ||||||
| @@ -148,5 +180,6 @@ gulp.task("copy-static-gallery", async () => { | |||||||
|   copyMapPanel(paths.gallery_output_static); |   copyMapPanel(paths.gallery_output_static); | ||||||
|   copyFonts(paths.gallery_output_static); |   copyFonts(paths.gallery_output_static); | ||||||
|   copyTranslations(paths.gallery_output_static); |   copyTranslations(paths.gallery_output_static); | ||||||
|  |   copyLocaleData(paths.gallery_output_static); | ||||||
|   copyMdiIcons(paths.gallery_output_static); |   copyMdiIcons(paths.gallery_output_static); | ||||||
| }); | }); | ||||||
|   | |||||||
| @@ -22,17 +22,40 @@ const getMeta = () => { | |||||||
|     const svg = fs.readFileSync(`${ICON_PATH}/${icon.name}.svg`, { |     const svg = fs.readFileSync(`${ICON_PATH}/${icon.name}.svg`, { | ||||||
|       encoding, |       encoding, | ||||||
|     }); |     }); | ||||||
|     return { path: svg.match(/ d="([^"]+)"/)[1], name: icon.name }; |     return { | ||||||
|  |       path: svg.match(/ d="([^"]+)"/)[1], | ||||||
|  |       name: icon.name, | ||||||
|  |       tags: icon.tags, | ||||||
|  |       aliases: icon.aliases, | ||||||
|  |     }; | ||||||
|   }); |   }); | ||||||
| }; | }; | ||||||
|  |  | ||||||
| const addRemovedMeta = (meta) => { | const addRemovedMeta = (meta) => { | ||||||
|   const file = fs.readFileSync(REMOVED_ICONS_PATH, { encoding }); |   const file = fs.readFileSync(REMOVED_ICONS_PATH, { encoding }); | ||||||
|   const removed = JSON.parse(file); |   const removed = JSON.parse(file); | ||||||
|   const combinedMeta = [...meta, ...removed]; |   const removedMeta = removed.map((removeIcon) => ({ | ||||||
|  |     path: removeIcon.path, | ||||||
|  |     name: removeIcon.name, | ||||||
|  |     tags: [], | ||||||
|  |     aliases: [], | ||||||
|  |   })); | ||||||
|  |   const combinedMeta = [...meta, ...removedMeta]; | ||||||
|   return combinedMeta.sort((a, b) => a.name.localeCompare(b.name)); |   return combinedMeta.sort((a, b) => a.name.localeCompare(b.name)); | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | const homeAutomationTag = "Home Automation"; | ||||||
|  |  | ||||||
|  | const orderMeta = (meta) => { | ||||||
|  |   const homeAutomationMeta = meta.filter((icon) => | ||||||
|  |     icon.tags.includes(homeAutomationTag) | ||||||
|  |   ); | ||||||
|  |   const otherMeta = meta.filter( | ||||||
|  |     (icon) => !icon.tags.includes(homeAutomationTag) | ||||||
|  |   ); | ||||||
|  |   return [...homeAutomationMeta, ...otherMeta]; | ||||||
|  | }; | ||||||
|  |  | ||||||
| const splitBySize = (meta) => { | const splitBySize = (meta) => { | ||||||
|   const chunks = []; |   const chunks = []; | ||||||
|   const CHUNK_SIZE = 50000; |   const CHUNK_SIZE = 50000; | ||||||
| @@ -77,8 +100,10 @@ const findDifferentiator = (curString, prevString) => { | |||||||
| }; | }; | ||||||
|  |  | ||||||
| gulp.task("gen-icons-json", (done) => { | gulp.task("gen-icons-json", (done) => { | ||||||
|   const meta = addRemovedMeta(getMeta()); |   const meta = getMeta(); | ||||||
|   const split = splitBySize(meta); |  | ||||||
|  |   const metaAndRemoved = addRemovedMeta(meta); | ||||||
|  |   const split = splitBySize(metaAndRemoved); | ||||||
|  |  | ||||||
|   if (!fs.existsSync(OUTPUT_DIR)) { |   if (!fs.existsSync(OUTPUT_DIR)) { | ||||||
|     fs.mkdirSync(OUTPUT_DIR, { recursive: true }); |     fs.mkdirSync(OUTPUT_DIR, { recursive: true }); | ||||||
| @@ -116,5 +141,18 @@ gulp.task("gen-icons-json", (done) => { | |||||||
|     JSON.stringify({ version: package.version, parts }) |     JSON.stringify({ version: package.version, parts }) | ||||||
|   ); |   ); | ||||||
|  |  | ||||||
|  |   fs.writeFileSync( | ||||||
|  |     path.resolve(OUTPUT_DIR, "iconList.json"), | ||||||
|  |     JSON.stringify( | ||||||
|  |       orderMeta(meta).map((icon) => ({ | ||||||
|  |         name: icon.name, | ||||||
|  |         keywords: [ | ||||||
|  |           ...icon.tags.map((t) => t.toLowerCase().replace(/\s\/\s/g, " ")), | ||||||
|  |           ...icon.aliases, | ||||||
|  |         ], | ||||||
|  |       })) | ||||||
|  |     ) | ||||||
|  |   ); | ||||||
|  |  | ||||||
|   done(); |   done(); | ||||||
| }); | }); | ||||||
|   | |||||||
| @@ -1,9 +1,6 @@ | |||||||
| const gulp = require("gulp"); | const gulp = require("gulp"); | ||||||
| const fs = require("fs"); |  | ||||||
| const path = require("path"); |  | ||||||
|  |  | ||||||
| const env = require("../env"); | const env = require("../env"); | ||||||
| const paths = require("../paths"); |  | ||||||
|  |  | ||||||
| require("./clean.js"); | require("./clean.js"); | ||||||
| require("./gen-icons-json.js"); | require("./gen-icons-json.js"); | ||||||
| @@ -20,10 +17,11 @@ gulp.task( | |||||||
|       process.env.NODE_ENV = "development"; |       process.env.NODE_ENV = "development"; | ||||||
|     }, |     }, | ||||||
|     "clean-hassio", |     "clean-hassio", | ||||||
|     "gen-icons-json", |  | ||||||
|     "gen-index-hassio-dev", |     "gen-index-hassio-dev", | ||||||
|     "build-supervisor-translations", |     "build-supervisor-translations", | ||||||
|     "copy-translations-supervisor", |     "copy-translations-supervisor", | ||||||
|  |     "build-locale-data", | ||||||
|  |     "copy-locale-data-supervisor", | ||||||
|     env.useRollup() ? "rollup-watch-hassio" : "webpack-watch-hassio" |     env.useRollup() ? "rollup-watch-hassio" : "webpack-watch-hassio" | ||||||
|   ) |   ) | ||||||
| ); | ); | ||||||
| @@ -35,9 +33,10 @@ gulp.task( | |||||||
|       process.env.NODE_ENV = "production"; |       process.env.NODE_ENV = "production"; | ||||||
|     }, |     }, | ||||||
|     "clean-hassio", |     "clean-hassio", | ||||||
|     "gen-icons-json", |  | ||||||
|     "build-supervisor-translations", |     "build-supervisor-translations", | ||||||
|     "copy-translations-supervisor", |     "copy-translations-supervisor", | ||||||
|  |     "build-locale-data", | ||||||
|  |     "copy-locale-data-supervisor", | ||||||
|     env.useRollup() ? "rollup-prod-hassio" : "webpack-prod-hassio", |     env.useRollup() ? "rollup-prod-hassio" : "webpack-prod-hassio", | ||||||
|     "gen-index-hassio-prod", |     "gen-index-hassio-prod", | ||||||
|     ...// Don't compress running tests |     ...// Don't compress running tests | ||||||
|   | |||||||
							
								
								
									
										74
									
								
								build-scripts/gulp/locale-data.js
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										74
									
								
								build-scripts/gulp/locale-data.js
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,74 @@ | |||||||
|  | /* eslint-disable @typescript-eslint/no-var-requires */ | ||||||
|  |  | ||||||
|  | const del = require("del"); | ||||||
|  | const path = require("path"); | ||||||
|  | const gulp = require("gulp"); | ||||||
|  | const fs = require("fs"); | ||||||
|  | const paths = require("../paths"); | ||||||
|  |  | ||||||
|  | const outDir = "build/locale-data"; | ||||||
|  |  | ||||||
|  | gulp.task("clean-locale-data", () => del([outDir])); | ||||||
|  |  | ||||||
|  | gulp.task("ensure-locale-data-build-dir", (done) => { | ||||||
|  |   if (!fs.existsSync(outDir)) { | ||||||
|  |     fs.mkdirSync(outDir, { recursive: true }); | ||||||
|  |   } | ||||||
|  |   done(); | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | const modules = { | ||||||
|  |   "intl-relativetimeformat": "RelativeTimeFormat", | ||||||
|  |   "intl-datetimeformat": "DateTimeFormat", | ||||||
|  |   "intl-numberformat": "NumberFormat", | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | gulp.task("create-locale-data", (done) => { | ||||||
|  |   const translationMeta = JSON.parse( | ||||||
|  |     fs.readFileSync( | ||||||
|  |       path.join(paths.translations_src, "translationMetadata.json") | ||||||
|  |     ) | ||||||
|  |   ); | ||||||
|  |   Object.entries(modules).forEach(([module, className]) => { | ||||||
|  |     Object.keys(translationMeta).forEach((lang) => { | ||||||
|  |       try { | ||||||
|  |         const localeData = String( | ||||||
|  |           fs.readFileSync( | ||||||
|  |             require.resolve(`@formatjs/${module}/locale-data/${lang}.js`) | ||||||
|  |           ) | ||||||
|  |         ) | ||||||
|  |           .replace( | ||||||
|  |             new RegExp( | ||||||
|  |               `\\/\\*\\s*@generated\\s*\\*\\/\\s*\\/\\/\\s*prettier-ignore\\s*if\\s*\\(Intl\\.${className}\\s*&&\\s*typeof\\s*Intl\\.${className}\\.__addLocaleData\\s*===\\s*'function'\\)\\s*{\\s*Intl\\.${className}\\.__addLocaleData\\(`, | ||||||
|  |               "im" | ||||||
|  |             ), | ||||||
|  |             "" | ||||||
|  |           ) | ||||||
|  |           .replace(/\)\s*}/im, ""); | ||||||
|  |         // make sure we have valid JSON | ||||||
|  |         JSON.parse(localeData); | ||||||
|  |         if (!fs.existsSync(path.join(outDir, module))) { | ||||||
|  |           fs.mkdirSync(path.join(outDir, module), { recursive: true }); | ||||||
|  |         } | ||||||
|  |         fs.writeFileSync( | ||||||
|  |           path.join(outDir, `${module}/${lang}.json`), | ||||||
|  |           localeData | ||||||
|  |         ); | ||||||
|  |       } catch (e) { | ||||||
|  |         if (e.code !== "MODULE_NOT_FOUND") { | ||||||
|  |           throw e; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |     done(); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | gulp.task( | ||||||
|  |   "build-locale-data", | ||||||
|  |   gulp.series( | ||||||
|  |     "clean-locale-data", | ||||||
|  |     "ensure-locale-data-build-dir", | ||||||
|  |     "create-locale-data" | ||||||
|  |   ) | ||||||
|  | ); | ||||||
| @@ -1,3 +1,5 @@ | |||||||
|  | /* eslint-disable @typescript-eslint/no-var-requires */ | ||||||
|  |  | ||||||
| const crypto = require("crypto"); | const crypto = require("crypto"); | ||||||
| const del = require("del"); | const del = require("del"); | ||||||
| const path = require("path"); | const path = require("path"); | ||||||
| @@ -15,7 +17,7 @@ const paths = require("../paths"); | |||||||
|  |  | ||||||
| const inFrontendDir = "translations/frontend"; | const inFrontendDir = "translations/frontend"; | ||||||
| const inBackendDir = "translations/backend"; | const inBackendDir = "translations/backend"; | ||||||
| const workDir = "build-translations"; | const workDir = "build/translations"; | ||||||
| const fullDir = workDir + "/full"; | const fullDir = workDir + "/full"; | ||||||
| const coreDir = workDir + "/core"; | const coreDir = workDir + "/core"; | ||||||
| const outDir = workDir + "/output"; | const outDir = workDir + "/output"; | ||||||
| @@ -26,13 +28,6 @@ gulp.task("translations-enable-merge-backend", (done) => { | |||||||
|   done(); |   done(); | ||||||
| }); | }); | ||||||
|  |  | ||||||
| String.prototype.rsplit = function (sep, maxsplit) { |  | ||||||
|   var split = this.split(sep); |  | ||||||
|   return maxsplit |  | ||||||
|     ? [split.slice(0, -maxsplit).join(sep)].concat(split.slice(-maxsplit)) |  | ||||||
|     : split; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| // Panel translations which should be split from the core translations. | // Panel translations which should be split from the core translations. | ||||||
| const TRANSLATION_FRAGMENTS = Object.keys( | const TRANSLATION_FRAGMENTS = Object.keys( | ||||||
|   require("../../src/translations/en.json").ui.panel |   require("../../src/translations/en.json").ui.panel | ||||||
| @@ -40,7 +35,7 @@ const TRANSLATION_FRAGMENTS = Object.keys( | |||||||
|  |  | ||||||
| function recursiveFlatten(prefix, data) { | function recursiveFlatten(prefix, data) { | ||||||
|   let output = {}; |   let output = {}; | ||||||
|   Object.keys(data).forEach(function (key) { |   Object.keys(data).forEach((key) => { | ||||||
|     if (typeof data[key] === "object") { |     if (typeof data[key] === "object") { | ||||||
|       output = { |       output = { | ||||||
|         ...output, |         ...output, | ||||||
| @@ -101,15 +96,19 @@ function lokaliseTransform(data, original, file) { | |||||||
|     if (value instanceof Object) { |     if (value instanceof Object) { | ||||||
|       output[key] = lokaliseTransform(value, original, file); |       output[key] = lokaliseTransform(value, original, file); | ||||||
|     } else { |     } else { | ||||||
|       output[key] = value.replace(re_key_reference, (match, key) => { |       output[key] = value.replace(re_key_reference, (_match, lokalise_key) => { | ||||||
|         const replace = key.split("::").reduce((tr, k) => { |         const replace = lokalise_key.split("::").reduce((tr, k) => { | ||||||
|           if (!tr) { |           if (!tr) { | ||||||
|             throw Error(`Invalid key placeholder ${key} in ${file.path}`); |             throw Error( | ||||||
|  |               `Invalid key placeholder ${lokalise_key} in ${file.path}` | ||||||
|  |             ); | ||||||
|           } |           } | ||||||
|           return tr[k]; |           return tr[k]; | ||||||
|         }, original); |         }, original); | ||||||
|         if (typeof replace !== "string") { |         if (typeof replace !== "string") { | ||||||
|           throw Error(`Invalid key placeholder ${key} in ${file.path}`); |           throw Error( | ||||||
|  |             `Invalid key placeholder ${lokalise_key} in ${file.path}` | ||||||
|  |           ); | ||||||
|         } |         } | ||||||
|         return replace; |         return replace; | ||||||
|       }); |       }); | ||||||
| @@ -118,18 +117,16 @@ function lokaliseTransform(data, original, file) { | |||||||
|   return output; |   return output; | ||||||
| } | } | ||||||
|  |  | ||||||
| gulp.task("clean-translations", function () { | gulp.task("clean-translations", () => del([workDir])); | ||||||
|   return del([workDir]); |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| gulp.task("ensure-translations-build-dir", (done) => { | gulp.task("ensure-translations-build-dir", (done) => { | ||||||
|   if (!fs.existsSync(workDir)) { |   if (!fs.existsSync(workDir)) { | ||||||
|     fs.mkdirSync(workDir); |     fs.mkdirSync(workDir, { recursive: true }); | ||||||
|   } |   } | ||||||
|   done(); |   done(); | ||||||
| }); | }); | ||||||
|  |  | ||||||
| gulp.task("create-test-metadata", function (cb) { | gulp.task("create-test-metadata", (cb) => { | ||||||
|   fs.writeFile( |   fs.writeFile( | ||||||
|     workDir + "/testMetadata.json", |     workDir + "/testMetadata.json", | ||||||
|     JSON.stringify({ |     JSON.stringify({ | ||||||
| @@ -143,17 +140,13 @@ gulp.task("create-test-metadata", function (cb) { | |||||||
|  |  | ||||||
| gulp.task( | gulp.task( | ||||||
|   "create-test-translation", |   "create-test-translation", | ||||||
|   gulp.series("create-test-metadata", function createTestTranslation() { |   gulp.series("create-test-metadata", () => | ||||||
|     return gulp |     gulp | ||||||
|       .src(path.join(paths.translations_src, "en.json")) |       .src(path.join(paths.translations_src, "en.json")) | ||||||
|       .pipe( |       .pipe(transform((data, _file) => recursiveEmpty(data))) | ||||||
|         transform(function (data, file) { |  | ||||||
|           return recursiveEmpty(data); |  | ||||||
|         }) |  | ||||||
|       ) |  | ||||||
|       .pipe(rename("test.json")) |       .pipe(rename("test.json")) | ||||||
|       .pipe(gulp.dest(workDir)); |       .pipe(gulp.dest(workDir)) | ||||||
|   }) |   ) | ||||||
| ); | ); | ||||||
|  |  | ||||||
| /** | /** | ||||||
| @@ -165,7 +158,7 @@ gulp.task( | |||||||
|  * project is buildable immediately after merging new translation keys, since |  * project is buildable immediately after merging new translation keys, since | ||||||
|  * the Lokalise update to translations/en.json will not happen immediately. |  * the Lokalise update to translations/en.json will not happen immediately. | ||||||
|  */ |  */ | ||||||
| gulp.task("build-master-translation", function () { | gulp.task("build-master-translation", () => { | ||||||
|   const src = [path.join(paths.translations_src, "en.json")]; |   const src = [path.join(paths.translations_src, "en.json")]; | ||||||
|  |  | ||||||
|   if (mergeBackend) { |   if (mergeBackend) { | ||||||
| @@ -174,11 +167,7 @@ gulp.task("build-master-translation", function () { | |||||||
|  |  | ||||||
|   return gulp |   return gulp | ||||||
|     .src(src) |     .src(src) | ||||||
|     .pipe( |     .pipe(transform((data, file) => lokaliseTransform(data, data, file))) | ||||||
|       transform(function (data, file) { |  | ||||||
|         return lokaliseTransform(data, data, file); |  | ||||||
|       }) |  | ||||||
|     ) |  | ||||||
|     .pipe( |     .pipe( | ||||||
|       merge({ |       merge({ | ||||||
|         fileName: "translationMaster.json", |         fileName: "translationMaster.json", | ||||||
| @@ -187,18 +176,14 @@ gulp.task("build-master-translation", function () { | |||||||
|     .pipe(gulp.dest(workDir)); |     .pipe(gulp.dest(workDir)); | ||||||
| }); | }); | ||||||
|  |  | ||||||
| gulp.task("build-merged-translations", function () { | gulp.task("build-merged-translations", () => | ||||||
|   return gulp |   gulp | ||||||
|     .src([inFrontendDir + "/*.json", workDir + "/test.json"], { |     .src([inFrontendDir + "/*.json", workDir + "/test.json"], { | ||||||
|       allowEmpty: true, |       allowEmpty: true, | ||||||
|     }) |     }) | ||||||
|  |     .pipe(transform((data, file) => lokaliseTransform(data, data, file))) | ||||||
|     .pipe( |     .pipe( | ||||||
|       transform(function (data, file) { |       foreach((stream, file) => { | ||||||
|         return lokaliseTransform(data, data, file); |  | ||||||
|       }) |  | ||||||
|     ) |  | ||||||
|     .pipe( |  | ||||||
|       foreach(function (stream, file) { |  | ||||||
|         // For each language generate a merged json file. It begins with the master |         // For each language generate a merged json file. It begins with the master | ||||||
|         // translation as a failsafe for untranslated strings, and merges all parent |         // translation as a failsafe for untranslated strings, and merges all parent | ||||||
|         // tags into one file for each specific subtag |         // tags into one file for each specific subtag | ||||||
| @@ -230,17 +215,17 @@ gulp.task("build-merged-translations", function () { | |||||||
|           ) |           ) | ||||||
|           .pipe(gulp.dest(fullDir)); |           .pipe(gulp.dest(fullDir)); | ||||||
|       }) |       }) | ||||||
|     ); |     ) | ||||||
| }); | ); | ||||||
|  |  | ||||||
| var taskName; | let taskName; | ||||||
|  |  | ||||||
| const splitTasks = []; | const splitTasks = []; | ||||||
| TRANSLATION_FRAGMENTS.forEach((fragment) => { | TRANSLATION_FRAGMENTS.forEach((fragment) => { | ||||||
|   taskName = "build-translation-fragment-" + fragment; |   taskName = "build-translation-fragment-" + fragment; | ||||||
|   gulp.task(taskName, function () { |   gulp.task(taskName, () => | ||||||
|     // Return only the translations for this fragment. |     // Return only the translations for this fragment. | ||||||
|     return gulp |     gulp | ||||||
|       .src(fullDir + "/*.json") |       .src(fullDir + "/*.json") | ||||||
|       .pipe( |       .pipe( | ||||||
|         transform((data) => ({ |         transform((data) => ({ | ||||||
| @@ -251,18 +236,18 @@ TRANSLATION_FRAGMENTS.forEach((fragment) => { | |||||||
|           }, |           }, | ||||||
|         })) |         })) | ||||||
|       ) |       ) | ||||||
|       .pipe(gulp.dest(workDir + "/" + fragment)); |       .pipe(gulp.dest(workDir + "/" + fragment)) | ||||||
|   }); |   ); | ||||||
|   splitTasks.push(taskName); |   splitTasks.push(taskName); | ||||||
| }); | }); | ||||||
|  |  | ||||||
| taskName = "build-translation-core"; | taskName = "build-translation-core"; | ||||||
| gulp.task(taskName, function () { | gulp.task(taskName, () => | ||||||
|   // Remove the fragment translations from the core translation. |   // Remove the fragment translations from the core translation. | ||||||
|   return gulp |   gulp | ||||||
|     .src(fullDir + "/*.json") |     .src(fullDir + "/*.json") | ||||||
|     .pipe( |     .pipe( | ||||||
|       transform((data, file) => { |       transform((data, _file) => { | ||||||
|         TRANSLATION_FRAGMENTS.forEach((fragment) => { |         TRANSLATION_FRAGMENTS.forEach((fragment) => { | ||||||
|           delete data.ui.panel[fragment]; |           delete data.ui.panel[fragment]; | ||||||
|         }); |         }); | ||||||
| @@ -270,14 +255,14 @@ gulp.task(taskName, function () { | |||||||
|         return data; |         return data; | ||||||
|       }) |       }) | ||||||
|     ) |     ) | ||||||
|     .pipe(gulp.dest(coreDir)); |     .pipe(gulp.dest(coreDir)) | ||||||
| }); | ); | ||||||
|  |  | ||||||
| splitTasks.push(taskName); | splitTasks.push(taskName); | ||||||
|  |  | ||||||
| gulp.task("build-flattened-translations", function () { | gulp.task("build-flattened-translations", () => | ||||||
|   // Flatten the split versions of our translations, and move them into outDir |   // Flatten the split versions of our translations, and move them into outDir | ||||||
|   return gulp |   gulp | ||||||
|     .src( |     .src( | ||||||
|       TRANSLATION_FRAGMENTS.map( |       TRANSLATION_FRAGMENTS.map( | ||||||
|         (fragment) => workDir + "/" + fragment + "/*.json" |         (fragment) => workDir + "/" + fragment + "/*.json" | ||||||
| @@ -285,41 +270,45 @@ gulp.task("build-flattened-translations", function () { | |||||||
|       { base: workDir } |       { base: workDir } | ||||||
|     ) |     ) | ||||||
|     .pipe( |     .pipe( | ||||||
|       transform(function (data) { |       transform((data) => | ||||||
|         // Polymer.AppLocalizeBehavior requires flattened json |         // Polymer.AppLocalizeBehavior requires flattened json | ||||||
|         return flatten(data); |         flatten(data) | ||||||
|       }) |       ) | ||||||
|     ) |     ) | ||||||
|     .pipe( |     .pipe( | ||||||
|       rename((filePath) => { |       rename((filePath) => { | ||||||
|         if (filePath.dirname === "core") { |         if (filePath.dirname === "core") { | ||||||
|           filePath.dirname = ""; |           filePath.dirname = ""; | ||||||
|         } |         } | ||||||
|  |         // In dev we create the file with the fake hash in the filename | ||||||
|  |         if (!env.isProdBuild()) { | ||||||
|  |           filePath.basename += "-dev"; | ||||||
|  |         } | ||||||
|       }) |       }) | ||||||
|     ) |     ) | ||||||
|     .pipe(gulp.dest(outDir)); |     .pipe(gulp.dest(outDir)) | ||||||
| }); | ); | ||||||
|  |  | ||||||
| const fingerprints = {}; | const fingerprints = {}; | ||||||
|  |  | ||||||
| gulp.task( | gulp.task("build-translation-fingerprints", () => { | ||||||
|   "build-translation-fingerprints", |   // Fingerprint full file of each language | ||||||
|   function fingerprintTranslationFiles() { |   const files = fs.readdirSync(fullDir); | ||||||
|     // Fingerprint full file of each language |  | ||||||
|     const files = fs.readdirSync(fullDir); |  | ||||||
|  |  | ||||||
|     for (let i = 0; i < files.length; i++) { |   for (let i = 0; i < files.length; i++) { | ||||||
|       fingerprints[files[i].split(".")[0]] = { |     fingerprints[files[i].split(".")[0]] = { | ||||||
|         // In dev we create fake hashes |       // In dev we create fake hashes | ||||||
|         hash: env.isProdBuild() |       hash: env.isProdBuild() | ||||||
|           ? crypto |         ? crypto | ||||||
|               .createHash("md5") |             .createHash("md5") | ||||||
|               .update(fs.readFileSync(path.join(fullDir, files[i]), "utf-8")) |             .update(fs.readFileSync(path.join(fullDir, files[i]), "utf-8")) | ||||||
|               .digest("hex") |             .digest("hex") | ||||||
|           : "dev", |         : "dev", | ||||||
|       }; |     }; | ||||||
|     } |   } | ||||||
|  |  | ||||||
|  |   // In dev we create the file with the fake hash in the filename | ||||||
|  |   if (env.isProdBuild()) { | ||||||
|     mapFiles(outDir, ".json", (filename) => { |     mapFiles(outDir, ".json", (filename) => { | ||||||
|       const parsed = path.parse(filename); |       const parsed = path.parse(filename); | ||||||
|  |  | ||||||
| @@ -335,35 +324,43 @@ gulp.task( | |||||||
|         }` |         }` | ||||||
|       ); |       ); | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     const stream = source("translationFingerprints.json"); |  | ||||||
|     stream.write(JSON.stringify(fingerprints)); |  | ||||||
|     process.nextTick(() => stream.end()); |  | ||||||
|     return stream.pipe(vinylBuffer()).pipe(gulp.dest(workDir)); |  | ||||||
|   } |   } | ||||||
| ); |  | ||||||
|  |  | ||||||
| gulp.task("build-translation-fragment-supervisor", function () { |   const stream = source("translationFingerprints.json"); | ||||||
|   return gulp |   stream.write(JSON.stringify(fingerprints)); | ||||||
|  |   process.nextTick(() => stream.end()); | ||||||
|  |   return stream.pipe(vinylBuffer()).pipe(gulp.dest(workDir)); | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | gulp.task("build-translation-fragment-supervisor", () => | ||||||
|  |   gulp | ||||||
|     .src(fullDir + "/*.json") |     .src(fullDir + "/*.json") | ||||||
|     .pipe(transform((data) => data.supervisor)) |     .pipe(transform((data) => data.supervisor)) | ||||||
|     .pipe(gulp.dest(workDir + "/supervisor")); |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| gulp.task("build-translation-flatten-supervisor", function () { |  | ||||||
|   return gulp |  | ||||||
|     .src(workDir + "/supervisor/*.json") |  | ||||||
|     .pipe( |     .pipe( | ||||||
|       transform(function (data) { |       rename((filePath) => { | ||||||
|         // Polymer.AppLocalizeBehavior requires flattened json |         // In dev we create the file with the fake hash in the filename | ||||||
|         return flatten(data); |         if (!env.isProdBuild()) { | ||||||
|  |           filePath.basename += "-dev"; | ||||||
|  |         } | ||||||
|       }) |       }) | ||||||
|     ) |     ) | ||||||
|     .pipe(gulp.dest(outDir)); |     .pipe(gulp.dest(workDir + "/supervisor")) | ||||||
| }); | ); | ||||||
|  |  | ||||||
| gulp.task("build-translation-write-metadata", function writeMetadata() { | gulp.task("build-translation-flatten-supervisor", () => | ||||||
|   return gulp |   gulp | ||||||
|  |     .src(workDir + "/supervisor/*.json") | ||||||
|  |     .pipe( | ||||||
|  |       transform((data) => | ||||||
|  |         // Polymer.AppLocalizeBehavior requires flattened json | ||||||
|  |         flatten(data) | ||||||
|  |       ) | ||||||
|  |     ) | ||||||
|  |     .pipe(gulp.dest(outDir)) | ||||||
|  | ); | ||||||
|  |  | ||||||
|  | gulp.task("build-translation-write-metadata", () => | ||||||
|  |   gulp | ||||||
|     .src( |     .src( | ||||||
|       [ |       [ | ||||||
|         path.join(paths.translations_src, "translationMetadata.json"), |         path.join(paths.translations_src, "translationMetadata.json"), | ||||||
| @@ -374,13 +371,14 @@ gulp.task("build-translation-write-metadata", function writeMetadata() { | |||||||
|     ) |     ) | ||||||
|     .pipe(merge({})) |     .pipe(merge({})) | ||||||
|     .pipe( |     .pipe( | ||||||
|       transform(function (data) { |       transform((data) => { | ||||||
|         const newData = {}; |         const newData = {}; | ||||||
|         Object.entries(data).forEach(([key, value]) => { |         Object.entries(data).forEach(([key, value]) => { | ||||||
|           // Filter out translations without native name. |           // Filter out translations without native name. | ||||||
|           if (value.nativeName) { |           if (value.nativeName) { | ||||||
|             newData[key] = value; |             newData[key] = value; | ||||||
|           } else { |           } else { | ||||||
|  |             // eslint-disable-next-line no-console | ||||||
|             console.warn( |             console.warn( | ||||||
|               `Skipping language ${key}. Native name was not translated.` |               `Skipping language ${key}. Native name was not translated.` | ||||||
|             ); |             ); | ||||||
| @@ -396,19 +394,26 @@ gulp.task("build-translation-write-metadata", function writeMetadata() { | |||||||
|       })) |       })) | ||||||
|     ) |     ) | ||||||
|     .pipe(rename("translationMetadata.json")) |     .pipe(rename("translationMetadata.json")) | ||||||
|     .pipe(gulp.dest(workDir)); |     .pipe(gulp.dest(workDir)) | ||||||
| }); | ); | ||||||
|  |  | ||||||
|  | gulp.task( | ||||||
|  |   "create-translations", | ||||||
|  |   gulp.series( | ||||||
|  |     env.isProdBuild() ? (done) => done() : "create-test-translation", | ||||||
|  |     "build-master-translation", | ||||||
|  |     "build-merged-translations", | ||||||
|  |     gulp.parallel(...splitTasks), | ||||||
|  |     "build-flattened-translations" | ||||||
|  |   ) | ||||||
|  | ); | ||||||
|  |  | ||||||
| gulp.task( | gulp.task( | ||||||
|   "build-translations", |   "build-translations", | ||||||
|   gulp.series( |   gulp.series( | ||||||
|     "clean-translations", |     "clean-translations", | ||||||
|     "ensure-translations-build-dir", |     "ensure-translations-build-dir", | ||||||
|     env.isProdBuild() ? (done) => done() : "create-test-translation", |     "create-translations", | ||||||
|     "build-master-translation", |  | ||||||
|     "build-merged-translations", |  | ||||||
|     gulp.parallel(...splitTasks), |  | ||||||
|     "build-flattened-translations", |  | ||||||
|     "build-translation-fingerprints", |     "build-translation-fingerprints", | ||||||
|     "build-translation-write-metadata" |     "build-translation-write-metadata" | ||||||
|   ) |   ) | ||||||
|   | |||||||
| @@ -1,4 +1,6 @@ | |||||||
|  | /* eslint-disable @typescript-eslint/no-var-requires */ | ||||||
| // Tasks to run webpack. | // Tasks to run webpack. | ||||||
|  | const fs = require("fs"); | ||||||
| const gulp = require("gulp"); | const gulp = require("gulp"); | ||||||
| const webpack = require("webpack"); | const webpack = require("webpack"); | ||||||
| const WebpackDevServer = require("webpack-dev-server"); | const WebpackDevServer = require("webpack-dev-server"); | ||||||
| @@ -18,6 +20,13 @@ const bothBuilds = (createConfigFunc, params) => [ | |||||||
|   createConfigFunc({ ...params, latestBuild: false }), |   createConfigFunc({ ...params, latestBuild: false }), | ||||||
| ]; | ]; | ||||||
|  |  | ||||||
|  | const isWsl = | ||||||
|  |   fs.existsSync("/proc/version") && | ||||||
|  |   fs | ||||||
|  |     .readFileSync("/proc/version", "utf-8") | ||||||
|  |     .toLocaleLowerCase() | ||||||
|  |     .includes("microsoft"); | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * @param {{ |  * @param {{ | ||||||
|  *   compiler: import("webpack").Compiler, |  *   compiler: import("webpack").Compiler, | ||||||
| @@ -26,26 +35,29 @@ const bothBuilds = (createConfigFunc, params) => [ | |||||||
|  *   listenHost?: string |  *   listenHost?: string | ||||||
|  * }} |  * }} | ||||||
|  */ |  */ | ||||||
| const runDevServer = ({ | const runDevServer = async ({ | ||||||
|   compiler, |   compiler, | ||||||
|   contentBase, |   contentBase, | ||||||
|   port, |   port, | ||||||
|   listenHost = "localhost", |   listenHost = "localhost", | ||||||
| }) => | }) => { | ||||||
|   new WebpackDevServer(compiler, { |   const server = new WebpackDevServer( | ||||||
|     open: true, |     { | ||||||
|     watchContentBase: true, |       open: true, | ||||||
|     contentBase, |       host: listenHost, | ||||||
|   }).listen(port, listenHost, function (err) { |       port, | ||||||
|     if (err) { |       static: { | ||||||
|       throw err; |         directory: contentBase, | ||||||
|     } |         watch: true, | ||||||
|     // Server listening |       }, | ||||||
|     log( |     }, | ||||||
|       "[webpack-dev-server]", |     compiler | ||||||
|       `Project is running at http://localhost:${port}` |   ); | ||||||
|     ); |  | ||||||
|   }); |   await server.start(); | ||||||
|  |   // Server listening | ||||||
|  |   log("[webpack-dev-server]", `Project is running at http://localhost:${port}`); | ||||||
|  | }; | ||||||
|  |  | ||||||
| const doneHandler = (done) => (err, stats) => { | const doneHandler = (done) => (err, stats) => { | ||||||
|   if (err) { |   if (err) { | ||||||
| @@ -57,6 +69,7 @@ const doneHandler = (done) => (err, stats) => { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   if (stats.hasErrors() || stats.hasWarnings()) { |   if (stats.hasErrors() || stats.hasWarnings()) { | ||||||
|  |     // eslint-disable-next-line no-console | ||||||
|     console.log(stats.toString("minimal")); |     console.log(stats.toString("minimal")); | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -78,13 +91,14 @@ const prodBuild = (conf) => | |||||||
|  |  | ||||||
| gulp.task("webpack-watch-app", () => { | gulp.task("webpack-watch-app", () => { | ||||||
|   // This command will run forever because we don't close compiler |   // This command will run forever because we don't close compiler | ||||||
|   webpack(createAppConfig({ isProdBuild: false, latestBuild: true })).watch( |   webpack( | ||||||
|     { ignored: /build-translations/ }, |     process.env.ES5 | ||||||
|     doneHandler() |       ? bothBuilds(createAppConfig, { isProdBuild: false }) | ||||||
|   ); |       : createAppConfig({ isProdBuild: false, latestBuild: true }) | ||||||
|  |   ).watch({ poll: isWsl }, doneHandler()); | ||||||
|   gulp.watch( |   gulp.watch( | ||||||
|     path.join(paths.translations_src, "en.json"), |     path.join(paths.translations_src, "en.json"), | ||||||
|     gulp.series("build-translations", "copy-translations-app") |     gulp.series("create-translations", "copy-translations-app") | ||||||
|   ); |   ); | ||||||
| }); | }); | ||||||
|  |  | ||||||
| @@ -96,13 +110,13 @@ gulp.task("webpack-prod-app", () => | |||||||
|   ) |   ) | ||||||
| ); | ); | ||||||
|  |  | ||||||
| gulp.task("webpack-dev-server-demo", () => { | gulp.task("webpack-dev-server-demo", () => | ||||||
|   runDevServer({ |   runDevServer({ | ||||||
|     compiler: webpack(bothBuilds(createDemoConfig, { isProdBuild: false })), |     compiler: webpack(bothBuilds(createDemoConfig, { isProdBuild: false })), | ||||||
|     contentBase: paths.demo_output_root, |     contentBase: paths.demo_output_root, | ||||||
|     port: 8090, |     port: 8090, | ||||||
|   }); |   }) | ||||||
| }); | ); | ||||||
|  |  | ||||||
| gulp.task("webpack-prod-demo", () => | gulp.task("webpack-prod-demo", () => | ||||||
|   prodBuild( |   prodBuild( | ||||||
| @@ -112,15 +126,15 @@ gulp.task("webpack-prod-demo", () => | |||||||
|   ) |   ) | ||||||
| ); | ); | ||||||
|  |  | ||||||
| gulp.task("webpack-dev-server-cast", () => { | gulp.task("webpack-dev-server-cast", () => | ||||||
|   runDevServer({ |   runDevServer({ | ||||||
|     compiler: webpack(bothBuilds(createCastConfig, { isProdBuild: false })), |     compiler: webpack(bothBuilds(createCastConfig, { isProdBuild: false })), | ||||||
|     contentBase: paths.cast_output_root, |     contentBase: paths.cast_output_root, | ||||||
|     port: 8080, |     port: 8080, | ||||||
|     // Accessible from the network, because that's how Cast hits it. |     // Accessible from the network, because that's how Cast hits it. | ||||||
|     listenHost: "0.0.0.0", |     listenHost: "0.0.0.0", | ||||||
|   }); |   }) | ||||||
| }); | ); | ||||||
|  |  | ||||||
| gulp.task("webpack-prod-cast", () => | gulp.task("webpack-prod-cast", () => | ||||||
|   prodBuild( |   prodBuild( | ||||||
| @@ -137,7 +151,7 @@ gulp.task("webpack-watch-hassio", () => { | |||||||
|       isProdBuild: false, |       isProdBuild: false, | ||||||
|       latestBuild: true, |       latestBuild: true, | ||||||
|     }) |     }) | ||||||
|   ).watch({ ignored: /build-translations/ }, doneHandler()); |   ).watch({ ignored: /build/, poll: isWsl }, doneHandler()); | ||||||
|  |  | ||||||
|   gulp.watch( |   gulp.watch( | ||||||
|     path.join(paths.translations_src, "en.json"), |     path.join(paths.translations_src, "en.json"), | ||||||
| @@ -153,14 +167,15 @@ gulp.task("webpack-prod-hassio", () => | |||||||
|   ) |   ) | ||||||
| ); | ); | ||||||
|  |  | ||||||
| gulp.task("webpack-dev-server-gallery", () => { | gulp.task("webpack-dev-server-gallery", () => | ||||||
|   runDevServer({ |   runDevServer({ | ||||||
|     // We don't use the es5 build, but the dev server will fuck up the publicPath if we don't |     // We don't use the es5 build, but the dev server will fuck up the publicPath if we don't | ||||||
|     compiler: webpack(bothBuilds(createGalleryConfig, { isProdBuild: false })), |     compiler: webpack(bothBuilds(createGalleryConfig, { isProdBuild: false })), | ||||||
|     contentBase: paths.gallery_output_root, |     contentBase: paths.gallery_output_root, | ||||||
|     port: 8100, |     port: 8100, | ||||||
|   }); |     listenHost: "0.0.0.0", | ||||||
| }); |   }) | ||||||
|  | ); | ||||||
|  |  | ||||||
| gulp.task("webpack-prod-gallery", () => | gulp.task("webpack-prod-gallery", () => | ||||||
|   prodBuild( |   prodBuild( | ||||||
|   | |||||||
| @@ -1,3 +1,4 @@ | |||||||
|  | /* eslint-disable @typescript-eslint/no-var-requires */ | ||||||
| const path = require("path"); | const path = require("path"); | ||||||
|  |  | ||||||
| module.exports = { | module.exports = { | ||||||
| @@ -25,6 +26,7 @@ module.exports = { | |||||||
|   cast_output_es5: path.resolve(__dirname, "../cast/dist/frontend_es5"), |   cast_output_es5: path.resolve(__dirname, "../cast/dist/frontend_es5"), | ||||||
|  |  | ||||||
|   gallery_dir: path.resolve(__dirname, "../gallery"), |   gallery_dir: path.resolve(__dirname, "../gallery"), | ||||||
|  |   gallery_build: path.resolve(__dirname, "../gallery/build"), | ||||||
|   gallery_output_root: path.resolve(__dirname, "../gallery/dist"), |   gallery_output_root: path.resolve(__dirname, "../gallery/dist"), | ||||||
|   gallery_output_latest: path.resolve( |   gallery_output_latest: path.resolve( | ||||||
|     __dirname, |     __dirname, | ||||||
|   | |||||||
| @@ -1,3 +1,4 @@ | |||||||
|  | /* eslint-disable @typescript-eslint/no-var-requires */ | ||||||
| const path = require("path"); | const path = require("path"); | ||||||
|  |  | ||||||
| const commonjs = require("@rollup/plugin-commonjs"); | const commonjs = require("@rollup/plugin-commonjs"); | ||||||
| @@ -32,88 +33,77 @@ const createRollupConfig = ({ | |||||||
|   publicPath, |   publicPath, | ||||||
|   dontHash, |   dontHash, | ||||||
|   isWDS, |   isWDS, | ||||||
| }) => { | }) => ({ | ||||||
|   return { |   /** | ||||||
|     /** |    * @type { import("rollup").InputOptions } | ||||||
|      * @type { import("rollup").InputOptions } |    */ | ||||||
|      */ |   inputOptions: { | ||||||
|     inputOptions: { |     input: entry, | ||||||
|       input: entry, |     // Some entry points contain no JavaScript. This setting silences a warning about that. | ||||||
|       // Some entry points contain no JavaScript. This setting silences a warning about that. |     // https://rollupjs.org/guide/en/#preserveentrysignatures | ||||||
|       // https://rollupjs.org/guide/en/#preserveentrysignatures |     preserveEntrySignatures: false, | ||||||
|       preserveEntrySignatures: false, |     plugins: [ | ||||||
|       plugins: [ |       ignore({ | ||||||
|         ignore({ |         files: bundle.emptyPackages({ latestBuild }), | ||||||
|           files: bundle.emptyPackages({ latestBuild }), |       }), | ||||||
|  |       resolve({ | ||||||
|  |         extensions, | ||||||
|  |         preferBuiltins: false, | ||||||
|  |         browser: true, | ||||||
|  |         rootDir: paths.polymer_dir, | ||||||
|  |       }), | ||||||
|  |       commonjs(), | ||||||
|  |       json(), | ||||||
|  |       babel({ | ||||||
|  |         ...bundle.babelOptions({ latestBuild }), | ||||||
|  |         extensions, | ||||||
|  |         babelHelpers: isWDS ? "inline" : "bundled", | ||||||
|  |       }), | ||||||
|  |       string({ | ||||||
|  |         // Import certain extensions as strings | ||||||
|  |         include: [path.join(paths.polymer_dir, "node_modules/**/*.css")], | ||||||
|  |       }), | ||||||
|  |       replace(bundle.definedVars({ isProdBuild, latestBuild, defineOverlay })), | ||||||
|  |       !isWDS && | ||||||
|  |         manifest({ | ||||||
|  |           publicPath, | ||||||
|         }), |         }), | ||||||
|         resolve({ |       !isWDS && worker(), | ||||||
|           extensions, |       !isWDS && dontHashPlugin({ dontHash }), | ||||||
|           preferBuiltins: false, |       !isWDS && isProdBuild && terser(bundle.terserOptions(latestBuild)), | ||||||
|           browser: true, |       !isWDS && | ||||||
|           rootDir: paths.polymer_dir, |         isStatsBuild && | ||||||
|  |         visualizer({ | ||||||
|  |           // https://github.com/btd/rollup-plugin-visualizer#options | ||||||
|  |           open: true, | ||||||
|  |           sourcemap: true, | ||||||
|         }), |         }), | ||||||
|         commonjs({ |     ].filter(Boolean), | ||||||
|           namedExports: { |   }, | ||||||
|             "js-yaml": ["safeDump", "safeLoad"], |   /** | ||||||
|           }, |    * @type { import("rollup").OutputOptions } | ||||||
|         }), |    */ | ||||||
|         json(), |   outputOptions: { | ||||||
|         babel({ |     // https://rollupjs.org/guide/en/#outputdir | ||||||
|           ...bundle.babelOptions({ latestBuild }), |     dir: outputPath, | ||||||
|           extensions, |     // https://rollupjs.org/guide/en/#outputformat | ||||||
|           exclude: bundle.babelExclude(), |     format: latestBuild ? "es" : "systemjs", | ||||||
|           babelHelpers: isWDS ? "inline" : "bundled", |     // https://rollupjs.org/guide/en/#outputexternallivebindings | ||||||
|         }), |     externalLiveBindings: false, | ||||||
|         string({ |     // https://rollupjs.org/guide/en/#outputentryfilenames | ||||||
|           // Import certain extensions as strings |     // https://rollupjs.org/guide/en/#outputchunkfilenames | ||||||
|           include: [path.join(paths.polymer_dir, "node_modules/**/*.css")], |     // https://rollupjs.org/guide/en/#outputassetfilenames | ||||||
|         }), |     entryFileNames: | ||||||
|         replace( |       isProdBuild && !isStatsBuild ? "[name]-[hash].js" : "[name].js", | ||||||
|           bundle.definedVars({ isProdBuild, latestBuild, defineOverlay }) |     chunkFileNames: isProdBuild && !isStatsBuild ? "c.[hash].js" : "[name].js", | ||||||
|         ), |     assetFileNames: isProdBuild && !isStatsBuild ? "a.[hash].js" : "[name].js", | ||||||
|         !isWDS && |     // https://rollupjs.org/guide/en/#outputsourcemap | ||||||
|           manifest({ |     sourcemap: isProdBuild ? true : "inline", | ||||||
|             publicPath, |   }, | ||||||
|           }), | }); | ||||||
|         !isWDS && worker(), |  | ||||||
|         !isWDS && dontHashPlugin({ dontHash }), |  | ||||||
|         !isWDS && isProdBuild && terser(bundle.terserOptions(latestBuild)), |  | ||||||
|         !isWDS && |  | ||||||
|           isStatsBuild && |  | ||||||
|           visualizer({ |  | ||||||
|             // https://github.com/btd/rollup-plugin-visualizer#options |  | ||||||
|             open: true, |  | ||||||
|             sourcemap: true, |  | ||||||
|           }), |  | ||||||
|       ].filter(Boolean), |  | ||||||
|     }, |  | ||||||
|     /** |  | ||||||
|      * @type { import("rollup").OutputOptions } |  | ||||||
|      */ |  | ||||||
|     outputOptions: { |  | ||||||
|       // https://rollupjs.org/guide/en/#outputdir |  | ||||||
|       dir: outputPath, |  | ||||||
|       // https://rollupjs.org/guide/en/#outputformat |  | ||||||
|       format: latestBuild ? "es" : "systemjs", |  | ||||||
|       // https://rollupjs.org/guide/en/#outputexternallivebindings |  | ||||||
|       externalLiveBindings: false, |  | ||||||
|       // https://rollupjs.org/guide/en/#outputentryfilenames |  | ||||||
|       // https://rollupjs.org/guide/en/#outputchunkfilenames |  | ||||||
|       // https://rollupjs.org/guide/en/#outputassetfilenames |  | ||||||
|       entryFileNames: |  | ||||||
|         isProdBuild && !isStatsBuild ? "[name]-[hash].js" : "[name].js", |  | ||||||
|       chunkFileNames: |  | ||||||
|         isProdBuild && !isStatsBuild ? "c.[hash].js" : "[name].js", |  | ||||||
|       assetFileNames: |  | ||||||
|         isProdBuild && !isStatsBuild ? "a.[hash].js" : "[name].js", |  | ||||||
|       // https://rollupjs.org/guide/en/#outputsourcemap |  | ||||||
|       sourcemap: isProdBuild ? true : "inline", |  | ||||||
|     }, |  | ||||||
|   }; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild, isWDS }) => { | const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild, isWDS }) => | ||||||
|   return createRollupConfig( |   createRollupConfig( | ||||||
|     bundle.config.app({ |     bundle.config.app({ | ||||||
|       isProdBuild, |       isProdBuild, | ||||||
|       latestBuild, |       latestBuild, | ||||||
| @@ -121,31 +111,24 @@ const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild, isWDS }) => { | |||||||
|       isWDS, |       isWDS, | ||||||
|     }) |     }) | ||||||
|   ); |   ); | ||||||
| }; |  | ||||||
|  |  | ||||||
| const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => { | const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => | ||||||
|   return createRollupConfig( |   createRollupConfig( | ||||||
|     bundle.config.demo({ |     bundle.config.demo({ | ||||||
|       isProdBuild, |       isProdBuild, | ||||||
|       latestBuild, |       latestBuild, | ||||||
|       isStatsBuild, |       isStatsBuild, | ||||||
|     }) |     }) | ||||||
|   ); |   ); | ||||||
| }; |  | ||||||
|  |  | ||||||
| const createCastConfig = ({ isProdBuild, latestBuild }) => { | const createCastConfig = ({ isProdBuild, latestBuild }) => | ||||||
|   return createRollupConfig(bundle.config.cast({ isProdBuild, latestBuild })); |   createRollupConfig(bundle.config.cast({ isProdBuild, latestBuild })); | ||||||
| }; |  | ||||||
|  |  | ||||||
| const createHassioConfig = ({ isProdBuild, latestBuild }) => { | const createHassioConfig = ({ isProdBuild, latestBuild }) => | ||||||
|   return createRollupConfig(bundle.config.hassio({ isProdBuild, latestBuild })); |   createRollupConfig(bundle.config.hassio({ isProdBuild, latestBuild })); | ||||||
| }; |  | ||||||
|  |  | ||||||
| const createGalleryConfig = ({ isProdBuild, latestBuild }) => { | const createGalleryConfig = ({ isProdBuild, latestBuild }) => | ||||||
|   return createRollupConfig( |   createRollupConfig(bundle.config.gallery({ isProdBuild, latestBuild })); | ||||||
|     bundle.config.gallery({ isProdBuild, latestBuild }) |  | ||||||
|   ); |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| module.exports = { | module.exports = { | ||||||
|   createAppConfig, |   createAppConfig, | ||||||
|   | |||||||
| @@ -1,3 +1,4 @@ | |||||||
|  | /* eslint-disable @typescript-eslint/no-var-requires */ | ||||||
| const path = require("path"); | const path = require("path"); | ||||||
| const fs = require("fs"); | const fs = require("fs"); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,10 +1,12 @@ | |||||||
|  | /* eslint-disable @typescript-eslint/no-var-requires */ | ||||||
| const webpack = require("webpack"); | const webpack = require("webpack"); | ||||||
| const path = require("path"); | const path = require("path"); | ||||||
| const TerserPlugin = require("terser-webpack-plugin"); | const TerserPlugin = require("terser-webpack-plugin"); | ||||||
| const { WebpackManifestPlugin } = require("webpack-manifest-plugin"); | const { WebpackManifestPlugin } = require("webpack-manifest-plugin"); | ||||||
| const paths = require("./paths.js"); | const paths = require("./paths.js"); | ||||||
| const bundle = require("./bundle"); | const bundle = require("./bundle.js"); | ||||||
| const log = require("fancy-log"); | const log = require("fancy-log"); | ||||||
|  | const WebpackBar = require("webpackbar"); | ||||||
|  |  | ||||||
| class LogStartCompilePlugin { | class LogStartCompilePlugin { | ||||||
|   ignoredFirst = false; |   ignoredFirst = false; | ||||||
| @@ -28,6 +30,7 @@ const createWebpackConfig = ({ | |||||||
|   isProdBuild, |   isProdBuild, | ||||||
|   latestBuild, |   latestBuild, | ||||||
|   isStatsBuild, |   isStatsBuild, | ||||||
|  |   isHassioBuild, | ||||||
|   dontHash, |   dontHash, | ||||||
| }) => { | }) => { | ||||||
|   if (!dontHash) { |   if (!dontHash) { | ||||||
| @@ -46,15 +49,18 @@ const createWebpackConfig = ({ | |||||||
|       rules: [ |       rules: [ | ||||||
|         { |         { | ||||||
|           test: /\.m?js$|\.ts$/, |           test: /\.m?js$|\.ts$/, | ||||||
|           exclude: bundle.babelExclude(), |  | ||||||
|           use: { |           use: { | ||||||
|             loader: "babel-loader", |             loader: "babel-loader", | ||||||
|             options: bundle.babelOptions({ latestBuild }), |             options: { | ||||||
|  |               ...bundle.babelOptions({ latestBuild }), | ||||||
|  |               cacheDirectory: !isProdBuild, | ||||||
|  |               cacheCompression: false, | ||||||
|  |             }, | ||||||
|           }, |           }, | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
|           test: /\.css$/, |           test: /\.css$/, | ||||||
|           use: "raw-loader", |           type: "asset/source", | ||||||
|         }, |         }, | ||||||
|       ], |       ], | ||||||
|     }, |     }, | ||||||
| @@ -66,8 +72,11 @@ const createWebpackConfig = ({ | |||||||
|           terserOptions: bundle.terserOptions(latestBuild), |           terserOptions: bundle.terserOptions(latestBuild), | ||||||
|         }), |         }), | ||||||
|       ], |       ], | ||||||
|  |       moduleIds: isProdBuild && !isStatsBuild ? "deterministic" : "named", | ||||||
|  |       chunkIds: isProdBuild && !isStatsBuild ? "deterministic" : "named", | ||||||
|     }, |     }, | ||||||
|     plugins: [ |     plugins: [ | ||||||
|  |       new WebpackBar({ fancy: !isProdBuild }), | ||||||
|       new WebpackManifestPlugin({ |       new WebpackManifestPlugin({ | ||||||
|         // Only include the JS of entrypoints |         // Only include the JS of entrypoints | ||||||
|         filter: (file) => file.isInitial && !file.name.endsWith(".map"), |         filter: (file) => file.isInitial && !file.name.endsWith(".map"), | ||||||
| @@ -94,6 +103,7 @@ const createWebpackConfig = ({ | |||||||
|               ? path.resolve(context, resource) |               ? path.resolve(context, resource) | ||||||
|               : require.resolve(resource); |               : require.resolve(resource); | ||||||
|           } catch (err) { |           } catch (err) { | ||||||
|  |             // eslint-disable-next-line no-console | ||||||
|             console.error( |             console.error( | ||||||
|               "Error in Home Assistant ignore plugin", |               "Error in Home Assistant ignore plugin", | ||||||
|               resource, |               resource, | ||||||
| @@ -107,70 +117,67 @@ const createWebpackConfig = ({ | |||||||
|           ); |           ); | ||||||
|         }, |         }, | ||||||
|       }), |       }), | ||||||
|       new webpack.NormalModuleReplacementPlugin( |  | ||||||
|         new RegExp(bundle.emptyPackages({ latestBuild }).join("|")), |  | ||||||
|         path.resolve(paths.polymer_dir, "src/util/empty.js") |  | ||||||
|       ), |  | ||||||
|       // We need to change the import of the polyfill for EventTarget, so we replace the polyfill file with our customized one |  | ||||||
|       new webpack.NormalModuleReplacementPlugin( |       new webpack.NormalModuleReplacementPlugin( | ||||||
|         new RegExp( |         new RegExp( | ||||||
|           require.resolve( |           bundle.emptyPackages({ latestBuild, isHassioBuild }).join("|") | ||||||
|             "lit-virtualizer/lib/uni-virtualizer/lib/polyfillLoaders/EventTarget.js" |  | ||||||
|           ) |  | ||||||
|         ), |         ), | ||||||
|         path.resolve(paths.polymer_dir, "src/resources/EventTarget-ponyfill.js") |         path.resolve(paths.polymer_dir, "src/util/empty.js") | ||||||
|       ), |       ), | ||||||
|       !isProdBuild && new LogStartCompilePlugin(), |       !isProdBuild && new LogStartCompilePlugin(), | ||||||
|     ].filter(Boolean), |     ].filter(Boolean), | ||||||
|     resolve: { |     resolve: { | ||||||
|       extensions: [".ts", ".js", ".json"], |       extensions: [".ts", ".js", ".json"], | ||||||
|  |       alias: { | ||||||
|  |         "lit/decorators$": "lit/decorators.js", | ||||||
|  |         "lit/directive$": "lit/directive.js", | ||||||
|  |         "lit/directives/until$": "lit/directives/until.js", | ||||||
|  |         "lit/directives/class-map$": "lit/directives/class-map.js", | ||||||
|  |         "lit/directives/style-map$": "lit/directives/style-map.js", | ||||||
|  |         "lit/directives/if-defined$": "lit/directives/if-defined.js", | ||||||
|  |         "lit/directives/guard$": "lit/directives/guard.js", | ||||||
|  |         "lit/directives/cache$": "lit/directives/cache.js", | ||||||
|  |         "lit/directives/repeat$": "lit/directives/repeat.js", | ||||||
|  |         "lit/polyfill-support$": "lit/polyfill-support.js", | ||||||
|  |       }, | ||||||
|     }, |     }, | ||||||
|     output: { |     output: { | ||||||
|       filename: ({ chunk }) => { |       filename: ({ chunk }) => { | ||||||
|         if (!isProdBuild || dontHash.has(chunk.name)) { |         if (!isProdBuild || isStatsBuild || dontHash.has(chunk.name)) { | ||||||
|           return `${chunk.name}.js`; |           return `${chunk.name}.js`; | ||||||
|         } |         } | ||||||
|         return `${chunk.name}.${chunk.hash.substr(0, 8)}.js`; |         return `${chunk.name}.${chunk.hash.substr(0, 8)}.js`; | ||||||
|       }, |       }, | ||||||
|       chunkFilename: |       chunkFilename: | ||||||
|         isProdBuild && !isStatsBuild |         isProdBuild && !isStatsBuild ? "[chunkhash:8].js" : "[id].chunk.js", | ||||||
|           ? "chunk.[chunkhash].js" |  | ||||||
|           : "[name].chunk.js", |  | ||||||
|       path: outputPath, |       path: outputPath, | ||||||
|       publicPath, |       publicPath, | ||||||
|       // To silence warning in worker plugin |       // To silence warning in worker plugin | ||||||
|       globalObject: "self", |       globalObject: "self", | ||||||
|     }, |     }, | ||||||
|  |     experiments: { | ||||||
|  |       topLevelAwait: true, | ||||||
|  |     }, | ||||||
|   }; |   }; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => { | const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => | ||||||
|   return createWebpackConfig( |   createWebpackConfig( | ||||||
|     bundle.config.app({ isProdBuild, latestBuild, isStatsBuild }) |     bundle.config.app({ isProdBuild, latestBuild, isStatsBuild }) | ||||||
|   ); |   ); | ||||||
| }; |  | ||||||
|  |  | ||||||
| const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => { | const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => | ||||||
|   return createWebpackConfig( |   createWebpackConfig( | ||||||
|     bundle.config.demo({ isProdBuild, latestBuild, isStatsBuild }) |     bundle.config.demo({ isProdBuild, latestBuild, isStatsBuild }) | ||||||
|   ); |   ); | ||||||
| }; |  | ||||||
|  |  | ||||||
| const createCastConfig = ({ isProdBuild, latestBuild }) => { | const createCastConfig = ({ isProdBuild, latestBuild }) => | ||||||
|   return createWebpackConfig(bundle.config.cast({ isProdBuild, latestBuild })); |   createWebpackConfig(bundle.config.cast({ isProdBuild, latestBuild })); | ||||||
| }; |  | ||||||
|  |  | ||||||
| const createHassioConfig = ({ isProdBuild, latestBuild }) => { | const createHassioConfig = ({ isProdBuild, latestBuild }) => | ||||||
|   return createWebpackConfig( |   createWebpackConfig(bundle.config.hassio({ isProdBuild, latestBuild })); | ||||||
|     bundle.config.hassio({ isProdBuild, latestBuild }) |  | ||||||
|   ); |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| const createGalleryConfig = ({ isProdBuild, latestBuild }) => { | const createGalleryConfig = ({ isProdBuild, latestBuild }) => | ||||||
|   return createWebpackConfig( |   createWebpackConfig(bundle.config.gallery({ isProdBuild, latestBuild })); | ||||||
|     bundle.config.gallery({ isProdBuild, latestBuild }) |  | ||||||
|   ); |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| module.exports = { | module.exports = { | ||||||
|   createAppConfig, |   createAppConfig, | ||||||
|   | |||||||
| @@ -139,7 +139,7 @@ | |||||||
|           Your authentication credentials or Home Assistant url are never sent |           Your authentication credentials or Home Assistant url are never sent | ||||||
|           to the Cloud. You can validate this behavior in |           to the Cloud. You can validate this behavior in | ||||||
|           <a |           <a | ||||||
|             href="https://github.com/home-assistant/home-assistant-polymer/tree/dev/cast" |             href="https://github.com/home-assistant/frontend/tree/dev/cast" | ||||||
|             target="_blank" |             target="_blank" | ||||||
|             >the source code</a |             >the source code</a | ||||||
|           >. |           >. | ||||||
|   | |||||||
							
								
								
									
										46
									
								
								cast/src/html/media.html.template
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								cast/src/html/media.html.template
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | |||||||
|  | <!DOCTYPE html> | ||||||
|  | <html> | ||||||
|  |   <head> | ||||||
|  |     <script src="//www.gstatic.com/cast/sdk/libs/caf_receiver/v3/cast_receiver_framework.js"></script> | ||||||
|  |     <style> | ||||||
|  |       body { | ||||||
|  |         --logo-image: url('https://www.home-assistant.io/images/home-assistant-logo.svg'); | ||||||
|  |         --logo-repeat: no-repeat; | ||||||
|  |         --playback-logo-image: url('https://www.home-assistant.io/images/home-assistant-logo.svg'); | ||||||
|  |         --theme-hue: 200; | ||||||
|  |         --progress-color: #03a9f4; | ||||||
|  |         --splash-image: url('https://home-assistant.io/images/cast/splash.png'); | ||||||
|  |         --splash-size: cover; | ||||||
|  |         --background-color: #41bdf5; | ||||||
|  |       } | ||||||
|  |     </style> | ||||||
|  |     <script> | ||||||
|  |       var _gaq=[['_setAccount','UA-57927901-10'],['_trackPageview']]; | ||||||
|  |       (function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0]; | ||||||
|  |       g.src=('https:'==location.protocol?'//ssl':'//www')+'.google-analytics.com/ga.js'; | ||||||
|  |       s.parentNode.insertBefore(g,s)}(document,'script')); | ||||||
|  |     </script> | ||||||
|  |   </head> | ||||||
|  |   <body> | ||||||
|  |     <%= renderTemplate('_js_base') %> | ||||||
|  |  | ||||||
|  |     <cast-media-player></cast-media-player> | ||||||
|  |  | ||||||
|  |     <script> | ||||||
|  |       import("<%= latestMediaJS %>"); | ||||||
|  |       window.latestJS = true; | ||||||
|  |     </script> | ||||||
|  |  | ||||||
|  |     <script> | ||||||
|  |       if (!window.latestJS) { | ||||||
|  |         <% if (useRollup) { %> | ||||||
|  |           _ls("/static/js/s.min.js").onload = function() { | ||||||
|  |             System.import("<%= es5MediaJS %>"); | ||||||
|  |           }; | ||||||
|  |         <% } else { %> | ||||||
|  |           _ls("<%= es5MediaJS %>"); | ||||||
|  |         <% } %> | ||||||
|  |       } | ||||||
|  |     </script> | ||||||
|  |   </body> | ||||||
|  | </html> | ||||||
| @@ -1,16 +1,10 @@ | |||||||
|  | import "@material/mwc-button/mwc-button"; | ||||||
|  | import { mdiCast, mdiCastConnected } from "@mdi/js"; | ||||||
| import "@polymer/paper-item/paper-icon-item"; | import "@polymer/paper-item/paper-icon-item"; | ||||||
| import "@polymer/paper-listbox/paper-listbox"; | import "@polymer/paper-listbox/paper-listbox"; | ||||||
| import { Auth, Connection } from "home-assistant-js-websocket"; | import { Auth, Connection } from "home-assistant-js-websocket"; | ||||||
| import { | import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; | ||||||
|   css, | import { customElement, property, state } from "lit/decorators"; | ||||||
|   CSSResult, |  | ||||||
|   customElement, |  | ||||||
|   html, |  | ||||||
|   LitElement, |  | ||||||
|   property, |  | ||||||
|   internalProperty, |  | ||||||
|   TemplateResult, |  | ||||||
| } from "lit-element"; |  | ||||||
| import { CastManager } from "../../../../src/cast/cast_manager"; | import { CastManager } from "../../../../src/cast/cast_manager"; | ||||||
| import { | import { | ||||||
|   castSendShowLovelaceView, |   castSendShowLovelaceView, | ||||||
| @@ -24,6 +18,7 @@ import { | |||||||
| import { atLeastVersion } from "../../../../src/common/config/version"; | import { atLeastVersion } from "../../../../src/common/config/version"; | ||||||
| import { toggleAttribute } from "../../../../src/common/dom/toggle_attribute"; | import { toggleAttribute } from "../../../../src/common/dom/toggle_attribute"; | ||||||
| import "../../../../src/components/ha-icon"; | import "../../../../src/components/ha-icon"; | ||||||
|  | import "../../../../src/components/ha-svg-icon"; | ||||||
| import { | import { | ||||||
|   getLegacyLovelaceCollection, |   getLegacyLovelaceCollection, | ||||||
|   getLovelaceCollection, |   getLovelaceCollection, | ||||||
| @@ -32,7 +27,6 @@ import { | |||||||
| import "../../../../src/layouts/hass-loading-screen"; | import "../../../../src/layouts/hass-loading-screen"; | ||||||
| import { generateDefaultViewConfig } from "../../../../src/panels/lovelace/common/generate-lovelace-config"; | import { generateDefaultViewConfig } from "../../../../src/panels/lovelace/common/generate-lovelace-config"; | ||||||
| import "./hc-layout"; | import "./hc-layout"; | ||||||
| import "@material/mwc-button/mwc-button"; |  | ||||||
|  |  | ||||||
| @customElement("hc-cast") | @customElement("hc-cast") | ||||||
| class HcCast extends LitElement { | class HcCast extends LitElement { | ||||||
| @@ -42,9 +36,9 @@ class HcCast extends LitElement { | |||||||
|  |  | ||||||
|   @property() public castManager!: CastManager; |   @property() public castManager!: CastManager; | ||||||
|  |  | ||||||
|   @internalProperty() private askWrite = false; |   @state() private askWrite = false; | ||||||
|  |  | ||||||
|   @internalProperty() private lovelaceConfig?: LovelaceConfig | null; |   @state() private lovelaceConfig?: LovelaceConfig | null; | ||||||
|  |  | ||||||
|   protected render(): TemplateResult { |   protected render(): TemplateResult { | ||||||
|     if (this.lovelaceConfig === undefined) { |     if (this.lovelaceConfig === undefined) { | ||||||
| @@ -54,9 +48,7 @@ class HcCast extends LitElement { | |||||||
|     const error = |     const error = | ||||||
|       this.castManager.castState === "NO_DEVICES_AVAILABLE" |       this.castManager.castState === "NO_DEVICES_AVAILABLE" | ||||||
|         ? html` |         ? html` | ||||||
|             <p> |             <p>There were no suitable Chromecast devices to cast to found.</p> | ||||||
|               There were no suitable Chromecast devices to cast to found. |  | ||||||
|             </p> |  | ||||||
|           ` |           ` | ||||||
|         : undefined; |         : undefined; | ||||||
|  |  | ||||||
| @@ -83,7 +75,7 @@ class HcCast extends LitElement { | |||||||
|           ? html` |           ? html` | ||||||
|               <p class="center-item"> |               <p class="center-item"> | ||||||
|                 <mwc-button raised @click=${this._handleLaunch}> |                 <mwc-button raised @click=${this._handleLaunch}> | ||||||
|                   <ha-icon icon="hass:cast"></ha-icon> |                   <ha-svg-icon .path=${mdiCast}></ha-svg-icon> | ||||||
|                   Start Casting |                   Start Casting | ||||||
|                 </mwc-button> |                 </mwc-button> | ||||||
|               </p> |               </p> | ||||||
| @@ -121,7 +113,7 @@ class HcCast extends LitElement { | |||||||
|           ${this.castManager.status |           ${this.castManager.status | ||||||
|             ? html` |             ? html` | ||||||
|                 <mwc-button @click=${this._handleLaunch}> |                 <mwc-button @click=${this._handleLaunch}> | ||||||
|                   <ha-icon icon="hass:cast-connected"></ha-icon> |                   <ha-svg-icon .path=${mdiCastConnected}></ha-svg-icon> | ||||||
|                   Manage |                   Manage | ||||||
|                 </mwc-button> |                 </mwc-button> | ||||||
|               ` |               ` | ||||||
| @@ -201,12 +193,12 @@ class HcCast extends LitElement { | |||||||
|       } |       } | ||||||
|       this.connection.close(); |       this.connection.close(); | ||||||
|       location.reload(); |       location.reload(); | ||||||
|     } catch (err) { |     } catch (err: any) { | ||||||
|       alert("Unable to log out!"); |       alert("Unable to log out!"); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   static get styles(): CSSResult { |   static get styles(): CSSResultGroup { | ||||||
|     return css` |     return css` | ||||||
|       .center-item { |       .center-item { | ||||||
|         display: flex; |         display: flex; | ||||||
| @@ -243,7 +235,7 @@ class HcCast extends LitElement { | |||||||
|         color: var(--secondary-text-color); |         color: var(--secondary-text-color); | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       mwc-button ha-icon { |       mwc-button ha-svg-icon { | ||||||
|         margin-right: 8px; |         margin-right: 8px; | ||||||
|         height: 18px; |         height: 18px; | ||||||
|       } |       } | ||||||
|   | |||||||
| @@ -1,4 +1,5 @@ | |||||||
| import "@material/mwc-button"; | import "@material/mwc-button"; | ||||||
|  | import { mdiCastConnected, mdiCast } from "@mdi/js"; | ||||||
| import "@polymer/paper-input/paper-input"; | import "@polymer/paper-input/paper-input"; | ||||||
| import { | import { | ||||||
|   Auth, |   Auth, | ||||||
| @@ -11,22 +12,15 @@ import { | |||||||
|   getAuth, |   getAuth, | ||||||
|   getAuthOptions, |   getAuthOptions, | ||||||
| } from "home-assistant-js-websocket"; | } from "home-assistant-js-websocket"; | ||||||
| import { | import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; | ||||||
|   css, | import { customElement, state } from "lit/decorators"; | ||||||
|   CSSResult, |  | ||||||
|   customElement, |  | ||||||
|   html, |  | ||||||
|   LitElement, |  | ||||||
|   TemplateResult, |  | ||||||
|   internalProperty, |  | ||||||
| } from "lit-element"; |  | ||||||
| import { CastManager, getCastManager } from "../../../../src/cast/cast_manager"; | import { CastManager, getCastManager } from "../../../../src/cast/cast_manager"; | ||||||
| import { castSendShowDemo } from "../../../../src/cast/receiver_messages"; | import { castSendShowDemo } from "../../../../src/cast/receiver_messages"; | ||||||
| import { | import { | ||||||
|   loadTokens, |   loadTokens, | ||||||
|   saveTokens, |   saveTokens, | ||||||
| } from "../../../../src/common/auth/token_storage"; | } from "../../../../src/common/auth/token_storage"; | ||||||
| import "../../../../src/components/ha-icon"; | import "../../../../src/components/ha-svg-icon"; | ||||||
| import "../../../../src/layouts/hass-loading-screen"; | import "../../../../src/layouts/hass-loading-screen"; | ||||||
| import { registerServiceWorker } from "../../../../src/util/register-service-worker"; | import { registerServiceWorker } from "../../../../src/util/register-service-worker"; | ||||||
| import "./hc-layout"; | import "./hc-layout"; | ||||||
| @@ -60,19 +54,19 @@ const INTRO = html` | |||||||
|  |  | ||||||
| @customElement("hc-connect") | @customElement("hc-connect") | ||||||
| export class HcConnect extends LitElement { | export class HcConnect extends LitElement { | ||||||
|   @internalProperty() private loading = false; |   @state() private loading = false; | ||||||
|  |  | ||||||
|   // If we had stored credentials but we cannot connect, |   // If we had stored credentials but we cannot connect, | ||||||
|   // show a screen asking retry or logout. |   // show a screen asking retry or logout. | ||||||
|   @internalProperty() private cannotConnect = false; |   @state() private cannotConnect = false; | ||||||
|  |  | ||||||
|   @internalProperty() private error?: string | TemplateResult; |   @state() private error?: string | TemplateResult; | ||||||
|  |  | ||||||
|   @internalProperty() private auth?: Auth; |   @state() private auth?: Auth; | ||||||
|  |  | ||||||
|   @internalProperty() private connection?: Connection; |   @state() private connection?: Connection; | ||||||
|  |  | ||||||
|   @internalProperty() private castManager?: CastManager | null; |   @state() private castManager?: CastManager | null; | ||||||
|  |  | ||||||
|   private openDemo = false; |   private openDemo = false; | ||||||
|  |  | ||||||
| @@ -86,9 +80,7 @@ export class HcConnect extends LitElement { | |||||||
|           </div> |           </div> | ||||||
|           <div class="card-actions"> |           <div class="card-actions"> | ||||||
|             <a href="/"> |             <a href="/"> | ||||||
|               <mwc-button> |               <mwc-button> Retry </mwc-button> | ||||||
|                 Retry |  | ||||||
|               </mwc-button> |  | ||||||
|             </a> |             </a> | ||||||
|             <div class="spacer"></div> |             <div class="spacer"></div> | ||||||
|             <mwc-button @click=${this._handleLogout}>Log out</mwc-button> |             <mwc-button @click=${this._handleLogout}>Log out</mwc-button> | ||||||
| @@ -136,11 +128,11 @@ export class HcConnect extends LitElement { | |||||||
|           <div class="card-actions"> |           <div class="card-actions"> | ||||||
|             <mwc-button @click=${this._handleDemo}> |             <mwc-button @click=${this._handleDemo}> | ||||||
|               Show Demo |               Show Demo | ||||||
|               <ha-icon |               <ha-svg-icon | ||||||
|                 .icon=${this.castManager.castState === "CONNECTED" |                 .path=${this.castManager.castState === "CONNECTED" | ||||||
|                   ? "hass:cast-connected" |                   ? mdiCastConnected | ||||||
|                   : "hass:cast"} |                   : mdiCast} | ||||||
|               ></ha-icon> |               ></ha-svg-icon> | ||||||
|             </mwc-button> |             </mwc-button> | ||||||
|             <div class="spacer"></div> |             <div class="spacer"></div> | ||||||
|             <mwc-button @click=${this._handleConnect}>Authorize</mwc-button> |             <mwc-button @click=${this._handleConnect}>Authorize</mwc-button> | ||||||
| @@ -221,7 +213,7 @@ export class HcConnect extends LitElement { | |||||||
|     let url: URL; |     let url: URL; | ||||||
|     try { |     try { | ||||||
|       url = new URL(value); |       url = new URL(value); | ||||||
|     } catch (err) { |     } catch (err: any) { | ||||||
|       this.error = "Invalid URL"; |       this.error = "Invalid URL"; | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
| @@ -249,7 +241,7 @@ export class HcConnect extends LitElement { | |||||||
|     try { |     try { | ||||||
|       this.loading = true; |       this.loading = true; | ||||||
|       auth = await getAuth(options); |       auth = await getAuth(options); | ||||||
|     } catch (err) { |     } catch (err: any) { | ||||||
|       if (init === "saved-tokens" && err === ERR_CANNOT_CONNECT) { |       if (init === "saved-tokens" && err === ERR_CANNOT_CONNECT) { | ||||||
|         this.cannotConnect = true; |         this.cannotConnect = true; | ||||||
|         return; |         return; | ||||||
| @@ -268,7 +260,7 @@ export class HcConnect extends LitElement { | |||||||
|  |  | ||||||
|     try { |     try { | ||||||
|       conn = await createConnection({ auth }); |       conn = await createConnection({ auth }); | ||||||
|     } catch (err) { |     } catch (err: any) { | ||||||
|       // In case of saved tokens, silently solve problems. |       // In case of saved tokens, silently solve problems. | ||||||
|       if (init === "saved-tokens") { |       if (init === "saved-tokens") { | ||||||
|         if (err === ERR_CANNOT_CONNECT) { |         if (err === ERR_CANNOT_CONNECT) { | ||||||
| @@ -294,12 +286,12 @@ export class HcConnect extends LitElement { | |||||||
|     try { |     try { | ||||||
|       saveTokens(null); |       saveTokens(null); | ||||||
|       location.reload(); |       location.reload(); | ||||||
|     } catch (err) { |     } catch (err: any) { | ||||||
|       alert("Unable to log out!"); |       alert("Unable to log out!"); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   static get styles(): CSSResult { |   static get styles(): CSSResultGroup { | ||||||
|     return css` |     return css` | ||||||
|       .card-content a { |       .card-content a { | ||||||
|         color: var(--primary-color); |         color: var(--primary-color); | ||||||
| @@ -316,7 +308,7 @@ export class HcConnect extends LitElement { | |||||||
|         color: darkred; |         color: darkred; | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       mwc-button ha-icon { |       mwc-button ha-svg-icon { | ||||||
|         margin-left: 8px; |         margin-left: 8px; | ||||||
|       } |       } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -4,15 +4,8 @@ import { | |||||||
|   getUser, |   getUser, | ||||||
|   HassUser, |   HassUser, | ||||||
| } from "home-assistant-js-websocket"; | } from "home-assistant-js-websocket"; | ||||||
| import { | import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; | ||||||
|   css, | import { customElement, property } from "lit/decorators"; | ||||||
|   CSSResult, |  | ||||||
|   customElement, |  | ||||||
|   html, |  | ||||||
|   LitElement, |  | ||||||
|   property, |  | ||||||
|   TemplateResult, |  | ||||||
| } from "lit-element"; |  | ||||||
| import "../../../../src/components/ha-card"; | import "../../../../src/components/ha-card"; | ||||||
|  |  | ||||||
| @customElement("hc-layout") | @customElement("hc-layout") | ||||||
| @@ -69,7 +62,7 @@ class HcLayout extends LitElement { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   static get styles(): CSSResult { |   static get styles(): CSSResultGroup { | ||||||
|     return css` |     return css` | ||||||
|       :host { |       :host { | ||||||
|         display: flex; |         display: flex; | ||||||
|   | |||||||
							
								
								
									
										22
									
								
								cast/src/media/entrypoint.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								cast/src/media/entrypoint.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | |||||||
|  | const castContext = cast.framework.CastReceiverContext.getInstance(); | ||||||
|  |  | ||||||
|  | const playerManager = castContext.getPlayerManager(); | ||||||
|  |  | ||||||
|  | playerManager.setMessageInterceptor( | ||||||
|  |   cast.framework.messages.MessageType.LOAD, | ||||||
|  |   (loadRequestData) => { | ||||||
|  |     const media = loadRequestData.media; | ||||||
|  |     // Special handling if it came from Google Assistant | ||||||
|  |     if (media.entity) { | ||||||
|  |       media.contentId = media.entity; | ||||||
|  |       media.streamType = cast.framework.messages.StreamType.LIVE; | ||||||
|  |       media.contentType = "application/vnd.apple.mpegurl"; | ||||||
|  |       // @ts-ignore | ||||||
|  |       media.hlsVideoSegmentFormat = | ||||||
|  |         cast.framework.messages.HlsVideoSegmentFormat.FMP4; | ||||||
|  |     } | ||||||
|  |     return loadRequestData; | ||||||
|  |   } | ||||||
|  | ); | ||||||
|  |  | ||||||
|  | castContext.start(); | ||||||
| @@ -5,8 +5,8 @@ import { | |||||||
| import { castContext } from "../cast_context"; | import { castContext } from "../cast_context"; | ||||||
|  |  | ||||||
| export const castDemoLovelace: () => LovelaceConfig = () => { | export const castDemoLovelace: () => LovelaceConfig = () => { | ||||||
|   const touchSupported = castContext.getDeviceCapabilities() |   const touchSupported = | ||||||
|     .touch_input_supported; |     castContext.getDeviceCapabilities().touch_input_supported; | ||||||
|   return { |   return { | ||||||
|     views: [ |     views: [ | ||||||
|       { |       { | ||||||
|   | |||||||
| @@ -8,6 +8,9 @@ import { ReceivedMessage } from "./types"; | |||||||
|  |  | ||||||
| const lovelaceController = new HcMain(); | const lovelaceController = new HcMain(); | ||||||
| document.body.append(lovelaceController); | document.body.append(lovelaceController); | ||||||
|  | lovelaceController.addEventListener("cast-view-changed", (ev) => { | ||||||
|  |   playDummyMedia(ev.detail.title); | ||||||
|  | }); | ||||||
|  |  | ||||||
| const mediaPlayer = document.createElement("cast-media-player"); | const mediaPlayer = document.createElement("cast-media-player"); | ||||||
| mediaPlayer.style.display = "none"; | mediaPlayer.style.display = "none"; | ||||||
| @@ -28,6 +31,31 @@ const setTouchControlsVisibility = (visible: boolean) => { | |||||||
|   } |   } | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | let timeOut: number | undefined; | ||||||
|  |  | ||||||
|  | const playDummyMedia = (viewTitle?: string) => { | ||||||
|  |   const loadRequestData = new cast.framework.messages.LoadRequestData(); | ||||||
|  |   loadRequestData.autoplay = true; | ||||||
|  |   loadRequestData.media = new cast.framework.messages.MediaInformation(); | ||||||
|  |   loadRequestData.media.contentId = | ||||||
|  |     "https://cast.home-assistant.io/images/google-nest-hub.png"; | ||||||
|  |   loadRequestData.media.contentType = "image/jpeg"; | ||||||
|  |   loadRequestData.media.streamType = cast.framework.messages.StreamType.NONE; | ||||||
|  |   const metadata = new cast.framework.messages.GenericMediaMetadata(); | ||||||
|  |   metadata.title = viewTitle; | ||||||
|  |   loadRequestData.media.metadata = metadata; | ||||||
|  |  | ||||||
|  |   loadRequestData.requestId = 0; | ||||||
|  |   playerManager.load(loadRequestData); | ||||||
|  |   if (timeOut) { | ||||||
|  |     clearTimeout(timeOut); | ||||||
|  |     timeOut = undefined; | ||||||
|  |   } | ||||||
|  |   if (castContext.getDeviceCapabilities().touch_input_supported) { | ||||||
|  |     timeOut = window.setTimeout(() => playDummyMedia(viewTitle), 540000); // repeat every 9 minutes to keep it active (gets deactivated after 10 minutes) | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  |  | ||||||
| const showLovelaceController = () => { | const showLovelaceController = () => { | ||||||
|   mediaPlayer.style.display = "none"; |   mediaPlayer.style.display = "none"; | ||||||
|   lovelaceController.style.display = "initial"; |   lovelaceController.style.display = "initial"; | ||||||
| @@ -51,6 +79,7 @@ const showMediaPlayer = () => { | |||||||
|       --progress-color: #03a9f4; |       --progress-color: #03a9f4; | ||||||
|       --splash-image: url('https://home-assistant.io/images/cast/splash.png'); |       --splash-image: url('https://home-assistant.io/images/cast/splash.png'); | ||||||
|       --splash-size: cover; |       --splash-size: cover; | ||||||
|  |       --background-color: #41bdf5; | ||||||
|     } |     } | ||||||
|     `; |     `; | ||||||
|     document.head.appendChild(style); |     document.head.appendChild(style); | ||||||
| @@ -63,22 +92,6 @@ options.customNamespaces = { | |||||||
|   [CAST_NS]: cast.framework.system.MessageType.JSON, |   [CAST_NS]: cast.framework.system.MessageType.JSON, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| // The docs say we need to set options.touchScreenOptimizeApp = true |  | ||||||
| // https://developers.google.com/cast/docs/caf_receiver/customize_ui#accessing_ui_controls |  | ||||||
| // This doesn't work. |  | ||||||
| // @ts-ignore |  | ||||||
| options.touchScreenOptimizedApp = true; |  | ||||||
|  |  | ||||||
| // The class reference say we can set a uiConfig in options to set it |  | ||||||
| // https://developers.google.com/cast/docs/reference/caf_receiver/cast.framework.CastReceiverOptions#uiConfig |  | ||||||
| // This doesn't work either. |  | ||||||
| // @ts-ignore |  | ||||||
| options.uiConfig = new cast.framework.ui.UiConfig(); |  | ||||||
| // @ts-ignore |  | ||||||
| options.uiConfig.touchScreenOptimizedApp = true; |  | ||||||
|  |  | ||||||
| castContext.setInactivityTimeout(86400); // 1 day |  | ||||||
|  |  | ||||||
| castContext.addCustomMessageListener( | castContext.addCustomMessageListener( | ||||||
|   CAST_NS, |   CAST_NS, | ||||||
|   // @ts-ignore |   // @ts-ignore | ||||||
| @@ -103,6 +116,12 @@ const playerManager = castContext.getPlayerManager(); | |||||||
| playerManager.setMessageInterceptor( | playerManager.setMessageInterceptor( | ||||||
|   cast.framework.messages.MessageType.LOAD, |   cast.framework.messages.MessageType.LOAD, | ||||||
|   (loadRequestData) => { |   (loadRequestData) => { | ||||||
|  |     if ( | ||||||
|  |       loadRequestData.media.contentId === | ||||||
|  |       "https://cast.home-assistant.io/images/google-nest-hub.png" | ||||||
|  |     ) { | ||||||
|  |       return loadRequestData; | ||||||
|  |     } | ||||||
|     // We received a play media command, hide Lovelace and show media player |     // We received a play media command, hide Lovelace and show media player | ||||||
|     showMediaPlayer(); |     showMediaPlayer(); | ||||||
|     const media = loadRequestData.media; |     const media = loadRequestData.media; | ||||||
|   | |||||||
| @@ -1,10 +1,5 @@ | |||||||
| import { | import { html, TemplateResult } from "lit"; | ||||||
|   customElement, | import { customElement, property, state } from "lit/decorators"; | ||||||
|   html, |  | ||||||
|   internalProperty, |  | ||||||
|   property, |  | ||||||
|   TemplateResult, |  | ||||||
| } from "lit-element"; |  | ||||||
| import { mockHistory } from "../../../../demo/src/stubs/history"; | import { mockHistory } from "../../../../demo/src/stubs/history"; | ||||||
| import { LovelaceConfig } from "../../../../src/data/lovelace"; | import { LovelaceConfig } from "../../../../src/data/lovelace"; | ||||||
| import { | import { | ||||||
| @@ -21,7 +16,7 @@ import "./hc-lovelace"; | |||||||
| class HcDemo extends HassElement { | class HcDemo extends HassElement { | ||||||
|   @property({ attribute: false }) public lovelacePath!: string; |   @property({ attribute: false }) public lovelacePath!: string; | ||||||
|  |  | ||||||
|   @internalProperty() private _lovelaceConfig?: LovelaceConfig; |   @state() private _lovelaceConfig?: LovelaceConfig; | ||||||
|  |  | ||||||
|   protected render(): TemplateResult { |   protected render(): TemplateResult { | ||||||
|     if (!this._lovelaceConfig) { |     if (!this._lovelaceConfig) { | ||||||
| @@ -38,10 +33,10 @@ class HcDemo extends HassElement { | |||||||
|  |  | ||||||
|   protected firstUpdated(changedProps) { |   protected firstUpdated(changedProps) { | ||||||
|     super.firstUpdated(changedProps); |     super.firstUpdated(changedProps); | ||||||
|     this._initialize(); |     this._initializeHass(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   private async _initialize() { |   private async _initializeHass() { | ||||||
|     const initial: Partial<MockHomeAssistant> = { |     const initial: Partial<MockHomeAssistant> = { | ||||||
|       // Override updateHass so that the correct hass lifecycle methods are called |       // Override updateHass so that the correct hass lifecycle methods are called | ||||||
|       updateHass: (hassUpdate: Partial<HomeAssistant>) => |       updateHass: (hassUpdate: Partial<HomeAssistant>) => | ||||||
|   | |||||||
| @@ -1,12 +1,5 @@ | |||||||
| import { | import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; | ||||||
|   css, | import { customElement, property } from "lit/decorators"; | ||||||
|   CSSResult, |  | ||||||
|   customElement, |  | ||||||
|   html, |  | ||||||
|   LitElement, |  | ||||||
|   property, |  | ||||||
|   TemplateResult, |  | ||||||
| } from "lit-element"; |  | ||||||
| import { HomeAssistant } from "../../../../src/types"; | import { HomeAssistant } from "../../../../src/types"; | ||||||
|  |  | ||||||
| @customElement("hc-launch-screen") | @customElement("hc-launch-screen") | ||||||
| @@ -29,7 +22,7 @@ class HcLaunchScreen extends LitElement { | |||||||
|     `; |     `; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   static get styles(): CSSResult { |   static get styles(): CSSResultGroup { | ||||||
|     return css` |     return css` | ||||||
|       :host { |       :host { | ||||||
|         display: block; |         display: block; | ||||||
|   | |||||||
| @@ -1,18 +1,15 @@ | |||||||
| import { | import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; | ||||||
|   css, | import { customElement, property } from "lit/decorators"; | ||||||
|   CSSResult, | import { fireEvent } from "../../../../src/common/dom/fire_event"; | ||||||
|   customElement, |  | ||||||
|   html, |  | ||||||
|   LitElement, |  | ||||||
|   property, |  | ||||||
|   TemplateResult, |  | ||||||
| } from "lit-element"; |  | ||||||
| import { LovelaceConfig } from "../../../../src/data/lovelace"; | import { LovelaceConfig } from "../../../../src/data/lovelace"; | ||||||
| import { Lovelace } from "../../../../src/panels/lovelace/types"; | import { Lovelace } from "../../../../src/panels/lovelace/types"; | ||||||
| import "../../../../src/panels/lovelace/views/hui-view"; | import "../../../../src/panels/lovelace/views/hui-view"; | ||||||
| import { HomeAssistant } from "../../../../src/types"; | import { HomeAssistant } from "../../../../src/types"; | ||||||
| import "./hc-launch-screen"; | import "./hc-launch-screen"; | ||||||
|  |  | ||||||
|  | (window as any).loadCardHelpers = () => | ||||||
|  |   import("../../../../src/panels/lovelace/custom-card-helpers"); | ||||||
|  |  | ||||||
| @customElement("hc-lovelace") | @customElement("hc-lovelace") | ||||||
| class HcLovelace extends LitElement { | class HcLovelace extends LitElement { | ||||||
|   @property({ attribute: false }) public hass!: HomeAssistant; |   @property({ attribute: false }) public hass!: HomeAssistant; | ||||||
| @@ -21,7 +18,7 @@ class HcLovelace extends LitElement { | |||||||
|  |  | ||||||
|   @property() public viewPath?: string | number; |   @property() public viewPath?: string | number; | ||||||
|  |  | ||||||
|   public urlPath?: string | null; |   @property() public urlPath: string | null = null; | ||||||
|  |  | ||||||
|   protected render(): TemplateResult { |   protected render(): TemplateResult { | ||||||
|     const index = this._viewIndex; |     const index = this._viewIndex; | ||||||
| @@ -35,8 +32,9 @@ class HcLovelace extends LitElement { | |||||||
|     } |     } | ||||||
|     const lovelace: Lovelace = { |     const lovelace: Lovelace = { | ||||||
|       config: this.lovelaceConfig, |       config: this.lovelaceConfig, | ||||||
|  |       rawConfig: this.lovelaceConfig, | ||||||
|       editMode: false, |       editMode: false, | ||||||
|       urlPath: this.urlPath!, |       urlPath: this.urlPath, | ||||||
|       enableFullEditMode: () => undefined, |       enableFullEditMode: () => undefined, | ||||||
|       mode: "storage", |       mode: "storage", | ||||||
|       locale: this.hass.locale, |       locale: this.hass.locale, | ||||||
| @@ -60,6 +58,21 @@ class HcLovelace extends LitElement { | |||||||
|       const index = this._viewIndex; |       const index = this._viewIndex; | ||||||
|  |  | ||||||
|       if (index !== undefined) { |       if (index !== undefined) { | ||||||
|  |         const dashboardTitle = this.lovelaceConfig.title || this.urlPath; | ||||||
|  |  | ||||||
|  |         const viewTitle = | ||||||
|  |           this.lovelaceConfig.views[index].title || | ||||||
|  |           this.lovelaceConfig.views[index].path; | ||||||
|  |  | ||||||
|  |         fireEvent(this, "cast-view-changed", { | ||||||
|  |           title: | ||||||
|  |             dashboardTitle || viewTitle | ||||||
|  |               ? `${dashboardTitle || ""}${ | ||||||
|  |                   dashboardTitle && viewTitle ? ": " : "" | ||||||
|  |                 }${viewTitle || ""}` | ||||||
|  |               : undefined, | ||||||
|  |         }); | ||||||
|  |  | ||||||
|         const configBackground = |         const configBackground = | ||||||
|           this.lovelaceConfig.views[index].background || |           this.lovelaceConfig.views[index].background || | ||||||
|           this.lovelaceConfig.background; |           this.lovelaceConfig.background; | ||||||
| @@ -90,7 +103,7 @@ class HcLovelace extends LitElement { | |||||||
|     return undefined; |     return undefined; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   static get styles(): CSSResult { |   static get styles(): CSSResultGroup { | ||||||
|     return css` |     return css` | ||||||
|       :host { |       :host { | ||||||
|         min-height: 100vh; |         min-height: 100vh; | ||||||
| @@ -107,8 +120,15 @@ class HcLovelace extends LitElement { | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | export interface CastViewChanged { | ||||||
|  |   title: string | undefined; | ||||||
|  | } | ||||||
|  |  | ||||||
| declare global { | declare global { | ||||||
|   interface HTMLElementTagNameMap { |   interface HTMLElementTagNameMap { | ||||||
|     "hc-lovelace": HcLovelace; |     "hc-lovelace": HcLovelace; | ||||||
|   } |   } | ||||||
|  |   interface HASSDomEvents { | ||||||
|  |     "cast-view-changed": CastViewChanged; | ||||||
|  |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -3,12 +3,8 @@ import { | |||||||
|   getAuth, |   getAuth, | ||||||
|   UnsubscribeFunc, |   UnsubscribeFunc, | ||||||
| } from "home-assistant-js-websocket"; | } from "home-assistant-js-websocket"; | ||||||
| import { | import { html, TemplateResult } from "lit"; | ||||||
|   customElement, | import { customElement, state } from "lit/decorators"; | ||||||
|   html, |  | ||||||
|   internalProperty, |  | ||||||
|   TemplateResult, |  | ||||||
| } from "lit-element"; |  | ||||||
| import { CAST_NS } from "../../../../src/cast/const"; | import { CAST_NS } from "../../../../src/cast/const"; | ||||||
| import { | import { | ||||||
|   ConnectMessage, |   ConnectMessage, | ||||||
| @@ -17,7 +13,11 @@ import { | |||||||
|   ShowDemoMessage, |   ShowDemoMessage, | ||||||
|   ShowLovelaceViewMessage, |   ShowLovelaceViewMessage, | ||||||
| } from "../../../../src/cast/receiver_messages"; | } from "../../../../src/cast/receiver_messages"; | ||||||
| import { ReceiverStatusMessage } from "../../../../src/cast/sender_messages"; | import { | ||||||
|  |   ReceiverErrorCode, | ||||||
|  |   ReceiverErrorMessage, | ||||||
|  |   ReceiverStatusMessage, | ||||||
|  | } from "../../../../src/cast/sender_messages"; | ||||||
| import { atLeastVersion } from "../../../../src/common/config/version"; | import { atLeastVersion } from "../../../../src/common/config/version"; | ||||||
| import { isNavigationClick } from "../../../../src/common/dom/is-navigation-click"; | import { isNavigationClick } from "../../../../src/common/dom/is-navigation-click"; | ||||||
| import { | import { | ||||||
| @@ -36,18 +36,18 @@ let resourcesLoaded = false; | |||||||
|  |  | ||||||
| @customElement("hc-main") | @customElement("hc-main") | ||||||
| export class HcMain extends HassElement { | export class HcMain extends HassElement { | ||||||
|   @internalProperty() private _showDemo = false; |   @state() private _showDemo = false; | ||||||
|  |  | ||||||
|   @internalProperty() private _lovelaceConfig?: LovelaceConfig; |   @state() private _lovelaceConfig?: LovelaceConfig; | ||||||
|  |  | ||||||
|   @internalProperty() private _lovelacePath: string | number | null = null; |   @state() private _lovelacePath: string | number | null = null; | ||||||
|  |  | ||||||
|   @internalProperty() private _error?: string; |   @state() private _error?: string; | ||||||
|  |  | ||||||
|  |   @state() private _urlPath?: string | null; | ||||||
|  |  | ||||||
|   private _unsubLovelace?: UnsubscribeFunc; |   private _unsubLovelace?: UnsubscribeFunc; | ||||||
|  |  | ||||||
|   private _urlPath?: string | null; |  | ||||||
|  |  | ||||||
|   public processIncomingMessage(msg: HassMessage) { |   public processIncomingMessage(msg: HassMessage) { | ||||||
|     if (msg.type === "connect") { |     if (msg.type === "connect") { | ||||||
|       this._handleConnectMessage(msg); |       this._handleConnectMessage(msg); | ||||||
| @@ -72,8 +72,10 @@ export class HcMain extends HassElement { | |||||||
|       !this._lovelaceConfig || |       !this._lovelaceConfig || | ||||||
|       this._lovelacePath === null || |       this._lovelacePath === null || | ||||||
|       // Guard against part of HA not being loaded yet. |       // Guard against part of HA not being loaded yet. | ||||||
|       (this.hass && |       !this.hass || | ||||||
|         (!this.hass.states || !this.hass.config || !this.hass.services)) |       !this.hass.states || | ||||||
|  |       !this.hass.config || | ||||||
|  |       !this.hass.services | ||||||
|     ) { |     ) { | ||||||
|       return html` |       return html` | ||||||
|         <hc-launch-screen |         <hc-launch-screen | ||||||
| @@ -111,6 +113,7 @@ export class HcMain extends HassElement { | |||||||
|         this._sendStatus(); |         this._sendStatus(); | ||||||
|       } |       } | ||||||
|     }); |     }); | ||||||
|  |     this.addEventListener("dialog-closed", this._dialogClosed); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   private _sendStatus(senderId?: string) { |   private _sendStatus(senderId?: string) { | ||||||
| @@ -122,7 +125,7 @@ export class HcMain extends HassElement { | |||||||
|  |  | ||||||
|     if (this.hass) { |     if (this.hass) { | ||||||
|       status.hassUrl = this.hass.auth.data.hassUrl; |       status.hassUrl = this.hass.auth.data.hassUrl; | ||||||
|       status.lovelacePath = this._lovelacePath!; |       status.lovelacePath = this._lovelacePath; | ||||||
|       status.urlPath = this._urlPath; |       status.urlPath = this._urlPath; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -135,6 +138,30 @@ export class HcMain extends HassElement { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   private _sendError( | ||||||
|  |     error_code: number, | ||||||
|  |     error_message: string, | ||||||
|  |     senderId?: string | ||||||
|  |   ) { | ||||||
|  |     const error: ReceiverErrorMessage = { | ||||||
|  |       type: "receiver_error", | ||||||
|  |       error_code, | ||||||
|  |       error_message, | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     if (senderId) { | ||||||
|  |       this.sendMessage(senderId, error); | ||||||
|  |     } else { | ||||||
|  |       for (const sender of castContext.getSenders()) { | ||||||
|  |         this.sendMessage(sender.id, error); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   private _dialogClosed = () => { | ||||||
|  |     document.body.setAttribute("style", "overflow-y: auto !important"); | ||||||
|  |   }; | ||||||
|  |  | ||||||
|   private async _handleGetStatusMessage(msg: GetStatusMessage) { |   private async _handleGetStatusMessage(msg: GetStatusMessage) { | ||||||
|     this._sendStatus(msg.senderId!); |     this._sendStatus(msg.senderId!); | ||||||
|   } |   } | ||||||
| @@ -152,15 +179,19 @@ export class HcMain extends HassElement { | |||||||
|           expires_in: 0, |           expires_in: 0, | ||||||
|         }), |         }), | ||||||
|       }); |       }); | ||||||
|     } catch (err) { |     } catch (err: any) { | ||||||
|       this._error = this._getErrorMessage(err); |       const errorMessage = this._getErrorMessage(err); | ||||||
|  |       this._error = errorMessage; | ||||||
|  |       this._sendError(err, errorMessage); | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|     let connection; |     let connection; | ||||||
|     try { |     try { | ||||||
|       connection = await createConnection({ auth }); |       connection = await createConnection({ auth }); | ||||||
|     } catch (err) { |     } catch (err: any) { | ||||||
|       this._error = this._getErrorMessage(err); |       const errorMessage = this._getErrorMessage(err); | ||||||
|  |       this._error = errorMessage; | ||||||
|  |       this._sendError(err, errorMessage); | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|     if (this.hass) { |     if (this.hass) { | ||||||
| @@ -172,24 +203,29 @@ export class HcMain extends HassElement { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   private async _handleShowLovelaceMessage(msg: ShowLovelaceViewMessage) { |   private async _handleShowLovelaceMessage(msg: ShowLovelaceViewMessage) { | ||||||
|  |     this._showDemo = false; | ||||||
|     // We should not get this command before we are connected. |     // We should not get this command before we are connected. | ||||||
|     // Means a client got out of sync. Let's send status to them. |     // Means a client got out of sync. Let's send status to them. | ||||||
|     if (!this.hass) { |     if (!this.hass) { | ||||||
|       this._sendStatus(msg.senderId!); |       this._sendStatus(msg.senderId!); | ||||||
|       this._error = "Cannot show Lovelace because we're not connected."; |       this._error = "Cannot show Lovelace because we're not connected."; | ||||||
|  |       this._sendError(ReceiverErrorCode.NOT_CONNECTED, this._error); | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|  |     this._error = undefined; | ||||||
|     if (msg.urlPath === "lovelace") { |     if (msg.urlPath === "lovelace") { | ||||||
|       msg.urlPath = null; |       msg.urlPath = null; | ||||||
|     } |     } | ||||||
|  |     this._lovelacePath = msg.viewPath; | ||||||
|     if (!this._unsubLovelace || this._urlPath !== msg.urlPath) { |     if (!this._unsubLovelace || this._urlPath !== msg.urlPath) { | ||||||
|       this._urlPath = msg.urlPath; |       this._urlPath = msg.urlPath; | ||||||
|  |       this._lovelaceConfig = undefined; | ||||||
|       if (this._unsubLovelace) { |       if (this._unsubLovelace) { | ||||||
|         this._unsubLovelace(); |         this._unsubLovelace(); | ||||||
|       } |       } | ||||||
|       const llColl = atLeastVersion(this.hass.connection.haVersion, 0, 107) |       const llColl = atLeastVersion(this.hass.connection.haVersion, 0, 107) | ||||||
|         ? getLovelaceCollection(this.hass!.connection, msg.urlPath) |         ? getLovelaceCollection(this.hass.connection, msg.urlPath) | ||||||
|         : getLegacyLovelaceCollection(this.hass!.connection); |         : getLegacyLovelaceCollection(this.hass.connection); | ||||||
|       // We first do a single refresh because we need to check if there is LL |       // We first do a single refresh because we need to check if there is LL | ||||||
|       // configuration. |       // configuration. | ||||||
|       try { |       try { | ||||||
| @@ -197,9 +233,17 @@ export class HcMain extends HassElement { | |||||||
|         this._unsubLovelace = llColl.subscribe((lovelaceConfig) => |         this._unsubLovelace = llColl.subscribe((lovelaceConfig) => | ||||||
|           this._handleNewLovelaceConfig(lovelaceConfig) |           this._handleNewLovelaceConfig(lovelaceConfig) | ||||||
|         ); |         ); | ||||||
|       } catch (err) { |       } catch (err: any) { | ||||||
|         // eslint-disable-next-line |         if ( | ||||||
|         console.log("Error fetching Lovelace configuration", err, msg); |           atLeastVersion(this.hass.connection.haVersion, 0, 107) && | ||||||
|  |           err.code !== "config_not_found" | ||||||
|  |         ) { | ||||||
|  |           // eslint-disable-next-line | ||||||
|  |           console.log("Error fetching Lovelace configuration", err, msg); | ||||||
|  |           this._error = `Error fetching Lovelace configuration: ${err.message}`; | ||||||
|  |           this._sendError(ReceiverErrorCode.FETCH_CONFIG_FAILED, this._error); | ||||||
|  |           return; | ||||||
|  |         } | ||||||
|         // Generate a Lovelace config. |         // Generate a Lovelace config. | ||||||
|         this._unsubLovelace = () => undefined; |         this._unsubLovelace = () => undefined; | ||||||
|         await this._generateLovelaceConfig(); |         await this._generateLovelaceConfig(); | ||||||
| @@ -214,23 +258,27 @@ export class HcMain extends HassElement { | |||||||
|         loadLovelaceResources(resources, this.hass!.auth.data.hassUrl); |         loadLovelaceResources(resources, this.hass!.auth.data.hassUrl); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     this._showDemo = false; |  | ||||||
|     this._lovelacePath = msg.viewPath; |  | ||||||
|  |  | ||||||
|     this._sendStatus(); |     this._sendStatus(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   private async _generateLovelaceConfig() { |   private async _generateLovelaceConfig() { | ||||||
|     const { generateLovelaceConfigFromHass } = await import( |     const { generateLovelaceDashboardStrategy } = await import( | ||||||
|       "../../../../src/panels/lovelace/common/generate-lovelace-config" |       "../../../../src/panels/lovelace/strategies/get-strategy" | ||||||
|     ); |     ); | ||||||
|     this._handleNewLovelaceConfig( |     this._handleNewLovelaceConfig( | ||||||
|       await generateLovelaceConfigFromHass(this.hass!) |       await generateLovelaceDashboardStrategy( | ||||||
|  |         { | ||||||
|  |           hass: this.hass!, | ||||||
|  |           narrow: false, | ||||||
|  |         }, | ||||||
|  |         "original-states" | ||||||
|  |       ) | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   private _handleNewLovelaceConfig(lovelaceConfig: LovelaceConfig) { |   private _handleNewLovelaceConfig(lovelaceConfig: LovelaceConfig) { | ||||||
|     castContext.setApplicationState(lovelaceConfig.title!); |     castContext.setApplicationState(lovelaceConfig.title || ""); | ||||||
|     this._lovelaceConfig = lovelaceConfig; |     this._lovelaceConfig = lovelaceConfig; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -246,11 +246,15 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) => | |||||||
|  |  | ||||||
|     "light.living_room_lights": { |     "light.living_room_lights": { | ||||||
|       entity_id: "light.living_room_lights", |       entity_id: "light.living_room_lights", | ||||||
|       state: "off", |       state: "on", | ||||||
|       attributes: { |       attributes: { | ||||||
|         min_mireds: 111, |         min_mireds: 111, | ||||||
|         max_mireds: 400, |         max_mireds: 400, | ||||||
|  |         brightness: 175, | ||||||
|  |         color_temp: 300, | ||||||
|  |         supported_color_modes: ["brightness", "color_temp"], | ||||||
|         friendly_name: "Living Room Lights", |         friendly_name: "Living Room Lights", | ||||||
|  |         color_mode: "color_temp", | ||||||
|         supported_features: 55, |         supported_features: 55, | ||||||
|       }, |       }, | ||||||
|     }, |     }, | ||||||
| @@ -263,13 +267,27 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) => | |||||||
|     }, |     }, | ||||||
|     "light.kitchen_lights": { |     "light.kitchen_lights": { | ||||||
|       entity_id: "light.kitchen_lights", |       entity_id: "light.kitchen_lights", | ||||||
|  |       state: "on", | ||||||
|  |       attributes: { | ||||||
|  |         min_mireds: 111, | ||||||
|  |         max_mireds: 400, | ||||||
|  |         brightness: 200, | ||||||
|  |         rgb_color: [255, 175, 96], | ||||||
|  |         supported_color_modes: ["brightness", "color_temp", "rgb"], | ||||||
|  |         color_mode: "rgb", | ||||||
|  |         friendly_name: "Kitchen Lights", | ||||||
|  |         supported_features: 55, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     "light.lifx5": { | ||||||
|  |       entity_id: "light.lifx5", | ||||||
|       state: "off", |       state: "off", | ||||||
|       attributes: { |       attributes: { | ||||||
|         friendly_name: "Kitchen Lights", |         supported_color_modes: ["brightness"], | ||||||
|  |         friendly_name: "Garage Lights", | ||||||
|         supported_features: 1, |         supported_features: 1, | ||||||
|       }, |       }, | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
|     "sensor.plexspy": { |     "sensor.plexspy": { | ||||||
|       entity_id: "sensor.plexspy", |       entity_id: "sensor.plexspy", | ||||||
|       state: "0", |       state: "0", | ||||||
| @@ -482,16 +500,6 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) => | |||||||
|         icon: "hademo:history", |         icon: "hademo:history", | ||||||
|       }, |       }, | ||||||
|     }, |     }, | ||||||
|     "light.lifx5": { |  | ||||||
|       entity_id: "light.lifx5", |  | ||||||
|       state: "on", |  | ||||||
|       attributes: { |  | ||||||
|         min_mireds: 111, |  | ||||||
|         max_mireds: 400, |  | ||||||
|         friendly_name: "Garage Lights", |  | ||||||
|         supported_features: 55, |  | ||||||
|       }, |  | ||||||
|     }, |  | ||||||
|     "sensor.alok_to_home": { |     "sensor.alok_to_home": { | ||||||
|       entity_id: "sensor.alok_to_home", |       entity_id: "sensor.alok_to_home", | ||||||
|       state: "41", |       state: "41", | ||||||
|   | |||||||
| @@ -29,6 +29,11 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({ | |||||||
|             }, |             }, | ||||||
|           ], |           ], | ||||||
|         }, |         }, | ||||||
|  |         { | ||||||
|  |           title: "Energy distribution today", | ||||||
|  |           type: "energy-distribution", | ||||||
|  |           link_dashboard: true, | ||||||
|  |         }, | ||||||
|         { |         { | ||||||
|           type: "thermostat", |           type: "thermostat", | ||||||
|           entity: "climate.upstairs", |           entity: "climate.upstairs", | ||||||
| @@ -113,8 +118,7 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({ | |||||||
|                 on: "/assets/arsaboo/icons/light_bulb_on.png", |                 on: "/assets/arsaboo/icons/light_bulb_on.png", | ||||||
|               }, |               }, | ||||||
|               state_filter: { |               state_filter: { | ||||||
|                 on: |                 on: "brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)", | ||||||
|                   "brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)", |  | ||||||
|                 off: "brightness(80%) saturate(0.8)", |                 off: "brightness(80%) saturate(0.8)", | ||||||
|               }, |               }, | ||||||
|               style: { |               style: { | ||||||
| @@ -196,8 +200,7 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({ | |||||||
|                 on: "/assets/arsaboo/icons/light_bulb_on.png", |                 on: "/assets/arsaboo/icons/light_bulb_on.png", | ||||||
|               }, |               }, | ||||||
|               state_filter: { |               state_filter: { | ||||||
|                 on: |                 on: "brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)", | ||||||
|                   "brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)", |  | ||||||
|                 off: "brightness(80%) saturate(0.8)", |                 off: "brightness(80%) saturate(0.8)", | ||||||
|               }, |               }, | ||||||
|               style: { |               style: { | ||||||
| @@ -277,8 +280,7 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({ | |||||||
|                 on: "/assets/arsaboo/icons/light_bulb_on.png", |                 on: "/assets/arsaboo/icons/light_bulb_on.png", | ||||||
|               }, |               }, | ||||||
|               state_filter: { |               state_filter: { | ||||||
|                 on: |                 on: "brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)", | ||||||
|                   "brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)", |  | ||||||
|                 off: "brightness(80%) saturate(0.8)", |                 off: "brightness(80%) saturate(0.8)", | ||||||
|               }, |               }, | ||||||
|               style: { |               style: { | ||||||
| @@ -315,8 +317,7 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({ | |||||||
|                 on: "/assets/arsaboo/icons/light_bulb_on.png", |                 on: "/assets/arsaboo/icons/light_bulb_on.png", | ||||||
|               }, |               }, | ||||||
|               state_filter: { |               state_filter: { | ||||||
|                 on: |                 on: "brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)", | ||||||
|                   "brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)", |  | ||||||
|                 off: "brightness(80%) saturate(0.8)", |                 off: "brightness(80%) saturate(0.8)", | ||||||
|               }, |               }, | ||||||
|               style: { |               style: { | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| import { MockHomeAssistant } from "../../../src/fake_data/provide_hass"; | import { MockHomeAssistant } from "../../../src/fake_data/provide_hass"; | ||||||
| import { Lovelace } from "../../../src/panels/lovelace/types"; | import { Lovelace } from "../../../src/panels/lovelace/types"; | ||||||
|  | import { energyEntities } from "../stubs/entities"; | ||||||
| import { DemoConfig } from "./types"; | import { DemoConfig } from "./types"; | ||||||
|  |  | ||||||
| export const demoConfigs: Array<() => Promise<DemoConfig>> = [ | export const demoConfigs: Array<() => Promise<DemoConfig>> = [ | ||||||
| @@ -12,9 +13,8 @@ export const demoConfigs: Array<() => Promise<DemoConfig>> = [ | |||||||
| // eslint-disable-next-line import/no-mutable-exports | // eslint-disable-next-line import/no-mutable-exports | ||||||
| export let selectedDemoConfigIndex = 0; | export let selectedDemoConfigIndex = 0; | ||||||
| // eslint-disable-next-line import/no-mutable-exports | // eslint-disable-next-line import/no-mutable-exports | ||||||
| export let selectedDemoConfig: Promise<DemoConfig> = demoConfigs[ | export let selectedDemoConfig: Promise<DemoConfig> = | ||||||
|   selectedDemoConfigIndex |   demoConfigs[selectedDemoConfigIndex](); | ||||||
| ](); |  | ||||||
|  |  | ||||||
| export const setDemoConfig = async ( | export const setDemoConfig = async ( | ||||||
|   hass: MockHomeAssistant, |   hass: MockHomeAssistant, | ||||||
| @@ -28,6 +28,7 @@ export const setDemoConfig = async ( | |||||||
|   selectedDemoConfig = confProm; |   selectedDemoConfig = confProm; | ||||||
|  |  | ||||||
|   hass.addEntities(config.entities(hass.localize), true); |   hass.addEntities(config.entities(hass.localize), true); | ||||||
|  |   hass.addEntities(energyEntities()); | ||||||
|   lovelace.saveConfig(config.lovelace(hass.localize)); |   lovelace.saveConfig(config.lovelace(hass.localize)); | ||||||
|   hass.mockTheme(config.theme()); |   hass.mockTheme(config.theme()); | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -980,8 +980,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () => | |||||||
|         icon: "mdi:account-off", |         icon: "mdi:account-off", | ||||||
|         custom_ui_state_card: "state-card-custom-ui", |         custom_ui_state_card: "state-card-custom-ui", | ||||||
|         templates: { |         templates: { | ||||||
|           icon: |           icon: "if (state === 'on') return 'mdi:account'; else if (state === 'off') return 'mdi:account-off';\n", | ||||||
|             "if (state === 'on') return 'mdi:account'; else if (state === 'off') return 'mdi:account-off';\n", |  | ||||||
|           icon_color: |           icon_color: | ||||||
|             "if (state === 'on') return 'rgb(56, 150, 56)'; else if (state === 'off') return 'rgb(249, 251, 255)';\n", |             "if (state === 'on') return 'rgb(56, 150, 56)'; else if (state === 'off') return 'rgb(249, 251, 255)';\n", | ||||||
|         }, |         }, | ||||||
| @@ -1005,8 +1004,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () => | |||||||
|         icon: "mdi:account-multiple-minus", |         icon: "mdi:account-multiple-minus", | ||||||
|         custom_ui_state_card: "state-card-custom-ui", |         custom_ui_state_card: "state-card-custom-ui", | ||||||
|         templates: { |         templates: { | ||||||
|           icon: |           icon: "if (state === 'on') return 'mdi:account-group'; else if (state === 'off') return 'mdi:account-multiple-minus';\n", | ||||||
|             "if (state === 'on') return 'mdi:account-group'; else if (state === 'off') return 'mdi:account-multiple-minus';\n", |  | ||||||
|           icon_color: |           icon_color: | ||||||
|             "if (state === 'on') return 'rgb(56, 150, 56)'; else if (state === 'off') return 'rgb(249, 251, 255)';\n", |             "if (state === 'on') return 'rgb(56, 150, 56)'; else if (state === 'off') return 'rgb(249, 251, 255)';\n", | ||||||
|         }, |         }, | ||||||
| @@ -1114,6 +1112,9 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () => | |||||||
|         min_mireds: 153, |         min_mireds: 153, | ||||||
|         max_mireds: 500, |         max_mireds: 500, | ||||||
|         brightness: 63, |         brightness: 63, | ||||||
|  |         color_temp: 200, | ||||||
|  |         supported_color_modes: ["brightness", "color_temp", "rgb"], | ||||||
|  |         color_mode: "color_temp", | ||||||
|         friendly_name: "Upstairs lights", |         friendly_name: "Upstairs lights", | ||||||
|         supported_features: 63, |         supported_features: 63, | ||||||
|         custom_ui_state_card: "state-card-custom-ui", |         custom_ui_state_card: "state-card-custom-ui", | ||||||
| @@ -1125,6 +1126,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () => | |||||||
|       attributes: { |       attributes: { | ||||||
|         friendly_name: "Walk in closet lights", |         friendly_name: "Walk in closet lights", | ||||||
|         supported_features: 41, |         supported_features: 41, | ||||||
|  |         supported_color_modes: ["brightness", "color_temp"], | ||||||
|         custom_ui_state_card: "state-card-custom-ui", |         custom_ui_state_card: "state-card-custom-ui", | ||||||
|         icon: "mdi:wall-sconce", |         icon: "mdi:wall-sconce", | ||||||
|       }, |       }, | ||||||
| @@ -1136,6 +1138,8 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () => | |||||||
|         brightness: 254, |         brightness: 254, | ||||||
|         friendly_name: "Outdoor lights", |         friendly_name: "Outdoor lights", | ||||||
|         supported_features: 41, |         supported_features: 41, | ||||||
|  |         supported_color_modes: ["brightness"], | ||||||
|  |         color_mode: "brightness", | ||||||
|         custom_ui_state_card: "state-card-custom-ui", |         custom_ui_state_card: "state-card-custom-ui", | ||||||
|         icon: "mdi:wall-sconce", |         icon: "mdi:wall-sconce", | ||||||
|       }, |       }, | ||||||
| @@ -1148,6 +1152,8 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () => | |||||||
|         max_mireds: 500, |         max_mireds: 500, | ||||||
|         brightness: 128, |         brightness: 128, | ||||||
|         color_temp: 366, |         color_temp: 366, | ||||||
|  |         supported_color_modes: ["brightness", "color_temp", "rgb"], | ||||||
|  |         color_mode: "color_temp", | ||||||
|         effect_list: ["colorloop"], |         effect_list: ["colorloop"], | ||||||
|         friendly_name: "Downstairs lights", |         friendly_name: "Downstairs lights", | ||||||
|         supported_features: 63, |         supported_features: 63, | ||||||
| @@ -1307,6 +1313,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () => | |||||||
|       attributes: { |       attributes: { | ||||||
|         min_mireds: 153, |         min_mireds: 153, | ||||||
|         max_mireds: 500, |         max_mireds: 500, | ||||||
|  |         supported_color_modes: ["brightness", "color_temp"], | ||||||
|         is_deconz_group: false, |         is_deconz_group: false, | ||||||
|         friendly_name: "Bedside Lamp", |         friendly_name: "Bedside Lamp", | ||||||
|         supported_features: 63, |         supported_features: 63, | ||||||
| @@ -1320,6 +1327,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () => | |||||||
|       attributes: { |       attributes: { | ||||||
|         min_mireds: 153, |         min_mireds: 153, | ||||||
|         max_mireds: 500, |         max_mireds: 500, | ||||||
|  |         supported_color_modes: ["brightness", "color_temp"], | ||||||
|         is_deconz_group: false, |         is_deconz_group: false, | ||||||
|         friendly_name: "Floorlamp Reading Light", |         friendly_name: "Floorlamp Reading Light", | ||||||
|         supported_features: 43, |         supported_features: 43, | ||||||
| @@ -1335,6 +1343,8 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () => | |||||||
|         max_mireds: 500, |         max_mireds: 500, | ||||||
|         brightness: 128, |         brightness: 128, | ||||||
|         color_temp: 366, |         color_temp: 366, | ||||||
|  |         supported_color_modes: ["brightness", "color_temp", "rgb"], | ||||||
|  |         color_mode: "color_temp", | ||||||
|         effect_list: ["colorloop"], |         effect_list: ["colorloop"], | ||||||
|         is_deconz_group: false, |         is_deconz_group: false, | ||||||
|         friendly_name: "Hallway window light", |         friendly_name: "Hallway window light", | ||||||
| @@ -1349,6 +1359,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () => | |||||||
|       attributes: { |       attributes: { | ||||||
|         brightness: 77, |         brightness: 77, | ||||||
|         is_deconz_group: false, |         is_deconz_group: false, | ||||||
|  |         supported_color_modes: ["brightness"], | ||||||
|         friendly_name: "Isa Ceiling Light", |         friendly_name: "Isa Ceiling Light", | ||||||
|         supported_features: 41, |         supported_features: 41, | ||||||
|         custom_ui_state_card: "state-card-custom-ui", |         custom_ui_state_card: "state-card-custom-ui", | ||||||
| @@ -1363,6 +1374,8 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () => | |||||||
|         max_mireds: 500, |         max_mireds: 500, | ||||||
|         brightness: 150, |         brightness: 150, | ||||||
|         color_temp: 366, |         color_temp: 366, | ||||||
|  |         supported_color_modes: ["brightness", "color_temp"], | ||||||
|  |         color_mode: "color_temp", | ||||||
|         effect_list: ["colorloop"], |         effect_list: ["colorloop"], | ||||||
|         is_deconz_group: false, |         is_deconz_group: false, | ||||||
|         friendly_name: "Floorlamp", |         friendly_name: "Floorlamp", | ||||||
| @@ -1377,6 +1390,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () => | |||||||
|       attributes: { |       attributes: { | ||||||
|         friendly_name: "Bedroom Ceiling Light", |         friendly_name: "Bedroom Ceiling Light", | ||||||
|         supported_features: 41, |         supported_features: 41, | ||||||
|  |         supported_color_modes: ["brightness"], | ||||||
|         custom_ui_state_card: "state-card-custom-ui", |         custom_ui_state_card: "state-card-custom-ui", | ||||||
|         icon: "mdi:ceiling-light", |         icon: "mdi:ceiling-light", | ||||||
|       }, |       }, | ||||||
| @@ -1387,6 +1401,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () => | |||||||
|       attributes: { |       attributes: { | ||||||
|         friendly_name: "Nightlight", |         friendly_name: "Nightlight", | ||||||
|         supported_features: 17, |         supported_features: 17, | ||||||
|  |         supported_color_modes: ["brightness"], | ||||||
|         custom_ui_state_card: "state-card-custom-ui", |         custom_ui_state_card: "state-card-custom-ui", | ||||||
|         icon: "mdi:lamp", |         icon: "mdi:lamp", | ||||||
|       }, |       }, | ||||||
| @@ -1753,6 +1768,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () => | |||||||
|         power_consumption: 2.2, |         power_consumption: 2.2, | ||||||
|         friendly_name: "Upstairs Hallway Light", |         friendly_name: "Upstairs Hallway Light", | ||||||
|         supported_features: 33, |         supported_features: 33, | ||||||
|  |         supported_color_modes: ["brightness"], | ||||||
|         custom_ui_state_card: "state-card-custom-ui", |         custom_ui_state_card: "state-card-custom-ui", | ||||||
|         icon: "mdi:ceiling-light", |         icon: "mdi:ceiling-light", | ||||||
|       }, |       }, | ||||||
| @@ -1768,6 +1784,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () => | |||||||
|         power_consumption: 0, |         power_consumption: 0, | ||||||
|         friendly_name: "Dining Room Light", |         friendly_name: "Dining Room Light", | ||||||
|         supported_features: 33, |         supported_features: 33, | ||||||
|  |         supported_color_modes: ["brightness"], | ||||||
|         custom_ui_state_card: "state-card-custom-ui", |         custom_ui_state_card: "state-card-custom-ui", | ||||||
|         icon: "mdi:ceiling-light", |         icon: "mdi:ceiling-light", | ||||||
|       }, |       }, | ||||||
| @@ -1783,6 +1800,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () => | |||||||
|         power_consumption: 0, |         power_consumption: 0, | ||||||
|         friendly_name: "Living room Spotlights", |         friendly_name: "Living room Spotlights", | ||||||
|         supported_features: 33, |         supported_features: 33, | ||||||
|  |         supported_color_modes: ["brightness"], | ||||||
|         custom_ui_state_card: "state-card-custom-ui", |         custom_ui_state_card: "state-card-custom-ui", | ||||||
|         icon: "mdi:track-light", |         icon: "mdi:track-light", | ||||||
|       }, |       }, | ||||||
| @@ -1799,6 +1817,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () => | |||||||
|         power_consumption: 2.5, |         power_consumption: 2.5, | ||||||
|         friendly_name: "Passage Lights", |         friendly_name: "Passage Lights", | ||||||
|         supported_features: 33, |         supported_features: 33, | ||||||
|  |         supported_color_modes: ["brightness"], | ||||||
|         custom_ui_state_card: "state-card-custom-ui", |         custom_ui_state_card: "state-card-custom-ui", | ||||||
|         icon: "mdi:track-light", |         icon: "mdi:track-light", | ||||||
|       }, |       }, | ||||||
| @@ -1843,6 +1862,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () => | |||||||
|         power_consumption: 37.4, |         power_consumption: 37.4, | ||||||
|         friendly_name: "Kitchen Lights", |         friendly_name: "Kitchen Lights", | ||||||
|         supported_features: 33, |         supported_features: 33, | ||||||
|  |         supported_color_modes: ["brightness"], | ||||||
|         custom_ui_state_card: "state-card-custom-ui", |         custom_ui_state_card: "state-card-custom-ui", | ||||||
|         icon: "mdi:track-light", |         icon: "mdi:track-light", | ||||||
|       }, |       }, | ||||||
|   | |||||||
| @@ -440,57 +440,43 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({ | |||||||
|           type: "horizontal-stack", |           type: "horizontal-stack", | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
|  |           type: "grid", | ||||||
|  |           columns: 2, | ||||||
|           cards: [ |           cards: [ | ||||||
|             { |             { | ||||||
|               cards: [ |               graph: "line", | ||||||
|                 { |               type: "sensor", | ||||||
|                   graph: "line", |               entity: "sensor.temperature_bedroom", | ||||||
|                   type: "sensor", |  | ||||||
|                   entity: "sensor.temperature_bedroom", |  | ||||||
|                 }, |  | ||||||
|                 { |  | ||||||
|                   graph: "line", |  | ||||||
|                   type: "sensor", |  | ||||||
|                   name: "S's room", |  | ||||||
|                   entity: "sensor.temperature_stefan", |  | ||||||
|                 }, |  | ||||||
|               ], |  | ||||||
|               type: "horizontal-stack", |  | ||||||
|             }, |             }, | ||||||
|             { |             { | ||||||
|               cards: [ |               graph: "line", | ||||||
|                 { |               type: "sensor", | ||||||
|                   graph: "line", |               name: "S's room", | ||||||
|                   type: "sensor", |               entity: "sensor.temperature_stefan", | ||||||
|                   entity: "sensor.temperature_passage", |  | ||||||
|                 }, |  | ||||||
|                 { |  | ||||||
|                   graph: "line", |  | ||||||
|                   type: "sensor", |  | ||||||
|                   name: "Bathroom", |  | ||||||
|                   entity: "sensor.temperature_downstairs_bathroom", |  | ||||||
|                 }, |  | ||||||
|               ], |  | ||||||
|               type: "horizontal-stack", |  | ||||||
|             }, |             }, | ||||||
|             { |             { | ||||||
|               cards: [ |               graph: "line", | ||||||
|                 { |               type: "sensor", | ||||||
|                   graph: "line", |               entity: "sensor.temperature_passage", | ||||||
|                   type: "sensor", |             }, | ||||||
|                   entity: "sensor.temperature_storage", |             { | ||||||
|                 }, |               graph: "line", | ||||||
|                 { |               type: "sensor", | ||||||
|                   graph: "line", |               name: "Bathroom", | ||||||
|                   type: "sensor", |               entity: "sensor.temperature_downstairs_bathroom", | ||||||
|                   name: "Refrigerator", |             }, | ||||||
|                   entity: "sensor.refrigerator", |             { | ||||||
|                 }, |               graph: "line", | ||||||
|               ], |               type: "sensor", | ||||||
|               type: "horizontal-stack", |               entity: "sensor.temperature_storage", | ||||||
|  |             }, | ||||||
|  |             { | ||||||
|  |               graph: "line", | ||||||
|  |               type: "sensor", | ||||||
|  |               name: "Refrigerator", | ||||||
|  |               entity: "sensor.refrigerator", | ||||||
|             }, |             }, | ||||||
|           ], |           ], | ||||||
|           type: "vertical-stack", |  | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
|           entities: [ |           entities: [ | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| /* eslint-disable */ | /* eslint-disable */ | ||||||
| import { LitElement } from "lit-element"; | import { LitElement } from "lit"; | ||||||
| import "./card-tools"; | import "./card-tools"; | ||||||
|  |  | ||||||
| class CardModder extends LitElement { | class CardModder extends LitElement { | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| /* eslint-disable */ | /* eslint-disable */ | ||||||
| import { html, LitElement } from "lit-element"; | import { html, LitElement } from "lit"; | ||||||
|  |  | ||||||
| if (!window.cardTools) { | if (!window.cardTools) { | ||||||
|   const version = 0.2; |   const version = 0.2; | ||||||
|   | |||||||
| @@ -1,12 +1,6 @@ | |||||||
| import { | import { mdiTelevision } from "@mdi/js"; | ||||||
|   css, | import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; | ||||||
|   CSSResult, | import { customElement, state } from "lit/decorators"; | ||||||
|   customElement, |  | ||||||
|   html, |  | ||||||
|   internalProperty, |  | ||||||
|   LitElement, |  | ||||||
|   TemplateResult, |  | ||||||
| } from "lit-element"; |  | ||||||
| import { CastManager } from "../../../src/cast/cast_manager"; | import { CastManager } from "../../../src/cast/cast_manager"; | ||||||
| import { castSendShowDemo } from "../../../src/cast/receiver_messages"; | import { castSendShowDemo } from "../../../src/cast/receiver_messages"; | ||||||
| import "../../../src/components/ha-icon"; | import "../../../src/components/ha-icon"; | ||||||
| @@ -20,7 +14,7 @@ import { HomeAssistant } from "../../../src/types"; | |||||||
| class CastDemoRow extends LitElement implements LovelaceRow { | class CastDemoRow extends LitElement implements LovelaceRow { | ||||||
|   public hass!: HomeAssistant; |   public hass!: HomeAssistant; | ||||||
|  |  | ||||||
|   @internalProperty() private _castManager?: CastManager | null; |   @state() private _castManager?: CastManager | null; | ||||||
|  |  | ||||||
|   public setConfig(_config: CastConfig): void { |   public setConfig(_config: CastConfig): void { | ||||||
|     // No config possible. |     // No config possible. | ||||||
| @@ -34,7 +28,7 @@ class CastDemoRow extends LitElement implements LovelaceRow { | |||||||
|       return html``; |       return html``; | ||||||
|     } |     } | ||||||
|     return html` |     return html` | ||||||
|       <ha-icon icon="hademo:television"></ha-icon> |       <ha-svg-icon .path=${mdiTelevision}></ha-svg-icon> | ||||||
|       <div class="flex"> |       <div class="flex"> | ||||||
|         <div class="name">Show Chromecast interface</div> |         <div class="name">Show Chromecast interface</div> | ||||||
|         <google-cast-launcher></google-cast-launcher> |         <google-cast-launcher></google-cast-launcher> | ||||||
| @@ -73,13 +67,13 @@ class CastDemoRow extends LitElement implements LovelaceRow { | |||||||
|     this.style.display = this._castManager ? "" : "none"; |     this.style.display = this._castManager ? "" : "none"; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   static get styles(): CSSResult { |   static get styles(): CSSResultGroup { | ||||||
|     return css` |     return css` | ||||||
|       :host { |       :host { | ||||||
|         display: flex; |         display: flex; | ||||||
|         align-items: center; |         align-items: center; | ||||||
|       } |       } | ||||||
|       ha-icon { |       ha-svg-icon { | ||||||
|         padding: 8px; |         padding: 8px; | ||||||
|         color: var(--paper-item-icon-color); |         color: var(--paper-item-icon-color); | ||||||
|       } |       } | ||||||
|   | |||||||
| @@ -1,14 +1,7 @@ | |||||||
| import "@material/mwc-button"; | import "@material/mwc-button"; | ||||||
| import { | import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; | ||||||
|   css, | import { property, state } from "lit/decorators"; | ||||||
|   CSSResult, | import { until } from "lit/directives/until"; | ||||||
|   html, |  | ||||||
|   internalProperty, |  | ||||||
|   LitElement, |  | ||||||
|   property, |  | ||||||
|   TemplateResult, |  | ||||||
| } from "lit-element"; |  | ||||||
| import { until } from "lit-html/directives/until"; |  | ||||||
| import "../../../src/components/ha-card"; | import "../../../src/components/ha-card"; | ||||||
| import "../../../src/components/ha-circular-progress"; | import "../../../src/components/ha-circular-progress"; | ||||||
| import { LovelaceCardConfig } from "../../../src/data/lovelace"; | import { LovelaceCardConfig } from "../../../src/data/lovelace"; | ||||||
| @@ -26,7 +19,7 @@ export class HADemoCard extends LitElement implements LovelaceCard { | |||||||
|  |  | ||||||
|   @property({ attribute: false }) public hass!: MockHomeAssistant; |   @property({ attribute: false }) public hass!: MockHomeAssistant; | ||||||
|  |  | ||||||
|   @internalProperty() private _switching?: boolean; |   @state() private _switching = false; | ||||||
|  |  | ||||||
|   private _hidden = localStorage.hide_demo_card; |   private _hidden = localStorage.hide_demo_card; | ||||||
|  |  | ||||||
| @@ -34,12 +27,7 @@ export class HADemoCard extends LitElement implements LovelaceCard { | |||||||
|     return this._hidden ? 0 : 2; |     return this._hidden ? 0 : 2; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   public setConfig( |   public setConfig(_config: LovelaceCardConfig) {} | ||||||
|     // @ts-ignore |  | ||||||
|     // eslint-disable-next-line @typescript-eslint/no-unused-vars |  | ||||||
|     config: LovelaceCardConfig |  | ||||||
|     // eslint-disable-next-line @typescript-eslint/no-empty-function |  | ||||||
|   ) {} |  | ||||||
|  |  | ||||||
|   protected render(): TemplateResult { |   protected render(): TemplateResult { | ||||||
|     if (this._hidden) { |     if (this._hidden) { | ||||||
| @@ -56,7 +44,7 @@ export class HADemoCard extends LitElement implements LovelaceCard { | |||||||
|                     (conf) => html` |                     (conf) => html` | ||||||
|                       ${conf.name} |                       ${conf.name} | ||||||
|                       <small> |                       <small> | ||||||
|                         <a target="_blank" href="${conf.authorUrl}"> |                         <a target="_blank" href=${conf.authorUrl}> | ||||||
|                           ${this.hass.localize( |                           ${this.hass.localize( | ||||||
|                             "ui.panel.page-demo.cards.demo.demo_by", |                             "ui.panel.page-demo.cards.demo.demo_by", | ||||||
|                             "name", |                             "name", | ||||||
| @@ -106,14 +94,14 @@ export class HADemoCard extends LitElement implements LovelaceCard { | |||||||
|     this._switching = true; |     this._switching = true; | ||||||
|     try { |     try { | ||||||
|       await setDemoConfig(this.hass, this.lovelace!, index); |       await setDemoConfig(this.hass, this.lovelace!, index); | ||||||
|     } catch (err) { |     } catch (err: any) { | ||||||
|       alert("Failed to switch config :-("); |       alert("Failed to switch config :-("); | ||||||
|     } finally { |     } finally { | ||||||
|       this._switching = false; |       this._switching = false; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   static get styles(): CSSResult[] { |   static get styles(): CSSResultGroup { | ||||||
|     return [ |     return [ | ||||||
|       css` |       css` | ||||||
|         a { |         a { | ||||||
|   | |||||||
| @@ -1,5 +1,3 @@ | |||||||
| import "@polymer/polymer/lib/elements/dom-if"; |  | ||||||
| import "@polymer/polymer/lib/elements/dom-repeat"; |  | ||||||
| import "../../src/resources/ha-style"; | import "../../src/resources/ha-style"; | ||||||
| import "../../src/resources/roboto"; | import "../../src/resources/roboto"; | ||||||
| import "../../src/resources/safari-14-attachshadow-patch"; | import "../../src/resources/safari-14-attachshadow-patch"; | ||||||
|   | |||||||
| @@ -20,11 +20,14 @@ import { mockShoppingList } from "./stubs/shopping_list"; | |||||||
| import { mockSystemLog } from "./stubs/system_log"; | import { mockSystemLog } from "./stubs/system_log"; | ||||||
| import { mockTemplate } from "./stubs/template"; | import { mockTemplate } from "./stubs/template"; | ||||||
| import { mockTranslations } from "./stubs/translations"; | import { mockTranslations } from "./stubs/translations"; | ||||||
|  | import { mockEnergy } from "./stubs/energy"; | ||||||
|  | import { mockConfig } from "./stubs/config"; | ||||||
|  | import { energyEntities } from "./stubs/entities"; | ||||||
|  |  | ||||||
| class HaDemo extends HomeAssistantAppEl { | class HaDemo extends HomeAssistantAppEl { | ||||||
|   protected async _initialize() { |   protected async _initializeHass() { | ||||||
|     const initial: Partial<MockHomeAssistant> = { |     const initial: Partial<MockHomeAssistant> = { | ||||||
|       panelUrl: (this as any).panelUrl, |       panelUrl: (this as any)._panelUrl, | ||||||
|       // Override updateHass so that the correct hass lifecycle methods are called |       // Override updateHass so that the correct hass lifecycle methods are called | ||||||
|       updateHass: (hassUpdate: Partial<HomeAssistant>) => |       updateHass: (hassUpdate: Partial<HomeAssistant>) => | ||||||
|         this._updateHass(hassUpdate), |         this._updateHass(hassUpdate), | ||||||
| @@ -47,8 +50,12 @@ class HaDemo extends HomeAssistantAppEl { | |||||||
|     mockEvents(hass); |     mockEvents(hass); | ||||||
|     mockMediaPlayer(hass); |     mockMediaPlayer(hass); | ||||||
|     mockFrontend(hass); |     mockFrontend(hass); | ||||||
|  |     mockEnergy(hass); | ||||||
|  |     mockConfig(hass); | ||||||
|     mockPersistentNotification(hass); |     mockPersistentNotification(hass); | ||||||
|  |  | ||||||
|  |     hass.addEntities(energyEntities()); | ||||||
|  |  | ||||||
|     // Once config is loaded AND localize, set entities and apply theme. |     // Once config is loaded AND localize, set entities and apply theme. | ||||||
|     Promise.all([selectedDemoConfig, localizePromise]).then( |     Promise.all([selectedDemoConfig, localizePromise]).then( | ||||||
|       ([conf, localize]) => { |       ([conf, localize]) => { | ||||||
| @@ -70,7 +77,7 @@ class HaDemo extends HomeAssistantAppEl { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         e.preventDefault(); |         e.preventDefault(); | ||||||
|         navigate(this, href); |         navigate(href); | ||||||
|       }, |       }, | ||||||
|       { capture: true } |       { capture: true } | ||||||
|     ); |     ); | ||||||
|   | |||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										7
									
								
								demo/src/stubs/area_registry.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								demo/src/stubs/area_registry.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | |||||||
|  | import { AreaRegistryEntry } from "../../../src/data/area_registry"; | ||||||
|  | import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass"; | ||||||
|  |  | ||||||
|  | export const mockAreaRegistry = ( | ||||||
|  |   hass: MockHomeAssistant, | ||||||
|  |   data: AreaRegistryEntry[] = [] | ||||||
|  | ) => hass.mockWS("config/area_registry/list", () => data); | ||||||
							
								
								
									
										41
									
								
								demo/src/stubs/config.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								demo/src/stubs/config.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | |||||||
|  | import { MockHomeAssistant } from "../../../src/fake_data/provide_hass"; | ||||||
|  |  | ||||||
|  | export const mockConfig = (hass: MockHomeAssistant) => { | ||||||
|  |   hass.mockAPI("config/config_entries/entry", () => [ | ||||||
|  |     { | ||||||
|  |       entry_id: "co2signal", | ||||||
|  |       domain: "co2signal", | ||||||
|  |       title: "CO2 Signal", | ||||||
|  |       source: "user", | ||||||
|  |       state: "loaded", | ||||||
|  |       supports_options: false, | ||||||
|  |       supports_unload: true, | ||||||
|  |       pref_disable_new_entities: false, | ||||||
|  |       pref_disable_polling: false, | ||||||
|  |       disabled_by: null, | ||||||
|  |       reason: null, | ||||||
|  |     }, | ||||||
|  |   ]); | ||||||
|  |   hass.mockWS("config/entity_registry/list", () => [ | ||||||
|  |     { | ||||||
|  |       config_entry_id: "co2signal", | ||||||
|  |       device_id: "co2signal", | ||||||
|  |       area_id: null, | ||||||
|  |       disabled_by: null, | ||||||
|  |       entity_id: "sensor.co2_intensity", | ||||||
|  |       name: null, | ||||||
|  |       icon: null, | ||||||
|  |       platform: "co2signal", | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       config_entry_id: "co2signal", | ||||||
|  |       device_id: "co2signal", | ||||||
|  |       area_id: null, | ||||||
|  |       disabled_by: null, | ||||||
|  |       entity_id: "sensor.grid_fossil_fuel_percentage", | ||||||
|  |       name: null, | ||||||
|  |       icon: null, | ||||||
|  |       platform: "co2signal", | ||||||
|  |     }, | ||||||
|  |   ]); | ||||||
|  | }; | ||||||
							
								
								
									
										7
									
								
								demo/src/stubs/device_registry.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								demo/src/stubs/device_registry.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | |||||||
|  | import { DeviceRegistryEntry } from "../../../src/data/device_registry"; | ||||||
|  | import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass"; | ||||||
|  |  | ||||||
|  | export const mockDeviceRegistry = ( | ||||||
|  |   hass: MockHomeAssistant, | ||||||
|  |   data: DeviceRegistryEntry[] = [] | ||||||
|  | ) => hass.mockWS("config/device_registry/list", () => data); | ||||||
							
								
								
									
										137
									
								
								demo/src/stubs/energy.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								demo/src/stubs/energy.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,137 @@ | |||||||
|  | import { format, startOfToday, startOfTomorrow } from "date-fns"; | ||||||
|  | import { EnergySolarForecasts } from "../../../src/data/energy"; | ||||||
|  | import { MockHomeAssistant } from "../../../src/fake_data/provide_hass"; | ||||||
|  |  | ||||||
|  | export const mockEnergy = (hass: MockHomeAssistant) => { | ||||||
|  |   hass.mockWS("energy/get_prefs", () => ({ | ||||||
|  |     energy_sources: [ | ||||||
|  |       { | ||||||
|  |         type: "grid", | ||||||
|  |         flow_from: [ | ||||||
|  |           { | ||||||
|  |             stat_energy_from: "sensor.energy_consumption_tarif_1", | ||||||
|  |             stat_cost: "sensor.energy_consumption_tarif_1_cost", | ||||||
|  |             entity_energy_from: "sensor.energy_consumption_tarif_1", | ||||||
|  |             entity_energy_price: null, | ||||||
|  |             number_energy_price: null, | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             stat_energy_from: "sensor.energy_consumption_tarif_2", | ||||||
|  |             stat_cost: "sensor.energy_consumption_tarif_2_cost", | ||||||
|  |             entity_energy_from: "sensor.energy_consumption_tarif_2", | ||||||
|  |             entity_energy_price: null, | ||||||
|  |             number_energy_price: null, | ||||||
|  |           }, | ||||||
|  |         ], | ||||||
|  |         flow_to: [ | ||||||
|  |           { | ||||||
|  |             stat_energy_to: "sensor.energy_production_tarif_1", | ||||||
|  |             stat_compensation: "sensor.energy_production_tarif_1_compensation", | ||||||
|  |             entity_energy_to: "sensor.energy_production_tarif_1", | ||||||
|  |             entity_energy_price: null, | ||||||
|  |             number_energy_price: null, | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             stat_energy_to: "sensor.energy_production_tarif_2", | ||||||
|  |             stat_compensation: "sensor.energy_production_tarif_2_compensation", | ||||||
|  |             entity_energy_to: "sensor.energy_production_tarif_2", | ||||||
|  |             entity_energy_price: null, | ||||||
|  |             number_energy_price: null, | ||||||
|  |           }, | ||||||
|  |         ], | ||||||
|  |         cost_adjustment_day: 0, | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         type: "solar", | ||||||
|  |         stat_energy_from: "sensor.solar_production", | ||||||
|  |         config_entry_solar_forecast: ["solar_forecast"], | ||||||
|  |       }, | ||||||
|  |       /* { | ||||||
|  |         type: "battery", | ||||||
|  |         stat_energy_from: "sensor.battery_output", | ||||||
|  |         stat_energy_to: "sensor.battery_input", | ||||||
|  |       }, */ | ||||||
|  |       { | ||||||
|  |         type: "gas", | ||||||
|  |         stat_energy_from: "sensor.energy_gas", | ||||||
|  |         stat_cost: "sensor.energy_gas_cost", | ||||||
|  |         entity_energy_from: "sensor.energy_gas", | ||||||
|  |         entity_energy_price: null, | ||||||
|  |         number_energy_price: null, | ||||||
|  |       }, | ||||||
|  |     ], | ||||||
|  |     device_consumption: [ | ||||||
|  |       { | ||||||
|  |         stat_consumption: "sensor.energy_car", | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         stat_consumption: "sensor.energy_ac", | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         stat_consumption: "sensor.energy_washing_machine", | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         stat_consumption: "sensor.energy_dryer", | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         stat_consumption: "sensor.energy_heat_pump", | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         stat_consumption: "sensor.energy_boiler", | ||||||
|  |       }, | ||||||
|  |     ], | ||||||
|  |   })); | ||||||
|  |   hass.mockWS("energy/info", () => ({ cost_sensors: [] })); | ||||||
|  |   hass.mockWS("energy/fossil_energy_consumption", ({ period }) => ({ | ||||||
|  |     start: period === "month" ? 250 : period === "day" ? 10 : 2, | ||||||
|  |   })); | ||||||
|  |   const todayString = format(startOfToday(), "yyyy-MM-dd"); | ||||||
|  |   const tomorrowString = format(startOfTomorrow(), "yyyy-MM-dd"); | ||||||
|  |   hass.mockWS( | ||||||
|  |     "energy/solar_forecast", | ||||||
|  |     (): EnergySolarForecasts => ({ | ||||||
|  |       solar_forecast: { | ||||||
|  |         wh_hours: { | ||||||
|  |           [`${todayString}T06:00:00`]: 0, | ||||||
|  |           [`${todayString}T06:23:00`]: 6, | ||||||
|  |           [`${todayString}T06:45:00`]: 39, | ||||||
|  |           [`${todayString}T07:00:00`]: 28, | ||||||
|  |           [`${todayString}T08:00:00`]: 208, | ||||||
|  |           [`${todayString}T09:00:00`]: 352, | ||||||
|  |           [`${todayString}T10:00:00`]: 544, | ||||||
|  |           [`${todayString}T11:00:00`]: 748, | ||||||
|  |           [`${todayString}T12:00:00`]: 1259, | ||||||
|  |           [`${todayString}T13:00:00`]: 1361, | ||||||
|  |           [`${todayString}T14:00:00`]: 1373, | ||||||
|  |           [`${todayString}T15:00:00`]: 1370, | ||||||
|  |           [`${todayString}T16:00:00`]: 1186, | ||||||
|  |           [`${todayString}T17:00:00`]: 937, | ||||||
|  |           [`${todayString}T18:00:00`]: 652, | ||||||
|  |           [`${todayString}T19:00:00`]: 370, | ||||||
|  |           [`${todayString}T20:00:00`]: 155, | ||||||
|  |           [`${todayString}T21:48:00`]: 24, | ||||||
|  |           [`${todayString}T22:36:00`]: 0, | ||||||
|  |           [`${tomorrowString}T06:01:00`]: 0, | ||||||
|  |           [`${tomorrowString}T06:23:00`]: 9, | ||||||
|  |           [`${tomorrowString}T06:45:00`]: 47, | ||||||
|  |           [`${tomorrowString}T07:00:00`]: 48, | ||||||
|  |           [`${tomorrowString}T08:00:00`]: 473, | ||||||
|  |           [`${tomorrowString}T09:00:00`]: 827, | ||||||
|  |           [`${tomorrowString}T10:00:00`]: 1153, | ||||||
|  |           [`${tomorrowString}T11:00:00`]: 1413, | ||||||
|  |           [`${tomorrowString}T12:00:00`]: 1590, | ||||||
|  |           [`${tomorrowString}T13:00:00`]: 1652, | ||||||
|  |           [`${tomorrowString}T14:00:00`]: 1612, | ||||||
|  |           [`${tomorrowString}T15:00:00`]: 1438, | ||||||
|  |           [`${tomorrowString}T16:00:00`]: 1149, | ||||||
|  |           [`${tomorrowString}T17:00:00`]: 830, | ||||||
|  |           [`${tomorrowString}T18:00:00`]: 542, | ||||||
|  |           [`${tomorrowString}T19:00:00`]: 311, | ||||||
|  |           [`${tomorrowString}T20:00:00`]: 140, | ||||||
|  |           [`${tomorrowString}T21:47:00`]: 22, | ||||||
|  |           [`${tomorrowString}T22:34:00`]: 0, | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |     }) | ||||||
|  |   ); | ||||||
|  | }; | ||||||
							
								
								
									
										178
									
								
								demo/src/stubs/entities.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										178
									
								
								demo/src/stubs/entities.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,178 @@ | |||||||
|  | import { convertEntities } from "../../../src/fake_data/entity"; | ||||||
|  |  | ||||||
|  | export const energyEntities = () => | ||||||
|  |   convertEntities({ | ||||||
|  |     "sensor.grid_fossil_fuel_percentage": { | ||||||
|  |       entity_id: "sensor.grid_fossil_fuel_percentage", | ||||||
|  |       state: "88.6", | ||||||
|  |       attributes: { | ||||||
|  |         unit_of_measurement: "%", | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     "sensor.solar_production": { | ||||||
|  |       entity_id: "sensor.solar_production", | ||||||
|  |       state: "88.6", | ||||||
|  |       attributes: { | ||||||
|  |         last_reset: "1970-01-01T00:00:00:00+00", | ||||||
|  |         friendly_name: "Solar", | ||||||
|  |         unit_of_measurement: "kWh", | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     "sensor.battery_input": { | ||||||
|  |       entity_id: "sensor.battery_input", | ||||||
|  |       state: "4", | ||||||
|  |       attributes: { | ||||||
|  |         last_reset: "1970-01-01T00:00:00:00+00", | ||||||
|  |         friendly_name: "Battery Input", | ||||||
|  |         unit_of_measurement: "kWh", | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     "sensor.battery_output": { | ||||||
|  |       entity_id: "sensor.battery_output", | ||||||
|  |       state: "3", | ||||||
|  |       attributes: { | ||||||
|  |         last_reset: "1970-01-01T00:00:00:00+00", | ||||||
|  |         friendly_name: "Battery Output", | ||||||
|  |         unit_of_measurement: "kWh", | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     "sensor.energy_consumption_tarif_1": { | ||||||
|  |       entity_id: "sensor.energy_consumption_tarif_1	", | ||||||
|  |       state: "88.6", | ||||||
|  |       attributes: { | ||||||
|  |         last_reset: "1970-01-01T00:00:00:00+00", | ||||||
|  |         friendly_name: "Grid consumption low tariff", | ||||||
|  |         unit_of_measurement: "kWh", | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     "sensor.energy_consumption_tarif_2": { | ||||||
|  |       entity_id: "sensor.energy_consumption_tarif_2", | ||||||
|  |       state: "88.6", | ||||||
|  |       attributes: { | ||||||
|  |         last_reset: "1970-01-01T00:00:00:00+00", | ||||||
|  |         friendly_name: "Grid consumption high tariff", | ||||||
|  |         unit_of_measurement: "kWh", | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     "sensor.energy_production_tarif_1": { | ||||||
|  |       entity_id: "sensor.energy_production_tarif_1", | ||||||
|  |       state: "88.6", | ||||||
|  |       attributes: { | ||||||
|  |         last_reset: "1970-01-01T00:00:00:00+00", | ||||||
|  |         friendly_name: "Returned to grid low tariff", | ||||||
|  |         unit_of_measurement: "kWh", | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     "sensor.energy_production_tarif_2": { | ||||||
|  |       entity_id: "sensor.energy_production_tarif_2", | ||||||
|  |       state: "88.6", | ||||||
|  |       attributes: { | ||||||
|  |         last_reset: "1970-01-01T00:00:00:00+00", | ||||||
|  |         friendly_name: "Returned to grid high tariff", | ||||||
|  |         unit_of_measurement: "kWh", | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     "sensor.energy_consumption_tarif_1_cost": { | ||||||
|  |       entity_id: "sensor.energy_consumption_tarif_1_cost", | ||||||
|  |       state: "2", | ||||||
|  |       attributes: { | ||||||
|  |         last_reset: "1970-01-01T00:00:00:00+00", | ||||||
|  |         unit_of_measurement: "EUR", | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     "sensor.energy_consumption_tarif_2_cost": { | ||||||
|  |       entity_id: "sensor.energy_consumption_tarif_2_cost", | ||||||
|  |       state: "2", | ||||||
|  |       attributes: { | ||||||
|  |         last_reset: "1970-01-01T00:00:00:00+00", | ||||||
|  |         unit_of_measurement: "EUR", | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     "sensor.energy_production_tarif_1_compensation": { | ||||||
|  |       entity_id: "sensor.energy_production_tarif_1_compensation", | ||||||
|  |       state: "2", | ||||||
|  |       attributes: { | ||||||
|  |         last_reset: "1970-01-01T00:00:00:00+00", | ||||||
|  |         unit_of_measurement: "EUR", | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     "sensor.energy_production_tarif_2_compensation": { | ||||||
|  |       entity_id: "sensor.energy_production_tarif_2_compensation", | ||||||
|  |       state: "2", | ||||||
|  |       attributes: { | ||||||
|  |         last_reset: "1970-01-01T00:00:00:00+00", | ||||||
|  |         unit_of_measurement: "EUR", | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     "sensor.energy_gas_cost": { | ||||||
|  |       entity_id: "sensor.energy_gas_cost", | ||||||
|  |       state: "2", | ||||||
|  |       attributes: { | ||||||
|  |         last_reset: "1970-01-01T00:00:00:00+00", | ||||||
|  |         unit_of_measurement: "EUR", | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     "sensor.energy_gas": { | ||||||
|  |       entity_id: "sensor.energy_gas", | ||||||
|  |       state: "4", | ||||||
|  |       attributes: { | ||||||
|  |         last_reset: "1970-01-01T00:00:00:00+00", | ||||||
|  |         friendly_name: "Gas", | ||||||
|  |         unit_of_measurement: "m³", | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     "sensor.energy_car": { | ||||||
|  |       entity_id: "sensor.energy_car", | ||||||
|  |       state: "4", | ||||||
|  |       attributes: { | ||||||
|  |         last_reset: "1970-01-01T00:00:00:00+00", | ||||||
|  |         friendly_name: "Electric car", | ||||||
|  |         unit_of_measurement: "kWh", | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     "sensor.energy_ac": { | ||||||
|  |       entity_id: "sensor.energy_ac", | ||||||
|  |       state: "3", | ||||||
|  |       attributes: { | ||||||
|  |         last_reset: "1970-01-01T00:00:00:00+00", | ||||||
|  |         friendly_name: "Air conditioning", | ||||||
|  |         unit_of_measurement: "kWh", | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     "sensor.energy_washing_machine": { | ||||||
|  |       entity_id: "sensor.energy_washing_machine", | ||||||
|  |       state: "6", | ||||||
|  |       attributes: { | ||||||
|  |         last_reset: "1970-01-01T00:00:00:00+00", | ||||||
|  |         friendly_name: "Washing machine", | ||||||
|  |         unit_of_measurement: "kWh", | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     "sensor.energy_dryer": { | ||||||
|  |       entity_id: "sensor.energy_dryer", | ||||||
|  |       state: "5.5", | ||||||
|  |       attributes: { | ||||||
|  |         last_reset: "1970-01-01T00:00:00:00+00", | ||||||
|  |         friendly_name: "Dryer", | ||||||
|  |         unit_of_measurement: "kWh", | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     "sensor.energy_heat_pump": { | ||||||
|  |       entity_id: "sensor.energy_heat_pump", | ||||||
|  |       state: "6", | ||||||
|  |       attributes: { | ||||||
|  |         last_reset: "1970-01-01T00:00:00:00+00", | ||||||
|  |         friendly_name: "Heat pump", | ||||||
|  |         unit_of_measurement: "kWh", | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     "sensor.energy_boiler": { | ||||||
|  |       entity_id: "sensor.energy_boiler", | ||||||
|  |       state: "7", | ||||||
|  |       attributes: { | ||||||
|  |         last_reset: "1970-01-01T00:00:00:00+00", | ||||||
|  |         friendly_name: "Boiler", | ||||||
|  |         unit_of_measurement: "kWh", | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   }); | ||||||
							
								
								
									
										7
									
								
								demo/src/stubs/entity_registry.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								demo/src/stubs/entity_registry.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | |||||||
|  | import { EntityRegistryEntry } from "../../../src/data/entity_registry"; | ||||||
|  | import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass"; | ||||||
|  |  | ||||||
|  | export const mockEntityRegistry = ( | ||||||
|  |   hass: MockHomeAssistant, | ||||||
|  |   data: EntityRegistryEntry[] = [] | ||||||
|  | ) => hass.mockWS("config/entity_registry/list", () => data); | ||||||
							
								
								
									
										59
									
								
								demo/src/stubs/hassio_supervisor.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								demo/src/stubs/hassio_supervisor.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | |||||||
|  | import { HassioSupervisorInfo } from "../../../src/data/hassio/supervisor"; | ||||||
|  | import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass"; | ||||||
|  |  | ||||||
|  | export const mockHassioSupervisor = (hass: MockHomeAssistant) => { | ||||||
|  |   hass.config.components.push("hassio"); | ||||||
|  |   hass.mockWS("supervisor/api", (msg) => { | ||||||
|  |     if (msg.endpoint === "/supervisor/info") { | ||||||
|  |       const data: HassioSupervisorInfo = { | ||||||
|  |         version: "2021.10.dev0805", | ||||||
|  |         version_latest: "2021.10.dev0806", | ||||||
|  |         update_available: true, | ||||||
|  |         channel: "dev", | ||||||
|  |         arch: "aarch64", | ||||||
|  |         supported: true, | ||||||
|  |         healthy: true, | ||||||
|  |         ip_address: "172.30.32.2", | ||||||
|  |         wait_boot: 5, | ||||||
|  |         timezone: "America/Los_Angeles", | ||||||
|  |         logging: "info", | ||||||
|  |         debug: false, | ||||||
|  |         debug_block: false, | ||||||
|  |         diagnostics: true, | ||||||
|  |         addons: [ | ||||||
|  |           { | ||||||
|  |             name: "Visual Studio Code", | ||||||
|  |             slug: "a0d7b954_vscode", | ||||||
|  |             description: | ||||||
|  |               "Fully featured VSCode experience, to edit your HA config in the browser, including auto-completion!", | ||||||
|  |             state: "started", | ||||||
|  |             version: "3.6.2", | ||||||
|  |             version_latest: "3.6.2", | ||||||
|  |             update_available: false, | ||||||
|  |             repository: "a0d7b954", | ||||||
|  |             icon: true, | ||||||
|  |             logo: true, | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             name: "Z-Wave JS", | ||||||
|  |             slug: "core_zwave_js", | ||||||
|  |             description: | ||||||
|  |               "Control a ZWave network with Home Assistant Z-Wave JS", | ||||||
|  |             state: "started", | ||||||
|  |             version: "0.1.45", | ||||||
|  |             version_latest: "0.1.45", | ||||||
|  |             update_available: false, | ||||||
|  |             repository: "core", | ||||||
|  |             icon: true, | ||||||
|  |             logo: true, | ||||||
|  |           }, | ||||||
|  |         ] as any, | ||||||
|  |         addons_repositories: [ | ||||||
|  |           "https://github.com/hassio-addons/repository", | ||||||
|  |         ] as any, | ||||||
|  |       }; | ||||||
|  |       return data; | ||||||
|  |     } | ||||||
|  |     return Promise.reject(`${msg.method} ${msg.endpoint} is not implemented`); | ||||||
|  |   }); | ||||||
|  | }; | ||||||
| @@ -1,4 +1,12 @@ | |||||||
|  | import { | ||||||
|  |   addDays, | ||||||
|  |   addHours, | ||||||
|  |   addMonths, | ||||||
|  |   differenceInHours, | ||||||
|  |   endOfDay, | ||||||
|  | } from "date-fns"; | ||||||
| import { HassEntity } from "home-assistant-js-websocket"; | import { HassEntity } from "home-assistant-js-websocket"; | ||||||
|  | import { StatisticValue } from "../../../src/data/history"; | ||||||
| import { MockHomeAssistant } from "../../../src/fake_data/provide_hass"; | import { MockHomeAssistant } from "../../../src/fake_data/provide_hass"; | ||||||
|  |  | ||||||
| interface HistoryQueryParams { | interface HistoryQueryParams { | ||||||
| @@ -64,17 +72,335 @@ const generateHistory = (state, deltas) => { | |||||||
|  |  | ||||||
| const incrementalUnits = ["clients", "queries", "ads"]; | const incrementalUnits = ["clients", "queries", "ads"]; | ||||||
|  |  | ||||||
|  | const generateMeanStatistics = ( | ||||||
|  |   id: string, | ||||||
|  |   start: Date, | ||||||
|  |   end: Date, | ||||||
|  |   period: "5minute" | "hour" | "day" | "month" = "hour", | ||||||
|  |   initValue: number, | ||||||
|  |   maxDiff: number | ||||||
|  | ) => { | ||||||
|  |   const statistics: StatisticValue[] = []; | ||||||
|  |   let currentDate = new Date(start); | ||||||
|  |   currentDate.setMinutes(0, 0, 0); | ||||||
|  |   let lastVal = initValue; | ||||||
|  |   const now = new Date(); | ||||||
|  |   while (end > currentDate && currentDate < now) { | ||||||
|  |     const delta = Math.random() * maxDiff; | ||||||
|  |     const mean = lastVal + delta; | ||||||
|  |     statistics.push({ | ||||||
|  |       statistic_id: id, | ||||||
|  |       start: currentDate.toISOString(), | ||||||
|  |       end: currentDate.toISOString(), | ||||||
|  |       mean, | ||||||
|  |       min: mean - Math.random() * maxDiff, | ||||||
|  |       max: mean + Math.random() * maxDiff, | ||||||
|  |       last_reset: "1970-01-01T00:00:00+00:00", | ||||||
|  |       state: mean, | ||||||
|  |       sum: null, | ||||||
|  |     }); | ||||||
|  |     lastVal = mean; | ||||||
|  |     currentDate = | ||||||
|  |       period === "day" | ||||||
|  |         ? addDays(currentDate, 1) | ||||||
|  |         : period === "month" | ||||||
|  |         ? addMonths(currentDate, 1) | ||||||
|  |         : addHours(currentDate, 1); | ||||||
|  |   } | ||||||
|  |   return statistics; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | const generateSumStatistics = ( | ||||||
|  |   id: string, | ||||||
|  |   start: Date, | ||||||
|  |   end: Date, | ||||||
|  |   period: "5minute" | "hour" | "day" | "month" = "hour", | ||||||
|  |   initValue: number, | ||||||
|  |   maxDiff: number | ||||||
|  | ) => { | ||||||
|  |   const statistics: StatisticValue[] = []; | ||||||
|  |   let currentDate = new Date(start); | ||||||
|  |   currentDate.setMinutes(0, 0, 0); | ||||||
|  |   let sum = initValue; | ||||||
|  |   const now = new Date(); | ||||||
|  |   while (end > currentDate && currentDate < now) { | ||||||
|  |     const add = Math.random() * maxDiff; | ||||||
|  |     sum += add; | ||||||
|  |     statistics.push({ | ||||||
|  |       statistic_id: id, | ||||||
|  |       start: currentDate.toISOString(), | ||||||
|  |       end: currentDate.toISOString(), | ||||||
|  |       mean: null, | ||||||
|  |       min: null, | ||||||
|  |       max: null, | ||||||
|  |       last_reset: "1970-01-01T00:00:00+00:00", | ||||||
|  |       state: initValue + sum, | ||||||
|  |       sum, | ||||||
|  |     }); | ||||||
|  |     currentDate = | ||||||
|  |       period === "day" | ||||||
|  |         ? addDays(currentDate, 1) | ||||||
|  |         : period === "month" | ||||||
|  |         ? addMonths(currentDate, 1) | ||||||
|  |         : addHours(currentDate, 1); | ||||||
|  |   } | ||||||
|  |   return statistics; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | const generateCurvedStatistics = ( | ||||||
|  |   id: string, | ||||||
|  |   start: Date, | ||||||
|  |   end: Date, | ||||||
|  |   _period: "5minute" | "hour" | "day" | "month" = "hour", | ||||||
|  |   initValue: number, | ||||||
|  |   maxDiff: number, | ||||||
|  |   metered: boolean | ||||||
|  | ) => { | ||||||
|  |   const statistics: StatisticValue[] = []; | ||||||
|  |   let currentDate = new Date(start); | ||||||
|  |   currentDate.setMinutes(0, 0, 0); | ||||||
|  |   let sum = initValue; | ||||||
|  |   const hours = differenceInHours(end, start) - 1; | ||||||
|  |   let i = 0; | ||||||
|  |   let half = false; | ||||||
|  |   const now = new Date(); | ||||||
|  |   while (end > currentDate && currentDate < now) { | ||||||
|  |     const add = Math.random() * maxDiff; | ||||||
|  |     sum += i * add; | ||||||
|  |     statistics.push({ | ||||||
|  |       statistic_id: id, | ||||||
|  |       start: currentDate.toISOString(), | ||||||
|  |       end: currentDate.toISOString(), | ||||||
|  |       mean: null, | ||||||
|  |       min: null, | ||||||
|  |       max: null, | ||||||
|  |       last_reset: "1970-01-01T00:00:00+00:00", | ||||||
|  |       state: initValue + sum, | ||||||
|  |       sum: metered ? sum : null, | ||||||
|  |     }); | ||||||
|  |     currentDate = addHours(currentDate, 1); | ||||||
|  |     if (!half && i > hours / 2) { | ||||||
|  |       half = true; | ||||||
|  |     } | ||||||
|  |     i += half ? -1 : 1; | ||||||
|  |   } | ||||||
|  |   return statistics; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | const statisticsFunctions: Record< | ||||||
|  |   string, | ||||||
|  |   ( | ||||||
|  |     id: string, | ||||||
|  |     start: Date, | ||||||
|  |     end: Date, | ||||||
|  |     period: "5minute" | "hour" | "day" | "month" | ||||||
|  |   ) => StatisticValue[] | ||||||
|  | > = { | ||||||
|  |   "sensor.energy_consumption_tarif_1": ( | ||||||
|  |     id: string, | ||||||
|  |     start: Date, | ||||||
|  |     end: Date, | ||||||
|  |     period = "hour" | ||||||
|  |   ) => { | ||||||
|  |     if (period !== "hour") { | ||||||
|  |       return generateSumStatistics( | ||||||
|  |         id, | ||||||
|  |         start, | ||||||
|  |         end, | ||||||
|  |         period, | ||||||
|  |         0, | ||||||
|  |         period === "day" ? 17 : 504 | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |     const morningEnd = new Date(start.getTime() + 10 * 60 * 60 * 1000); | ||||||
|  |     const morningLow = generateSumStatistics( | ||||||
|  |       id, | ||||||
|  |       start, | ||||||
|  |       morningEnd, | ||||||
|  |       period, | ||||||
|  |       0, | ||||||
|  |       0.7 | ||||||
|  |     ); | ||||||
|  |     const eveningStart = new Date(start.getTime() + 20 * 60 * 60 * 1000); | ||||||
|  |     const morningFinalVal = morningLow.length | ||||||
|  |       ? morningLow[morningLow.length - 1].sum! | ||||||
|  |       : 0; | ||||||
|  |     const empty = generateSumStatistics( | ||||||
|  |       id, | ||||||
|  |       morningEnd, | ||||||
|  |       eveningStart, | ||||||
|  |       period, | ||||||
|  |       morningFinalVal, | ||||||
|  |       0 | ||||||
|  |     ); | ||||||
|  |     const eveningLow = generateSumStatistics( | ||||||
|  |       id, | ||||||
|  |       eveningStart, | ||||||
|  |       end, | ||||||
|  |       period, | ||||||
|  |       morningFinalVal, | ||||||
|  |       0.7 | ||||||
|  |     ); | ||||||
|  |     return [...morningLow, ...empty, ...eveningLow]; | ||||||
|  |   }, | ||||||
|  |   "sensor.energy_consumption_tarif_2": ( | ||||||
|  |     id: string, | ||||||
|  |     start: Date, | ||||||
|  |     end: Date, | ||||||
|  |     period = "hour" | ||||||
|  |   ) => { | ||||||
|  |     if (period !== "hour") { | ||||||
|  |       return generateSumStatistics( | ||||||
|  |         id, | ||||||
|  |         start, | ||||||
|  |         end, | ||||||
|  |         period, | ||||||
|  |         0, | ||||||
|  |         period === "day" ? 17 : 504 | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |     const morningEnd = new Date(start.getTime() + 9 * 60 * 60 * 1000); | ||||||
|  |     const eveningStart = new Date(start.getTime() + 20 * 60 * 60 * 1000); | ||||||
|  |     const highTarif = generateSumStatistics( | ||||||
|  |       id, | ||||||
|  |       morningEnd, | ||||||
|  |       eveningStart, | ||||||
|  |       period, | ||||||
|  |       0, | ||||||
|  |       0.3 | ||||||
|  |     ); | ||||||
|  |     const highTarifFinalVal = highTarif.length | ||||||
|  |       ? highTarif[highTarif.length - 1].sum! | ||||||
|  |       : 0; | ||||||
|  |     const morning = generateSumStatistics(id, start, morningEnd, period, 0, 0); | ||||||
|  |     const evening = generateSumStatistics( | ||||||
|  |       id, | ||||||
|  |       eveningStart, | ||||||
|  |       end, | ||||||
|  |       period, | ||||||
|  |       highTarifFinalVal, | ||||||
|  |       0 | ||||||
|  |     ); | ||||||
|  |     return [...morning, ...highTarif, ...evening]; | ||||||
|  |   }, | ||||||
|  |   "sensor.energy_production_tarif_1": (id, start, end, period = "hour") => | ||||||
|  |     generateSumStatistics(id, start, end, period, 0, 0), | ||||||
|  |   "sensor.energy_production_tarif_1_compensation": ( | ||||||
|  |     id, | ||||||
|  |     start, | ||||||
|  |     end, | ||||||
|  |     period = "hour" | ||||||
|  |   ) => generateSumStatistics(id, start, end, period, 0, 0), | ||||||
|  |   "sensor.energy_production_tarif_2": (id, start, end, period = "hour") => { | ||||||
|  |     if (period !== "hour") { | ||||||
|  |       return generateSumStatistics( | ||||||
|  |         id, | ||||||
|  |         start, | ||||||
|  |         end, | ||||||
|  |         period, | ||||||
|  |         0, | ||||||
|  |         period === "day" ? 17 : 504 | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |     const productionStart = new Date(start.getTime() + 9 * 60 * 60 * 1000); | ||||||
|  |     const productionEnd = new Date(start.getTime() + 21 * 60 * 60 * 1000); | ||||||
|  |     const dayEnd = new Date(endOfDay(productionEnd)); | ||||||
|  |     const production = generateCurvedStatistics( | ||||||
|  |       id, | ||||||
|  |       productionStart, | ||||||
|  |       productionEnd, | ||||||
|  |       period, | ||||||
|  |       0, | ||||||
|  |       0.15, | ||||||
|  |       true | ||||||
|  |     ); | ||||||
|  |     const productionFinalVal = production.length | ||||||
|  |       ? production[production.length - 1].sum! | ||||||
|  |       : 0; | ||||||
|  |     const morning = generateSumStatistics( | ||||||
|  |       id, | ||||||
|  |       start, | ||||||
|  |       productionStart, | ||||||
|  |       period, | ||||||
|  |       0, | ||||||
|  |       0 | ||||||
|  |     ); | ||||||
|  |     const evening = generateSumStatistics( | ||||||
|  |       id, | ||||||
|  |       productionEnd, | ||||||
|  |       dayEnd, | ||||||
|  |       period, | ||||||
|  |       productionFinalVal, | ||||||
|  |       0 | ||||||
|  |     ); | ||||||
|  |     const rest = generateSumStatistics( | ||||||
|  |       id, | ||||||
|  |       dayEnd, | ||||||
|  |       end, | ||||||
|  |       period, | ||||||
|  |       productionFinalVal, | ||||||
|  |       1 | ||||||
|  |     ); | ||||||
|  |     return [...morning, ...production, ...evening, ...rest]; | ||||||
|  |   }, | ||||||
|  |   "sensor.solar_production": (id, start, end, period = "hour") => { | ||||||
|  |     if (period !== "hour") { | ||||||
|  |       return generateSumStatistics( | ||||||
|  |         id, | ||||||
|  |         start, | ||||||
|  |         end, | ||||||
|  |         period, | ||||||
|  |         0, | ||||||
|  |         period === "day" ? 17 : 504 | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |     const productionStart = new Date(start.getTime() + 7 * 60 * 60 * 1000); | ||||||
|  |     const productionEnd = new Date(start.getTime() + 23 * 60 * 60 * 1000); | ||||||
|  |     const dayEnd = new Date(endOfDay(productionEnd)); | ||||||
|  |     const production = generateCurvedStatistics( | ||||||
|  |       id, | ||||||
|  |       productionStart, | ||||||
|  |       productionEnd, | ||||||
|  |       period, | ||||||
|  |       0, | ||||||
|  |       0.3, | ||||||
|  |       true | ||||||
|  |     ); | ||||||
|  |     const productionFinalVal = production.length | ||||||
|  |       ? production[production.length - 1].sum! | ||||||
|  |       : 0; | ||||||
|  |     const morning = generateSumStatistics( | ||||||
|  |       id, | ||||||
|  |       start, | ||||||
|  |       productionStart, | ||||||
|  |       period, | ||||||
|  |       0, | ||||||
|  |       0 | ||||||
|  |     ); | ||||||
|  |     const evening = generateSumStatistics( | ||||||
|  |       id, | ||||||
|  |       productionEnd, | ||||||
|  |       dayEnd, | ||||||
|  |       period, | ||||||
|  |       productionFinalVal, | ||||||
|  |       0 | ||||||
|  |     ); | ||||||
|  |     const rest = generateSumStatistics( | ||||||
|  |       id, | ||||||
|  |       dayEnd, | ||||||
|  |       end, | ||||||
|  |       period, | ||||||
|  |       productionFinalVal, | ||||||
|  |       2 | ||||||
|  |     ); | ||||||
|  |     return [...morning, ...production, ...evening, ...rest]; | ||||||
|  |   }, | ||||||
|  | }; | ||||||
|  |  | ||||||
| export const mockHistory = (mockHass: MockHomeAssistant) => { | export const mockHistory = (mockHass: MockHomeAssistant) => { | ||||||
|   mockHass.mockAPI( |   mockHass.mockAPI( | ||||||
|     new RegExp("history/period/.+"), |     new RegExp("history/period/.+"), | ||||||
|     ( |     (hass, _method, path, _parameters) => { | ||||||
|       hass, |  | ||||||
|       // @ts-ignore |  | ||||||
|       method, |  | ||||||
|       path, |  | ||||||
|       // @ts-ignore |  | ||||||
|       parameters |  | ||||||
|     ) => { |  | ||||||
|       const params = parseQuery<HistoryQueryParams>(path.split("?")[1]); |       const params = parseQuery<HistoryQueryParams>(path.split("?")[1]); | ||||||
|       const entities = params.filter_entity_id.split(","); |       const entities = params.filter_entity_id.split(","); | ||||||
|  |  | ||||||
| @@ -95,7 +421,7 @@ export const mockHistory = (mockHass: MockHomeAssistant) => { | |||||||
|         const numberState = Number(state.state); |         const numberState = Number(state.state); | ||||||
|  |  | ||||||
|         if (isNaN(numberState)) { |         if (isNaN(numberState)) { | ||||||
|           // eslint-disable-next-line |           // eslint-disable-next-line no-console | ||||||
|           console.log( |           console.log( | ||||||
|             "Ignoring state with unparsable state but with a unit", |             "Ignoring state with unparsable state but with a unit", | ||||||
|             entityId, |             entityId, | ||||||
| @@ -140,4 +466,42 @@ export const mockHistory = (mockHass: MockHomeAssistant) => { | |||||||
|       return results; |       return results; | ||||||
|     } |     } | ||||||
|   ); |   ); | ||||||
|  |   mockHass.mockWS("history/list_statistic_ids", () => []); | ||||||
|  |   mockHass.mockWS( | ||||||
|  |     "history/statistics_during_period", | ||||||
|  |     ({ statistic_ids, start_time, end_time, period }, hass) => { | ||||||
|  |       const start = new Date(start_time); | ||||||
|  |       const end = end_time ? new Date(end_time) : new Date(); | ||||||
|  |  | ||||||
|  |       const statistics: Record<string, StatisticValue[]> = {}; | ||||||
|  |  | ||||||
|  |       statistic_ids.forEach((id: string) => { | ||||||
|  |         if (id in statisticsFunctions) { | ||||||
|  |           statistics[id] = statisticsFunctions[id](id, start, end, period); | ||||||
|  |         } else { | ||||||
|  |           const entityState = hass.states[id]; | ||||||
|  |           const state = entityState ? Number(entityState.state) : 1; | ||||||
|  |           statistics[id] = | ||||||
|  |             entityState && "last_reset" in entityState.attributes | ||||||
|  |               ? generateSumStatistics( | ||||||
|  |                   id, | ||||||
|  |                   start, | ||||||
|  |                   end, | ||||||
|  |                   period, | ||||||
|  |                   state, | ||||||
|  |                   state * (state > 80 ? 0.01 : 0.05) | ||||||
|  |                 ) | ||||||
|  |               : generateMeanStatistics( | ||||||
|  |                   id, | ||||||
|  |                   start, | ||||||
|  |                   end, | ||||||
|  |                   period, | ||||||
|  |                   state, | ||||||
|  |                   state * (state > 80 ? 0.05 : 0.1) | ||||||
|  |                 ); | ||||||
|  |         } | ||||||
|  |       }); | ||||||
|  |       return statistics; | ||||||
|  |     } | ||||||
|  |   ); | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -10,10 +10,9 @@ export const mockLovelace = ( | |||||||
|   localizePromise: Promise<LocalizeFunc> |   localizePromise: Promise<LocalizeFunc> | ||||||
| ) => { | ) => { | ||||||
|   hass.mockWS("lovelace/config", () => |   hass.mockWS("lovelace/config", () => | ||||||
|     Promise.all([ |     Promise.all([selectedDemoConfig, localizePromise]).then( | ||||||
|       selectedDemoConfig, |       ([config, localize]) => config.lovelace(localize) | ||||||
|       localizePromise, |     ) | ||||||
|     ]).then(([config, localize]) => config.lovelace(localize)) |  | ||||||
|   ); |   ); | ||||||
|  |  | ||||||
|   hass.mockWS("lovelace/config/save", () => Promise.resolve()); |   hass.mockWS("lovelace/config/save", () => Promise.resolve()); | ||||||
| @@ -24,9 +23,9 @@ customElements.whenDefined("hui-view").then(() => { | |||||||
|   // eslint-disable-next-line |   // eslint-disable-next-line | ||||||
|   const HUIView = customElements.get("hui-view"); |   const HUIView = customElements.get("hui-view"); | ||||||
|   // Patch HUI-VIEW to make the lovelace object available to the demo card |   // Patch HUI-VIEW to make the lovelace object available to the demo card | ||||||
|   const oldCreateCard = HUIView.prototype.createCardElement; |   const oldCreateCard = HUIView!.prototype.createCardElement; | ||||||
|  |  | ||||||
|   HUIView.prototype.createCardElement = function (config) { |   HUIView!.prototype.createCardElement = function (config) { | ||||||
|     const el = oldCreateCard.call(this, config); |     const el = oldCreateCard.call(this, config); | ||||||
|     if (el.tagName === "HA-DEMO-CARD") { |     if (el.tagName === "HA-DEMO-CARD") { | ||||||
|       (el as HADemoCard).lovelace = this.lovelace; |       (el as HADemoCard).lovelace = this.lovelace; | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ export const mockTemplate = (hass: MockHomeAssistant) => { | |||||||
|       body: { message: "Template dev tool does not work in the demo." }, |       body: { message: "Template dev tool does not work in the demo." }, | ||||||
|     }) |     }) | ||||||
|   ); |   ); | ||||||
|   hass.mockWS("render_template", (msg, onChange) => { |   hass.mockWS("render_template", (msg, _hass, onChange) => { | ||||||
|     onChange!({ |     onChange!({ | ||||||
|       result: msg.template, |       result: msg.template, | ||||||
|       listeners: { all: false, domains: [], entities: [], time: false }, |       listeners: { all: false, domains: [], entities: [], time: false }, | ||||||
|   | |||||||
| @@ -3,8 +3,6 @@ import { MockHomeAssistant } from "../../../src/fake_data/provide_hass"; | |||||||
| export const mockTranslations = (hass: MockHomeAssistant) => { | export const mockTranslations = (hass: MockHomeAssistant) => { | ||||||
|   hass.mockWS( |   hass.mockWS( | ||||||
|     "frontend/get_translations", |     "frontend/get_translations", | ||||||
|     (/* msg: {language: string, category: string} */) => { |     (/* msg: {language: string, category: string} */) => ({ resources: {} }) | ||||||
|       return { resources: {} }; |  | ||||||
|     } |  | ||||||
|   ); |   ); | ||||||
| }; | }; | ||||||
|   | |||||||
							
								
								
									
										
											BIN
										
									
								
								gallery/public/images/office.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								gallery/public/images/office.jpg
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 147 KiB | 
							
								
								
									
										35
									
								
								gallery/script/netlify_build_gallery
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										35
									
								
								gallery/script/netlify_build_gallery
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,35 @@ | |||||||
|  | #!/bin/bash | ||||||
|  |  | ||||||
|  | TARGET_LABEL="needs design preview" | ||||||
|  |  | ||||||
|  | if [[ "$NETLIFY" != "true" ]]; then | ||||||
|  |   echo "This script can only be run on Netlify" | ||||||
|  |   exit 1 | ||||||
|  | fi | ||||||
|  |  | ||||||
|  | function createStatus() { | ||||||
|  |   state="$1" | ||||||
|  |   description="$2" | ||||||
|  |   target_url="$3" | ||||||
|  |   curl -X POST -H "Accept: application/vnd.github.v3+json" -H "Authorization: token $GITHUB_TOKEN" \ | ||||||
|  |     "https://api.github.com/repos/home-assistant/frontend/statuses/$COMMIT_REF" \ | ||||||
|  |     -d '{"state": "'"${state}"'", "context": "Netlify/Design Preview Build", "description": "'"$description"'", "target_url":  "'"$target_url"'"}' | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | if [[ "${PULL_REQUEST}" == "true" ]]; then | ||||||
|  |   if [[ "$(curl -sSLf -H "Accept: application/vnd.github.v3+json" -H "Authorization: token $GITHUB_TOKEN" \ | ||||||
|  |     "https://api.github.com/repos/home-assistant/frontend/pulls/${REVIEW_ID}" | jq '.labels[].name' -r)" =~ "$TARGET_LABEL" ]]; then | ||||||
|  |     createStatus "pending" "Building design preview" "https://app.netlify.com/sites/home-assistant-gallery/deploys/$BUILD_ID" | ||||||
|  |     gulp build-gallery | ||||||
|  |     if [ $? -eq 0 ]; then | ||||||
|  |       createStatus "success" "Build complete" "$DEPLOY_URL" | ||||||
|  |     else | ||||||
|  |       createStatus "error" "Build failed" "https://app.netlify.com/sites/home-assistant-gallery/deploys/$BUILD_ID" | ||||||
|  |     fi | ||||||
|  |   else | ||||||
|  |     createStatus "success" "Build was not requested by PR label" | ||||||
|  |   fi | ||||||
|  | elif [[ "$INCOMING_HOOK_BODY" == "NIGHTLY" ]]; then | ||||||
|  |   gulp build-gallery | ||||||
|  | fi | ||||||
							
								
								
									
										48
									
								
								gallery/sidebar.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								gallery/sidebar.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | |||||||
|  | module.exports = [ | ||||||
|  |   { | ||||||
|  |     // This section has no header and so all page links are shown directly in the sidebar | ||||||
|  |     category: "concepts", | ||||||
|  |     pages: ["home"], | ||||||
|  |   }, | ||||||
|  |  | ||||||
|  |   { | ||||||
|  |     category: "lovelace", | ||||||
|  |     // Label for in the sidebar | ||||||
|  |     header: "Lovelace", | ||||||
|  |     // Specify order of pages. Any pages in the category folder but not listed here will | ||||||
|  |     // automatically be added after the pages listed here. | ||||||
|  |     pages: ["introduction"], | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     category: "automation", | ||||||
|  |     header: "Automation", | ||||||
|  |     pages: [ | ||||||
|  |       "editor-trigger", | ||||||
|  |       "editor-condition", | ||||||
|  |       "editor-action", | ||||||
|  |       "selectors", | ||||||
|  |       "trace", | ||||||
|  |       "trace-timeline", | ||||||
|  |     ], | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     category: "components", | ||||||
|  |     header: "Components", | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     category: "more-info", | ||||||
|  |     header: "More Info dialogs", | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     category: "misc", | ||||||
|  |     header: "Miscelaneous", | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     category: "user-test", | ||||||
|  |     header: "User Tests", | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     category: "design.home-assistant.io", | ||||||
|  |     header: "Design Documentation", | ||||||
|  |   }, | ||||||
|  | ]; | ||||||
							
								
								
									
										140
									
								
								gallery/src/components/demo-black-white-row.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								gallery/src/components/demo-black-white-row.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,140 @@ | |||||||
|  | import { Button } from "@material/mwc-button"; | ||||||
|  | import { html, LitElement, css, TemplateResult } from "lit"; | ||||||
|  | import { customElement, property } from "lit/decorators"; | ||||||
|  | import { applyThemesOnElement } from "../../../src/common/dom/apply_themes_on_element"; | ||||||
|  | import { fireEvent } from "../../../src/common/dom/fire_event"; | ||||||
|  | import "../../../src/components/ha-card"; | ||||||
|  |  | ||||||
|  | @customElement("demo-black-white-row") | ||||||
|  | class DemoBlackWhiteRow extends LitElement { | ||||||
|  |   @property() title!: string; | ||||||
|  |  | ||||||
|  |   @property() value!: any; | ||||||
|  |  | ||||||
|  |   @property() disabled = false; | ||||||
|  |  | ||||||
|  |   protected render(): TemplateResult { | ||||||
|  |     return html` | ||||||
|  |       <div class="row"> | ||||||
|  |         <div class="content light"> | ||||||
|  |           <ha-card .header=${this.title}> | ||||||
|  |             <div class="card-content"> | ||||||
|  |               <slot name="light"></slot> | ||||||
|  |             </div> | ||||||
|  |             <div class="card-actions"> | ||||||
|  |               <mwc-button | ||||||
|  |                 .disabled=${this.disabled} | ||||||
|  |                 @click=${this.handleSubmit} | ||||||
|  |               > | ||||||
|  |                 Submit | ||||||
|  |               </mwc-button> | ||||||
|  |             </div> | ||||||
|  |           </ha-card> | ||||||
|  |         </div> | ||||||
|  |         <div class="content dark"> | ||||||
|  |           <ha-card .header=${this.title}> | ||||||
|  |             <div class="card-content"> | ||||||
|  |               <slot name="dark"></slot> | ||||||
|  |             </div> | ||||||
|  |             <div class="card-actions"> | ||||||
|  |               <mwc-button | ||||||
|  |                 .disabled=${this.disabled} | ||||||
|  |                 @click=${this.handleSubmit} | ||||||
|  |               > | ||||||
|  |                 Submit | ||||||
|  |               </mwc-button> | ||||||
|  |             </div> | ||||||
|  |           </ha-card> | ||||||
|  |           <pre>${JSON.stringify(this.value, undefined, 2)}</pre> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |     `; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   firstUpdated(changedProps) { | ||||||
|  |     super.firstUpdated(changedProps); | ||||||
|  |     applyThemesOnElement(this.shadowRoot!.querySelector(".dark"), { | ||||||
|  |       default_theme: "default", | ||||||
|  |       default_dark_theme: "default", | ||||||
|  |       themes: {}, | ||||||
|  |       darkMode: true, | ||||||
|  |       theme: "default", | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   handleSubmit(ev) { | ||||||
|  |     const content = (ev.target as Button).closest(".content")!; | ||||||
|  |     fireEvent(this, "submitted" as any, { | ||||||
|  |       slot: content.classList.contains("light") ? "light" : "dark", | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   static styles = css` | ||||||
|  |     .row { | ||||||
|  |       display: flex; | ||||||
|  |     } | ||||||
|  |     .content { | ||||||
|  |       padding: 50px 0; | ||||||
|  |       background-color: var(--primary-background-color); | ||||||
|  |     } | ||||||
|  |     .light { | ||||||
|  |       flex: 1; | ||||||
|  |       padding-left: 50px; | ||||||
|  |       padding-right: 50px; | ||||||
|  |       box-sizing: border-box; | ||||||
|  |     } | ||||||
|  |     .light ha-card { | ||||||
|  |       margin-left: auto; | ||||||
|  |     } | ||||||
|  |     .dark { | ||||||
|  |       display: flex; | ||||||
|  |       flex: 1; | ||||||
|  |       padding-left: 50px; | ||||||
|  |       box-sizing: border-box; | ||||||
|  |       flex-wrap: wrap; | ||||||
|  |     } | ||||||
|  |     ha-card { | ||||||
|  |       width: 400px; | ||||||
|  |     } | ||||||
|  |     pre { | ||||||
|  |       width: 300px; | ||||||
|  |       margin: 0 16px 0; | ||||||
|  |       overflow: auto; | ||||||
|  |       color: var(--primary-text-color); | ||||||
|  |     } | ||||||
|  |     .card-actions { | ||||||
|  |       display: flex; | ||||||
|  |       flex-direction: row-reverse; | ||||||
|  |       border-top: none; | ||||||
|  |     } | ||||||
|  |     @media only screen and (max-width: 1500px) { | ||||||
|  |       .light { | ||||||
|  |         flex: initial; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     @media only screen and (max-width: 1000px) { | ||||||
|  |       .light, | ||||||
|  |       .dark { | ||||||
|  |         padding: 16px; | ||||||
|  |       } | ||||||
|  |       .row, | ||||||
|  |       .dark { | ||||||
|  |         flex-direction: column; | ||||||
|  |       } | ||||||
|  |       ha-card { | ||||||
|  |         margin: 0 auto; | ||||||
|  |         width: 100%; | ||||||
|  |         max-width: 400px; | ||||||
|  |       } | ||||||
|  |       pre { | ||||||
|  |         margin: 16px auto; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   `; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | declare global { | ||||||
|  |   interface HTMLElementTagNameMap { | ||||||
|  |     "demo-black-white-row": DemoBlackWhiteRow; | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -1,106 +0,0 @@ | |||||||
| import { html } from "@polymer/polymer/lib/utils/html-tag"; |  | ||||||
| /* eslint-plugin-disable lit */ |  | ||||||
| import { PolymerElement } from "@polymer/polymer/polymer-element"; |  | ||||||
| import { safeLoad } from "js-yaml"; |  | ||||||
| import { createCardElement } from "../../../src/panels/lovelace/create-element/create-card-element"; |  | ||||||
|  |  | ||||||
| class DemoCard extends PolymerElement { |  | ||||||
|   static get template() { |  | ||||||
|     return html` |  | ||||||
|       <style> |  | ||||||
|         .root { |  | ||||||
|           display: flex; |  | ||||||
|         } |  | ||||||
|         h2 { |  | ||||||
|           margin: 0 0 20px; |  | ||||||
|           color: var(--primary-color); |  | ||||||
|         } |  | ||||||
|         #card { |  | ||||||
|           max-width: 400px; |  | ||||||
|           width: 100vw; |  | ||||||
|         } |  | ||||||
|         pre { |  | ||||||
|           width: 400px; |  | ||||||
|           margin: 0 16px; |  | ||||||
|           overflow: auto; |  | ||||||
|           color: var(--primary-text-color); |  | ||||||
|         } |  | ||||||
|         @media only screen and (max-width: 800px) { |  | ||||||
|           .root { |  | ||||||
|             flex-direction: column; |  | ||||||
|           } |  | ||||||
|           pre { |  | ||||||
|             margin: 16px 0; |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
|       </style> |  | ||||||
|       <h2>[[config.heading]]</h2> |  | ||||||
|       <div class="root"> |  | ||||||
|         <div id="card"></div> |  | ||||||
|         <template is="dom-if" if="[[showConfig]]"> |  | ||||||
|           <pre>[[_trim(config.config)]]</pre> |  | ||||||
|         </template> |  | ||||||
|       </div> |  | ||||||
|     `; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   static get properties() { |  | ||||||
|     return { |  | ||||||
|       hass: { |  | ||||||
|         type: Object, |  | ||||||
|         observer: "_hassChanged", |  | ||||||
|       }, |  | ||||||
|       config: { |  | ||||||
|         type: Object, |  | ||||||
|         observer: "_configChanged", |  | ||||||
|       }, |  | ||||||
|       showConfig: Boolean, |  | ||||||
|     }; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   ready() { |  | ||||||
|     super.ready(); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   _configChanged(config) { |  | ||||||
|     const card = this.$.card; |  | ||||||
|     while (card.lastChild) { |  | ||||||
|       card.removeChild(card.lastChild); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     const el = this._createCardElement(safeLoad(config.config)[0]); |  | ||||||
|     card.appendChild(el); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   _createCardElement(cardConfig) { |  | ||||||
|     const element = createCardElement(cardConfig); |  | ||||||
|     if (this.hass) { |  | ||||||
|       element.hass = this.hass; |  | ||||||
|     } |  | ||||||
|     element.addEventListener( |  | ||||||
|       "ll-rebuild", |  | ||||||
|       (ev) => { |  | ||||||
|         ev.stopPropagation(); |  | ||||||
|         this._rebuildCard(element, cardConfig); |  | ||||||
|       }, |  | ||||||
|       { once: true } |  | ||||||
|     ); |  | ||||||
|     return element; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   _rebuildCard(cardElToReplace, config) { |  | ||||||
|     const newCardEl = this._createCardElement(config); |  | ||||||
|     cardElToReplace.parentElement.replaceChild(newCardEl, cardElToReplace); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   _hassChanged(hass) { |  | ||||||
|     const card = this.$.card.lastChild; |  | ||||||
|     if (card) card.hass = hass; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   _trim(config) { |  | ||||||
|     return config.trim(); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| customElements.define("demo-card", DemoCard); |  | ||||||
							
								
								
									
										129
									
								
								gallery/src/components/demo-card.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								gallery/src/components/demo-card.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,129 @@ | |||||||
|  | import { load } from "js-yaml"; | ||||||
|  | import { html, css, LitElement, PropertyValues } from "lit"; | ||||||
|  | import { customElement, property, query, state } from "lit/decorators"; | ||||||
|  | import { createCardElement } from "../../../src/panels/lovelace/create-element/create-card-element"; | ||||||
|  | import { HomeAssistant } from "../../../src/types"; | ||||||
|  |  | ||||||
|  | export interface DemoCardConfig { | ||||||
|  |   heading: string; | ||||||
|  |   config: string; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @customElement("demo-card") | ||||||
|  | class DemoCard extends LitElement { | ||||||
|  |   @property() public hass!: HomeAssistant; | ||||||
|  |  | ||||||
|  |   @property() public config!: DemoCardConfig; | ||||||
|  |  | ||||||
|  |   @property() public showConfig = false; | ||||||
|  |  | ||||||
|  |   @state() private _size?: number; | ||||||
|  |  | ||||||
|  |   @query("#card") private _card!: HTMLElement; | ||||||
|  |  | ||||||
|  |   render() { | ||||||
|  |     return html` | ||||||
|  |       <h2> | ||||||
|  |         ${this.config.heading} | ||||||
|  |         ${this._size !== undefined | ||||||
|  |           ? html`<small>(size ${this._size})</small>` | ||||||
|  |           : ""} | ||||||
|  |       </h2> | ||||||
|  |       <div class="root"> | ||||||
|  |         <div id="card"></div> | ||||||
|  |         ${this.showConfig ? html`<pre>${this.config.config.trim()}</pre>` : ""} | ||||||
|  |       </div> | ||||||
|  |     `; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   updated(changedProps: PropertyValues) { | ||||||
|  |     super.updated(changedProps); | ||||||
|  |  | ||||||
|  |     if (changedProps.has("config")) { | ||||||
|  |       const card = this._card; | ||||||
|  |       while (card.lastChild) { | ||||||
|  |         card.removeChild(card.lastChild); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       const el = this._createCardElement((load(this.config.config) as any)[0]); | ||||||
|  |       card.appendChild(el); | ||||||
|  |       this._getSize(el); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (changedProps.has("hass")) { | ||||||
|  |       const card = this._card.lastChild; | ||||||
|  |       if (card) { | ||||||
|  |         (card as any).hass = this.hass; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   async _getSize(el) { | ||||||
|  |     await customElements.whenDefined(el.localName); | ||||||
|  |  | ||||||
|  |     if (!("getCardSize" in el)) { | ||||||
|  |       this._size = undefined; | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     this._size = await el.getCardSize(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   _createCardElement(cardConfig) { | ||||||
|  |     const element = createCardElement(cardConfig); | ||||||
|  |     if (this.hass) { | ||||||
|  |       element.hass = this.hass; | ||||||
|  |     } | ||||||
|  |     element.addEventListener( | ||||||
|  |       "ll-rebuild", | ||||||
|  |       (ev) => { | ||||||
|  |         ev.stopPropagation(); | ||||||
|  |         this._rebuildCard(element, cardConfig); | ||||||
|  |       }, | ||||||
|  |       { once: true } | ||||||
|  |     ); | ||||||
|  |     return element; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   _rebuildCard(cardElToReplace, config) { | ||||||
|  |     const newCardEl = this._createCardElement(config); | ||||||
|  |     cardElToReplace.parentElement.replaceChild(newCardEl, cardElToReplace); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   static styles = css` | ||||||
|  |     .root { | ||||||
|  |       display: flex; | ||||||
|  |     } | ||||||
|  |     h2 { | ||||||
|  |       margin: 0 0 20px; | ||||||
|  |       color: var(--primary-color); | ||||||
|  |     } | ||||||
|  |     h2 small { | ||||||
|  |       font-size: 0.5em; | ||||||
|  |       color: var(--primary-text-color); | ||||||
|  |     } | ||||||
|  |     #card { | ||||||
|  |       max-width: 400px; | ||||||
|  |       width: 100vw; | ||||||
|  |     } | ||||||
|  |     pre { | ||||||
|  |       width: 400px; | ||||||
|  |       margin: 0 16px; | ||||||
|  |       overflow: auto; | ||||||
|  |       color: var(--primary-text-color); | ||||||
|  |     } | ||||||
|  |     @media only screen and (max-width: 800px) { | ||||||
|  |       .root { | ||||||
|  |         flex-direction: column; | ||||||
|  |       } | ||||||
|  |       pre { | ||||||
|  |         margin: 16px 0; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   `; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | declare global { | ||||||
|  |   interface HTMLElementTagNameMap { | ||||||
|  |     "demo-card": DemoCard; | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -1,83 +0,0 @@ | |||||||
| import "@polymer/app-layout/app-toolbar/app-toolbar"; |  | ||||||
| import { html } from "@polymer/polymer/lib/utils/html-tag"; |  | ||||||
| /* eslint-plugin-disable lit */ |  | ||||||
| import { PolymerElement } from "@polymer/polymer/polymer-element"; |  | ||||||
| import { applyThemesOnElement } from "../../../src/common/dom/apply_themes_on_element"; |  | ||||||
| import "../../../src/components/ha-formfield"; |  | ||||||
| import "../../../src/components/ha-switch"; |  | ||||||
| import "./demo-card"; |  | ||||||
|  |  | ||||||
| class DemoCards extends PolymerElement { |  | ||||||
|   static get template() { |  | ||||||
|     return html` |  | ||||||
|       <style> |  | ||||||
|         #container { |  | ||||||
|           min-height: calc(100vh - 128px); |  | ||||||
|           background: var(--primary-background-color); |  | ||||||
|         } |  | ||||||
|         .cards { |  | ||||||
|           display: flex; |  | ||||||
|           flex-wrap: wrap; |  | ||||||
|           justify-content: center; |  | ||||||
|         } |  | ||||||
|         demo-card { |  | ||||||
|           margin: 16px 16px 32px; |  | ||||||
|         } |  | ||||||
|         app-toolbar { |  | ||||||
|           background-color: var(--light-primary-color); |  | ||||||
|         } |  | ||||||
|         .filters { |  | ||||||
|           margin-left: 60px; |  | ||||||
|         } |  | ||||||
|         ha-formfield { |  | ||||||
|           margin-right: 16px; |  | ||||||
|         } |  | ||||||
|       </style> |  | ||||||
|       <app-toolbar> |  | ||||||
|         <div class="filters"> |  | ||||||
|           <ha-formfield label="Show config"> |  | ||||||
|             <ha-switch checked="[[_showConfig]]" on-change="_showConfigToggled"> |  | ||||||
|             </ha-switch> |  | ||||||
|           </ha-formfield> |  | ||||||
|           <ha-formfield label="Dark theme"> |  | ||||||
|             <ha-switch on-change="_darkThemeToggled"> </ha-switch> |  | ||||||
|           </ha-formfield> |  | ||||||
|         </div> |  | ||||||
|       </app-toolbar> |  | ||||||
|       <div id="container"> |  | ||||||
|         <div class="cards"> |  | ||||||
|           <template is="dom-repeat" items="[[configs]]"> |  | ||||||
|             <demo-card |  | ||||||
|               config="[[item]]" |  | ||||||
|               show-config="[[_showConfig]]" |  | ||||||
|               hass="[[hass]]" |  | ||||||
|             ></demo-card> |  | ||||||
|           </template> |  | ||||||
|         </div> |  | ||||||
|       </div> |  | ||||||
|     `; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   static get properties() { |  | ||||||
|     return { |  | ||||||
|       configs: Object, |  | ||||||
|       hass: Object, |  | ||||||
|       _showConfig: { |  | ||||||
|         type: Boolean, |  | ||||||
|         value: false, |  | ||||||
|       }, |  | ||||||
|     }; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   _showConfigToggled(ev) { |  | ||||||
|     this._showConfig = ev.target.checked; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   _darkThemeToggled(ev) { |  | ||||||
|     applyThemesOnElement(this.$.container, { themes: {} }, "default", { |  | ||||||
|       dark: ev.target.checked, |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| customElements.define("demo-cards", DemoCards); |  | ||||||
							
								
								
									
										88
									
								
								gallery/src/components/demo-cards.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								gallery/src/components/demo-cards.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,88 @@ | |||||||
|  | import "@polymer/app-layout/app-toolbar/app-toolbar"; | ||||||
|  | import { html, css, LitElement } from "lit"; | ||||||
|  | import { customElement, property, query, state } from "lit/decorators"; | ||||||
|  | import { applyThemesOnElement } from "../../../src/common/dom/apply_themes_on_element"; | ||||||
|  | import "../../../src/components/ha-formfield"; | ||||||
|  | import "../../../src/components/ha-switch"; | ||||||
|  | import { HomeAssistant } from "../../../src/types"; | ||||||
|  | import "./demo-card"; | ||||||
|  | import type { DemoCardConfig } from "./demo-card"; | ||||||
|  |  | ||||||
|  | @customElement("demo-cards") | ||||||
|  | class DemoCards extends LitElement { | ||||||
|  |   @property() public configs!: DemoCardConfig[]; | ||||||
|  |  | ||||||
|  |   @property() public hass!: HomeAssistant; | ||||||
|  |  | ||||||
|  |   @state() private _showConfig = false; | ||||||
|  |  | ||||||
|  |   @query("#container") private _container!: HTMLElement; | ||||||
|  |  | ||||||
|  |   render() { | ||||||
|  |     return html` | ||||||
|  |       <app-toolbar> | ||||||
|  |         <div class="filters"> | ||||||
|  |           <ha-formfield label="Show config"> | ||||||
|  |             <ha-switch | ||||||
|  |               .checked=${this._showConfig} | ||||||
|  |               @change=${this._showConfigToggled} | ||||||
|  |             > | ||||||
|  |             </ha-switch> | ||||||
|  |           </ha-formfield> | ||||||
|  |           <ha-formfield label="Dark theme"> | ||||||
|  |             <ha-switch @change=${this._darkThemeToggled}> </ha-switch> | ||||||
|  |           </ha-formfield> | ||||||
|  |         </div> | ||||||
|  |       </app-toolbar> | ||||||
|  |       <div id="container"> | ||||||
|  |         <div class="cards"> | ||||||
|  |           ${this.configs.map( | ||||||
|  |             (config) => html` | ||||||
|  |               <demo-card | ||||||
|  |                 .config=${config} | ||||||
|  |                 .showConfig=${this._showConfig} | ||||||
|  |                 .hass=${this.hass} | ||||||
|  |               ></demo-card> | ||||||
|  |             ` | ||||||
|  |           )} | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |     `; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   _showConfigToggled(ev) { | ||||||
|  |     this._showConfig = ev.target.checked; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   _darkThemeToggled(ev) { | ||||||
|  |     applyThemesOnElement(this._container, { themes: {} } as any, "default", { | ||||||
|  |       dark: ev.target.checked, | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   static styles = css` | ||||||
|  |     .cards { | ||||||
|  |       display: flex; | ||||||
|  |       flex-wrap: wrap; | ||||||
|  |       justify-content: center; | ||||||
|  |     } | ||||||
|  |     demo-card { | ||||||
|  |       margin: 16px 16px 32px; | ||||||
|  |     } | ||||||
|  |     app-toolbar { | ||||||
|  |       background-color: var(--light-primary-color); | ||||||
|  |     } | ||||||
|  |     .filters { | ||||||
|  |       margin-left: 60px; | ||||||
|  |     } | ||||||
|  |     ha-formfield { | ||||||
|  |       margin-right: 16px; | ||||||
|  |     } | ||||||
|  |   `; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | declare global { | ||||||
|  |   interface HTMLElementTagNameMap { | ||||||
|  |     "demo-cards": DemoCards; | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										46
									
								
								gallery/src/components/page-description.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								gallery/src/components/page-description.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | |||||||
|  | import { html, css } from "lit"; | ||||||
|  | import { customElement, property } from "lit/decorators"; | ||||||
|  | import { until } from "lit/directives/until"; | ||||||
|  | import { HaMarkdown } from "../../../src/components/ha-markdown"; | ||||||
|  | import { PAGES } from "../../build/import-pages"; | ||||||
|  |  | ||||||
|  | @customElement("page-description") | ||||||
|  | class PageDescription extends HaMarkdown { | ||||||
|  |   @property() public page!: string; | ||||||
|  |  | ||||||
|  |   render() { | ||||||
|  |     if (!PAGES[this.page].description) { | ||||||
|  |       return html``; | ||||||
|  |     } | ||||||
|  |     return html` | ||||||
|  |       ${until( | ||||||
|  |         PAGES[this.page] | ||||||
|  |           .description() | ||||||
|  |           .then((content) => html`<div class="root">${content}</div>`), | ||||||
|  |         "" | ||||||
|  |       )} | ||||||
|  |     `; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   static styles = [ | ||||||
|  |     HaMarkdown.styles, | ||||||
|  |     css` | ||||||
|  |       .root { | ||||||
|  |         max-width: 800px; | ||||||
|  |         margin: 0 auto; | ||||||
|  |       } | ||||||
|  |       .root > *:first-child { | ||||||
|  |         margin-top: 0; | ||||||
|  |       } | ||||||
|  |       .root > *:last-child { | ||||||
|  |         margin-bottom: 0; | ||||||
|  |       } | ||||||
|  |     `, | ||||||
|  |   ]; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | declare global { | ||||||
|  |   interface HTMLElementTagNameMap { | ||||||
|  |     "page-description": PageDescription; | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -1,88 +0,0 @@ | |||||||
| import { |  | ||||||
|   customElement, |  | ||||||
|   html, |  | ||||||
|   LitElement, |  | ||||||
|   property, |  | ||||||
|   PropertyValues, |  | ||||||
|   query, |  | ||||||
|   TemplateResult, |  | ||||||
| } from "lit-element"; |  | ||||||
| import "../../../src/components/ha-card"; |  | ||||||
| import { |  | ||||||
|   SUPPORT_BRIGHTNESS, |  | ||||||
|   SUPPORT_COLOR, |  | ||||||
|   SUPPORT_COLOR_TEMP, |  | ||||||
|   SUPPORT_EFFECT, |  | ||||||
|   SUPPORT_FLASH, |  | ||||||
|   SUPPORT_TRANSITION, |  | ||||||
|   SUPPORT_WHITE_VALUE, |  | ||||||
| } from "../../../src/data/light"; |  | ||||||
| import "../../../src/dialogs/more-info/more-info-content"; |  | ||||||
| import { getEntity } from "../../../src/fake_data/entity"; |  | ||||||
| import { |  | ||||||
|   MockHomeAssistant, |  | ||||||
|   provideHass, |  | ||||||
| } from "../../../src/fake_data/provide_hass"; |  | ||||||
| import "../components/demo-more-infos"; |  | ||||||
|  |  | ||||||
| const ENTITIES = [ |  | ||||||
|   getEntity("light", "bed_light", "on", { |  | ||||||
|     friendly_name: "Basic Light", |  | ||||||
|   }), |  | ||||||
|   getEntity("light", "kitchen_light", "on", { |  | ||||||
|     friendly_name: "Brightness Light", |  | ||||||
|     brightness: 200, |  | ||||||
|     supported_features: SUPPORT_BRIGHTNESS, |  | ||||||
|   }), |  | ||||||
|   getEntity("light", "color_temperature_light", "on", { |  | ||||||
|     friendly_name: "White Color Temperature Light", |  | ||||||
|     brightness: 128, |  | ||||||
|     color_temp: 75, |  | ||||||
|     min_mireds: 30, |  | ||||||
|     max_mireds: 150, |  | ||||||
|     supported_features: SUPPORT_BRIGHTNESS + SUPPORT_COLOR_TEMP, |  | ||||||
|   }), |  | ||||||
|   getEntity("light", "color_effectslight", "on", { |  | ||||||
|     friendly_name: "Color Effets Light", |  | ||||||
|     brightness: 255, |  | ||||||
|     hs_color: [30, 100], |  | ||||||
|     white_value: 36, |  | ||||||
|     supported_features: |  | ||||||
|       SUPPORT_BRIGHTNESS + |  | ||||||
|       SUPPORT_EFFECT + |  | ||||||
|       SUPPORT_FLASH + |  | ||||||
|       SUPPORT_COLOR + |  | ||||||
|       SUPPORT_TRANSITION + |  | ||||||
|       SUPPORT_WHITE_VALUE, |  | ||||||
|     effect_list: ["random", "colorloop"], |  | ||||||
|   }), |  | ||||||
| ]; |  | ||||||
|  |  | ||||||
| @customElement("demo-more-info-light") |  | ||||||
| class DemoMoreInfoLight extends LitElement { |  | ||||||
|   @property() public hass!: MockHomeAssistant; |  | ||||||
|  |  | ||||||
|   @query("demo-more-infos") private _demoRoot!: HTMLElement; |  | ||||||
|  |  | ||||||
|   protected render(): TemplateResult { |  | ||||||
|     return html` |  | ||||||
|       <demo-more-infos |  | ||||||
|         .hass=${this.hass} |  | ||||||
|         .entities=${ENTITIES.map((ent) => ent.entityId)} |  | ||||||
|       ></demo-more-infos> |  | ||||||
|     `; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   protected firstUpdated(changedProperties: PropertyValues) { |  | ||||||
|     super.firstUpdated(changedProperties); |  | ||||||
|     const hass = provideHass(this._demoRoot); |  | ||||||
|     hass.updateTranslations(null, "en"); |  | ||||||
|     hass.addEntities(ENTITIES); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| declare global { |  | ||||||
|   interface HTMLElementTagNameMap { |  | ||||||
|     "demo-more-info-light": DemoMoreInfoLight; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @@ -1,221 +0,0 @@ | |||||||
| import "@polymer/app-layout/app-header-layout/app-header-layout"; |  | ||||||
| import "@polymer/app-layout/app-header/app-header"; |  | ||||||
| import "@polymer/app-layout/app-toolbar/app-toolbar"; |  | ||||||
| import "@polymer/paper-item/paper-item"; |  | ||||||
| import "@polymer/paper-item/paper-item-body"; |  | ||||||
| import { html } from "@polymer/polymer/lib/utils/html-tag"; |  | ||||||
| /* eslint-plugin-disable lit */ |  | ||||||
| import { PolymerElement } from "@polymer/polymer/polymer-element"; |  | ||||||
| import "../../src/components/ha-card"; |  | ||||||
| import "../../src/components/ha-icon"; |  | ||||||
| import "../../src/components/ha-icon-button"; |  | ||||||
| import "../../src/managers/notification-manager"; |  | ||||||
| import "../../src/styles/polymer-ha-style"; |  | ||||||
| // eslint-disable-next-line import/extensions |  | ||||||
| import { DEMOS } from "../build/import-demos"; |  | ||||||
|  |  | ||||||
| class HaGallery extends PolymerElement { |  | ||||||
|   static get template() { |  | ||||||
|     return html` |  | ||||||
|       <style include="iron-positioning ha-style"> |  | ||||||
|         :host { |  | ||||||
|           -ms-user-select: initial; |  | ||||||
|           -webkit-user-select: initial; |  | ||||||
|           -moz-user-select: initial; |  | ||||||
|         } |  | ||||||
|         app-header-layout { |  | ||||||
|           min-height: 100vh; |  | ||||||
|         } |  | ||||||
|         ha-icon-button.invisible { |  | ||||||
|           visibility: hidden; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         .pickers { |  | ||||||
|           display: flex; |  | ||||||
|           flex-wrap: wrap; |  | ||||||
|           justify-content: center; |  | ||||||
|           align-items: start; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         .pickers ha-card { |  | ||||||
|           width: 400px; |  | ||||||
|           display: block; |  | ||||||
|           margin: 16px 8px; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         .pickers ha-card:last-child { |  | ||||||
|           margin-bottom: 16px; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         .intro { |  | ||||||
|           margin: -1em 0; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         p a { |  | ||||||
|           color: var(--primary-color); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         a { |  | ||||||
|           color: var(--primary-text-color); |  | ||||||
|           text-decoration: none; |  | ||||||
|         } |  | ||||||
|       </style> |  | ||||||
|  |  | ||||||
|       <app-header-layout> |  | ||||||
|         <app-header slot="header" fixed> |  | ||||||
|           <app-toolbar> |  | ||||||
|             <ha-icon-button |  | ||||||
|               icon="hass:arrow-left" |  | ||||||
|               on-click="_backTapped" |  | ||||||
|               class$="[[_computeHeaderButtonClass(_demo)]]" |  | ||||||
|             ></ha-icon-button> |  | ||||||
|             <div main-title> |  | ||||||
|               [[_withDefault(_demo, "Home Assistant Gallery")]] |  | ||||||
|             </div> |  | ||||||
|           </app-toolbar> |  | ||||||
|         </app-header> |  | ||||||
|  |  | ||||||
|         <div class="content"> |  | ||||||
|           <div id="demo"></div> |  | ||||||
|           <template is="dom-if" if="[[!_demo]]"> |  | ||||||
|             <div class="pickers"> |  | ||||||
|               <ha-card header="Lovelace Card Demos"> |  | ||||||
|                 <div class="card-content intro"> |  | ||||||
|                   <p> |  | ||||||
|                     Lovelace has many different cards. Each card allows the user |  | ||||||
|                     to tell a different story about what is going on in their |  | ||||||
|                     house. These cards are very customizable, as no household is |  | ||||||
|                     the same. |  | ||||||
|                   </p> |  | ||||||
|  |  | ||||||
|                   <p> |  | ||||||
|                     This gallery helps our developers and designers to see all |  | ||||||
|                     the different states that each card can be in. |  | ||||||
|                   </p> |  | ||||||
|  |  | ||||||
|                   <p> |  | ||||||
|                     Check |  | ||||||
|                     <a href="https://www.home-assistant.io/lovelace" |  | ||||||
|                       >the official website</a |  | ||||||
|                     > |  | ||||||
|                     for instructions on how to get started with Lovelace. |  | ||||||
|                   </p> |  | ||||||
|                 </div> |  | ||||||
|                 <template is="dom-repeat" items="[[_lovelaceDemos]]"> |  | ||||||
|                   <a href="#[[item]]"> |  | ||||||
|                     <paper-item> |  | ||||||
|                       <paper-item-body>{{ item }}</paper-item-body> |  | ||||||
|                       <ha-icon icon="hass:chevron-right"></ha-icon> |  | ||||||
|                     </paper-item> |  | ||||||
|                   </a> |  | ||||||
|                 </template> |  | ||||||
|               </ha-card> |  | ||||||
|  |  | ||||||
|               <ha-card header="Other Demos"> |  | ||||||
|                 <div class="card-content intro"></div> |  | ||||||
|                 <template is="dom-repeat" items="[[_restDemos]]"> |  | ||||||
|                   <a href="#[[item]]"> |  | ||||||
|                     <paper-item> |  | ||||||
|                       <paper-item-body>{{ item }}</paper-item-body> |  | ||||||
|                       <ha-icon icon="hass:chevron-right"></ha-icon> |  | ||||||
|                     </paper-item> |  | ||||||
|                   </a> |  | ||||||
|                 </template> |  | ||||||
|               </ha-card> |  | ||||||
|             </div> |  | ||||||
|           </template> |  | ||||||
|         </div> |  | ||||||
|       </app-header-layout> |  | ||||||
|       <notification-manager |  | ||||||
|         hass="[[_fakeHass]]" |  | ||||||
|         id="notifications" |  | ||||||
|       ></notification-manager> |  | ||||||
|     `; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   static get properties() { |  | ||||||
|     return { |  | ||||||
|       _fakeHass: { |  | ||||||
|         type: Object, |  | ||||||
|         // Just enough for computeRTL |  | ||||||
|         value: { |  | ||||||
|           language: "en", |  | ||||||
|           translationMetadata: { |  | ||||||
|             translations: {}, |  | ||||||
|           }, |  | ||||||
|         }, |  | ||||||
|       }, |  | ||||||
|       _demo: { |  | ||||||
|         type: String, |  | ||||||
|         value: document.location.hash.substr(1), |  | ||||||
|         observer: "_demoChanged", |  | ||||||
|       }, |  | ||||||
|       _demos: { |  | ||||||
|         type: Array, |  | ||||||
|         value: Object.keys(DEMOS), |  | ||||||
|       }, |  | ||||||
|       _lovelaceDemos: { |  | ||||||
|         type: Array, |  | ||||||
|         computed: "_computeLovelace(_demos)", |  | ||||||
|       }, |  | ||||||
|       _restDemos: { |  | ||||||
|         type: Array, |  | ||||||
|         computed: "_computeRest(_demos)", |  | ||||||
|       }, |  | ||||||
|     }; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   ready() { |  | ||||||
|     super.ready(); |  | ||||||
|  |  | ||||||
|     this.addEventListener("show-notification", (ev) => |  | ||||||
|       this.$.notifications.showDialog({ message: ev.detail.message }) |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     this.addEventListener("hass-more-info", (ev) => { |  | ||||||
|       if (ev.detail.entityId) { |  | ||||||
|         this.$.notifications.showDialog({ |  | ||||||
|           message: `Showing more info for ${ev.detail.entityId}`, |  | ||||||
|         }); |  | ||||||
|       } |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     window.addEventListener("hashchange", () => { |  | ||||||
|       this._demo = document.location.hash.substr(1); |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   _withDefault(value, def) { |  | ||||||
|     return value || def; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   _demoChanged(demo) { |  | ||||||
|     const root = this.$.demo; |  | ||||||
|  |  | ||||||
|     while (root.lastChild) root.removeChild(root.lastChild); |  | ||||||
|  |  | ||||||
|     if (demo) { |  | ||||||
|       DEMOS[demo](); |  | ||||||
|       const el = document.createElement(demo); |  | ||||||
|       root.appendChild(el); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   _computeHeaderButtonClass(demo) { |  | ||||||
|     return demo ? "" : "invisible"; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   _backTapped() { |  | ||||||
|     document.location.hash = ""; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   _computeLovelace(demos) { |  | ||||||
|     return demos.filter((demo) => demo.includes("hui")); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   _computeRest(demos) { |  | ||||||
|     return demos.filter((demo) => !demo.includes("hui")); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| customElements.define("ha-gallery", HaGallery); |  | ||||||
							
								
								
									
										257
									
								
								gallery/src/ha-gallery.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										257
									
								
								gallery/src/ha-gallery.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,257 @@ | |||||||
|  | import { mdiMenu } from "@mdi/js"; | ||||||
|  | import "@material/mwc-drawer"; | ||||||
|  | import "@material/mwc-top-app-bar-fixed"; | ||||||
|  | import { html, css, LitElement, PropertyValues } from "lit"; | ||||||
|  | import { customElement, property, query } from "lit/decorators"; | ||||||
|  | import "../../src/components/ha-icon-button"; | ||||||
|  | import "../../src/managers/notification-manager"; | ||||||
|  | import { haStyle } from "../../src/resources/styles"; | ||||||
|  | import { PAGES, SIDEBAR } from "../build/import-pages"; | ||||||
|  | import { dynamicElement } from "../../src/common/dom/dynamic-element-directive"; | ||||||
|  | import "./components/page-description"; | ||||||
|  |  | ||||||
|  | const GITHUB_DEMO_URL = | ||||||
|  |   "https://github.com/home-assistant/frontend/blob/dev/gallery/src/pages/"; | ||||||
|  |  | ||||||
|  | const FAKE_HASS = { | ||||||
|  |   // Just enough for computeRTL for notification-manager | ||||||
|  |   language: "en", | ||||||
|  |   translationMetadata: { | ||||||
|  |     translations: {}, | ||||||
|  |   }, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | @customElement("ha-gallery") | ||||||
|  | class HaGallery extends LitElement { | ||||||
|  |   @property() private _page = | ||||||
|  |     document.location.hash.substring(1) || | ||||||
|  |     `${SIDEBAR[0].category}/${SIDEBAR[0].pages![0]}`; | ||||||
|  |  | ||||||
|  |   @query("notification-manager") | ||||||
|  |   private _notifications!: HTMLElementTagNameMap["notification-manager"]; | ||||||
|  |  | ||||||
|  |   @query("mwc-drawer") | ||||||
|  |   private _drawer!: HTMLElementTagNameMap["mwc-drawer"]; | ||||||
|  |  | ||||||
|  |   private _narrow = window.matchMedia("(max-width: 600px)").matches; | ||||||
|  |  | ||||||
|  |   render() { | ||||||
|  |     const sidebar: unknown[] = []; | ||||||
|  |  | ||||||
|  |     for (const group of SIDEBAR) { | ||||||
|  |       const links: unknown[] = []; | ||||||
|  |  | ||||||
|  |       for (const page of group.pages!) { | ||||||
|  |         const key = `${group.category}/${page}`; | ||||||
|  |         const active = this._page === key; | ||||||
|  |         const title = PAGES[key].metadata.title || page; | ||||||
|  |         links.push(html` | ||||||
|  |           <a ?active=${active} href=${`#${group.category}/${page}`}>${title}</a> | ||||||
|  |         `); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       sidebar.push( | ||||||
|  |         group.header | ||||||
|  |           ? html` | ||||||
|  |               <details> | ||||||
|  |                 <summary class="section">${group.header}</summary> | ||||||
|  |                 ${links} | ||||||
|  |               </details> | ||||||
|  |             ` | ||||||
|  |           : links | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return html` | ||||||
|  |       <mwc-drawer | ||||||
|  |         hasHeader | ||||||
|  |         .open=${!this._narrow} | ||||||
|  |         .type=${this._narrow ? "modal" : "dismissible"} | ||||||
|  |       > | ||||||
|  |         <span slot="title">Home Assistant Design</span> | ||||||
|  |         <!-- <span slot="subtitle">subtitle</span> --> | ||||||
|  |         <div class="sidebar">${sidebar}</div> | ||||||
|  |         <div slot="appContent"> | ||||||
|  |           <mwc-top-app-bar-fixed> | ||||||
|  |             <ha-icon-button | ||||||
|  |               slot="navigationIcon" | ||||||
|  |               @click=${this._menuTapped} | ||||||
|  |               .path=${mdiMenu} | ||||||
|  |             ></ha-icon-button> | ||||||
|  |  | ||||||
|  |             <div slot="title"> | ||||||
|  |               ${PAGES[this._page].metadata.title || this._page.split("/")[1]} | ||||||
|  |             </div> | ||||||
|  |           </mwc-top-app-bar-fixed> | ||||||
|  |           <div class="content"> | ||||||
|  |             ${PAGES[this._page].description | ||||||
|  |               ? html` | ||||||
|  |                   <page-description .page=${this._page}></page-description> | ||||||
|  |                 ` | ||||||
|  |               : ""} | ||||||
|  |             ${dynamicElement(`demo-${this._page.replace("/", "-")}`)} | ||||||
|  |           </div> | ||||||
|  |           <div class="page-footer"> | ||||||
|  |             ${PAGES[this._page].description || | ||||||
|  |             Object.keys(PAGES[this._page].metadata).length > 0 | ||||||
|  |               ? html` | ||||||
|  |                   <a | ||||||
|  |                     href=${`${GITHUB_DEMO_URL}${this._page}.markdown`} | ||||||
|  |                     target="_blank" | ||||||
|  |                   > | ||||||
|  |                     Edit text | ||||||
|  |                   </a> | ||||||
|  |                 ` | ||||||
|  |               : ""} | ||||||
|  |             ${PAGES[this._page].demo | ||||||
|  |               ? html` | ||||||
|  |                   <a | ||||||
|  |                     href=${`${GITHUB_DEMO_URL}${this._page}.ts`} | ||||||
|  |                     target="_blank" | ||||||
|  |                   > | ||||||
|  |                     Edit demo | ||||||
|  |                   </a> | ||||||
|  |                 ` | ||||||
|  |               : ""} | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |       </mwc-drawer> | ||||||
|  |       <notification-manager | ||||||
|  |         .hass=${FAKE_HASS} | ||||||
|  |         id="notifications" | ||||||
|  |       ></notification-manager> | ||||||
|  |     `; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   firstUpdated(changedProps: PropertyValues) { | ||||||
|  |     super.firstUpdated(changedProps); | ||||||
|  |  | ||||||
|  |     this.addEventListener("show-notification", (ev) => | ||||||
|  |       this._notifications.showDialog({ message: ev.detail.message }) | ||||||
|  |     ); | ||||||
|  |     this.addEventListener("alert-dismissed-clicked", () => | ||||||
|  |       this._notifications.showDialog({ message: "Alert dismissed clicked" }) | ||||||
|  |     ); | ||||||
|  |     this.addEventListener("hass-more-info", (ev) => { | ||||||
|  |       if (ev.detail.entityId) { | ||||||
|  |         this._notifications.showDialog({ | ||||||
|  |           message: `Showing more info for ${ev.detail.entityId}`, | ||||||
|  |         }); | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     document.location.hash = this._page; | ||||||
|  |  | ||||||
|  |     window.addEventListener("hashchange", () => { | ||||||
|  |       this._page = document.location.hash.substring(1); | ||||||
|  |       if (this._narrow) { | ||||||
|  |         this._drawer.open = false; | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   updated(changedProps: PropertyValues) { | ||||||
|  |     super.updated(changedProps); | ||||||
|  |     if (!changedProps.has("_page")) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (PAGES[this._page].demo) { | ||||||
|  |       PAGES[this._page].demo(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const menuItem = this.shadowRoot!.querySelector( | ||||||
|  |       `a[href="#${this._page}"]` | ||||||
|  |     )!; | ||||||
|  |     // Make sure section is expanded | ||||||
|  |     if (menuItem.parentElement instanceof HTMLDetailsElement) { | ||||||
|  |       menuItem.parentElement.open = true; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   _menuTapped() { | ||||||
|  |     this._drawer.open = !this._drawer.open; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   static styles = [ | ||||||
|  |     haStyle, | ||||||
|  |     css` | ||||||
|  |       :host { | ||||||
|  |         -ms-user-select: initial; | ||||||
|  |         -webkit-user-select: initial; | ||||||
|  |         -moz-user-select: initial; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .sidebar { | ||||||
|  |         padding: 4px; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .sidebar details { | ||||||
|  |         margin-top: 1em; | ||||||
|  |         margin-left: 1em; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .sidebar summary { | ||||||
|  |         cursor: pointer; | ||||||
|  |         font-weight: bold; | ||||||
|  |         margin-bottom: 8px; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .sidebar a { | ||||||
|  |         color: var(--primary-text-color); | ||||||
|  |         display: block; | ||||||
|  |         padding: 4px 12px; | ||||||
|  |         text-decoration: none; | ||||||
|  |         position: relative; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .sidebar a[active]::before { | ||||||
|  |         border-radius: 4px; | ||||||
|  |         position: absolute; | ||||||
|  |         top: 0; | ||||||
|  |         right: 2px; | ||||||
|  |         bottom: 0; | ||||||
|  |         left: 2px; | ||||||
|  |         pointer-events: none; | ||||||
|  |         content: ""; | ||||||
|  |         transition: opacity 15ms linear; | ||||||
|  |         will-change: opacity; | ||||||
|  |         background-color: var(--sidebar-selected-icon-color); | ||||||
|  |         opacity: 0.12; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       div[slot="appContent"] { | ||||||
|  |         display: flex; | ||||||
|  |         flex-direction: column; | ||||||
|  |         min-height: 100vh; | ||||||
|  |         background: var(--primary-background-color); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .content { | ||||||
|  |         flex: 1; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       page-description { | ||||||
|  |         margin: 16px; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .page-footer { | ||||||
|  |         text-align: center; | ||||||
|  |         margin: 16px 0; | ||||||
|  |         padding-top: 16px; | ||||||
|  |         border-top: 1px solid rgba(0, 0, 0, 0.12); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .page-footer a { | ||||||
|  |         display: inline-block; | ||||||
|  |         margin: 0 8px; | ||||||
|  |       } | ||||||
|  |     `, | ||||||
|  |   ]; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | declare global { | ||||||
|  |   interface HTMLElementTagNameMap { | ||||||
|  |     "ha-gallery": HaGallery; | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -7,7 +7,7 @@ | |||||||
|       content="width=device-width, initial-scale=1, shrink-to-fit=no" |       content="width=device-width, initial-scale=1, shrink-to-fit=no" | ||||||
|     /> |     /> | ||||||
|     <meta name="theme-color" content="#2157BC" /> |     <meta name="theme-color" content="#2157BC" /> | ||||||
|     <title>HAGallery</title> |     <title>Home Assistant Design</title> | ||||||
|  |  | ||||||
|     <script type="module" src="<%= latestGalleryJS %>"></script> |     <script type="module" src="<%= latestGalleryJS %>"></script> | ||||||
|     <style> |     <style> | ||||||
|   | |||||||
							
								
								
									
										3
									
								
								gallery/src/pages/automation/describe-action.markdown
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								gallery/src/pages/automation/describe-action.markdown
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | --- | ||||||
|  | title: Describe Action | ||||||
|  | --- | ||||||
| @@ -1,16 +1,10 @@ | |||||||
| import { safeDump } from "js-yaml"; | import { dump } from "js-yaml"; | ||||||
| import { | import { html, css, LitElement, TemplateResult } from "lit"; | ||||||
|   customElement, | import { customElement, property } from "lit/decorators"; | ||||||
|   html, | import "../../../../src/components/ha-card"; | ||||||
|   css, | import { describeAction } from "../../../../src/data/script_i18n"; | ||||||
|   LitElement, | import { provideHass } from "../../../../src/fake_data/provide_hass"; | ||||||
|   TemplateResult, | import { HomeAssistant } from "../../../../src/types"; | ||||||
|   property, |  | ||||||
| } from "lit-element"; |  | ||||||
| import "../../../src/components/ha-card"; |  | ||||||
| import { describeAction } from "../../../src/data/script_i18n"; |  | ||||||
| import { provideHass } from "../../../src/fake_data/provide_hass"; |  | ||||||
| import { HomeAssistant } from "../../../src/types"; |  | ||||||
| 
 | 
 | ||||||
| const actions = [ | const actions = [ | ||||||
|   { wait_template: "{{ true }}", alias: "Something with an alias" }, |   { wait_template: "{{ true }}", alias: "Something with an alias" }, | ||||||
| @@ -62,7 +56,7 @@ export class DemoAutomationDescribeAction extends LitElement { | |||||||
|           (conf) => html` |           (conf) => html` | ||||||
|             <div class="action"> |             <div class="action"> | ||||||
|               <span>${describeAction(this.hass, conf as any)}</span> |               <span>${describeAction(this.hass, conf as any)}</span> | ||||||
|               <pre>${safeDump(conf)}</pre> |               <pre>${dump(conf)}</pre> | ||||||
|             </div> |             </div> | ||||||
|           ` |           ` | ||||||
|         )} |         )} | ||||||
							
								
								
									
										3
									
								
								gallery/src/pages/automation/describe-condition.markdown
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								gallery/src/pages/automation/describe-condition.markdown
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | --- | ||||||
|  | title: Describe Condition | ||||||
|  | --- | ||||||
| @@ -1,13 +1,8 @@ | |||||||
| import { safeDump } from "js-yaml"; | import { dump } from "js-yaml"; | ||||||
| import { | import { html, css, LitElement, TemplateResult } from "lit"; | ||||||
|   customElement, | import { customElement } from "lit/decorators"; | ||||||
|   html, | import "../../../../src/components/ha-card"; | ||||||
|   css, | import { describeCondition } from "../../../../src/data/automation_i18n"; | ||||||
|   LitElement, |  | ||||||
|   TemplateResult, |  | ||||||
| } from "lit-element"; |  | ||||||
| import "../../../src/components/ha-card"; |  | ||||||
| import { describeCondition } from "../../../src/data/automation_i18n"; |  | ||||||
| 
 | 
 | ||||||
| const conditions = [ | const conditions = [ | ||||||
|   { condition: "and" }, |   { condition: "and" }, | ||||||
| @@ -31,7 +26,7 @@ export class DemoAutomationDescribeCondition extends LitElement { | |||||||
|           (conf) => html` |           (conf) => html` | ||||||
|             <div class="condition"> |             <div class="condition"> | ||||||
|               <span>${describeCondition(conf as any)}</span> |               <span>${describeCondition(conf as any)}</span> | ||||||
|               <pre>${safeDump(conf)}</pre> |               <pre>${dump(conf)}</pre> | ||||||
|             </div> |             </div> | ||||||
|           ` |           ` | ||||||
|         )} |         )} | ||||||
							
								
								
									
										3
									
								
								gallery/src/pages/automation/describe-trigger.markdown
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								gallery/src/pages/automation/describe-trigger.markdown
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | --- | ||||||
|  | title: Describe Trigger | ||||||
|  | --- | ||||||
| @@ -1,13 +1,8 @@ | |||||||
| import { safeDump } from "js-yaml"; | import { dump } from "js-yaml"; | ||||||
| import { | import { html, css, LitElement, TemplateResult } from "lit"; | ||||||
|   customElement, | import { customElement } from "lit/decorators"; | ||||||
|   html, | import "../../../../src/components/ha-card"; | ||||||
|   css, | import { describeTrigger } from "../../../../src/data/automation_i18n"; | ||||||
|   LitElement, |  | ||||||
|   TemplateResult, |  | ||||||
| } from "lit-element"; |  | ||||||
| import "../../../src/components/ha-card"; |  | ||||||
| import { describeTrigger } from "../../../src/data/automation_i18n"; |  | ||||||
| 
 | 
 | ||||||
| const triggers = [ | const triggers = [ | ||||||
|   { platform: "state" }, |   { platform: "state" }, | ||||||
| @@ -34,7 +29,7 @@ export class DemoAutomationDescribeTrigger extends LitElement { | |||||||
|           (conf) => html` |           (conf) => html` | ||||||
|             <div class="trigger"> |             <div class="trigger"> | ||||||
|               <span>${describeTrigger(conf as any)}</span> |               <span>${describeTrigger(conf as any)}</span> | ||||||
|               <pre>${safeDump(conf)}</pre> |               <pre>${dump(conf)}</pre> | ||||||
|             </div> |             </div> | ||||||
|           ` |           ` | ||||||
|         )} |         )} | ||||||
							
								
								
									
										3
									
								
								gallery/src/pages/automation/editor-action.markdown
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								gallery/src/pages/automation/editor-action.markdown
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | --- | ||||||
|  | title: Actions | ||||||
|  | --- | ||||||
							
								
								
									
										91
									
								
								gallery/src/pages/automation/editor-action.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								gallery/src/pages/automation/editor-action.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,91 @@ | |||||||
|  | /* eslint-disable lit/no-template-arrow */ | ||||||
|  | import { LitElement, TemplateResult, html } from "lit"; | ||||||
|  | import { customElement, state } from "lit/decorators"; | ||||||
|  | import { provideHass } from "../../../../src/fake_data/provide_hass"; | ||||||
|  | import type { HomeAssistant } from "../../../../src/types"; | ||||||
|  | import "../../components/demo-black-white-row"; | ||||||
|  | import { mockEntityRegistry } from "../../../../demo/src/stubs/entity_registry"; | ||||||
|  | import { mockDeviceRegistry } from "../../../../demo/src/stubs/device_registry"; | ||||||
|  | import { mockAreaRegistry } from "../../../../demo/src/stubs/area_registry"; | ||||||
|  | import { mockHassioSupervisor } from "../../../../demo/src/stubs/hassio_supervisor"; | ||||||
|  | import "../../../../src/panels/config/automation/action/ha-automation-action"; | ||||||
|  | import { HaChooseAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-choose"; | ||||||
|  | import { HaDelayAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-delay"; | ||||||
|  | import { HaDeviceAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-device_id"; | ||||||
|  | import { HaEventAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-event"; | ||||||
|  | import { HaRepeatAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-repeat"; | ||||||
|  | import { HaSceneAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-scene"; | ||||||
|  | import { HaServiceAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-service"; | ||||||
|  | import { HaWaitForTriggerAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-wait_for_trigger"; | ||||||
|  | import { HaWaitAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-wait_template"; | ||||||
|  | import { Action } from "../../../../src/data/script"; | ||||||
|  | import { HaConditionAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-condition"; | ||||||
|  |  | ||||||
|  | const SCHEMAS: { name: string; actions: Action[] }[] = [ | ||||||
|  |   { name: "Event", actions: [HaEventAction.defaultConfig] }, | ||||||
|  |   { name: "Device", actions: [HaDeviceAction.defaultConfig] }, | ||||||
|  |   { name: "Service", actions: [HaServiceAction.defaultConfig] }, | ||||||
|  |   { name: "Condition", actions: [HaConditionAction.defaultConfig] }, | ||||||
|  |   { name: "Delay", actions: [HaDelayAction.defaultConfig] }, | ||||||
|  |   { name: "Scene", actions: [HaSceneAction.defaultConfig] }, | ||||||
|  |   { name: "Wait", actions: [HaWaitAction.defaultConfig] }, | ||||||
|  |   { name: "WaitForTrigger", actions: [HaWaitForTriggerAction.defaultConfig] }, | ||||||
|  |   { name: "Repeat", actions: [HaRepeatAction.defaultConfig] }, | ||||||
|  |   { name: "Choose", actions: [HaChooseAction.defaultConfig] }, | ||||||
|  |   { name: "Variables", actions: [{ variables: { hello: "1" } }] }, | ||||||
|  | ]; | ||||||
|  |  | ||||||
|  | @customElement("demo-automation-editor-action") | ||||||
|  | class DemoHaAutomationEditorAction extends LitElement { | ||||||
|  |   @state() private hass!: HomeAssistant; | ||||||
|  |  | ||||||
|  |   private data: any = SCHEMAS.map((info) => info.actions); | ||||||
|  |  | ||||||
|  |   constructor() { | ||||||
|  |     super(); | ||||||
|  |     const hass = provideHass(this); | ||||||
|  |     hass.updateTranslations(null, "en"); | ||||||
|  |     hass.updateTranslations("config", "en"); | ||||||
|  |     mockEntityRegistry(hass); | ||||||
|  |     mockDeviceRegistry(hass); | ||||||
|  |     mockAreaRegistry(hass); | ||||||
|  |     mockHassioSupervisor(hass); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   protected render(): TemplateResult { | ||||||
|  |     const valueChanged = (ev) => { | ||||||
|  |       const sampleIdx = ev.target.sampleIdx; | ||||||
|  |       this.data[sampleIdx] = ev.detail.value; | ||||||
|  |       this.requestUpdate(); | ||||||
|  |     }; | ||||||
|  |     return html` | ||||||
|  |       ${SCHEMAS.map( | ||||||
|  |         (info, sampleIdx) => html` | ||||||
|  |           <demo-black-white-row | ||||||
|  |             .title=${info.name} | ||||||
|  |             .value=${this.data[sampleIdx]} | ||||||
|  |           > | ||||||
|  |             ${["light", "dark"].map( | ||||||
|  |               (slot) => | ||||||
|  |                 html` | ||||||
|  |                   <ha-automation-action | ||||||
|  |                     slot=${slot} | ||||||
|  |                     .hass=${this.hass} | ||||||
|  |                     .actions=${this.data[sampleIdx]} | ||||||
|  |                     .sampleIdx=${sampleIdx} | ||||||
|  |                     @value-changed=${valueChanged} | ||||||
|  |                   ></ha-automation-action> | ||||||
|  |                 ` | ||||||
|  |             )} | ||||||
|  |           </demo-black-white-row> | ||||||
|  |         ` | ||||||
|  |       )} | ||||||
|  |     `; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | declare global { | ||||||
|  |   interface HTMLElementTagNameMap { | ||||||
|  |     "demo-ha-automation-editor-action": DemoHaAutomationEditorAction; | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										3
									
								
								gallery/src/pages/automation/editor-condition.markdown
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								gallery/src/pages/automation/editor-condition.markdown
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | --- | ||||||
|  | title: Conditions | ||||||
|  | --- | ||||||
							
								
								
									
										127
									
								
								gallery/src/pages/automation/editor-condition.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								gallery/src/pages/automation/editor-condition.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,127 @@ | |||||||
|  | /* eslint-disable lit/no-template-arrow */ | ||||||
|  | import { LitElement, TemplateResult, html } from "lit"; | ||||||
|  | import { customElement, state } from "lit/decorators"; | ||||||
|  | import { provideHass } from "../../../../src/fake_data/provide_hass"; | ||||||
|  | import type { HomeAssistant } from "../../../../src/types"; | ||||||
|  | import "../../components/demo-black-white-row"; | ||||||
|  | import { mockEntityRegistry } from "../../../../demo/src/stubs/entity_registry"; | ||||||
|  | import { mockDeviceRegistry } from "../../../../demo/src/stubs/device_registry"; | ||||||
|  | import { mockAreaRegistry } from "../../../../demo/src/stubs/area_registry"; | ||||||
|  | import { mockHassioSupervisor } from "../../../../demo/src/stubs/hassio_supervisor"; | ||||||
|  | import type { Condition } from "../../../../src/data/automation"; | ||||||
|  | import "../../../../src/panels/config/automation/condition/ha-automation-condition"; | ||||||
|  | import { HaDeviceCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-device"; | ||||||
|  | import { HaLogicalCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-logical"; | ||||||
|  | import HaNumericStateCondition from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-numeric_state"; | ||||||
|  | import { HaStateCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-state"; | ||||||
|  | import { HaSunCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-sun"; | ||||||
|  | import { HaTemplateCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-template"; | ||||||
|  | import { HaTimeCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-time"; | ||||||
|  | import { HaTriggerCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-trigger"; | ||||||
|  | import { HaZoneCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-zone"; | ||||||
|  |  | ||||||
|  | const SCHEMAS: { name: string; conditions: Condition[] }[] = [ | ||||||
|  |   { | ||||||
|  |     name: "State", | ||||||
|  |     conditions: [{ condition: "state", ...HaStateCondition.defaultConfig }], | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     name: "Numeric State", | ||||||
|  |     conditions: [ | ||||||
|  |       { condition: "numeric_state", ...HaNumericStateCondition.defaultConfig }, | ||||||
|  |     ], | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     name: "Sun", | ||||||
|  |     conditions: [{ condition: "sun", ...HaSunCondition.defaultConfig }], | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     name: "Zone", | ||||||
|  |     conditions: [{ condition: "zone", ...HaZoneCondition.defaultConfig }], | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     name: "Time", | ||||||
|  |     conditions: [{ condition: "time", ...HaTimeCondition.defaultConfig }], | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     name: "Template", | ||||||
|  |     conditions: [ | ||||||
|  |       { condition: "template", ...HaTemplateCondition.defaultConfig }, | ||||||
|  |     ], | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     name: "Device", | ||||||
|  |     conditions: [{ condition: "device", ...HaDeviceCondition.defaultConfig }], | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     name: "And", | ||||||
|  |     conditions: [{ condition: "and", ...HaLogicalCondition.defaultConfig }], | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     name: "Or", | ||||||
|  |     conditions: [{ condition: "or", ...HaLogicalCondition.defaultConfig }], | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     name: "Not", | ||||||
|  |     conditions: [{ condition: "not", ...HaLogicalCondition.defaultConfig }], | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     name: "Trigger", | ||||||
|  |     conditions: [{ condition: "trigger", ...HaTriggerCondition.defaultConfig }], | ||||||
|  |   }, | ||||||
|  | ]; | ||||||
|  |  | ||||||
|  | @customElement("demo-automation-editor-condition") | ||||||
|  | class DemoHaAutomationEditorCondition extends LitElement { | ||||||
|  |   @state() private hass!: HomeAssistant; | ||||||
|  |  | ||||||
|  |   private data: any = SCHEMAS.map((info) => info.conditions); | ||||||
|  |  | ||||||
|  |   constructor() { | ||||||
|  |     super(); | ||||||
|  |     const hass = provideHass(this); | ||||||
|  |     hass.updateTranslations(null, "en"); | ||||||
|  |     hass.updateTranslations("config", "en"); | ||||||
|  |     mockEntityRegistry(hass); | ||||||
|  |     mockDeviceRegistry(hass); | ||||||
|  |     mockAreaRegistry(hass); | ||||||
|  |     mockHassioSupervisor(hass); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   protected render(): TemplateResult { | ||||||
|  |     const valueChanged = (ev) => { | ||||||
|  |       const sampleIdx = ev.target.sampleIdx; | ||||||
|  |       this.data[sampleIdx] = ev.detail.value; | ||||||
|  |       this.requestUpdate(); | ||||||
|  |     }; | ||||||
|  |     return html` | ||||||
|  |       ${SCHEMAS.map( | ||||||
|  |         (info, sampleIdx) => html` | ||||||
|  |           <demo-black-white-row | ||||||
|  |             .title=${info.name} | ||||||
|  |             .value=${this.data[sampleIdx]} | ||||||
|  |           > | ||||||
|  |             ${["light", "dark"].map( | ||||||
|  |               (slot) => | ||||||
|  |                 html` | ||||||
|  |                   <ha-automation-condition | ||||||
|  |                     slot=${slot} | ||||||
|  |                     .hass=${this.hass} | ||||||
|  |                     .conditions=${this.data[sampleIdx]} | ||||||
|  |                     .sampleIdx=${sampleIdx} | ||||||
|  |                     @value-changed=${valueChanged} | ||||||
|  |                   ></ha-automation-condition> | ||||||
|  |                 ` | ||||||
|  |             )} | ||||||
|  |           </demo-black-white-row> | ||||||
|  |         ` | ||||||
|  |       )} | ||||||
|  |     `; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | declare global { | ||||||
|  |   interface HTMLElementTagNameMap { | ||||||
|  |     "demo-ha-automation-editor-condition": DemoHaAutomationEditorCondition; | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										3
									
								
								gallery/src/pages/automation/editor-trigger.markdown
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								gallery/src/pages/automation/editor-trigger.markdown
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | --- | ||||||
|  | title: Triggers | ||||||
|  | --- | ||||||
							
								
								
									
										159
									
								
								gallery/src/pages/automation/editor-trigger.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										159
									
								
								gallery/src/pages/automation/editor-trigger.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,159 @@ | |||||||
|  | /* eslint-disable lit/no-template-arrow */ | ||||||
|  | import { LitElement, TemplateResult, html } from "lit"; | ||||||
|  | import { customElement, state } from "lit/decorators"; | ||||||
|  | import { provideHass } from "../../../../src/fake_data/provide_hass"; | ||||||
|  | import type { HomeAssistant } from "../../../../src/types"; | ||||||
|  | import "../../components/demo-black-white-row"; | ||||||
|  | import { mockEntityRegistry } from "../../../../demo/src/stubs/entity_registry"; | ||||||
|  | import { mockDeviceRegistry } from "../../../../demo/src/stubs/device_registry"; | ||||||
|  | import { mockAreaRegistry } from "../../../../demo/src/stubs/area_registry"; | ||||||
|  | import { mockHassioSupervisor } from "../../../../demo/src/stubs/hassio_supervisor"; | ||||||
|  | import type { Trigger } from "../../../../src/data/automation"; | ||||||
|  | import { HaGeolocationTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-geo_location"; | ||||||
|  | import { HaEventTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-event"; | ||||||
|  | import { HaHassTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-homeassistant"; | ||||||
|  | import { HaNumericStateTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-numeric_state"; | ||||||
|  | import { HaSunTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-sun"; | ||||||
|  | import { HaTagTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-tag"; | ||||||
|  | import { HaTemplateTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-template"; | ||||||
|  | import { HaTimeTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-time"; | ||||||
|  | import { HaTimePatternTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-time_pattern"; | ||||||
|  | import { HaWebhookTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-webhook"; | ||||||
|  | import { HaZoneTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-zone"; | ||||||
|  | import { HaDeviceTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-device"; | ||||||
|  | import { HaStateTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-state"; | ||||||
|  | import { HaMQTTTrigger } from "../../../../src/panels/config/automation/trigger/types/ha-automation-trigger-mqtt"; | ||||||
|  | import "../../../../src/panels/config/automation/trigger/ha-automation-trigger"; | ||||||
|  |  | ||||||
|  | const SCHEMAS: { name: string; triggers: Trigger[] }[] = [ | ||||||
|  |   { | ||||||
|  |     name: "State", | ||||||
|  |     triggers: [{ platform: "state", ...HaStateTrigger.defaultConfig }], | ||||||
|  |   }, | ||||||
|  |  | ||||||
|  |   { | ||||||
|  |     name: "MQTT", | ||||||
|  |     triggers: [{ platform: "mqtt", ...HaMQTTTrigger.defaultConfig }], | ||||||
|  |   }, | ||||||
|  |  | ||||||
|  |   { | ||||||
|  |     name: "GeoLocation", | ||||||
|  |     triggers: [ | ||||||
|  |       { platform: "geo_location", ...HaGeolocationTrigger.defaultConfig }, | ||||||
|  |     ], | ||||||
|  |   }, | ||||||
|  |  | ||||||
|  |   { | ||||||
|  |     name: "Home Assistant", | ||||||
|  |     triggers: [{ platform: "homeassistant", ...HaHassTrigger.defaultConfig }], | ||||||
|  |   }, | ||||||
|  |  | ||||||
|  |   { | ||||||
|  |     name: "Numeric State", | ||||||
|  |     triggers: [ | ||||||
|  |       { platform: "numeric_state", ...HaNumericStateTrigger.defaultConfig }, | ||||||
|  |     ], | ||||||
|  |   }, | ||||||
|  |  | ||||||
|  |   { | ||||||
|  |     name: "Sun", | ||||||
|  |     triggers: [{ platform: "sun", ...HaSunTrigger.defaultConfig }], | ||||||
|  |   }, | ||||||
|  |  | ||||||
|  |   { | ||||||
|  |     name: "Time Pattern", | ||||||
|  |     triggers: [ | ||||||
|  |       { platform: "time_pattern", ...HaTimePatternTrigger.defaultConfig }, | ||||||
|  |     ], | ||||||
|  |   }, | ||||||
|  |  | ||||||
|  |   { | ||||||
|  |     name: "Webhook", | ||||||
|  |     triggers: [{ platform: "webhook", ...HaWebhookTrigger.defaultConfig }], | ||||||
|  |   }, | ||||||
|  |  | ||||||
|  |   { | ||||||
|  |     name: "Zone", | ||||||
|  |     triggers: [{ platform: "zone", ...HaZoneTrigger.defaultConfig }], | ||||||
|  |   }, | ||||||
|  |  | ||||||
|  |   { | ||||||
|  |     name: "Tag", | ||||||
|  |     triggers: [{ platform: "tag", ...HaTagTrigger.defaultConfig }], | ||||||
|  |   }, | ||||||
|  |  | ||||||
|  |   { | ||||||
|  |     name: "Time", | ||||||
|  |     triggers: [{ platform: "time", ...HaTimeTrigger.defaultConfig }], | ||||||
|  |   }, | ||||||
|  |  | ||||||
|  |   { | ||||||
|  |     name: "Template", | ||||||
|  |     triggers: [{ platform: "template", ...HaTemplateTrigger.defaultConfig }], | ||||||
|  |   }, | ||||||
|  |  | ||||||
|  |   { | ||||||
|  |     name: "Event", | ||||||
|  |     triggers: [{ platform: "event", ...HaEventTrigger.defaultConfig }], | ||||||
|  |   }, | ||||||
|  |  | ||||||
|  |   { | ||||||
|  |     name: "Device Trigger", | ||||||
|  |     triggers: [{ platform: "device", ...HaDeviceTrigger.defaultConfig }], | ||||||
|  |   }, | ||||||
|  | ]; | ||||||
|  |  | ||||||
|  | @customElement("demo-automation-editor-trigger") | ||||||
|  | class DemoHaAutomationEditorTrigger extends LitElement { | ||||||
|  |   @state() private hass!: HomeAssistant; | ||||||
|  |  | ||||||
|  |   private data: any = SCHEMAS.map((info) => info.triggers); | ||||||
|  |  | ||||||
|  |   constructor() { | ||||||
|  |     super(); | ||||||
|  |     const hass = provideHass(this); | ||||||
|  |     hass.updateTranslations(null, "en"); | ||||||
|  |     hass.updateTranslations("config", "en"); | ||||||
|  |     mockEntityRegistry(hass); | ||||||
|  |     mockDeviceRegistry(hass); | ||||||
|  |     mockAreaRegistry(hass); | ||||||
|  |     mockHassioSupervisor(hass); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   protected render(): TemplateResult { | ||||||
|  |     const valueChanged = (ev) => { | ||||||
|  |       const sampleIdx = ev.target.sampleIdx; | ||||||
|  |       this.data[sampleIdx] = ev.detail.value; | ||||||
|  |       this.requestUpdate(); | ||||||
|  |     }; | ||||||
|  |     return html` | ||||||
|  |       ${SCHEMAS.map( | ||||||
|  |         (info, sampleIdx) => html` | ||||||
|  |           <demo-black-white-row | ||||||
|  |             .title=${info.name} | ||||||
|  |             .value=${this.data[sampleIdx]} | ||||||
|  |           > | ||||||
|  |             ${["light", "dark"].map( | ||||||
|  |               (slot) => | ||||||
|  |                 html` | ||||||
|  |                   <ha-automation-trigger | ||||||
|  |                     slot=${slot} | ||||||
|  |                     .hass=${this.hass} | ||||||
|  |                     .triggers=${this.data[sampleIdx]} | ||||||
|  |                     .sampleIdx=${sampleIdx} | ||||||
|  |                     @value-changed=${valueChanged} | ||||||
|  |                   ></ha-automation-trigger> | ||||||
|  |                 ` | ||||||
|  |             )} | ||||||
|  |           </demo-black-white-row> | ||||||
|  |         ` | ||||||
|  |       )} | ||||||
|  |     `; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | declare global { | ||||||
|  |   interface HTMLElementTagNameMap { | ||||||
|  |     "demo-ha-automation-editor-trigger": DemoHaAutomationEditorTrigger; | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										3
									
								
								gallery/src/pages/automation/selectors.markdown
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								gallery/src/pages/automation/selectors.markdown
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | --- | ||||||
|  | title: Selectors | ||||||
|  | --- | ||||||
							
								
								
									
										102
									
								
								gallery/src/pages/automation/selectors.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								gallery/src/pages/automation/selectors.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,102 @@ | |||||||
|  | /* eslint-disable lit/no-template-arrow */ | ||||||
|  | import { LitElement, TemplateResult, html } from "lit"; | ||||||
|  | import { customElement, state } from "lit/decorators"; | ||||||
|  | import { provideHass } from "../../../../src/fake_data/provide_hass"; | ||||||
|  | import type { HomeAssistant } from "../../../../src/types"; | ||||||
|  | import "../../components/demo-black-white-row"; | ||||||
|  | import { mockEntityRegistry } from "../../../../demo/src/stubs/entity_registry"; | ||||||
|  | import { mockDeviceRegistry } from "../../../../demo/src/stubs/device_registry"; | ||||||
|  | import { mockAreaRegistry } from "../../../../demo/src/stubs/area_registry"; | ||||||
|  | import { mockHassioSupervisor } from "../../../../demo/src/stubs/hassio_supervisor"; | ||||||
|  | import "../../../../src/panels/config/automation/trigger/ha-automation-trigger"; | ||||||
|  | import { Selector } from "../../../../src/data/selector"; | ||||||
|  | import "../../../../src/components/ha-selector/ha-selector"; | ||||||
|  |  | ||||||
|  | const SCHEMAS: { name: string; selector: Selector }[] = [ | ||||||
|  |   { name: "Addon", selector: { addon: {} } }, | ||||||
|  |  | ||||||
|  |   { name: "Entity", selector: { entity: {} } }, | ||||||
|  |   { name: "Device", selector: { device: {} } }, | ||||||
|  |   { name: "Area", selector: { area: {} } }, | ||||||
|  |   { name: "Target", selector: { target: {} } }, | ||||||
|  |   { | ||||||
|  |     name: "Number", | ||||||
|  |     selector: { | ||||||
|  |       number: { | ||||||
|  |         min: 0, | ||||||
|  |         max: 10, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   { name: "Boolean", selector: { boolean: {} } }, | ||||||
|  |   { name: "Time", selector: { time: {} } }, | ||||||
|  |   { name: "Action", selector: { action: {} } }, | ||||||
|  |   { name: "Text", selector: { text: { multiline: false } } }, | ||||||
|  |   { name: "Text Multiline", selector: { text: { multiline: true } } }, | ||||||
|  |   { name: "Object", selector: { object: {} } }, | ||||||
|  |   { | ||||||
|  |     name: "Select", | ||||||
|  |     selector: { | ||||||
|  |       select: { | ||||||
|  |         options: ["Everyone Home", "Some Home", "All gone"], | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | ]; | ||||||
|  |  | ||||||
|  | @customElement("demo-automation-selectors") | ||||||
|  | class DemoHaSelector extends LitElement { | ||||||
|  |   @state() private hass!: HomeAssistant; | ||||||
|  |  | ||||||
|  |   private data: any = SCHEMAS.map(() => undefined); | ||||||
|  |  | ||||||
|  |   constructor() { | ||||||
|  |     super(); | ||||||
|  |     const hass = provideHass(this); | ||||||
|  |     hass.updateTranslations(null, "en"); | ||||||
|  |     hass.updateTranslations("config", "en"); | ||||||
|  |     mockEntityRegistry(hass); | ||||||
|  |     mockDeviceRegistry(hass); | ||||||
|  |     mockAreaRegistry(hass); | ||||||
|  |     mockHassioSupervisor(hass); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   protected render(): TemplateResult { | ||||||
|  |     const valueChanged = (ev) => { | ||||||
|  |       const sampleIdx = ev.target.sampleIdx; | ||||||
|  |       this.data[sampleIdx] = ev.detail.value; | ||||||
|  |       this.requestUpdate(); | ||||||
|  |     }; | ||||||
|  |     return html` | ||||||
|  |       ${SCHEMAS.map( | ||||||
|  |         (info, sampleIdx) => html` | ||||||
|  |           <demo-black-white-row | ||||||
|  |             .title=${info.name} | ||||||
|  |             .value=${{ selector: info.selector, data: this.data[sampleIdx] }} | ||||||
|  |           > | ||||||
|  |             ${["light", "dark"].map( | ||||||
|  |               (slot) => | ||||||
|  |                 html` | ||||||
|  |                   <ha-selector | ||||||
|  |                     slot=${slot} | ||||||
|  |                     .hass=${this.hass} | ||||||
|  |                     .selector=${info.selector} | ||||||
|  |                     .label=${info.name} | ||||||
|  |                     .value=${this.data[sampleIdx]} | ||||||
|  |                     .sampleIdx=${sampleIdx} | ||||||
|  |                     @value-changed=${valueChanged} | ||||||
|  |                   ></ha-selector> | ||||||
|  |                 ` | ||||||
|  |             )} | ||||||
|  |           </demo-black-white-row> | ||||||
|  |         ` | ||||||
|  |       )} | ||||||
|  |     `; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | declare global { | ||||||
|  |   interface HTMLElementTagNameMap { | ||||||
|  |     "demo-automation-selectors": DemoHaSelector; | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										3
									
								
								gallery/src/pages/automation/trace-timeline.markdown
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								gallery/src/pages/automation/trace-timeline.markdown
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | --- | ||||||
|  | title: Trace Timelines | ||||||
|  | --- | ||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user