Compare commits

...

310 Commits

Author SHA1 Message Date
Thomas Lovén
3459d0bb8c Fix accidentally removed tag 2022-01-26 08:03:07 +00:00
Thomas Lovén
8a9a93ef20 Hide entity pickers when in entity-filter 2022-01-25 23:43:08 +00:00
Thomas Lovén
94b561301f Lint and work with nesting 2022-01-25 21:25:16 +00:00
Thomas Lovén
86f5fe51c4 Fix #3703 again 2022-01-25 20:53:29 +00:00
Yosi Levy
c4cad5bccd Missing translation, code editor make ltr, checkbox alignment in rtl (#11419) 2022-01-25 08:46:33 -06:00
Steve Repsher
e4085fe1f6 Allow tab to show/hide password button for keyboard accessibility (#11416) 2022-01-24 21:26:44 -06:00
Paulus Schoutsen
8bfef92c86 Bumped version to 20220124.0 2022-01-24 15:52:45 -08:00
Patrick ZAJDA
0c07178c0a Add em dash "—" instead of a blank value in devices and entities tables to improve accessibility (#11078) 2022-01-24 20:18:23 +01:00
Michael Gorven
1010777139 Add weekday to formatTimeWeekday() (#11020) 2022-01-24 19:25:45 +01:00
Philip Allgaier
e57477c16a Use consistent font size for quick bar "Nothing found" note (#11418) 2022-01-24 18:14:11 +00:00
Radu Cotescu
30fa92c120 fix #11041: The gauge card doesn't render correctly on iOS 15.2 / macOS 12.1 in the companion apps (#11363)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
Co-authored-by: Radu Cotescu <radu-likes-to-code@cotescu.com>
2022-01-24 17:58:02 +00:00
Zack Barett
b32438dc18 Remove padding (#11417) 2022-01-24 17:54:00 +00:00
Zack Barett
614bd2f451 Fix Quickbar for Safari - Change to MWC Textfield (#11414) 2022-01-24 09:14:29 -08:00
Paulus Schoutsen
6c12a5a4b1 Allow an external sidebar (#11347) 2022-01-24 09:08:35 -08:00
Paulus Schoutsen
bbcec38450 Play audio in the bottom bar media player (#11413)
Co-authored-by: Zack <zackbarett@hey.com>
2022-01-24 17:07:47 +00:00
Paulus Schoutsen
416e2e26c0 Add check for updates in config menu (#11415)
Co-authored-by: Philip Allgaier <mail@spacegaier.de>
Co-authored-by: Zack Barett <arnett.zackary@gmail.com>
2022-01-24 16:48:07 +00:00
Zack Barett
1a7164b466 Fix Logbook Icons, Card Editor Close/Cancel buttons, View Editor Dirty (#11153) 2022-01-24 17:36:49 +01:00
Philip Allgaier
3ddcd2d0f6 Ensure forecast temperatures are properly positioned + show em-dash when n/a (#9066)
Co-authored-by: Zack Barett <zackbarett@hey.com>
2022-01-24 10:10:44 -06:00
Bram Kragten
648c02e622 Don't sync dev tools service data between tabs (#10980) 2022-01-24 08:43:28 -06:00
Erik Montnemery
b0b953bfac Set mandatory bool service data without a default value to false (#11094) 2022-01-24 10:27:38 +01:00
Zack Barett
abeaa63005 Lovelace Menu Edits (#11323) 2022-01-24 10:14:32 +01:00
Paulus Schoutsen
9cd23374f4 Hide actions footer if no action links (#11388) 2022-01-24 10:06:29 +01:00
Philip Allgaier
72bd5f84d6 Use en "–" and em "—" dashes consistently (#11401) 2022-01-24 10:02:44 +01:00
Paulus Schoutsen
22b4550fdf Rename media browser to media (#11412) 2022-01-24 02:13:47 +01:00
Paulus Schoutsen
87c22229e0 Add demo for selectors (#11398)
* Add demo for selectors

* Update label name
2022-01-22 14:44:50 -08:00
Philip Allgaier
971fd8dc60 Adjust padding for "No items" in media browser (#11397) 2022-01-22 02:51:03 +01:00
Philip Allgaier
049c3caadd Remove "authSig" from media player source (#11394) 2022-01-22 02:27:40 +01:00
Philip Allgaier
fb2a24d11e Remove capitalization from media player state card media title (#11396) 2022-01-21 19:01:15 -06:00
Paulus Schoutsen
d4646bac01 Restore energy config in quickbar (#11391) 2022-01-21 17:06:42 -06:00
Paulus Schoutsen
14e5b2a7a5 Single device links to device page (#11387) 2022-01-21 14:10:01 -08:00
Zack Barett
734a733a4c Add Search Icon to Config Dashboard (#11375)
Co-authored-by: Philip Allgaier <mail@spacegaier.de>
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2022-01-21 14:06:25 -08:00
Philip Allgaier
8f31c182f6 Remove SVG icons from accessibility tree (#11389) 2022-01-21 15:56:36 -06:00
Zack Barett
e51a819bfd Move energy to Dashboards (#11386) 2022-01-21 13:30:24 -08:00
Zack Barett
05d7e85aa3 Remove Show button on Update. Make row clickable (#11385) 2022-01-21 13:28:05 -08:00
Paulus Schoutsen
cf527e4bc2 Hide download diagnostics if config entry is not loaded (#11383) 2022-01-21 09:41:02 -08:00
Matthias de Baat
197b581e8e Adding results of the Configuration menu user test (#11381)
* Adding results of the Configuration menu user test

* Update title

* Add sidebar entry

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2022-01-21 08:39:17 -08:00
Paulus Schoutsen
f75bf1f676 Check for updates in add-on store (#11382) 2022-01-21 08:16:45 -08:00
Allen Porter
28df79cfda Enable/Disable LL-HLS support based on http/2 availability (#11372)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
Co-authored-by: Philip Allgaier <philip.allgaier@gmx.de>
2022-01-21 08:06:00 -08:00
Zack Barett
3bf19883a8 Fix Date Time Helper (#11367) 2022-01-21 08:36:39 -06:00
Zack Barett
303e065433 Media Browser Bar (#11369)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2022-01-20 14:37:30 -08:00
Joakim Sørensen
7ad0b37a9e Use backend logic for partial backup while updating (#11364) 2022-01-20 10:12:52 -08:00
Joakim Sørensen
930c7e4afa Add backup size to backup table (#11365) 2022-01-20 10:08:53 -06:00
Paulus Schoutsen
81faae6f74 Allow downloading device diagnostics (#11370) 2022-01-19 20:48:24 -08:00
Philip Allgaier
f7fc83ac12 Add space between number input field and unit (#11366) 2022-01-19 12:46:38 -08:00
Paulus Schoutsen
21a099ee9f Clean up users table (#11333)
* Clean up users table

* Add decicated icon for data tables

* Change tooltip and icons

* Only use icons for narrow view

* Shorten headers

* Add chips to the user detail dialog

* Lint

* Hide system badge on mobile
2022-01-19 12:28:13 -08:00
Paulus Schoutsen
7d1ce1b240 Allow creating automation from TTS try dialog (#11161) 2022-01-19 11:32:24 -06:00
Matthias de Baat
d1f1309198 Added examples and corrected some text (#11156) 2022-01-19 09:02:22 -08:00
Philip Allgaier
68dd818f7a Translate "No integration" in device dashboard table (#11362) 2022-01-19 08:57:46 -08:00
Paulus Schoutsen
50bea33a19 Localize config flow title (#11358) 2022-01-18 14:18:22 -08:00
Paulus Schoutsen
27cae037ce Bumped version to 20220118.0 2022-01-18 14:10:29 -08:00
Paulus Schoutsen
dbb5bf7550 Use system log WS API (#11361) 2022-01-18 14:10:18 -08:00
Philip Allgaier
9ef743a695 Call correct "button.press" service for input_button entity toggle action (#11350) 2022-01-18 09:05:19 +01:00
Paulus Schoutsen
f3642a1677 Use download util for diagnostics (#11349) 2022-01-18 08:52:29 +01:00
Raman Gupta
2d651c2a66 Remove action to download state dump from zwave_js (#11348) 2022-01-17 21:53:41 -08:00
Philip Allgaier
ef39317019 Call correct "button.press" service for button entity toggle action (#11346) 2022-01-17 21:26:56 -08:00
Paulus Schoutsen
441f1fbcb5 Allow downloading diagnostics of a config entry (#11345)
Co-authored-by: Zack Barett <arnett.zackary@gmail.com>
2022-01-18 02:25:52 +00:00
Paulus Schoutsen
09a27a6791 Reflect media browser panel state in URL (#11317) 2022-01-17 09:40:51 -06:00
Philip Allgaier
32bbdc194a Ensure disabled device entity names are shown (#11334) 2022-01-16 23:21:50 -06:00
Philip Allgaier
52588a3915 Ensure numeric attributes are formatted correctly (#11336) 2022-01-16 23:21:04 -06:00
Paulus Schoutsen
9fffc93e5d Show error message if login is blocked (#11331) 2022-01-16 23:20:23 -06:00
Philip Allgaier
effec839af Translate assigned-to / targeting-this area info (#11337) 2022-01-16 22:52:35 -06:00
Matthias de Baat
884ed561a1 Update Home text (#11319)
Co-authored-by: Zack Barett <zackbarett@hey.com>
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2022-01-14 23:46:31 +00:00
Zack Barett
4165e64ce0 Area card gallery updates (#11324) 2022-01-14 15:27:54 -08:00
Kuba Wolanin
6053b64b2e DevTools: Navigate to History from last changed/last updated (#11133) 2022-01-14 10:01:39 -06:00
Allen Porter
ed462dc257 Add authenticated thumbnails to Media Player and lazy fetch thumbnails (#10978)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2022-01-13 22:31:10 -08:00
Paulus Schoutsen
74a05929be Allow pressing enter to pick item at the top of the list (#11139) 2022-01-12 17:52:29 -08:00
Paulus Schoutsen
5e388b1f02 Add entity ID being browsed to URL of media browser (#11164) 2022-01-12 10:22:04 -08:00
Bram Kragten
ff2fa9a78c Add period option to statistics card (#10982)
Co-authored-by: Zack Barett <zackbarett@hey.com>
2022-01-12 10:35:10 -06:00
Marius
6d1be9e73f fix domain icons for switch (#11167) 2022-01-12 09:29:14 -06:00
Franck Nijhof
ba570f4004 Add overflow menu to discovered integration item (#11165)
Co-authored-by: Zack Barett <arnett.zackary@gmail.com>
2022-01-12 15:02:08 +00:00
Marius
6ab497edf8 use motion-sensor/-off (#11142) 2022-01-12 08:52:43 -06:00
Paulus Schoutsen
8c1cd273df Update translation of edit config button (#11163) 2022-01-12 09:56:47 +01:00
David F. Mulcahey
8f68bcbba9 Add checkbox to enable / disable physics for the ZHA network visualization page (#11017) 2022-01-11 19:01:50 -06:00
Marius
8291cf9daa Make input_boolean icon state dependent (#10526)
Co-authored-by: Zack Barett <arnett.zackary@gmail.com>
2022-01-11 18:56:59 -06:00
Paulus Schoutsen
bb40e66833 Adjust fossil energy consumption in demo to avoid negative numbers (#11160) 2022-01-11 18:55:32 -06:00
Franck Nijhof
f852208eff Add support for input_button / Button Helper (#10974)
Co-authored-by: Zack Barett <arnett.zackary@gmail.com>
2022-01-11 12:24:59 -06:00
zmarties
3bbe1603eb Remove max-width constraint on areas container (#10877) 2022-01-11 17:39:53 +00:00
Franck Nijhof
25d60e11da Add known issues link to integrations card (#10994)
Co-authored-by: Zack Barett <arnett.zackary@gmail.com>
2022-01-11 11:32:22 -06:00
Patrick ZAJDA
78d06426cf Add ARIA role heading for each integration to improve screen reader experience (#11049) 2022-01-11 11:25:04 -06:00
epenet
320b2bb48b Add CO to binary_sensor platform (#10935) 2022-01-11 10:56:42 -06:00
Erik Montnemery
a7b8382617 Add sensor device classes for apparent and reactive power (#10942) 2022-01-11 10:53:24 -06:00
Paulus Schoutsen
77fe687ec2 Re-order automation menu (#11154) 2022-01-11 08:50:00 -06:00
Bram Kragten
4b9ff641ba Wait with navigate until history.back is done (#11152)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2022-01-10 15:30:21 -08:00
Paulus Schoutsen
1520b5832a Improve integration search (#11140) 2022-01-10 17:18:40 -06:00
Paulus Schoutsen
04f2e2e70c Move trigger ID to overflow menu (#11136) 2022-01-10 14:54:23 -08:00
Paulus Schoutsen
920d2972ea Add helper entity rows to design page (#11150) 2022-01-10 14:54:06 -08:00
Paulus Schoutsen
e94fc493b8 Fix timer removing the default value of 00:00:00 if name or icon set (#11147) 2022-01-10 15:46:04 -06:00
Paulus Schoutsen
3e22270c2c Fix scenes activate button being disabled (#11145) 2022-01-10 15:45:41 -06:00
Matthias de Baat
27fa34e24e Add guidelines and implementation (#11144)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2022-01-10 10:29:52 -08:00
Paulus Schoutsen
4e0cebaf32 Handle device without name (#11138) 2022-01-10 09:14:12 -06:00
Paulus Schoutsen
f021480bc5 Fix design nightly build (#11134) 2022-01-10 09:24:29 +01:00
Paulus Schoutsen
34c3374d84 Small UI tweaks to design website (#11135) 2022-01-09 21:20:15 -08:00
Paulus Schoutsen
4cb7154917 Hide local webhooks (#11123)
Co-authored-by: Joakim Sørensen <joasoe@gmail.com>
2022-01-09 11:40:36 +00:00
Paulus Schoutsen
08863348dc Update integration-card.ts 2022-01-07 17:24:56 -08:00
Paulus Schoutsen
2bcf816b77 Update util-long-press.ts 2022-01-07 17:24:18 -08:00
Paulus Schoutsen
d2b99e6963 Add description pages to all pages (#11124) 2022-01-07 15:52:07 -06:00
Philip Allgaier
48a800882e Add reload_themes and reload_core_config to quick bar (#11072) 2022-01-07 08:47:57 -08:00
Philip Allgaier
595e13ecac Fix HLS player cleanup (null access to removeAttribute) (#11074) 2022-01-07 08:44:57 -08:00
Erik Montnemery
5261d583a8 Only interpolate sensor line charts (#11082) 2022-01-07 08:44:09 -08:00
Joakim Sørensen
5c488f8298 Limit design site builds (#11120) 2022-01-07 06:52:08 -08:00
Paulus Schoutsen
6fc87a6f66 Fix design footer 2022-01-06 23:37:50 -08:00
Paulus Schoutsen
3133f9b01f Use page instead of demo (#11118)
* Use page instead of demo

* Update netlify script

* Update ci.yml

* Rename demo -> page
2022-01-06 22:32:10 -08:00
Paulus Schoutsen
2c0d330f1f Reorganize gallery (#11116)
* Reorganize gallery

* GitHub edit links

* Render sidebar during build

* Auto rebuild when sidebar changes

* Yarn dedupe

* Fixes

* Allow just metadata without text

* Show edit text link if metadata defined

* Update build-scripts/gulp/gallery.js

Co-authored-by: Zack Barett <arnett.zackary@gmail.com>

Co-authored-by: Zack Barett <arnett.zackary@gmail.com>
2022-01-06 20:20:57 -08:00
Franck Nijhof
fb9ea981ed Ignore button in scenes (#11109) 2022-01-06 14:52:08 +01:00
Paulus Schoutsen
63c113f78d Gallery: Make sidebar collapsible + more tweaks (#11104) 2022-01-06 10:21:17 +01:00
Paulus Schoutsen
a67799a670 Allow markdown readme above the gallery pages (#11103) 2022-01-05 23:08:21 +01:00
Paulus Schoutsen
e3d78d6dc5 Convert ha-gallery to TS (#11102)
* Convert ha-gallery to TS

* Prepare gallery to be design portal

* Import card in introduction

* Clean up demo IDs

* Convert demo-cards

* TypeScript conversion of demo-card

* Fix default demo
2022-01-05 12:49:05 -08:00
OzGav
76a4b1efbd Update "electricitymap.com" app URL in hui-energy-distribution-card (#11040) 2022-01-02 17:35:43 +01:00
Marc Randolph
882e79524b Spelling fix ('a NFC tag' to 'an NFC tag') (#11073) 2022-01-02 16:32:12 +00:00
Bram Kragten
86b9eb0bd7 Bumped version to 20211229.0 2021-12-29 10:37:39 +01:00
Paulus Schoutsen
011cbe7d22 Bump HAWS to 6.0.1 (#11042) 2021-12-28 22:27:58 -08:00
Bram Kragten
be72bf7b3c Bumped version to 20211227.0 2021-12-27 20:45:56 +01:00
Paulus Schoutsen
3e062ba673 Show error screen when connection fails (#11030) 2021-12-27 11:43:38 +01:00
Paulus Schoutsen
322d965539 Bump HAWS to 6 (#11031) 2021-12-27 11:38:59 +01:00
Bram Kragten
7b840527b5 Fix gauge on ios 15.2 (#10992) 2021-12-26 21:56:24 -08:00
Marius
dced053ba2 Take out 'Stop' (#11024) 2021-12-26 21:55:17 -08:00
Joakim Sørensen
fe4322e64b Remove stop button for all installation types (#10987) 2021-12-21 14:28:51 +01:00
Bram Kragten
b6d6e2fd4b Fix demo loadFragmentTranslation 2021-12-20 13:42:52 +01:00
Bram Kragten
2bbb1bfa7e Bumped version to 20211220.0 2021-12-20 13:36:32 +01:00
Bram Kragten
e2af8ac3cc Fix theme updating entities card (#10979) 2021-12-20 12:14:54 +01:00
Philip Allgaier
25ff5fef14 More granular control for row interaction catching and cursor (#10971) 2021-12-20 10:27:43 +01:00
Philip Allgaier
2f9c088091 Make "Timers" plural on server control reload page (#10972) 2021-12-20 10:26:35 +01:00
Philip Allgaier
50c397901b Show correct cursor if tap_action is set to "none" (#10963) 2021-12-20 10:26:02 +01:00
Philip Allgaier
1f7d4c25d4 Add word wrap to device model and manufacturer (#10941) 2021-12-20 10:24:44 +01:00
Philip Allgaier
29819fac23 Ensure button and automation row inputs are clickable on mobile (#10940) 2021-12-20 10:24:07 +01:00
Paulus Schoutsen
cc301df57d Force reconnect when triggered from external bus (#10938) 2021-12-20 09:59:45 +01:00
Joakim Sørensen
7d5b566312 Disable repository removal if used for installed add-ons (#10922) 2021-12-17 22:21:46 -08:00
Joakim Sørensen
07cd68f5d0 Always send homeassistant for partial restore (#10927) 2021-12-16 13:57:08 -08:00
Joakim Sørensen
99bf6fa781 Handle update-complete event on add-on page (#10929) 2021-12-16 12:39:21 -08:00
Joakim Sørensen
bfad1eb5ac Add bottom margin to update card (#10948) 2021-12-16 12:33:46 -08:00
J. Nick Koston
6f9b2ee569 Add hardware version to the device info card (#10914) 2021-12-16 05:16:23 -06:00
Bram Kragten
4ebdca2a46 Bumped version to 20211215.0 2021-12-15 13:36:34 +01:00
Philip Allgaier
fc700fdaf0 Outline new collapsable area in state dev tools + auto-expand (#10917) 2021-12-15 13:15:50 +01:00
Philip Allgaier
d8e12f4280 Add tooltips and aria-labels to media player buttons (#10881) 2021-12-13 16:33:34 -08:00
krazos
86114758c3 Add group to input row domains to fix mobile focus issue (#10897) 2021-12-13 16:30:21 -08:00
Joakim Sørensen
792278cf17 Hide stop for hassio (#10905)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2021-12-13 16:29:01 -08:00
Joakim Sørensen
b8832f2121 Change entrypoint for Settings (#10904) 2021-12-13 16:08:19 -08:00
Joakim Sørensen
76339c90f7 Show app configuration in sidebar for non-admin users (#10890) 2021-12-13 16:06:46 -08:00
Bram Kragten
b3d4451035 Not valid config, but we support it in the editor (#10893) 2021-12-13 11:01:41 -08:00
Joakim Sørensen
dc58481918 Fix overriding username suggestion (#10899) 2021-12-13 18:56:44 +01:00
Philip Allgaier
14af735507 Fix tooltip and aria-label for password input field (#10898)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-12-13 16:32:45 +00:00
Joakim Sørensen
a7b558b64a Add no update available message (#10891) 2021-12-13 17:20:38 +01:00
Joakim Sørensen
b7665bef6f Don't backup core for supervisor/os updates (#10886) 2021-12-13 10:53:02 +01:00
Christopher Toth
5ec37a35f1 Fix all instances where HTML ARIA-ROLE should actually just be role (#10888) 2021-12-13 08:35:46 +00:00
Philip Allgaier
91bb2ddcc4 Make energy graph colors brighter in dark mode (#10789) 2021-12-12 14:10:30 +01:00
Bram Kragten
85168b3a35 Bumped version to 20211212.0 2021-12-12 13:37:28 +01:00
Bram Kragten
942150cda2 Remove milliseconds from state trigger when 0 (#10879) 2021-12-12 12:27:14 +00:00
Philip Allgaier
2606d55895 Add tooltips and aria-labels to climate modes (#10875) 2021-12-12 13:25:05 +01:00
Philip Allgaier
1f671198aa Fix tooltip and aria-label for ZWave JS log download (#10876) 2021-12-12 13:24:24 +01:00
Bram Kragten
deb65e7108 Fix button with images (#10872) 2021-12-12 13:19:32 +01:00
Philip Allgaier
cd00f7f874 Fix typo in cover close tilt translation key (#10871) 2021-12-11 20:59:42 +01:00
Bram Kragten
2b0359edba Bumped version to 20211211.0 2021-12-11 17:15:38 +01:00
Philip Allgaier
35e9687170 Replace mwc-icon-button with ha-icon-button in automation picker (#10858) 2021-12-11 17:15:16 +01:00
Bram Kragten
b730676914 Fix translations cover controls (#10868) 2021-12-11 17:13:43 +01:00
Bram Kragten
2890192c05 Fix formfield label touch (#10867) 2021-12-11 17:13:24 +01:00
Bram Kragten
bfb84a834f Still have manual input if camera is not supported (#10849)
* Still have manual input if camera is not supported

* Adjust & fix
2021-12-11 17:12:41 +01:00
Philip Allgaier
ca6fd6c770 Prevent quickbar command entry duplicates (#10861) 2021-12-11 17:01:24 +01:00
Joakim Sørensen
585648ac4c Revert "handle ha-radio and ha-checkbox in ha-formfield" (#10863) 2021-12-10 23:30:35 -08:00
Matthias de Baat
bec5c564b6 Update blueprint description (#10854) 2021-12-10 11:18:05 -08:00
Erik Montnemery
48c66e6349 Tweak some energy related translation strings (#10852) 2021-12-10 09:49:53 -08:00
Bram Kragten
cea40610c0 Add base trigger to struct (#10851) 2021-12-10 14:44:40 +01:00
Bram Kragten
0c3fd8f3ad typo login -> log in (#10850) 2021-12-10 14:41:09 +01:00
Paulus Schoutsen
02bdeebc82 Bumped version to 20211209.0 2021-12-09 13:39:03 -08:00
Bram Kragten
60c7669d8f Put set state in expansion panel (#10845) 2021-12-09 13:38:27 -08:00
Bram Kragten
919bf94a03 Only add milliseconds when enabled or if it has a value (#10842) 2021-12-09 13:38:03 -08:00
Bram Kragten
ead5e288eb Use normal card color in narrow config screen too (#10843) 2021-12-09 13:37:45 -08:00
Bram Kragten
add8a702cc Change select camera UI, remove manual QR input (#10844) 2021-12-09 13:37:30 -08:00
Paulus Schoutsen
39774c0e02 Allow trigger reconnect from external bus (#10819) 2021-12-09 13:30:20 -08:00
Joakim Sørensen
149f381bc3 Make dashboard entries translatable (#10831) 2021-12-09 09:59:27 -08:00
Bram Kragten
faccb12430 Fix keep me logged in (#10835) 2021-12-09 09:57:11 -08:00
Paulus Schoutsen
7039bae9be Disable local only option for system generated users (#10827) 2021-12-09 11:22:32 +01:00
Bram Kragten
0a7b703d57 Clear warnings when yaml changes (#10820) 2021-12-08 09:12:09 +01:00
Joakim Sørensen
24e8028e8f Use _version_latest for change log URL (#10821) 2021-12-07 23:37:06 +01:00
Paulus Schoutsen
8412cd71cb Bumped version to 20211206.0 2021-12-06 15:11:11 -08:00
Joakim Sørensen
5c78b74005 Reorder configuration (#10817) 2021-12-06 15:10:50 -08:00
Bram Kragten
2459477ec4 Add struct for state trigger and condition (#10811)
* Add struct for state trigger and condition

* remove `milliseconds` from struct
2021-12-06 20:13:32 +01:00
Raman Gupta
a065740c91 zwave_js config param should only be a toggle if there are 2 states (#10812) 2021-12-06 10:49:23 -08:00
Bram Kragten
f3104d3c93 Fix zwavejs provisioned view (#10809) 2021-12-06 10:47:35 -08:00
Paulus Schoutsen
1916c179b4 Merge pull request #10816 from spacegaier/issue-10751 2021-12-06 10:43:33 -08:00
Philip Allgaier
e8b9766eb6 Fix camera stream rendering in area card without picture (#10815) 2021-12-06 10:39:09 -08:00
Philip Allgaier
ff7a2c8cb7 Fix clearing of picture (e.g. area and person config) 2021-12-06 19:29:51 +01:00
Bram Kragten
7ccde2cb41 Fix disabled date input (#10813) 2021-12-06 17:25:23 +00:00
Bram Kragten
d6b9b16f02 Add toggle for camera view in area card (#10810)
Co-authored-by: Zack Barett <arnett.zackary@gmail.com>
2021-12-06 18:02:32 +01:00
Joakim Sørensen
66df15007a Fetch cloud and updates on reconnect (#10808) 2021-12-06 12:54:16 +01:00
Philip Allgaier
f164d21c44 Filter out invalid text input for input_text (#10797) 2021-12-06 12:53:45 +01:00
Philip Allgaier
911d322aac Mark more trigger fields as optional (#10798) 2021-12-06 12:52:38 +01:00
Joakim Sørensen
419879ee7a Fix core changelog URL (#10804) 2021-12-06 12:51:56 +01:00
Joakim Sørensen
c3e1a2edf0 Use ha-logo-svg on info page (#10807) 2021-12-06 12:51:21 +01:00
Philip Allgaier
8f5751d5bb Use correct label in area card editor (#10799) 2021-12-05 12:09:06 -06:00
Philip Allgaier
4095450476 Add switch to input row domains to fix mobile focus issue (#10792) 2021-12-04 11:43:14 +01:00
Bram Kragten
e61f587c51 Bumped version to 20211203.0 2021-12-03 18:07:07 +01:00
Bram Kragten
d43d19190e Fix entity marker (#10787) 2021-12-03 17:04:50 +00:00
Bram Kragten
a283acaabf safari doesnt support overflow-wrap: anywhere 2021-12-03 18:04:03 +01:00
Philip Allgaier
ea18fc0078 Ensure we always have an active theme name (fixes dark theme issues) (#10780)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-12-03 17:02:54 +00:00
Bram Kragten
1df11e9bf1 Use groupBy (#10786) 2021-12-03 08:42:23 -08:00
Bram Kragten
c71b2e6b9d Add provisioned device overview to zwave js (#10785) 2021-12-03 08:34:26 -08:00
Bram Kragten
db4aa05bf4 Differentiate between assigned and targeting scene/automations/script (#10781) 2021-12-03 08:21:26 -08:00
Bram Kragten
a54a2a54f8 Add support for local only users (#10784)
Co-authored-by: Joakim Sørensen <joasoe@gmail.com>
2021-12-03 16:34:34 +01:00
Philip Allgaier
0bcb4d0e09 Restore flex alignment for select and input-select rows (#10783) 2021-12-03 16:19:00 +01:00
Bram Kragten
95dbc811d3 Allow overriding device class (#10777) 2021-12-03 16:07:49 +01:00
Philip Allgaier
e28a11964e Use correct styling for cloud certificate dialog (#10782) 2021-12-03 15:08:49 +01:00
Bram Kragten
46a9e36516 Guard for non numeric states (#10775)
Co-authored-by: Joakim Sørensen <joasoe@gmail.com>
2021-12-03 12:53:50 +01:00
Paulus Schoutsen
e99f20c4f3 Tweak ZJS dashboard (#10772) 2021-12-03 10:51:50 +01:00
Joakim Sørensen
2100603cdc Remove handling of the supervisor panel from the sidebar (#10773) 2021-12-03 10:49:42 +01:00
Joakim Sørensen
da4942aca3 Add default icons for button entities (#10774) 2021-12-03 09:11:29 +01:00
Paulus Schoutsen
7c78fb314e Show add devices fab on devices page for ZJS (#10771) 2021-12-03 08:42:39 +01:00
Paulus Schoutsen
5bc2468cbc Bumped version to 20211202.0 2021-12-02 14:31:09 -08:00
Bram Kragten
a580904c52 Use chips for button rows (#10770) 2021-12-02 23:29:52 +01:00
Bram Kragten
48d12ceafe Group entities in area card by domain (#10767)
* Group entities in area card by domain

* Update hui-area-card.ts

* Update

* Add background color when no image

* Add camera support

* exclude unavailable states

* Update hui-area-card.ts
2021-12-02 23:15:18 +01:00
Carlos Garcia Saura
60ce805b3b Update hui-graph-header-footer.ts (#10476) 2021-12-02 13:32:38 -08:00
Paulus Schoutsen
251416b51d Add missing translation (#10769) 2021-12-02 13:01:19 -08:00
Bram Kragten
c41c6eedd8 Remove thingtalk cleanup create new automation dialog (#10748)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2021-12-02 11:26:41 -08:00
Joakim Sørensen
6877fd9e00 Hide updates for dev as well (#10761) 2021-12-02 17:32:18 +01:00
Joakim Sørensen
4cc104a99f Use add-ons for mobile header (#10760) 2021-12-02 17:31:41 +01:00
Joakim Sørensen
6494177821 Fix SU sidebar issues (#10757) 2021-12-02 17:31:09 +01:00
Joakim Sørensen
cea1a62867 handle ha-radio and ha-checkbox in ha-formfield (#10759) 2021-12-02 17:30:10 +01:00
rianadon
a6b5262d02 Use unit system definitions for weather units (#10657) 2021-12-02 17:27:23 +01:00
Joakim Sørensen
2a5fc5181e Fix create backup checkbox (#10756) 2021-12-02 11:54:05 +01:00
Joakim Sørensen
2fe8f5ff27 Use puzzle for addons and blur entries on click (#10755) 2021-12-02 11:05:14 +01:00
Philip Allgaier
0c75d5afc9 Make graph colors themable (#10698) 2021-12-02 10:49:46 +01:00
Philip Allgaier
cf062bf0f4 Fix pointer/more-info inconsistencies for entity rows (#10025)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-12-02 10:48:30 +01:00
Paulus Schoutsen
acf4d59fde Bumped version to 20211201.0 2021-12-01 14:47:17 -08:00
Paulus Schoutsen
05333ac2d9 Show disabled entity names on the device page (#10743)
* Show disabled entity names on the device page

* Update src/panels/config/devices/device-detail/ha-device-entities-card.ts

Co-authored-by: Bram Kragten <mail@bramkragten.nl>

Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-12-01 14:46:40 -08:00
Bram Kragten
4b49da58b1 Add SmartStart/QR scan support for Z-Wave JS (#10726) 2021-12-01 14:12:52 -08:00
Joakim Sørensen
68373e6372 Focus Add-ons & Backups in config panel when clicking Supervisor in sidebar (#10745)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-12-01 08:46:38 -08:00
Matthias de Baat
01049e8eb8 Updated text (#10747)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-12-01 14:10:32 +00:00
Joakim Sørensen
87f7981144 Fix faded element in change log (#10737) 2021-12-01 13:55:01 +01:00
Paulus Schoutsen
ceac9834b9 Change the area of scenes in editor (#10731) 2021-12-01 13:54:28 +01:00
Joakim Sørensen
ac8f748656 Hide ha-icon-next if narrow (#10746) 2021-12-01 09:23:13 +01:00
Joakim Sørensen
1d97d8dca9 Handle 0 updates and show back on supervisor panels (#10744) 2021-11-30 23:30:38 -08:00
Bram Kragten
fd6785b593 Use backend for day month stats in energy dashboard (#10728) 2021-11-30 09:22:06 -08:00
Joakim Sørensen
d5fc751da6 Revert 10711 (#10736) 2021-11-30 18:02:02 +01:00
Joakim Sørensen
933fd72629 Fix typo (#10734) 2021-11-30 11:41:59 -05:00
Joakim Sørensen
0611133065 Move companion app config from sidebar to configuration dashboard (#10733)
* Move companion app config from sidebar to configuration dashboard

* Remove translation refrence
2021-11-30 08:03:10 -08:00
Allen Porter
02644b923f Improve hls stream view error handling (#10714)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-11-30 08:48:24 +01:00
Paulus Schoutsen
67f06112c6 Bumped version to 20211130.0 2021-11-29 16:57:58 -08:00
Paulus Schoutsen
49e39644f3 Tweak how scenes behave in generated lovelace (#10730) 2021-11-29 16:56:08 -08:00
Joakim Sørensen
990ad1bb67 Dashboard tweaks (#10729) 2021-11-29 23:56:59 +01:00
Philip Allgaier
dbbf246060 Installation type property during onboarding was misspelled (#10721) 2021-11-29 14:41:21 -08:00
amitfin
d2c20837a5 Fixed invalid hour handling in AMPM mode (#10717)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-11-29 18:49:33 +00:00
Philip Allgaier
e91d1777d0 Ensure conditional rows getting state_color value (#10708)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-11-29 12:30:08 +01:00
Joakim Sørensen
a5be143c3b Fix chip text color variable overrides (#10722) 2021-11-29 11:31:49 +01:00
Philip Allgaier
0ef07e4835 Ensure markdown card input is a string (#10705) 2021-11-29 10:50:08 +01:00
Philip Allgaier
9361e4cf9c Ensure required translations are loaded in safe-mode (#10709) 2021-11-29 10:34:25 +01:00
Luca Cavalli
e7fd75703f Fixed ellipsis usage on graph legend entries. (#10707) 2021-11-29 10:30:27 +01:00
Philip Allgaier
2c0b2f4bc5 Convert cover UI to Lit + ensure proper tilt rendering (#10671) 2021-11-29 10:30:14 +01:00
Philip Allgaier
faec09f0d1 Filter out disabled entities in the statistics dev tools (#10677) 2021-11-29 10:19:33 +01:00
Nathan Orick
b79c06ad71 Default to yaml editing when there are multiple states in condition (#10481) 2021-11-29 10:14:09 +01:00
Philip Allgaier
5614e0d29c Make "Energy distribution today" translatable (#10696) 2021-11-29 10:09:54 +01:00
Philip Allgaier
0b7fc177f9 Prevent errors in more-info-climate if no modes are provided despite support flags (#10694) 2021-11-29 10:03:30 +01:00
Philip Allgaier
367322415e Use ha-icon-button in ha-icon-overflow-menu (#10692) 2021-11-29 09:58:34 +01:00
Joakim Sørensen
117b50f3ea Add ha-faded (#10651) 2021-11-28 22:27:53 -08:00
Philip Allgaier
366aa8aed1 Fix typo on config page + adjust icon color (#10713) 2021-11-28 17:52:39 +01:00
Joakim Sørensen
43011179eb Finish up config changes (#10710)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-11-26 17:24:30 +01:00
Joakim Sørensen
6177d2b416 Use app-header-text-color (#10711) 2021-11-26 17:11:06 +01:00
Joakim Sørensen
f70485bc49 Don't make button disabled on error (#10699) 2021-11-25 16:56:57 +01:00
Erik Montnemery
921763b5f1 Improve device information when via device is unknown (#10685) 2021-11-24 09:09:21 +01:00
Joakim Sørensen
5fd4315789 Fix addon slug (#10693) 2021-11-23 08:53:17 -08:00
Joakim Sørensen
ed291b57d0 Render update card on add-on page (#10681) 2021-11-23 08:18:40 -08:00
Joakim Sørensen
f833701e7c Update background colors of navigation icons (#10691) 2021-11-23 14:36:11 +01:00
Paulus Schoutsen
8533b90957 Bumped version to 20211123.0 2021-11-22 17:28:13 -08:00
Laszlo Magyar
c95a54c6f3 Fixing typo in #10626 (#10686) 2021-11-22 18:59:35 +01:00
Joakim Sørensen
a991640f52 Remove first part of the update description (#10669) 2021-11-22 09:09:23 -08:00
Joakim Sørensen
3d99b92c07 Limit setting up supervisor subscriptions to the supervisor panel (#10680) 2021-11-22 08:59:28 -08:00
Philip Allgaier
d28ad17135 Use component to ensure relative-time in Glance card gets updated (#10666) 2021-11-22 11:12:04 +01:00
Philip Allgaier
3c67fc96b1 Make "Show more" show everything starting from yesterday (#10533) 2021-11-22 10:56:40 +01:00
Joakim Sørensen
4719636176 Fix dark main-content and split gallery demo (#10675) 2021-11-21 21:01:51 -08:00
Paulus Schoutsen
45efee28b8 Add scenes and scripts as buttons in footer of area cards (#10673)
* Add scenes and scripts as chips in footer of area cards

* Remove unused chips config type

* Update src/panels/lovelace/common/generate-lovelace-config.ts

Co-authored-by: Zack Barett <arnett.zackary@gmail.com>

* Fix typing

Co-authored-by: Zack Barett <arnett.zackary@gmail.com>
2021-11-21 20:59:56 -08:00
Joakim Sørensen
3bcf225380 Fix color overlay in ha-alert content (#10674) 2021-11-21 20:16:19 +01:00
Joakim Sørensen
2e81f843ce Use white for icons with backgound (#10672) 2021-11-21 18:07:55 +00:00
Joakim Sørensen
a430142296 Add iconColor to ha-config-navigation entries (#10658) 2021-11-21 09:52:58 -08:00
Joakim Sørensen
6335b13c5e Remove core note on update page (#10661) 2021-11-21 09:16:06 -08:00
Joakim Sørensen
6c4e987a24 Make ha-chip-set slot-able (#10647) 2021-11-21 09:15:38 -08:00
Joakim Sørensen
1a5c43d72a Fix color over slotted image in ha-alert (#10652) 2021-11-21 09:13:48 -08:00
epenet
91dbfca899 Add frequency device class for sensor (#10621) 2021-11-21 05:05:32 +01:00
Bram Kragten
96f103644a Send error message to sender (#10660) 2021-11-19 13:22:49 -08:00
Paulus Schoutsen
5304e5a670 Always render groups/areas in a single column (#10655) 2021-11-19 13:16:43 -08:00
Lasse Rosenow
390e5b3881 Simplify launch screen svg (#10643) 2021-11-18 16:20:45 -08:00
Joakim Sørensen
9f5756c9fa Use ha-formfield around backup checkbox (#10653) 2021-11-18 16:09:39 -08:00
Joakim Sørensen
0ca35d7012 Remove ha-alert actionText (#10646) 2021-11-18 16:09:13 -08:00
Joakim Sørensen
0d19f4792f Fix active tab (#10654) 2021-11-18 19:21:19 +00:00
Joakim Sørensen
91b009af79 Fix back button color (#10650) 2021-11-18 18:57:15 +01:00
Paulus Schoutsen
1ebd2fb9f1 Bumped version to 20211117.0 2021-11-17 10:54:08 -08:00
Zack Barett
4684979ae7 Area Card (#10141)
Co-authored-by: Philip Allgaier <mail@spacegaier.de>
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2021-11-17 19:43:41 +01:00
Joakim Sørensen
a567312bdb Show updates on dashboard for dev (#10637) 2021-11-17 18:39:16 +00:00
Joakim Sørensen
1e851e0e8c Remove customize UI (#10632) 2021-11-17 10:34:20 -08:00
Bram Kragten
7d94615f47 Cast fixes (#10598) 2021-11-17 10:33:15 -08:00
Bram Kragten
582fab7ea1 Update Lovelace Cast app ID (#10592) 2021-11-17 10:32:15 -08:00
Philip Allgaier
822590ec8a Add correct button label to "no_state" statistics fix dialog (#10628) 2021-11-17 10:22:34 -08:00
Joakim Sørensen
e9f0967578 Move updates (#10626) 2021-11-17 10:21:27 -08:00
Joakim Sørensen
481da19c74 Fix datatable checkbox width (#10631) 2021-11-16 19:46:41 +01:00
Joakim Sørensen
b969db0c0f Use ha-form for onboarding-create-user (#10604) 2021-11-15 14:21:29 -08:00
Joakim Sørensen
a6b98fc3c3 Add markers-updated to ha-locations-editor (#10601) 2021-11-15 14:11:42 -08:00
Joakim Sørensen
87c2046ab5 Remove add-on store tab (#10624) 2021-11-15 09:15:20 -08:00
David F. Mulcahey
4b992fb0c4 Correct ZHA LQI sort in device children dialog (#10616) 2021-11-15 09:11:31 -08:00
Lasse Rosenow
3154011c65 Improve startup experience by removing AppBar skeleton (#10569) 2021-11-15 07:54:59 -08:00
Bram Kragten
4e68383cf7 Remove deprecated icons (#10622) 2021-11-15 11:54:59 +01:00
Michael Irigoyen
db6ef22ebb Update MDI to v6.5.95 (#10618) 2021-11-15 09:49:53 +01:00
Allen Porter
c238c7dbbc WebRTC fix for Safari (#10602) 2021-11-11 10:48:56 +01:00
Bram Kragten
d04823b4c5 Update image-cropper-dialog.ts 2021-11-10 22:55:05 +01:00
Bram Kragten
4cb45d6313 Add picture uploader to area (#10544) 2021-11-10 21:42:43 +01:00
Bram Kragten
6623e5f017 Fix thingktalk dialog (#10600) 2021-11-10 19:36:18 +00:00
Bram Kragten
6518aefb7f Prevent cast timeout after 10 mins, show current shown Lovelace view (#10586) 2021-11-09 21:53:40 +01:00
Bram Kragten
d5600b7c08 Bumped version to 20211109.0 2021-11-09 21:42:04 +01:00
Philip Allgaier
4789295d32 Add CSS var for ha-dialog border radius (#10424) 2021-11-09 17:24:39 +01:00
Philip Allgaier
70d54aa855 Ensure theme picker row uses correct theme name (#10589) 2021-11-09 17:22:48 +01:00
Bram Kragten
77549efc47 Bump codemirror (#10588) 2021-11-09 16:10:42 +01:00
Bram Kragten
00299bc74d Fix multi select ha-form (#10585) 2021-11-09 16:10:26 +01:00
Philip Allgaier
b74fc5578d Consistently show a close button for config dialogs (#10587) 2021-11-09 13:52:56 +00:00
Bram Kragten
9018d4cc18 Update translations 2021-11-08 19:58:29 +01:00
481 changed files with 14012 additions and 7543 deletions

View File

@@ -30,7 +30,7 @@ jobs:
env:
CI: true
- name: Build resources
run: ./node_modules/.bin/gulp gen-icons-json build-translations build-locale-data gather-gallery-demos
run: ./node_modules/.bin/gulp gen-icons-json build-translations build-locale-data gather-gallery-pages
- name: Run eslint
run: yarn run lint:eslint
- name: Run tsc

View File

@@ -15,5 +15,5 @@ jobs:
- name: Trigger Demo build
run: curl -X POST -d {} https://api.netlify.com/build_hooks/${{ secrets.NETLIFY_DEMO_DEV_BUILD_HOOK }}
- name: Trigger Gallery build
run: curl -X POST -d {} https://api.netlify.com/build_hooks/${{ secrets.NETLIFY_GALLERY_DEV_BUILD_HOOK }}
- name: Trigger Design build
run: curl -X POST -d "NIGHTLY" https://api.netlify.com/build_hooks/${{ secrets.NETLIFY_GALLERY_DEV_BUILD_HOOK }}

View File

@@ -31,6 +31,6 @@ gulp.task("clean-hassio", () =>
gulp.task(
"clean-gallery",
gulp.parallel("clean-translations", () =>
del([paths.gallery_output_root, paths.build_dir])
del([paths.gallery_output_root, paths.gallery_build, paths.build_dir])
)
);

View File

@@ -1,7 +1,11 @@
/* eslint-disable */
// Run demo develop mode
const gulp = require("gulp");
const fs = require("fs");
const path = require("path");
const marked = require("marked");
const glob = require("glob");
const yaml = require("js-yaml");
const env = require("../env");
const paths = require("../paths");
@@ -15,26 +19,129 @@ require("./service-worker.js");
require("./entry-html.js");
require("./rollup.js");
gulp.task("gather-gallery-demos", async function gatherDemos() {
const files = await fs.promises.readdir(
path.resolve(paths.gallery_dir, "src/demos")
);
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 += "};";
gulp.task("gather-gallery-pages", async function gatherPages() {
const pageDir = path.resolve(paths.gallery_dir, "src/pages");
const files = glob.sync(path.resolve(pageDir, "**/*"));
const galleryBuild = path.resolve(paths.gallery_dir, "build");
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(
path.resolve(galleryBuild, "import-demos.ts"),
path.resolve(galleryBuild, "import-pages.ts"),
content,
"utf-8"
);
@@ -52,11 +159,24 @@ gulp.task(
"gen-icons-json",
"build-translations",
"build-locale-data",
"gather-gallery-demos"
"gather-gallery-pages"
),
"copy-static-gallery",
"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")
);
}
)
)
);
@@ -72,7 +192,7 @@ gulp.task(
"gen-icons-json",
"build-translations",
"build-locale-data",
"gather-gallery-demos"
"gather-gallery-pages"
),
"copy-static-gallery",
env.useRollup() ? "rollup-prod-gallery" : "webpack-prod-gallery",

View File

@@ -79,6 +79,11 @@ function copyFonts(staticDir) {
);
}
function copyQrScannerWorker(staticDir) {
const staticPath = genStaticPath(staticDir);
copyFileDir(npmPath("qr-scanner/qr-scanner-worker.min.js"), staticPath("js"));
}
function copyMapPanel(staticDir) {
const staticPath = genStaticPath(staticDir);
copyFileDir(
@@ -125,6 +130,9 @@ gulp.task("copy-static-app", async () => {
// Panel assets
copyMapPanel(staticDir);
// Qr Scanner assets
copyQrScannerWorker(staticDir);
});
gulp.task("copy-static-demo", async () => {

View File

@@ -26,6 +26,7 @@ module.exports = {
cast_output_es5: path.resolve(__dirname, "../cast/dist/frontend_es5"),
gallery_dir: path.resolve(__dirname, "../gallery"),
gallery_build: path.resolve(__dirname, "../gallery/build"),
gallery_output_root: path.resolve(__dirname, "../gallery/dist"),
gallery_output_latest: path.resolve(
__dirname,

File diff suppressed because one or more lines are too long

View File

@@ -11,6 +11,7 @@
--progress-color: #03a9f4;
--splash-image: url('https://home-assistant.io/images/cast/splash.png');
--splash-size: cover;
--background-color: #41bdf5;
}
</style>
<script>

View File

@@ -1,7 +1,4 @@
import { CastReceiverContext } from "chromecast-caf-receiver/cast.framework";
const castContext =
cast.framework.CastContext.getInstance() as unknown as CastReceiverContext;
const castContext = cast.framework.CastReceiverContext.getInstance();
const playerManager = castContext.getPlayerManager();

View File

@@ -8,6 +8,9 @@ import { ReceivedMessage } from "./types";
const lovelaceController = new HcMain();
document.body.append(lovelaceController);
lovelaceController.addEventListener("cast-view-changed", (ev) => {
playDummyMedia(ev.detail.title);
});
const mediaPlayer = document.createElement("cast-media-player");
mediaPlayer.style.display = "none";
@@ -28,21 +31,29 @@ const setTouchControlsVisibility = (visible: boolean) => {
}
};
const playDummyMedia = () => {
const playerManager = castContext.getPlayerManager();
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://www.home-assistant.io/images/blog/2018-09-thinking-big/social.png";
"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 = "Home Assistant Lovelace";
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 = () => {
@@ -50,7 +61,6 @@ const showLovelaceController = () => {
lovelaceController.style.display = "initial";
document.body.setAttribute("style", "overflow-y: auto !important");
setTouchControlsVisibility(false);
playDummyMedia();
};
const showMediaPlayer = () => {
@@ -69,6 +79,7 @@ const showMediaPlayer = () => {
--progress-color: #03a9f4;
--splash-image: url('https://home-assistant.io/images/cast/splash.png');
--splash-size: cover;
--background-color: #41bdf5;
}
`;
document.head.appendChild(style);
@@ -81,22 +92,6 @@ options.customNamespaces = {
[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(
CAST_NS,
// @ts-ignore
@@ -123,7 +118,7 @@ playerManager.setMessageInterceptor(
(loadRequestData) => {
if (
loadRequestData.media.contentId ===
"https://www.home-assistant.io/images/blog/2018-09-thinking-big/social.png"
"https://cast.home-assistant.io/images/google-nest-hub.png"
) {
return loadRequestData;
}

View File

@@ -1,5 +1,6 @@
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../../../src/common/dom/fire_event";
import { LovelaceConfig } from "../../../../src/data/lovelace";
import { Lovelace } from "../../../../src/panels/lovelace/types";
import "../../../../src/panels/lovelace/views/hui-view";
@@ -14,7 +15,7 @@ class HcLovelace extends LitElement {
@property() public viewPath?: string | number;
public urlPath?: string | null;
@property() public urlPath: string | null = null;
protected render(): TemplateResult {
const index = this._viewIndex;
@@ -30,7 +31,7 @@ class HcLovelace extends LitElement {
config: this.lovelaceConfig,
rawConfig: this.lovelaceConfig,
editMode: false,
urlPath: this.urlPath!,
urlPath: this.urlPath,
enableFullEditMode: () => undefined,
mode: "storage",
locale: this.hass.locale,
@@ -54,6 +55,21 @@ class HcLovelace extends LitElement {
const index = this._viewIndex;
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 =
this.lovelaceConfig.views[index].background ||
this.lovelaceConfig.background;
@@ -101,8 +117,15 @@ class HcLovelace extends LitElement {
}
}
export interface CastViewChanged {
title: string | undefined;
}
declare global {
interface HTMLElementTagNameMap {
"hc-lovelace": HcLovelace;
}
interface HASSDomEvents {
"cast-view-changed": CastViewChanged;
}
}

View File

@@ -13,7 +13,11 @@ import {
ShowDemoMessage,
ShowLovelaceViewMessage,
} 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 { isNavigationClick } from "../../../../src/common/dom/is-navigation-click";
import {
@@ -40,9 +44,9 @@ export class HcMain extends HassElement {
@state() private _error?: string;
private _unsubLovelace?: UnsubscribeFunc;
@state() private _urlPath?: string | null;
private _urlPath?: string | null;
private _unsubLovelace?: UnsubscribeFunc;
public processIncomingMessage(msg: HassMessage) {
if (msg.type === "connect") {
@@ -68,8 +72,10 @@ export class HcMain extends HassElement {
!this._lovelaceConfig ||
this._lovelacePath === null ||
// Guard against part of HA not being loaded yet.
(this.hass &&
(!this.hass.states || !this.hass.config || !this.hass.services))
!this.hass ||
!this.hass.states ||
!this.hass.config ||
!this.hass.services
) {
return html`
<hc-launch-screen
@@ -119,7 +125,7 @@ export class HcMain extends HassElement {
if (this.hass) {
status.hassUrl = this.hass.auth.data.hassUrl;
status.lovelacePath = this._lovelacePath!;
status.lovelacePath = this._lovelacePath;
status.urlPath = this._urlPath;
}
@@ -132,6 +138,26 @@ 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");
};
@@ -154,14 +180,18 @@ export class HcMain extends HassElement {
}),
});
} catch (err: any) {
this._error = this._getErrorMessage(err);
const errorMessage = this._getErrorMessage(err);
this._error = errorMessage;
this._sendError(err, errorMessage);
return;
}
let connection;
try {
connection = await createConnection({ auth });
} catch (err: any) {
this._error = this._getErrorMessage(err);
const errorMessage = this._getErrorMessage(err);
this._error = errorMessage;
this._sendError(err, errorMessage);
return;
}
if (this.hass) {
@@ -173,24 +203,29 @@ export class HcMain extends HassElement {
}
private async _handleShowLovelaceMessage(msg: ShowLovelaceViewMessage) {
this._showDemo = false;
// We should not get this command before we are connected.
// Means a client got out of sync. Let's send status to them.
if (!this.hass) {
this._sendStatus(msg.senderId!);
this._error = "Cannot show Lovelace because we're not connected.";
this._sendError(ReceiverErrorCode.NOT_CONNECTED, this._error);
return;
}
this._error = undefined;
if (msg.urlPath === "lovelace") {
msg.urlPath = null;
}
this._lovelacePath = msg.viewPath;
if (!this._unsubLovelace || this._urlPath !== msg.urlPath) {
this._urlPath = msg.urlPath;
this._lovelaceConfig = undefined;
if (this._unsubLovelace) {
this._unsubLovelace();
}
const llColl = atLeastVersion(this.hass.connection.haVersion, 0, 107)
? getLovelaceCollection(this.hass!.connection, msg.urlPath)
: getLegacyLovelaceCollection(this.hass!.connection);
? getLovelaceCollection(this.hass.connection, msg.urlPath)
: getLegacyLovelaceCollection(this.hass.connection);
// We first do a single refresh because we need to check if there is LL
// configuration.
try {
@@ -199,8 +234,16 @@ export class HcMain extends HassElement {
this._handleNewLovelaceConfig(lovelaceConfig)
);
} catch (err: any) {
// eslint-disable-next-line
console.log("Error fetching Lovelace configuration", err, msg);
if (
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.
this._unsubLovelace = () => undefined;
await this._generateLovelaceConfig();
@@ -215,8 +258,6 @@ export class HcMain extends HassElement {
loadLovelaceResources(resources, this.hass!.auth.data.hassUrl);
}
}
this._showDemo = false;
this._lovelacePath = msg.viewPath;
this._sendStatus();
}
@@ -237,7 +278,7 @@ export class HcMain extends HassElement {
}
private _handleNewLovelaceConfig(lovelaceConfig: LovelaceConfig) {
castContext.setApplicationState(lovelaceConfig.title!);
castContext.setApplicationState(lovelaceConfig.title || "");
this._lovelaceConfig = lovelaceConfig;
}

File diff suppressed because one or more lines are too long

View File

@@ -82,6 +82,9 @@ export const mockEnergy = (hass: MockHomeAssistant) => {
],
}));
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(

View File

@@ -1,4 +1,10 @@
import { addHours, differenceInHours, endOfDay } from "date-fns";
import {
addDays,
addHours,
addMonths,
differenceInHours,
endOfDay,
} from "date-fns";
import { HassEntity } from "home-assistant-js-websocket";
import { StatisticValue } from "../../../src/data/history";
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
@@ -70,6 +76,7 @@ const generateMeanStatistics = (
id: string,
start: Date,
end: Date,
period: "5minute" | "hour" | "day" | "month" = "hour",
initValue: number,
maxDiff: number
) => {
@@ -84,6 +91,7 @@ const generateMeanStatistics = (
statistics.push({
statistic_id: id,
start: currentDate.toISOString(),
end: currentDate.toISOString(),
mean,
min: mean - Math.random() * maxDiff,
max: mean + Math.random() * maxDiff,
@@ -92,7 +100,12 @@ const generateMeanStatistics = (
sum: null,
});
lastVal = mean;
currentDate = addHours(currentDate, 1);
currentDate =
period === "day"
? addDays(currentDate, 1)
: period === "month"
? addMonths(currentDate, 1)
: addHours(currentDate, 1);
}
return statistics;
};
@@ -101,6 +114,7 @@ const generateSumStatistics = (
id: string,
start: Date,
end: Date,
period: "5minute" | "hour" | "day" | "month" = "hour",
initValue: number,
maxDiff: number
) => {
@@ -115,6 +129,7 @@ const generateSumStatistics = (
statistics.push({
statistic_id: id,
start: currentDate.toISOString(),
end: currentDate.toISOString(),
mean: null,
min: null,
max: null,
@@ -122,7 +137,12 @@ const generateSumStatistics = (
state: initValue + sum,
sum,
});
currentDate = addHours(currentDate, 1);
currentDate =
period === "day"
? addDays(currentDate, 1)
: period === "month"
? addMonths(currentDate, 1)
: addHours(currentDate, 1);
}
return statistics;
};
@@ -131,6 +151,7 @@ const generateCurvedStatistics = (
id: string,
start: Date,
end: Date,
_period: "5minute" | "hour" | "day" | "month" = "hour",
initValue: number,
maxDiff: number,
metered: boolean
@@ -149,6 +170,7 @@ const generateCurvedStatistics = (
statistics.push({
statistic_id: id,
start: currentDate.toISOString(),
end: currentDate.toISOString(),
mean: null,
min: null,
max: null,
@@ -167,11 +189,38 @@ const generateCurvedStatistics = (
const statisticsFunctions: Record<
string,
(id: string, start: Date, end: Date) => StatisticValue[]
(
id: string,
start: Date,
end: Date,
period: "5minute" | "hour" | "day" | "month"
) => StatisticValue[]
> = {
"sensor.energy_consumption_tarif_1": (id: string, start: Date, end: Date) => {
"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, 0, 0.7);
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!
@@ -180,6 +229,7 @@ const statisticsFunctions: Record<
id,
morningEnd,
eveningStart,
period,
morningFinalVal,
0
);
@@ -187,39 +237,71 @@ const statisticsFunctions: Record<
id,
eveningStart,
end,
period,
morningFinalVal,
0.7
);
return [...morningLow, ...empty, ...eveningLow];
},
"sensor.energy_consumption_tarif_2": (id: string, start: Date, end: Date) => {
"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, 0, 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) =>
generateSumStatistics(id, start, end, 0, 0),
"sensor.energy_production_tarif_1_compensation": (id, start, end) =>
generateSumStatistics(id, start, end, 0, 0),
"sensor.energy_production_tarif_2": (id, start, end) => {
"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));
@@ -227,6 +309,7 @@ const statisticsFunctions: Record<
id,
productionStart,
productionEnd,
period,
0,
0.15,
true
@@ -234,18 +317,43 @@ const statisticsFunctions: Record<
const productionFinalVal = production.length
? production[production.length - 1].sum!
: 0;
const morning = generateSumStatistics(id, start, productionStart, 0, 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, productionFinalVal, 1);
const rest = generateSumStatistics(
id,
dayEnd,
end,
period,
productionFinalVal,
1
);
return [...morning, ...production, ...evening, ...rest];
},
"sensor.solar_production": (id, start, end) => {
"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));
@@ -253,6 +361,7 @@ const statisticsFunctions: Record<
id,
productionStart,
productionEnd,
period,
0,
0.3,
true
@@ -260,19 +369,32 @@ const statisticsFunctions: Record<
const productionFinalVal = production.length
? production[production.length - 1].sum!
: 0;
const morning = generateSumStatistics(id, start, productionStart, 0, 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, productionFinalVal, 2);
const rest = generateSumStatistics(
id,
dayEnd,
end,
period,
productionFinalVal,
2
);
return [...morning, ...production, ...evening, ...rest];
},
"sensor.grid_fossil_fuel_percentage": (id, start, end) =>
generateMeanStatistics(id, start, end, 35, 1.3),
};
export const mockHistory = (mockHass: MockHomeAssistant) => {
@@ -347,7 +469,7 @@ export const mockHistory = (mockHass: MockHomeAssistant) => {
mockHass.mockWS("history/list_statistic_ids", () => []);
mockHass.mockWS(
"history/statistics_during_period",
({ statistic_ids, start_time, end_time }, hass) => {
({ statistic_ids, start_time, end_time, period }, hass) => {
const start = new Date(start_time);
const end = end_time ? new Date(end_time) : new Date();
@@ -355,7 +477,7 @@ export const mockHistory = (mockHass: MockHomeAssistant) => {
statistic_ids.forEach((id: string) => {
if (id in statisticsFunctions) {
statistics[id] = statisticsFunctions[id](id, start, end);
statistics[id] = statisticsFunctions[id](id, start, end, period);
} else {
const entityState = hass.states[id];
const state = entityState ? Number(entityState.state) : 1;
@@ -365,6 +487,7 @@ export const mockHistory = (mockHass: MockHomeAssistant) => {
id,
start,
end,
period,
state,
state * (state > 80 ? 0.01 : 0.05)
)
@@ -372,6 +495,7 @@ export const mockHistory = (mockHass: MockHomeAssistant) => {
id,
start,
end,
period,
state,
state * (state > 80 ? 0.05 : 0.1)
);

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

View File

@@ -1,6 +1,6 @@
#!/bin/bash
TARGET_LABEL="Needs gallery preview"
TARGET_LABEL="needs design preview"
if [[ "$NETLIFY" != "true" ]]; then
echo "This script can only be run on Netlify"
@@ -13,16 +13,14 @@ function createStatus() {
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/Gallery Preview Build", "description": "'"$description"'", "target_url": "'"$target_url"'"}'
-d '{"state": "'"${state}"'", "context": "Netlify/Design Preview Build", "description": "'"$description"'", "target_url": "'"$target_url"'"}'
}
if [[ "${PULL_REQUEST}" == "false" ]]; then
gulp build-gallery
else
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 gallery preview" "https://app.netlify.com/sites/home-assistant-gallery/deploys/$BUILD_ID"
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"
@@ -32,4 +30,6 @@ else
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
View 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",
},
];

View File

@@ -52,17 +52,13 @@ class DemoBlackWhiteRow extends LitElement {
firstUpdated(changedProps) {
super.firstUpdated(changedProps);
applyThemesOnElement(
this.shadowRoot!.querySelector(".dark"),
{
default_theme: "default",
default_dark_theme: "default",
themes: {},
darkMode: false,
},
"default",
{ dark: true }
);
applyThemesOnElement(this.shadowRoot!.querySelector(".dark"), {
default_theme: "default",
default_dark_theme: "default",
themes: {},
darkMode: true,
theme: "default",
});
}
handleSubmit(ev) {

View File

@@ -1,129 +0,0 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import { load } 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);
}
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;
}
}
</style>
<h2>
[[config.heading]]
<template is="dom-if" if="[[_size]]">
<small>(size [[_size]])</small>
</template>
</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,
_size: {
type: Number,
},
};
}
ready() {
super.ready();
}
_configChanged(config) {
const card = this.$.card;
while (card.lastChild) {
card.removeChild(card.lastChild);
}
const el = this._createCardElement(load(config.config)[0]);
card.appendChild(el);
this._getSize(el);
}
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);
}
_hassChanged(hass) {
const card = this.$.card.lastChild;
if (card) card.hass = hass;
}
_trim(config) {
return config.trim();
}
}
customElements.define("demo-card", DemoCard);

View 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;
}
}

View File

@@ -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);

View 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;
}
}

View 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;
}
}

View File

@@ -1,230 +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
on-click="_backTapped"
class$="[[_computeHeaderButtonClass(_demo)]]"
>
<ha-icon icon="hass:arrow-left"></ha-icon>
</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("alert-dismissed-clicked", () =>
this.$.notifications.showDialog({ message: "Alert dismissed clicked" })
);
this.addEventListener("alert-action-clicked", () =>
this.$.notifications.showDialog({ message: "Alert action clicked" })
);
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);

256
gallery/src/ha-gallery.ts Normal file
View File

@@ -0,0 +1,256 @@
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;
}
.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;
}
}

View File

@@ -7,7 +7,7 @@
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<meta name="theme-color" content="#2157BC" />
<title>HAGallery</title>
<title>Home Assistant Design</title>
<script type="module" src="<%= latestGalleryJS %>"></script>
<style>

View File

@@ -0,0 +1,3 @@
---
title: Describe Action
---

View File

@@ -1,10 +1,10 @@
import { dump } from "js-yaml";
import { html, css, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
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";
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 = [
{ wait_template: "{{ true }}", alias: "Something with an alias" },

View File

@@ -0,0 +1,3 @@
---
title: Describe Condition
---

View File

@@ -1,8 +1,8 @@
import { dump } from "js-yaml";
import { html, css, LitElement, TemplateResult } from "lit";
import { customElement } from "lit/decorators";
import "../../../src/components/ha-card";
import { describeCondition } from "../../../src/data/automation_i18n";
import "../../../../src/components/ha-card";
import { describeCondition } from "../../../../src/data/automation_i18n";
const conditions = [
{ condition: "and" },

View File

@@ -0,0 +1,3 @@
---
title: Describe Trigger
---

View File

@@ -1,8 +1,8 @@
import { dump } from "js-yaml";
import { html, css, LitElement, TemplateResult } from "lit";
import { customElement } from "lit/decorators";
import "../../../src/components/ha-card";
import { describeTrigger } from "../../../src/data/automation_i18n";
import "../../../../src/components/ha-card";
import { describeTrigger } from "../../../../src/data/automation_i18n";
const triggers = [
{ platform: "state" },

View File

@@ -0,0 +1,3 @@
---
title: Actions
---

View File

@@ -1,25 +1,25 @@
/* 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";
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] },

View File

@@ -0,0 +1,3 @@
---
title: Conditions
---

View File

@@ -1,24 +1,24 @@
/* 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";
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[] }[] = [
{

View File

@@ -0,0 +1,3 @@
---
title: Triggers
---

View File

@@ -1,29 +1,29 @@
/* 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";
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[] }[] = [
{

View File

@@ -0,0 +1,3 @@
---
title: Selectors
---

View 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;
}
}

View File

@@ -0,0 +1,3 @@
---
title: Trace Timelines
---

View File

@@ -1,13 +1,13 @@
/* eslint-disable lit/no-template-arrow */
import { html, css, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import "../../../src/components/ha-card";
import "../../../src/components/trace/hat-script-graph";
import "../../../src/components/trace/hat-trace-timeline";
import { provideHass } from "../../../src/fake_data/provide_hass";
import { HomeAssistant } from "../../../src/types";
import { mockDemoTrace } from "../data/traces/mock-demo-trace";
import { DemoTrace } from "../data/traces/types";
import "../../../../src/components/ha-card";
import "../../../../src/components/trace/hat-script-graph";
import "../../../../src/components/trace/hat-trace-timeline";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import { HomeAssistant } from "../../../../src/types";
import { mockDemoTrace } from "../../data/traces/mock-demo-trace";
import { DemoTrace } from "../../data/traces/types";
const traces: DemoTrace[] = [
mockDemoTrace({ state: "running" }),

View File

@@ -0,0 +1,3 @@
---
title: Trace Graphs
---

View File

@@ -1,14 +1,14 @@
/* eslint-disable lit/no-template-arrow */
import { html, css, LitElement, TemplateResult } from "lit";
import "../../../src/components/ha-card";
import "../../../src/components/trace/hat-script-graph";
import "../../../src/components/trace/hat-trace-timeline";
import "../../../../src/components/ha-card";
import "../../../../src/components/trace/hat-script-graph";
import "../../../../src/components/trace/hat-trace-timeline";
import { customElement, property, state } from "lit/decorators";
import { provideHass } from "../../../src/fake_data/provide_hass";
import { HomeAssistant } from "../../../src/types";
import { DemoTrace } from "../data/traces/types";
import { basicTrace } from "../data/traces/basic_trace";
import { motionLightTrace } from "../data/traces/motion-light-trace";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import { HomeAssistant } from "../../../../src/types";
import { DemoTrace } from "../../data/traces/types";
import { basicTrace } from "../../data/traces/basic_trace";
import { motionLightTrace } from "../../data/traces/motion-light-trace";
const traces: DemoTrace[] = [basicTrace, motionLightTrace];

View File

@@ -0,0 +1,181 @@
---
title: Alerts
---
# Alert `<ha-alert>`
The alert offers four severity levels that set a distinctive icon and color.
<ha-alert alert-type="error">
This is an error alert — check it out!
</ha-alert>
<ha-alert alert-type="warning">
This is an warning alert — check it out!
</ha-alert>
<ha-alert alert-type="info">
This is an info alert — check it out!
</ha-alert>
<ha-alert alert-type="success">
This is an success alert — check it out!
</ha-alert>
**Note:** This component is by <a href="https://mui.com/components/alert/" rel="noopener noreferrer" target="_blank">MUI</a> and is not documented in the <a href="https://material.io" rel="noopener noreferrer" target="_blank">Material Design guidelines</a>.
1. [Guidelines](#guidelines)
2. [Implementation](#implementation)
### Resources
| Type | Link | Status |
|----------------|----------------------------------|-----------|
| Design | <a href="https://www.figma.com/community/file/967153512097289521/Home-Assistant-DesignKit" rel="noopener noreferrer" target="_blank">Home Assistant DesignKit</a> (Figma) | Available |
| Implementation | <a href="https://github.com/home-assistant/frontend/blob/dev/src/components/ha-alert.ts" rel="noopener noreferrer" target="_blank">Web Component</a> (GitHub) | Available |
## Guidelines
### Usage
An alert displays a short, important message in a way that attracts the user's attention without interrupting the user's task.
### Anatomy
*Documentation coming soon*
### Error alert
Error alerts
*Real world example coming soon*
### Warning alert
Warning alerts
*Real world example coming soon*
### Info alert
Info alerts
*Real world example coming soon*
### Success alert
Success alerts
*Real world example coming soon*
### Placement
### Accessibility
(WAI-ARIA: [https://www.w3.org/TR/wai-aria-practices/#alert](https://www.w3.org/TR/wai-aria-practices/#alert))
When the component is dynamically displayed, the content is automatically announced by most screen readers. At this time, screen readers do not inform users of alerts that are present when the page loads.
Using color to add meaning only provides a visual indication, which will not be conveyed to users of assistive technologies such as screen readers. Ensure that information denoted by the color is either obvious from the content itself (for example the visible text), or is included through alternative means, such as additional hidden text.
Actions must have a tab index of 0 so that they can be reached by keyboard-only users.
## Implementation
### Example Usage
**Alert type**
<ha-alert alert-type="error">
This is an error alert — check it out!
</ha-alert>
<ha-alert alert-type="warning">
This is an warning alert — check it out!
</ha-alert>
<ha-alert alert-type="info">
This is an info alert — check it out!
</ha-alert>
<ha-alert alert-type="success">
This is an success alert — check it out!
</ha-alert>
```html
<ha-alert alert-type="error">
This is an error alert — check it out!
</ha-alert>
<ha-alert alert-type="warning">
This is a warning alert — check it out!
</ha-alert>
<ha-alert alert-type="info">
This is an info alert — check it out!
</ha-alert>
<ha-alert alert-type="success">
This is a success alert — check it out!
</ha-alert>
```
**Title**
The `title ` option should not be used without a description.
<ha-alert alert-type="success" title="Success">
This is an success alert — check it out!
</ha-alert>
```html
<ha-alert alert-type="success" title="Success">
This is an success alert — check it out!
</ha-alert>
```
**Dismissable**
<ha-alert alert-type="success" dismissable>
This is an success alert — check it out!
</ha-alert>
```html
<ha-alert alert-type="success" dismissable>
This is an success alert — check it out!
</ha-alert>
```
**Slotted action**
<ha-alert alert-type="success">
This is an success alert — check it out!
<mwc-button slot="action" label="Undo"></mwc-button>
</ha-alert>
```html
<ha-alert alert-type="success">
This is an success alert — check it out!
<mwc-button slot="action" label="Undo"></mwc-button>
</ha-alert>
```
**Slotted icon**
*Documentation coming soon*
**Right to left**
<ha-alert alert-type="success" rtl>
This is an info alert — check it out!
</ha-alert>
```html
<ha-alert alert-type="success" rtl>
This is an info alert — check it out!
</ha-alert>
```
### API
**Properties/Attributes**
| Name | Type | Default | Description |
|-------------|---------|---------|-------------------------------------------------------|
| title | string | `` | Title to display. |
| alertType | string | `info` | Severity level that set a distinctive icon and color. |
| dismissable | boolean | `false` | Gives the option to close the alert. |
| icon | string | `` | Icon to display. |
| action | string | `` | Add an action button to the alert. |
| rtl | boolean | `false` | Support languages that use right-to-left. |
**Events**
*Documentation coming soon*
**CSS Custom Properties**
*Documentation coming soon*

View File

@@ -1,15 +1,19 @@
import { html, css, LitElement, TemplateResult } from "lit";
import "@material/mwc-button/mwc-button";
import { css, html, LitElement, TemplateResult } from "lit";
import { customElement } from "lit/decorators";
import "../../../src/components/ha-alert";
import "../../../src/components/ha-card";
import { applyThemesOnElement } from "../../../../src/common/dom/apply_themes_on_element";
import "../../../../src/components/ha-alert";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-logo-svg";
const alerts: {
title?: string;
description: string | TemplateResult;
type: "info" | "warning" | "error" | "success";
dismissable?: boolean;
action?: string;
rtl?: boolean;
iconSlot?: TemplateResult;
actionSlot?: TemplateResult;
}[] = [
{
title: "Test info alert",
@@ -73,13 +77,35 @@ const alerts: {
title: "Error with action",
description: "This is a test error alert with action",
type: "error",
action: "restart",
actionSlot: html`<mwc-button slot="action" label="restart"></mwc-button>`,
},
{
title: "Unsaved data",
description: "You have unsaved data",
type: "warning",
action: "save",
actionSlot: html`<mwc-button slot="action" label="save"></mwc-button>`,
},
{
title: "Slotted icon",
description: "Alert with slotted icon",
type: "warning",
iconSlot: html`<span slot="icon" class="image">
<ha-logo-svg></ha-logo-svg>
</span>`,
},
{
title: "Slotted image",
description: "Alert with slotted image",
type: "warning",
iconSlot: html`<span slot="icon" class="image"
><img src="https://www.home-assistant.io/images/home-assistant-logo.svg"
/></span>`,
},
{
title: "Slotted action",
description: "Alert with slotted action",
type: "info",
actionSlot: html`<mwc-button slot="action" label="action"></mwc-button>`,
},
{
description: "Dismissable information (RTL)",
@@ -91,7 +117,7 @@ const alerts: {
title: "Error with action",
description: "This is a test error alert with action (RTL)",
type: "error",
action: "restart",
actionSlot: html`<mwc-button slot="action" label="restart"></mwc-button>`,
rtl: true,
},
{
@@ -102,34 +128,60 @@ const alerts: {
},
];
@customElement("demo-ha-alert")
@customElement("demo-components-ha-alert")
export class DemoHaAlert extends LitElement {
protected render(): TemplateResult {
return html`
<ha-card header="ha-alert demo">
<div class="card-content">
${alerts.map(
(alert) => html`
<ha-alert
.title=${alert.title || ""}
.alertType=${alert.type}
.dismissable=${alert.dismissable || false}
.actionText=${alert.action || ""}
.rtl=${alert.rtl || false}
>
${alert.description}
</ha-alert>
`
)}
</div>
</ha-card>
${["light", "dark"].map(
(mode) => html`
<div class=${mode}>
<ha-card header="ha-alert ${mode} demo">
<div class="card-content">
${alerts.map(
(alert) => html`
<ha-alert
.title=${alert.title || ""}
.alertType=${alert.type}
.dismissable=${alert.dismissable || false}
.rtl=${alert.rtl || false}
>
${alert.iconSlot} ${alert.description} ${alert.actionSlot}
</ha-alert>
`
)}
</div>
</ha-card>
</div>
`
)}
`;
}
firstUpdated(changedProps) {
super.firstUpdated(changedProps);
applyThemesOnElement(this.shadowRoot!.querySelector(".dark"), {
default_theme: "default",
default_dark_theme: "default",
themes: {},
darkMode: true,
theme: "default",
});
}
static get styles() {
return css`
:host {
display: flex;
flex-direction: row;
justify-content: space-between;
}
.dark,
.light {
display: block;
background-color: var(--primary-background-color);
padding: 0 50px;
}
ha-card {
max-width: 600px;
margin: 24px auto;
}
ha-alert {
@@ -142,8 +194,17 @@ export class DemoHaAlert extends LitElement {
align-items: center;
justify-content: space-between;
}
span {
margin-right: 16px;
.image {
display: inline-flex;
height: 100%;
align-items: center;
}
img {
max-height: 24px;
width: 24px;
}
mwc-button {
--mdc-theme-primary: var(--primary-text-color);
}
`;
}
@@ -151,6 +212,6 @@ export class DemoHaAlert extends LitElement {
declare global {
interface HTMLElementTagNameMap {
"demo-ha-alert": DemoHaAlert;
"demo-components-ha-alert": DemoHaAlert;
}
}

View File

@@ -0,0 +1,3 @@
---
title: Progress Bars
---

View File

@@ -1,8 +1,8 @@
import { html, css, LitElement, TemplateResult } from "lit";
import { customElement } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import "../../../src/components/ha-bar";
import "../../../src/components/ha-card";
import "../../../../src/components/ha-bar";
import "../../../../src/components/ha-card";
const bars: {
min?: number;
@@ -34,7 +34,7 @@ const bars: {
},
];
@customElement("demo-ha-bar")
@customElement("demo-components-ha-bar")
export class DemoHaBar extends LitElement {
protected render(): TemplateResult {
return html`
@@ -80,6 +80,6 @@ export class DemoHaBar extends LitElement {
declare global {
interface HTMLElementTagNameMap {
"demo-ha-bar": DemoHaBar;
"demo-components-ha-bar": DemoHaBar;
}
}

View File

@@ -0,0 +1,3 @@
---
title: Chips
---

View File

@@ -1,9 +1,10 @@
import { mdiHomeAssistant } from "@mdi/js";
import { css, html, LitElement, TemplateResult } from "lit";
import { customElement } from "lit/decorators";
import "../../../src/components/ha-card";
import "../../../src/components/ha-chip";
import "../../../src/components/ha-svg-icon";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-chip";
import "../../../../src/components/ha-chip-set";
import "../../../../src/components/ha-svg-icon";
const chips: {
icon?: string;
@@ -22,8 +23,8 @@ const chips: {
},
];
@customElement("demo-ha-chip")
export class DemoHaChip extends LitElement {
@customElement("demo-components-ha-chips")
export class DemoHaChips extends LitElement {
protected render(): TemplateResult {
return html`
<ha-card header="ha-chip demo">
@@ -41,6 +42,23 @@ export class DemoHaChip extends LitElement {
)}
</div>
</ha-card>
<ha-card header="ha-chip-set demo">
<div class="card-content">
<ha-chip-set>
${chips.map(
(chip) => html`
<ha-chip .hasIcon=${chip.icon !== undefined}>
${chip.icon
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
</ha-svg-icon>`
: ""}
${chip.content}
</ha-chip>
`
)}
</ha-chip-set>
</div>
</ha-card>
`;
}
@@ -50,12 +68,19 @@ export class DemoHaChip extends LitElement {
max-width: 600px;
margin: 24px auto;
}
ha-chip {
margin-bottom: 4px;
}
.card-content {
display: flex;
flex-direction: column;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"demo-ha-chip": DemoHaChip;
"demo-components-ha-chips": DemoHaChips;
}
}

View File

@@ -0,0 +1,3 @@
---
title: Faded Content
---

View File

@@ -0,0 +1,88 @@
import { css, html, LitElement, TemplateResult } from "lit";
import { customElement } from "lit/decorators";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-faded";
import "../../../../src/components/ha-markdown";
const LONG_TEXT = `
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc laoreet velit ut elit volutpat, eget ultrices odio lacinia. In imperdiet malesuada est, nec sagittis metus ultricies quis. Sed nisl ex, convallis porttitor ante quis, hendrerit tristique justo. Mauris pharetra venenatis augue, eu maximus sem cursus in. Quisque sed consequat risus. Suspendisse facilisis ligula a odio consectetur condimentum. Curabitur vehicula elit nec augue mollis, et volutpat massa dictum.
Nam pellentesque auctor rutrum. Suspendisse elit est, sodales vel diam nec, porttitor faucibus massa. Ut pretium ac orci eu pharetra. Praesent in nibh at magna viverra rutrum eu vitae tortor. Etiam eget sem ex. Fusce tristique odio nec lacus mattis, vitae tempor nunc malesuada. Maecenas faucibus magna vel libero maximus egestas. Vestibulum luctus semper velit, in lobortis risus tempus non. Curabitur bibendum ornare commodo. Quisque commodo neque sit amet tincidunt lacinia. Proin elementum ante velit, eu congue nulla semper quis. Pellentesque consequat vel nunc at scelerisque. Mauris sit amet venenatis diam, blandit viverra leo. Integer commodo laoreet orci.
Curabitur ipsum tortor, sodales ut augue sed, commodo porttitor libero. Pellentesque molestie vitae mi consectetur tempor. In sed lectus consequat, lobortis neque non, semper ipsum. Etiam eget ex et nibh sagittis pulvinar lacinia ac mauris. Aenean ligula eros, viverra ac nibh at, venenatis semper quam. Sed interdum ligula sit amet massa tincidunt tincidunt. Suspendisse potenti. Aliquam egestas facilisis est, sed faucibus erat scelerisque id. Duis dolor quam, viverra vitae orci euismod, laoreet pellentesque justo. Nunc malesuada non erat at ullamcorper. Mauris eget posuere odio. Vestibulum turpis nunc, pharetra eget ante in, feugiat mollis justo. Proin porttitor, diam nec vulputate pretium, tellus arcu rhoncus turpis, a blandit nisi nulla quis arcu. Nunc ac ullamcorper ligula, nec facilisis leo.
In vitae eros sollicitudin, iaculis ex eget, egestas orci. Etiam sed pretium lorem. Nam nisi enim, consectetur sit amet semper ac, semper pharetra diam. In pulvinar neque sapien, ac ullamcorper est lacinia a. Etiam tincidunt velit sed diam malesuada, eu ornare ex consectetur. Phasellus in imperdiet tellus. Sed bibendum, dui sit amet fringilla aliquet, enim odio sollicitudin lorem, vel semper turpis mauris vel mauris. Aenean congue magna ac massa cursus, in dictum orci commodo. Pellentesque mollis velit in sollicitudin tincidunt. Vestibulum et efficitur nulla.
Quisque posuere, velit sed porttitor dapibus, neque augue fringilla felis, eu luctus nisi nisl nec ipsum. Curabitur pellentesque ac lectus eget ultricies. Vestibulum est dolor, lacinia pharetra vulputate a, facilisis a magna. Nam vitae arcu nibh. Praesent finibus blandit ante, ac gravida ex mollis eget. Donec quam est, pulvinar vitae neque ut, bibendum aliquam erat. Nullam mollis arcu at sem tincidunt, in tristique lectus facilisis. Aenean ut lacus vel nisl finibus iaculis non a turpis. Integer eget ipsum ante. Donec nunc neque, vestibulum ac magna ac, posuere scelerisque dui. Pellentesque massa nibh, rhoncus id dolor quis, placerat posuere turpis. Donec aliquet augue nisi, eu finibus dui auctor et. Vestibulum eu varius lorem. Quisque lectus ante, malesuada pretium risus eget, interdum mattis enim.
`;
const SMALL_TEXT = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
@customElement("demo-components-ha-faded")
export class DemoHaFaded extends LitElement {
protected render(): TemplateResult {
return html`
<ha-card header="ha-faded demo">
<div class="card-content">
<h3>Long text directly as slotted content</h3>
<ha-faded>${LONG_TEXT}</ha-faded>
<h3>Long text with slotted element</h3>
<ha-faded><span>${LONG_TEXT}</span></ha-faded>
<h3>No text</h3>
<ha-faded><span></span></ha-faded>
<h3>Smal text</h3>
<ha-faded><span>${SMALL_TEXT}</span></ha-faded>
<h3>Long text in markdown</h3>
<ha-faded>
<ha-markdown .content=${LONG_TEXT}> </ha-markdown>
</ha-faded>
<h3>Missing 1px from hiding</h3>
<ha-faded faded-height="87">
<span>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc
laoreet velit ut elit volutpat, eget ultrices odio lacinia. In
imperdiet malesuada est, nec sagittis metus ultricies quis. Sed
nisl ex, convallis porttitor ante quis, hendrerit tristique justo.
Mauris pharetra venenatis augue, eu maximus sem cursus in. Quisque
sed consequat risus. Suspendisse facilisis ligula a odio
consectetur condimentum. Curabitur vehicula elit nec augue mollis,
et volutpat massa dictum. Nam pellentesque auctor rutrum.
Suspendisse elit est, sodales vel diam nec, porttitor faucibus
massa. Ut pretium ac orci eu pharetra.
</span>
</ha-faded>
<h3>1px over hiding point</h3>
<ha-faded faded-height="85">
<span>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc
laoreet velit ut elit volutpat, eget ultrices odio lacinia. In
imperdiet malesuada est, nec sagittis metus ultricies quis. Sed
nisl ex, convallis porttitor ante quis, hendrerit tristique justo.
Mauris pharetra venenatis augue, eu maximus sem cursus in. Quisque
sed consequat risus. Suspendisse facilisis ligula a odio
consectetur condimentum. Curabitur vehicula elit nec augue mollis,
et volutpat massa dictum. Nam pellentesque auctor rutrum.
Suspendisse elit est, sodales vel diam nec, porttitor faucibus
massa. Ut pretium ac orci eu pharetra.
</span>
</ha-faded>
</div>
</ha-card>
`;
}
static get styles() {
return css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"demo-components-ha-faded": DemoHaFaded;
}
}

View File

@@ -0,0 +1,3 @@
---
title: Forms
---

View File

@@ -2,10 +2,10 @@
import "@material/mwc-button";
import { LitElement, TemplateResult, html } from "lit";
import { customElement } from "lit/decorators";
import { computeInitialHaFormData } from "../../../src/components/ha-form/compute-initial-ha-form-data";
import type { HaFormSchema } from "../../../src/components/ha-form/types";
import "../../../src/components/ha-form/ha-form";
import "../components/demo-black-white-row";
import { computeInitialHaFormData } from "../../../../src/components/ha-form/compute-initial-ha-form-data";
import type { HaFormSchema } from "../../../../src/components/ha-form/types";
import "../../../../src/components/ha-form/ha-form";
import "../../components/demo-black-white-row";
const SCHEMAS: {
title: string;
@@ -248,7 +248,7 @@ const SCHEMAS: {
},
];
@customElement("demo-ha-form")
@customElement("demo-components-ha-form")
class DemoHaForm extends LitElement {
private data = SCHEMAS.map(
({ schema, data }) => data || computeInitialHaFormData(schema)
@@ -301,6 +301,6 @@ class DemoHaForm extends LitElement {
declare global {
interface HTMLElementTagNameMap {
"demo-ha-form": DemoHaForm;
"demo-components-ha-form": DemoHaForm;
}
}

View File

@@ -0,0 +1,3 @@
---
title: Label Badge
---

View File

@@ -1,7 +1,7 @@
import { html, css, LitElement, TemplateResult } from "lit";
import { customElement } from "lit/decorators";
import "../../../src/components/ha-label-badge";
import "../../../src/components/ha-card";
import "../../../../src/components/ha-label-badge";
import "../../../../src/components/ha-card";
const colors = ["#03a9f4", "#ffa600", "#43a047"];
@@ -50,7 +50,7 @@ const badges: {
},
];
@customElement("demo-ha-label-badge")
@customElement("demo-components-ha-label-badge")
export class DemoHaLabelBadge extends LitElement {
protected render(): TemplateResult {
return html`
@@ -117,6 +117,6 @@ export class DemoHaLabelBadge extends LitElement {
declare global {
interface HTMLElementTagNameMap {
"demo-ha-label-badge": DemoHaLabelBadge;
"demo-components-ha-label-badge": DemoHaLabelBadge;
}
}

View File

@@ -0,0 +1,3 @@
---
title: Target Selectors
---

View File

@@ -2,16 +2,16 @@
import "@material/mwc-button";
import { LitElement, TemplateResult, css, html } from "lit";
import { customElement, state } from "lit/decorators";
import "../../../src/components/ha-selector/ha-selector";
import "../../../src/components/ha-settings-row";
import { provideHass } from "../../../src/fake_data/provide_hass";
import type { HomeAssistant } from "../../../src/types";
import "../components/demo-black-white-row";
import { BlueprintInput } from "../../../src/data/blueprint";
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/components/ha-selector/ha-selector";
import "../../../../src/components/ha-settings-row";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import type { HomeAssistant } from "../../../../src/types";
import "../../components/demo-black-white-row";
import { BlueprintInput } from "../../../../src/data/blueprint";
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";
const SCHEMAS: {
name: string;
@@ -62,7 +62,7 @@ const SCHEMAS: {
},
];
@customElement("demo-ha-selector")
@customElement("demo-components-ha-selector")
class DemoHaSelector extends LitElement {
@state() private hass!: HomeAssistant;
@@ -126,6 +126,6 @@ class DemoHaSelector extends LitElement {
declare global {
interface HTMLElementTagNameMap {
"demo-ha-selector": DemoHaSelector;
"demo-components-ha-selector": DemoHaSelector;
}
}

View File

@@ -0,0 +1,24 @@
---
title: Home
---
# Welcome to Home Assistant Design
This portal aims to aid designers and developers on improving the Home Assistant interface. It consists of working code, resources and guidelines.
## Home Assistant interface
The Home Assistant frontend allows users to browse and control the state of their home, manage their automations and configure integrations. The frontend is designed as a mobile-first experience. It is a progressive web application and offers an app-like experience to our users. The Home Assistant frontend needs to be fast. But it also needs to work on a wide range of old devices.
### Material Design
The Home Assistant interface is based on Material Design. It's a design system created by Google to quickly build high-quality digital experiences. Components and guidelines that are custom made for Home Assistant are documented on this portal. For all other components check <a href="https://material.io" rel="noopener noreferrer" target="_blank">material.io</a>.
## Designers
We want to make it as easy for designers to contribute as it is for developers. Theres a lot a designer can contribute to:
- Meet us at <a href="https://discord.gg/BPBc8rZ9" rel="noopener noreferrer" target="_blank">devs_ux Discord</a>. Feel free to share your designs, user test or strategic ideas.
- Start designing with our <a href="https://www.figma.com/community/file/967153512097289521/Home-Assistant-DesignKit" rel="noopener noreferrer" target="_blank">Figma DesignKit</a>.
- Find the lates UX <a href="https://github.com/home-assistant/frontend/labels/ux" rel="noopener noreferrer" target="_blank">discussions</a> and <a href="https://github.com/home-assistant/frontend/discussions?discussions_q=label%3Aux" rel="noopener noreferrer" target="_blank">issues</a> on GitHub. Everyone can start a new issue or discussion!
## Developers
Everything you need to get started developing can be found in our <a href="https://developers.home-assistant.io" rel="noopener noreferrer" target="_blank">Home Assistant Developer Docs</a>.

View File

@@ -0,0 +1,80 @@
---
title: Editing design.home-assistant.io
---
# How to edit design.home-assistant.io
All pages are stored in [the pages folder][pages-folder] on GitHub. Pages are grouped in a folder per sidebar section. Each page can contain a `<page name>.markdown` description file, a `<page name>.ts` demo file or both. If both are defined the description is rendered first. The description can contain metadata to specify the title of the page.
## Development
You can develop design.home-assistant.io locally by checking out [the Home Assistant frontend repository](https://github.com/home-assistant/frontend). The command to run the gallery is `gallery/script/develop_gallery`. It will automatically open a browser window and load the development version of the website.
## Creating a page
Navigate to the [the pages folder][pages-folder] on GitHub. If the folder for your category does not exist yet, create it. Create a new Markdown file inside this folder for your description, ie `usability.markdown`. This filename will be used in the URL. Add the following content:
```markdown
---
title: My new page
---
Hello and welcome to my new page!
```
Once saved, the page will be automatically added to the bottom of the sidebar. The title specified in the header will be shown as the page title and used in the sidebar.
## Linking the page in the sidebar
By default the sidebar will gather all pages and group them by category. You can override the order of the categories, define a name for categories and change the order of the pages in [`sidebar.js`](https://github.com/home-assistant/frontend/blob/dev/gallery/sidebar.js).
Any category not listed in `sidebar.js` will be placed at the end of the sidebar.
Any page not listed in `sidebar.js` will be placed at the end of its category.
## Adding a demo to a page
Create a file next to the description file with the same name as the description file, but with the `.ts` extension: `usability.ts`. For this example, we assume that the category folder that contains `usability.markdown` and `usability.ts` is called `user-experience`. Add the following content to `usability.ts`:
```ts
import { html, css, LitElement } from "lit";
import { customElement } from "lit/decorators";
import "../../../../src/components/ha-card";
@customElement("demo-user-experience-usability")
export class DemoUserExperienceUsability extends LitElement {
protected render() {
return html`
<ha-card>
<div class="card-content">
Hello world!
</div>
</ha-card>
`;
}
static get styles() {
return css`
ha-card {
max-width: 600px;
margin: 24px auto;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"demo-user-experience-usability": DemoUserExperienceUsability;
}
}
```
Note that the demo deosn't need to render anything itself. It can also be used to declare web components to be used by the page description. Because page descriptions are using markdown, they can embed any HTML.
## Publishing changes
The website is automatically published whenever the source files in the `dev` branch change. So to get your changes published, open a pull request with your changes.
[pages-folder]: https://github.com/home-assistant/frontend/tree/dev/gallery/src/pages

View File

@@ -0,0 +1,3 @@
---
title: Alarm Panel Card
---

View File

@@ -1,8 +1,8 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
const ENTITIES = [
getEntity("alarm_control_panel", "alarm", "disarmed", {
@@ -70,7 +70,7 @@ const CONFIGS = [
},
];
@customElement("demo-hui-alarm-panel-card")
@customElement("demo-lovelace-alarm-panel-card")
class DemoAlarmPanelEntity extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
@@ -89,6 +89,6 @@ class DemoAlarmPanelEntity extends LitElement {
declare global {
interface HTMLElementTagNameMap {
"demo-hui-alarm-panel-card": DemoAlarmPanelEntity;
"demo-lovelace-alarm-panel-card": DemoAlarmPanelEntity;
}
}

View File

@@ -0,0 +1,3 @@
---
title: Area Card
---

View File

@@ -0,0 +1,156 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
const ENTITIES = [
getEntity("light", "bed_light", "on", {
friendly_name: "Bed Light",
}),
getEntity("switch", "bed_ac", "on", {
friendly_name: "Ecobee",
}),
getEntity("sensor", "bed_temp", "72", {
friendly_name: "Bedroom Temp",
device_class: "temperature",
unit_of_measurement: "°F",
}),
getEntity("light", "living_room_light", "off", {
friendly_name: "Living Room Light",
}),
getEntity("fan", "living_room", "on", {
friendly_name: "Living Room Fan",
}),
getEntity("sensor", "office_humidity", "73", {
friendly_name: "Office Humidity",
device_class: "humidity",
unit_of_measurement: "%",
}),
getEntity("light", "office", "on", {
friendly_name: "Office Light",
}),
getEntity("fan", "kitchen", "on", {
friendly_name: "Second Office Fan",
}),
getEntity("binary_sensor", "kitchen_door", "on", {
friendly_name: "Office Door",
device_class: "door",
}),
];
// TODO: Update image here
const CONFIGS = [
{
heading: "Bedroom",
config: `
- type: area
area: bedroom
`,
},
{
heading: "Living Room",
config: `
- type: area
area: living_room
`,
},
{
heading: "Office",
config: `
- type: area
area: office
`,
},
{
heading: "Kitchen",
config: `
- type: area
area: kitchen
`,
},
];
@customElement("demo-lovelace-area-card")
class DemoArea extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
}
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
hass.mockWS("config/area_registry/list", () => [
{
name: "Bedroom",
area_id: "bedroom",
picture: "/images/bed.png",
},
{
name: "Living Room",
area_id: "living_room",
picture: "/images/living_room.png",
},
{
name: "Office",
area_id: "office",
picture: "/images/office.jpg",
},
{
name: "Second Office",
area_id: "kitchen",
picture: "/images/kitchen.png",
},
]);
hass.mockWS("config/device_registry/list", () => []);
hass.mockWS("config/entity_registry/list", () => [
{
area_id: "bedroom",
entity_id: "light.bed_light",
},
{
area_id: "bedroom",
entity_id: "switch.bed_ac",
},
{
area_id: "bedroom",
entity_id: "sensor.bed_temp",
},
{
area_id: "living_room",
entity_id: "light.living_room_light",
},
{
area_id: "living_room",
entity_id: "fan.living_room",
},
{
area_id: "office",
entity_id: "light.office",
},
{
area_id: "office",
entity_id: "sensor.office_humidity",
},
{
area_id: "kitchen",
entity_id: "fan.kitchen",
},
{
area_id: "kitchen",
entity_id: "binary_sensor.kitchen_door",
},
]);
}
}
declare global {
interface HTMLElementTagNameMap {
"demo-lovelace-area-card": DemoArea;
}
}

View File

@@ -0,0 +1,3 @@
---
title: Conditional Card
---

View File

@@ -1,8 +1,8 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
const ENTITIES = [
getEntity("light", "controller_1", "on", {
@@ -52,7 +52,7 @@ const CONFIGS = [
},
];
@customElement("demo-hui-conditional-card")
@customElement("demo-lovelace-conditional-card")
class DemoConditional extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
@@ -71,6 +71,6 @@ class DemoConditional extends LitElement {
declare global {
interface HTMLElementTagNameMap {
"demo-hui-conditional-card": DemoConditional;
"demo-lovelace-conditional-card": DemoConditional;
}
}

View File

@@ -0,0 +1,3 @@
---
title: Entities Card
---

View File

@@ -1,8 +1,8 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
const ENTITIES = [
getEntity("light", "bed_light", "on", {
@@ -11,10 +11,10 @@ const ENTITIES = [
getEntity("group", "kitchen", "on", {
entity_id: ["light.bed_light"],
order: 8,
friendly_name: "Kitchen",
friendly_name: "Kitchen Group",
}),
getEntity("lock", "kitchen_door", "locked", {
friendly_name: "Kitchen Door",
friendly_name: "Kitchen Lock",
}),
getEntity("cover", "kitchen_window", "open", {
friendly_name: "Kitchen Window",
@@ -22,7 +22,7 @@ const ENTITIES = [
}),
getEntity("scene", "romantic_lights", "scening", {
entity_id: ["light.bed_light", "light.ceiling_lights"],
friendly_name: "Romantic lights",
friendly_name: "Romantic Scene",
}),
getEntity("device_tracker", "demo_paulus", "home", {
source_type: "gps",
@@ -50,15 +50,51 @@ const ENTITIES = [
friendly_name: "Ecobee",
supported_features: 1014,
}),
getEntity("input_number", "noise_allowance", 5, {
getEntity("input_number", "number", 5, {
min: 0,
max: 10,
step: 1,
mode: "slider",
unit_of_measurement: "dB",
friendly_name: "Allowed Noise",
friendly_name: "Number",
icon: "mdi:bell-ring",
}),
getEntity("input_boolean", "toggle", "on", {
friendly_name: "Toggle",
}),
getEntity("input_datetime", "date_and_time", "2022-01-10 00:00:00", {
has_date: true,
has_time: true,
editable: true,
year: 2022,
month: 1,
day: 10,
hour: 0,
minute: 0,
second: 0,
timestamp: 1641801600,
friendly_name: "Date and Time",
}),
getEntity("input_select", "dropdown", "Soda", {
friendly_name: "Dropdown",
options: ["Soda", "Beer", "Wine"],
}),
getEntity("input_text", "text", "Inspiration", {
friendly_name: "Text",
mode: "text",
}),
getEntity("timer", "timer", "idle", {
friendly_name: "Timer",
duration: "0:05:00",
}),
getEntity("counter", "counter", "3", {
friendly_name: "Counter",
initial: 0,
step: 1,
minimum: 0,
maximum: 10,
}),
getEntity("light", "unavailable", "unavailable", {
friendly_name: "Bed Light",
}),
@@ -70,7 +106,7 @@ const ENTITIES = [
supported_features: 11,
}),
getEntity("scene", "unavailable", "unavailable", {
friendly_name: "Romantic lights",
friendly_name: "Romantic Scene",
}),
getEntity("device_tracker", "unavailable", "unavailable", {
friendly_name: "Paulus",
@@ -105,7 +141,22 @@ const CONFIGS = [
- light.bed_light
- light.non_existing
- climate.ecobee
- input_number.noise_allowance
- input_number.number
`,
},
{
heading: "Helpers",
config: `
- type: entities
title: Helpers
entities:
- entity: input_boolean.toggle
- entity: input_datetime.date_and_time
- entity: input_number.number
- entity: input_select.dropdown
- entity: input_text.text
- entity: timer.timer
- entity: counter.counter
`,
},
{
@@ -120,7 +171,7 @@ const CONFIGS = [
- lock.kitchen_door
- light.bed_light
- climate.ecobee
- input_number.noise_allowance
- input_number.number
title: Random group
`,
},
@@ -136,7 +187,7 @@ const CONFIGS = [
- lock.kitchen_door
- light.bed_light
- climate.ecobee
- input_number.noise_allowance
- input_number.number
title: Random group
show_header_toggle: false
`,
@@ -183,7 +234,7 @@ const CONFIGS = [
icon: mdi:alarm-light
name: Bed Light Custom Icon
- climate.ecobee
- input_number.noise_allowance
- input_number.number
title: Random group
show_header_toggle: false
`,
@@ -216,7 +267,7 @@ const CONFIGS = [
},
];
@customElement("demo-hui-entities-card")
@customElement("demo-lovelace-entities-card")
class DemoEntities extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
@@ -235,6 +286,6 @@ class DemoEntities extends LitElement {
declare global {
interface HTMLElementTagNameMap {
"demo-hui-entities-card": DemoEntities;
"demo-lovelace-entities-card": DemoEntities;
}
}

View File

@@ -0,0 +1,3 @@
---
title: Entity Button Card
---

View File

@@ -1,8 +1,8 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
const ENTITIES = [
getEntity("light", "bed_light", "on", {
@@ -68,7 +68,7 @@ const CONFIGS = [
},
];
@customElement("demo-hui-entity-button-card")
@customElement("demo-lovelace-entity-button-card")
class DemoButtonEntity extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
@@ -87,6 +87,6 @@ class DemoButtonEntity extends LitElement {
declare global {
interface HTMLElementTagNameMap {
"demo-hui-entity-button-card": DemoButtonEntity;
"demo-lovelace-entity-button-card": DemoButtonEntity;
}
}

View File

@@ -0,0 +1,3 @@
---
title: Entity Filter Card
---

View File

@@ -1,8 +1,8 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
const ENTITIES = [
getEntity("device_tracker", "demo_paulus", "work", {
@@ -109,7 +109,7 @@ const CONFIGS = [
},
];
@customElement("demo-hui-entity-filter-card")
@customElement("demo-lovelace-entity-filter-card")
class DemoEntityFilter extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
@@ -128,6 +128,6 @@ class DemoEntityFilter extends LitElement {
declare global {
interface HTMLElementTagNameMap {
"demo-hui-entity-filter-card": DemoEntityFilter;
"demo-lovelace-entity-filter-card": DemoEntityFilter;
}
}

View File

@@ -0,0 +1,3 @@
---
title: Gauge Card
---

View File

@@ -1,8 +1,8 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
const ENTITIES = [
getEntity("sensor", "brightness", "12", {}),
@@ -106,7 +106,7 @@ const CONFIGS = [
},
];
@customElement("demo-hui-gauge-card")
@customElement("demo-lovelace-gauge-card")
class DemoGaugeEntity extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
@@ -125,6 +125,6 @@ class DemoGaugeEntity extends LitElement {
declare global {
interface HTMLElementTagNameMap {
"demo-hui-gauge-card": DemoGaugeEntity;
"demo-lovelace-gauge-card": DemoGaugeEntity;
}
}

View File

@@ -0,0 +1,3 @@
---
title: Glance Card
---

View File

@@ -1,8 +1,8 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
const ENTITIES = [
getEntity("device_tracker", "demo_paulus", "home", {
@@ -209,7 +209,7 @@ const CONFIGS = [
},
];
@customElement("demo-hui-glance-card")
@customElement("demo-lovelace-glance-card")
class DemoGlanceEntity extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
@@ -228,6 +228,6 @@ class DemoGlanceEntity extends LitElement {
declare global {
interface HTMLElementTagNameMap {
"demo-hui-glance-card": DemoGlanceEntity;
"demo-lovelace-glance-card": DemoGlanceEntity;
}
}

View File

@@ -0,0 +1,3 @@
---
title: Grid And Stack Card
---

View File

@@ -1,9 +1,9 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators";
import { mockHistory } from "../../../demo/src/stubs/history";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
import { mockHistory } from "../../../../demo/src/stubs/history";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
const ENTITIES = [
getEntity("light", "kitchen_lights", "on", {
@@ -199,7 +199,7 @@ const CONFIGS = [
},
];
@customElement("demo-hui-grid-and-stack-card")
@customElement("demo-lovelace-grid-and-stack-card")
class DemoStack extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
@@ -219,6 +219,6 @@ class DemoStack extends LitElement {
declare global {
interface HTMLElementTagNameMap {
"demo-hui-grid-and-stack-card": DemoStack;
"demo-lovelace-grid-and-stack-card": DemoStack;
}
}

View File

@@ -0,0 +1,3 @@
---
title: Website Card
---

View File

@@ -1,6 +1,7 @@
import { html, LitElement, TemplateResult } from "lit";
import { customElement } from "lit/decorators";
import "../components/demo-cards";
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
const CONFIGS = [
{
@@ -36,15 +37,22 @@ const CONFIGS = [
},
];
@customElement("demo-hui-iframe-card")
@customElement("demo-lovelace-iframe-card")
class DemoIframe extends LitElement {
@query("demo-cards") private _demos!: HTMLElement;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
}
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
provideHass(this._demos);
}
}
declare global {
interface HTMLElementTagNameMap {
"demo-hui-iframe-card": DemoIframe;
"demo-lovelace-iframe-card": DemoIframe;
}
}

View File

@@ -0,0 +1,11 @@
---
title: Introduction
---
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.
This gallery helps our developers and designers to see all the
different states that each card can be in.
Check [the Lovelace documentation](https://www.home-assistant.io/lovelace) for instructions on how to get started with Lovelace.

View File

@@ -0,0 +1,3 @@
---
title: Light Card
---

View File

@@ -1,8 +1,8 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
const ENTITIES = [
getEntity("light", "bed_light", "on", {
@@ -62,7 +62,7 @@ const CONFIGS = [
},
];
@customElement("demo-hui-light-card")
@customElement("demo-lovelace-light-card")
class DemoLightEntity extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
@@ -81,6 +81,6 @@ class DemoLightEntity extends LitElement {
declare global {
interface HTMLElementTagNameMap {
"demo-hui-light-card": DemoLightEntity;
"demo-lovelace-light-card": DemoLightEntity;
}
}

View File

@@ -0,0 +1,3 @@
---
title: Map Card
---

View File

@@ -1,8 +1,8 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
const ENTITIES = [
getEntity("device_tracker", "demo_paulus", "not_home", {
@@ -160,7 +160,7 @@ const CONFIGS = [
},
];
@customElement("demo-hui-map-card")
@customElement("demo-lovelace-map-card")
class DemoMap extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
@@ -179,6 +179,6 @@ class DemoMap extends LitElement {
declare global {
interface HTMLElementTagNameMap {
"demo-hui-map-card": DemoMap;
"demo-lovelace-map-card": DemoMap;
}
}

View File

@@ -0,0 +1,3 @@
---
title: Markdown Card
---

View File

@@ -1,8 +1,8 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators";
import { mockTemplate } from "../../../demo/src/stubs/template";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
import { mockTemplate } from "../../../../demo/src/stubs/template";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
const CONFIGS = [
{
@@ -253,7 +253,7 @@ const CONFIGS = [
},
];
@customElement("demo-hui-markdown-card")
@customElement("demo-lovelace-markdown-card")
class DemoMarkdown extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
@@ -272,6 +272,6 @@ class DemoMarkdown extends LitElement {
declare global {
interface HTMLElementTagNameMap {
"demo-hui-markdown-card": DemoMarkdown;
"demo-lovelace-markdown-card": DemoMarkdown;
}
}

View File

@@ -0,0 +1,3 @@
---
title: Media Control Card
---

View File

@@ -1,8 +1,8 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
import { createMediaPlayerEntities } from "../data/media_players";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
import { createMediaPlayerEntities } from "../../data/media_players";
const CONFIGS = [
{
@@ -157,7 +157,7 @@ const CONFIGS = [
},
];
@customElement("demo-hui-media-control-card")
@customElement("demo-lovelace-media-control-card")
class DemoHuiMediaControlCard extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
@@ -176,6 +176,6 @@ class DemoHuiMediaControlCard extends LitElement {
declare global {
interface HTMLElementTagNameMap {
"demo-hui-media-control-card": DemoHuiMediaControlCard;
"demo-lovelace-media-control-card": DemoHuiMediaControlCard;
}
}

View File

@@ -0,0 +1,3 @@
---
title: Media Player Row
---

View File

@@ -1,8 +1,8 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
import { createMediaPlayerEntities } from "../data/media_players";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
import { createMediaPlayerEntities } from "../../data/media_players";
const CONFIGS = [
{
@@ -54,7 +54,7 @@ const CONFIGS = [
},
];
@customElement("demo-hui-media-player-row")
@customElement("demo-lovelace-media-player-row")
class DemoHuiMediaPlayerRow extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
@@ -73,6 +73,6 @@ class DemoHuiMediaPlayerRow extends LitElement {
declare global {
interface HTMLElementTagNameMap {
"demo-hui-media-player-row": DemoHuiMediaPlayerRow;
"demo-lovelace-media-player-rows": DemoHuiMediaPlayerRow;
}
}

View File

@@ -0,0 +1,3 @@
---
title: Picture Elements Card
---

View File

@@ -1,8 +1,8 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
const ENTITIES = [
getEntity("light", "bed_light", "on", {
@@ -124,7 +124,7 @@ const CONFIGS = [
},
];
@customElement("demo-hui-picture-elements-card")
@customElement("demo-lovelace-picture-elements-card")
class DemoPictureElements extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
@@ -143,6 +143,6 @@ class DemoPictureElements extends LitElement {
declare global {
interface HTMLElementTagNameMap {
"demo-hui-picture-elements-card": DemoPictureElements;
"demo-lovelace-picture-elements-card": DemoPictureElements;
}
}

View File

@@ -0,0 +1,3 @@
---
title: Picture Entity Card
---

View File

@@ -1,8 +1,8 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
const ENTITIES = [
getEntity("light", "kitchen_lights", "on", {
@@ -79,7 +79,7 @@ const CONFIGS = [
},
];
@customElement("demo-hui-picture-entity-card")
@customElement("demo-lovelace-picture-entity-card")
class DemoPictureEntity extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
@@ -98,6 +98,6 @@ class DemoPictureEntity extends LitElement {
declare global {
interface HTMLElementTagNameMap {
"demo-hui-picture-entity-card": DemoPictureEntity;
"demo-lovelace-picture-entity-card": DemoPictureEntity;
}
}

View File

@@ -0,0 +1,3 @@
---
title: Picture Glance Card
---

View File

@@ -1,8 +1,8 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
const ENTITIES = [
getEntity("switch", "decorative_lights", "on", {
@@ -120,7 +120,7 @@ const CONFIGS = [
},
];
@customElement("demo-hui-picture-glance-card")
@customElement("demo-lovelace-picture-glance-card")
class DemoPictureGlance extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
@@ -139,6 +139,6 @@ class DemoPictureGlance extends LitElement {
declare global {
interface HTMLElementTagNameMap {
"demo-hui-picture-glance-card": DemoPictureGlance;
"demo-lovelace-picture-glance-card": DemoPictureGlance;
}
}

View File

@@ -0,0 +1,3 @@
---
title: Plant Card
---

View File

@@ -1,8 +1,8 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
import { createPlantEntities } from "../data/plants";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
import { createPlantEntities } from "../../data/plants";
const CONFIGS = [
{
@@ -29,7 +29,7 @@ const CONFIGS = [
},
];
@customElement("demo-hui-plant-card")
@customElement("demo-lovelace-plant-card")
export class DemoPlantEntity extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
@@ -48,6 +48,6 @@ export class DemoPlantEntity extends LitElement {
declare global {
interface HTMLElementTagNameMap {
"demo-hui-plant-card": DemoPlantEntity;
"demo-lovelace-plant-card": DemoPlantEntity;
}
}

Some files were not shown because too many files have changed in this diff Show More