Compare commits

...

539 Commits

Author SHA1 Message Date
Ludeeus b77ce9fc0e localize 2021-03-05 14:40:05 +00:00
Ludeeus bc14d6dfcb Add supervisor-connectivity 2021-03-05 14:33:01 +00:00
Bram Kragten 845411b48c Fix codemirror active line (#8558)
fixes #8556
2021-03-05 15:01:22 +01:00
Joakim Sørensen d715867b09 More consistant ignoring errors (#8553) 2021-03-05 10:40:49 +01:00
GitHub Action 0ca2cdfbed Translation update 2021-03-05 01:23:51 +00:00
Bram Kragten 0d1c72386e Bump codemirror to 0.18 (#8546) 2021-03-04 16:43:34 +01:00
Joakim Sørensen c91779dffe Add supervisor_add_addon_repository redirect (#8545) 2021-03-04 16:31:32 +01:00
Joakim Sørensen 3853cc9214 Check if addon is valid before navigating (#8538)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-03-04 11:51:35 +01:00
Joakim Sørensen a66b3f6b80 Fix managing custom addon repositories (#8536) 2021-03-04 10:29:00 +01:00
Joakim Sørensen c97ec32343 Fix missing name in full snapshot (#8535) 2021-03-04 10:25:51 +01:00
Josh McCarty 2abba7e445 Alarm numeric inputmode (#8521) 2021-03-04 10:08:16 +01:00
Tierney Cyren f887c27ad1 fix: move @types modules from deps to devDeps (#8539) 2021-03-04 10:05:28 +01:00
Joakim Sørensen 6ee8d74899 Remove duplicate save (#8532) 2021-03-04 10:03:09 +01:00
GitHub Action f196c72563 Translation update 2021-03-04 01:22:38 +00:00
Joakim Sørensen 419e564441 Use correct version (#8530) 2021-03-03 16:09:57 +01:00
Joakim Sørensen de97b54c95 Fix localize keys for supervisor update dialog (#8529) 2021-03-03 16:01:30 +01:00
Philip Allgaier 07001f7b5c Fix add-on toggles description translation keys (#8528) 2021-03-03 15:33:52 +01:00
Joakim Sørensen bee17fce64 Fix second load in firefox and localize init (#8525) 2021-03-03 15:06:36 +01:00
Bram Kragten 718904a853 Add max height to yaml editor (#8527) 2021-03-03 14:31:39 +01:00
Bram Kragten 72af4a69d6 Bump codemirror (#8524) 2021-03-03 12:25:51 +01:00
Joakim Sørensen fe50f4229c Fix missing localize on old core versions (#8522) 2021-03-03 11:05:04 +01:00
J. Nick Koston ca4de877c1 Add remote more info card (#8506)
Co-authored-by: Philip Allgaier <philip.allgaier@gmx.de>
2021-03-02 17:57:49 -10:00
GitHub Action 1dfecf9618 Translation update 2021-03-03 01:23:10 +00:00
Bram Kragten 0a3505ed89 Dont show config changes when user saved it (#8520) 2021-03-02 21:43:45 +01:00
Joakim Sørensen 33cbf7eabe Fix localize action (#8519) 2021-03-02 20:45:35 +01:00
Joakim Sørensen 935d97ce1a Fix reload of addon after update (#8518) 2021-03-02 17:05:11 +01:00
Joakim Sørensen 9f73f0ca8d Merge update dialogs (#8516) 2021-03-02 16:39:54 +01:00
Bram Kragten d8cdbac15e Bumped version to 20210302.0 2021-03-02 15:49:51 +01:00
Joakim Sørensen 03b8c1348c Use localize in the Supervisor panel (#8515) 2021-03-02 14:46:30 +01:00
Bram Kragten 25a0be7672 Bump codemirror view (#8512)
Fixes search bug in Chrome
2021-03-02 10:05:16 +01:00
GitHub Action 08f1ce2d54 Translation update 2021-03-02 01:09:30 +00:00
Joakim Sørensen bea20d0495 🌐 Add MVP for translation in the Supervisor panel (#8425)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-03-02 00:37:39 +01:00
Bram Kragten 5ae10e8516 Bumped version to 20210301.0 2021-03-01 23:17:24 +01:00
Philip Allgaier e3f4a9ce5b Bump MDI icons to 5.9.55 (#8508) 2021-03-01 23:08:04 +01:00
Bram Kragten cf1fb606fb Bump codemirror + add more styling (#8503) 2021-03-01 22:55:02 +01:00
Bram Kragten 54ec81b67d Fix en-gb translations (#8502)
Renamed lang key in lokalise
2021-03-01 22:48:36 +01:00
Philip Allgaier f2a9725572 Make spelling more consistent (#8507) 2021-03-01 22:41:52 +01:00
Bram Kragten 4765114e80 Allow decimal slider steps (#8501) 2021-03-01 18:41:36 +01:00
Bram Kragten 5ff757ad65 Handle reconnect while in raw edit (#8500) 2021-03-01 16:55:39 +01:00
David F. Mulcahey 1642c68493 Add view in visualization button to the device page for ZHA devices (#8090) 2021-03-01 15:54:49 +01:00
Philip Allgaier f31f10cea9 Take cover "opening" and "closing" into account (#8490) 2021-03-01 12:58:12 +01:00
Philip Allgaier 76e0bbb55d Make section row text color themeable (#8488) 2021-03-01 12:56:56 +01:00
Joakim Sørensen f43af9c0a5 Show config if options or schema (#8487) 2021-03-01 12:55:14 +01:00
Paulus Schoutsen f7a3d2705c Preserve url params in redirect uri (#8495) 2021-03-01 12:54:39 +01:00
Joakim Sørensen 22c8af0cc5 Fix add-on store search (#8479) 2021-03-01 12:41:55 +01:00
Joakim Sørensen f263a5221d Adjust header and wording in update dialogs (#8476) 2021-03-01 12:40:52 +01:00
Bram Kragten 3834ab8ede Service dev tools: Add service picker to YAML mode (#8482) 2021-03-01 11:09:15 +01:00
GitHub Action e2e167630d Translation update 2021-03-01 01:25:12 +00:00
GitHub Action 01dd44300b Translation update 2021-02-28 01:24:14 +00:00
Bram Kragten b30160d671 Fix device picker (#8481) 2021-02-27 21:20:28 +01:00
GitHub Action f44d505b41 Translation update 2021-02-27 01:20:54 +00:00
Bram Kragten b58c17e75e make setup.py quite 2021-02-26 22:03:32 +01:00
Bram Kragten ae590d42dc Bumped version to 20210226.0 2021-02-26 21:39:43 +01:00
Bram Kragten d7917160c0 Update translations 2021-02-26 21:39:28 +01:00
Joakim Sørensen 01e4414d17 Ignore error if we are not connected (#8472) 2021-02-26 21:37:06 +01:00
Joakim Sørensen 0bc2eb530d Remove closing event from dialog (#8470) 2021-02-26 18:14:55 +01:00
Bram Kragten 12b124e5a3 Add search, history to codemirror (#8469)
And prevent jump on focus
2021-02-26 18:01:48 +01:00
Joakim Sørensen 478a4b2593 Add snapshot to core update dialogs (#8468) 2021-02-26 15:07:29 +01:00
Joakim Sørensen 9752e30eb4 Add snapshot to add-on update dialog. (#8463) 2021-02-26 14:44:27 +01:00
Joakim Sørensen af6e87ba31 Fix messaging when addon is not available (#8454) 2021-02-26 14:15:36 +01:00
Bram Kragten 64d390ad0f Fix wrong tag component (#8451)
Will rename the keys in Lokalise after merge
2021-02-26 14:01:16 +01:00
Joakim Sørensen c94bcb6896 Subscribe to message instead of event (#8443) 2021-02-26 13:47:48 +01:00
Joakim Sørensen 97f9df2f2d Add toggle to show_hide optional fields in add-on config (#8430) 2021-02-26 13:47:08 +01:00
GitHub Action 4e7f68a86c Translation update 2021-02-26 01:21:07 +00:00
Philip Allgaier 2f7f677549 Restore previous codemirror tab behavior (#8461)
* Restore previous tab behavior

* Handle via ondemand logic

* Combine imports
2021-02-25 22:24:07 +01:00
Bram Kragten f44d867d3a Bumped version to 20210225.0 2021-02-25 18:40:34 +01:00
Bram Kragten 6f636187f7 Clean translations (#8458) 2021-02-25 17:18:38 +01:00
Marc Randolph 9414f89e50 Add theme variables for text of picture cards (#8022) 2021-02-25 16:47:33 +01:00
Bram Kragten 60bf1a5451 Fix integrations page (#8457) 2021-02-25 15:59:04 +01:00
Philip Allgaier 32ba8f4731 Make clear that automation run button skips conditions + remove word "execute" from UI (#8259)
* Do not skip conditions when triggering an automation

* Remove usage of word "execute"

* More concise function names
2021-02-25 14:17:31 +01:00
Philip Allgaier 81f96de2bd Fix codemirror caret color (#8452) 2021-02-25 13:49:42 +01:00
Jesse Hills 0c417755ed Fix my redirect for tags (#8450) 2021-02-25 12:03:03 +01:00
GitHub Action 93e5bde797 Translation update 2021-02-25 01:20:40 +00:00
Bram Kragten b6eaf0a7c5 Fix setting service data on load when in yaml mode 2021-02-24 20:17:31 +01:00
Bram Kragten 5f1851bade Bumped version to 20210224.0 2021-02-24 20:06:20 +01:00
Bram Kragten 5c66a02711 My redirects tweaks (#8447) 2021-02-24 20:05:40 +01:00
Bram Kragten bde925a0e3 Migrate to codemirror 6 (#8382) 2021-02-24 19:16:54 +01:00
larena1 0f574a765b Fix excessive rerendering of history charts (#8340)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-02-24 17:48:50 +01:00
Kendell R 782b941531 Save attribute checkbox state (#8010) 2021-02-24 17:44:37 +01:00
Kendell R f42c0a0717 Add clipboard button (#8411) 2021-02-24 17:36:18 +01:00
Marc Mueller 13ac14d449 Add additional weblink attributes (#8295) 2021-02-24 17:34:44 +01:00
Philip Allgaier db9cea81db Correctly color script state icon + handle "single" mode for cancel buttons (#8383) 2021-02-24 17:18:38 +01:00
Bram Kragten 7c1fd542da Allow to disable config entry (#8442) 2021-02-24 17:10:59 +01:00
Joakim Sørensen 54a2b2534a Add add-on selector/picker (#8422) 2021-02-24 17:05:42 +01:00
Álvaro Fernández Rojas f5fb6c1e03 Support binary sensor batteries (#8367) 2021-02-24 17:00:07 +01:00
Bram Kragten 781c0701fc Show correct fields in UI mode (#8445) 2021-02-24 14:27:00 +01:00
GitHub Action 742f1f85dc Translation update 2021-02-24 01:20:56 +00:00
Joakim Sørensen a648e9be49 Fix atLeastVersion (#8437)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-02-23 22:17:22 +01:00
Joakim Sørensen fd9441dde2 Fix blank page in ingress when resizing window (#8439) 2021-02-23 16:16:24 +01:00
Philip Allgaier b5ec59c396 Dev-tools service: Tweak to target description (#8434) 2021-02-23 16:10:15 +01:00
Bram Kragten 60e4594abd Fix area picker with both entity and device filter (#8438) 2021-02-23 15:07:45 +01:00
GitHub Action 79692ef58a Translation update 2021-02-23 01:19:57 +00:00
J. Nick Koston ace7ee5622 Add support for percentage step size to fans (#8393) 2021-02-22 15:59:59 -06:00
Philip Allgaier 741ac679a0 Ensure we have all mandatory action keys present in action editor (#8424) 2021-02-22 21:10:06 +01:00
Bram Kragten d76af2cb61 Bumped version to 20210222.0 2021-02-22 20:06:30 +01:00
Bram Kragten b7d4c40736 Show flows in progress when picking a handler (#8368) 2021-02-22 20:06:18 +01:00
Bram Kragten 6092af8de6 Re-do developer tools service (#8410) 2021-02-22 19:53:52 +01:00
Bram Kragten 627424b8b9 Migrate mfa to Lit (#8276)
Co-authored-by: Joakim Sørensen <joasoe@gmail.com>
2021-02-22 19:53:37 +01:00
Joakim Sørensen e33aff7cf3 Fix mouseevent in blueprint import popup (#8432) 2021-02-22 17:56:43 +01:00
Joakim Sørensen ef0bfb237a bump webpack-manifest-plugin to 3.0.0 (#8426) 2021-02-22 10:30:02 +01:00
Joakim Sørensen c042c5568b Fix WS command for validating ingress session (#8427) 2021-02-22 10:28:18 +01:00
GitHub Action d84a7ee358 Translation update 2021-02-22 01:20:19 +00:00
GitHub Action 8bfc8ece9d Translation update 2021-02-21 01:21:30 +00:00
GitHub Action 2d3cf7d84d Translation update 2021-02-20 01:18:25 +00:00
Franck Nijhof 520ef8f1df Update GitHub Issue Form template (#8423) 2021-02-19 22:13:27 +01:00
Bram Kragten f251d4267f Revert "Allow viewport scaling (zooming) of frontend" (#8353)
This reverts commit da9faccada.
2021-02-19 18:06:36 +01:00
Bram Kragten 2052a5351c Ha-form: Don't change data (#8277) 2021-02-19 18:03:31 +01:00
Bram Kragten 9807d0aede Move localizing to render (#8419) 2021-02-19 18:02:25 +01:00
Bram Kragten a41afcd714 Update lovelace call service action (#8421) 2021-02-19 17:58:14 +01:00
Bram Kragten d93d2b5945 Fix password field in ha-form (#8400) 2021-02-19 17:47:51 +01:00
Bram Kragten d54a129605 Bump marked (#8420) 2021-02-19 17:46:33 +01:00
Philip Allgaier 77911980cb Correctly handle seconds in top "delay" key (#8415)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-02-19 11:03:25 +01:00
Bram Kragten d51fd1e2f9 Add supervisor_logs and supervisor_info redirects (#8417) 2021-02-19 10:01:21 +01:00
GitHub Action fe54f8eb16 Translation update 2021-02-19 01:18:52 +00:00
Bram Kragten fc7c4af27a Add more redirects (#8413) 2021-02-18 20:35:16 +01:00
Joakim Sørensen 09e7600d86 Use websockets (#8403)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-02-18 18:18:05 +01:00
GitHub Action 17410874e3 Translation update 2021-02-18 01:18:34 +00:00
GitHub Action 03d4174163 Translation update 2021-02-17 01:18:01 +00:00
Bram Kragten 99eff73b0d Add support for target to automation call service action (#8372) 2021-02-16 21:46:47 +01:00
Joakim Sørensen acefa39796 Update supervisor info on addon action (#8404) 2021-02-16 21:38:23 +01:00
Joakim Sørensen c01c0528a6 Show options if no options and schema (#8408) 2021-02-16 21:25:59 +01:00
Bram Kragten 0ec58007c9 Add my support to supervisor (#8405)
* Add my support to supervisor

* Remove localize

* Comments

* Update ha-panel-my.ts
2021-02-16 19:50:35 +01:00
GitHub Action e8daf88729 Translation update 2021-02-16 01:18:09 +00:00
Matteo Agnoletto ab74c7f7eb Add select selector for blueprints (#8297) 2021-02-15 10:22:00 +01:00
GitHub Action 6b673c7f44 Translation update 2021-02-15 01:18:50 +00:00
GitHub Action 53510a3cb9 Translation update 2021-02-14 01:19:47 +00:00
GitHub Action d4d38a880d Translation update 2021-02-13 01:17:10 +00:00
GitHub Action 18783d5e3b Translation update 2021-02-12 01:17:41 +00:00
Philip Allgaier eb235cb552 Add bottom margin to button card icon (#8362) 2021-02-11 13:39:31 +01:00
GitHub Action 435a6b6d53 Translation update 2021-02-11 01:17:07 +00:00
GitHub Action 8d13745c6b Translation update 2021-02-10 01:16:43 +00:00
Franck Nijhof 14c7cfc64c Add GitHub Issue Form (#8363) 2021-02-09 18:15:57 +01:00
Joakim Sørensen c7821b9cee Don't show add-on config if no schema (#8361) 2021-02-09 11:51:46 +01:00
GitHub Action a1d66aef0c Translation update 2021-02-09 01:17:05 +00:00
Jaroslav Hanslík e275f1f4b9 Fixed state card of number entity (#8325) 2021-02-08 16:28:28 +01:00
Joakim Sørensen 48de8b0739 Block snapshots when system is not running (#8350) 2021-02-08 16:18:33 +01:00
Joakim Sørensen b75dc0efe0 Fix issue with jumping config (#8355) 2021-02-08 16:18:01 +01:00
Paulus Schoutsen 1d498349c5 Update container port (#8352)
* Update container port

* Update .devcontainer/devcontainer.json

Co-authored-by: Joakim Sørensen <joasoe@gmail.com>
2021-02-08 16:09:40 +01:00
Bram Kragten 5cdcec699b Merge branch 'master' into dev 2021-02-08 15:14:36 +01:00
Bram Kragten cd72287d99 Bumped version to 20210208.0 2021-02-08 15:12:42 +01:00
Bram Kragten c8717bfa32 Add my panel (#8349) 2021-02-08 14:48:54 +01:00
GitHub Action 83de75b689 Translation update 2021-02-08 01:17:33 +00:00
Philip Allgaier e5ea762cbc Resolve merge conflict from PR #8121 2021-02-07 16:36:25 +01:00
Philip Allgaier 01df01cd66 Provide stub config for entity-filter (#8121)
* Provide stub config for entity-filter

* "card" option is optional since it has a default

* Search dynamically for stub config entities
2021-02-07 14:38:54 +01:00
Philip Allgaier 2c07a2c825 Correct typo in "find-entities.ts" file name (#8343) 2021-02-07 14:37:35 +01:00
chriss158 c3f50ba0fb Fix no disconnect after 5 minute timeout (#8339) 2021-02-07 14:33:44 +01:00
GitHub Action c04419fd09 Translation update 2021-02-07 01:18:53 +00:00
Paulus Schoutsen 9c7af0dfce Drop margin from cast header (#8331) 2021-02-06 23:00:06 +01:00
GitHub Action b66d14e980 Translation update 2021-02-06 01:15:59 +00:00
GitHub Action 6a553e9554 Translation update 2021-02-05 01:17:26 +00:00
Joakim Sørensen 4273b72d71 Fix issue where schema is null (#8322)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2021-02-04 14:01:53 +01:00
GitHub Action 9ccfa79199 Translation update 2021-02-04 01:16:30 +00:00
Tobias Sauerwein fe3d22d4f8 Only display current temp when not None (#8316)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-02-03 20:51:28 +01:00
Joakim Sørensen e06642e892 Show the reason why an add-on is not available (#8312)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-02-03 17:35:55 +01:00
Joakim Sørensen 5199e946a1 Fix button layout for addon-info (#8315) 2021-02-03 16:08:29 +01:00
Joakim Sørensen 17aff2f9b8 Move save button to the right (#8314) 2021-02-03 15:58:17 +01:00
Joakim Sørensen f7c7ac44f7 Show eMMC lifetime (#8302) 2021-02-03 15:52:52 +01:00
Joakim Sørensen 62dd0a561e Fix display issue wtih addon-info grid (#8313) 2021-02-03 15:45:01 +01:00
GitHub Action 858eacddea Translation update 2021-02-03 01:23:54 +00:00
Bram Kragten 471bb5169c Bumped version to 20210127.7 2021-02-02 21:24:52 +01:00
Bram Kragten 9d89aa329c Revert "Add icon support to gauge" (#8303)
Co-authored-by: Joakim Sørensen <joasoe@gmail.com>
2021-02-02 21:23:08 +01:00
Bram Kragten 4e4d8bdc5e Revert "Add icon support to gauge" (#8303)
Co-authored-by: Joakim Sørensen <joasoe@gmail.com>
2021-02-02 21:17:38 +01:00
GitHub Action a30ec32ac1 Translation update 2021-02-02 01:31:33 +00:00
Joakim Sørensen a9192ae2e1 Force YAML if schema has multiple (#8298) 2021-02-01 13:00:38 +01:00
Mike Morrison 3d4a0b02e5 Fix gaps in history charts (#8293) 2021-02-01 11:52:12 +01:00
GitHub Action 3659cf7c87 Translation update 2021-02-01 01:37:09 +00:00
GitHub Action fbcf35414c Translation update 2021-01-31 01:36:47 +00:00
Bram Kragten d79e5dd8fb Bumped version to 20210127.6 2021-01-30 22:51:57 +01:00
Philip Allgaier 92b116c0da More precise name handling for auto-generated dashboards (#8289)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-01-30 22:51:46 +01:00
Bram Kragten da3f911deb Fix tts try on ios (#8292) 2021-01-30 22:51:32 +01:00
Philip Allgaier 9d82ce8ab4 Add missing device_classes to sensor (#8288) 2021-01-30 22:51:13 +01:00
Bram Kragten db9597d2e7 Don't use badges in generated Lovelace + group entities by area (#8291) 2021-01-30 22:50:56 +01:00
Philip Allgaier 1523558f4c More precise name handling for auto-generated dashboards (#8289)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-01-30 22:49:48 +01:00
Bram Kragten bd9f4fe41c Fix tts try on ios (#8292) 2021-01-30 22:42:01 +01:00
Philip Allgaier 6b938b2597 Add missing device_classes to sensor (#8288) 2021-01-30 21:32:25 +01:00
Bram Kragten 5a8009e46e Don't use badges in generated Lovelace + group entities by area (#8291) 2021-01-30 21:30:47 +01:00
GitHub Action c92eeb6bb7 Translation update 2021-01-30 01:33:03 +00:00
Bram Kragten 8ea6baaf5d Bumped version to 20210127.5 2021-01-29 18:38:36 +01:00
Bram Kragten 1ed03842c0 Fix grid + map editor (#8284) 2021-01-29 18:38:25 +01:00
Bram Kragten 0e9984413c Fix grid + map editor (#8284) 2021-01-29 18:37:58 +01:00
Philip Allgaier 362b419814 Add missing extra field translation for cover position (#8273)
* Ensure ha-form-integer passes "0" to form data

* Only keep the translation change
2021-01-29 18:14:07 +01:00
chriss158 bffcccc1fe Fix external auth reconnection loop if connection lost after refresh token expiration (#8279) 2021-01-29 18:13:48 +01:00
Bram Kragten b8e9a4ce9f Fix map editor (#8280) 2021-01-29 18:13:26 +01:00
Bram Kragten bdff3fd452 Z-wave migration tweaks (#8283) 2021-01-29 18:11:25 +01:00
Bram Kragten 1fc51f0087 Bumped version to 20210127.4 2021-01-29 18:10:58 +01:00
Bram Kragten 226013d999 Z-wave migration tweaks (#8283) 2021-01-29 18:10:18 +01:00
Joakim Sørensen 86847263b8 Initial UI config for add-ons (#8271) 2021-01-29 18:06:38 +01:00
Bram Kragten b798523d53 Fix map editor (#8280) 2021-01-29 17:19:01 +01:00
chriss158 bd59c4fccf Fix external auth reconnection loop if connection lost after refresh token expiration (#8279) 2021-01-29 13:40:09 +01:00
GitHub Action 566ffe24a4 Translation update 2021-01-29 01:33:55 +00:00
Philip Allgaier 0e5c1b2041 Add missing extra field translation for cover position (#8273)
* Ensure ha-form-integer passes "0" to form data

* Only keep the translation change
2021-01-28 22:39:45 +01:00
Bram Kragten 9a088a21da Bumped version to 20210127.3 2021-01-28 22:35:49 +01:00
Bram Kragten 1160d27004 Revert "Bumped version to 20210127.2"
This reverts commit 3766f44787.
2021-01-28 22:34:31 +01:00
Bram Kragten b4e5740050 Fix race condition in zwave migration (#8268) 2021-01-28 20:59:54 +01:00
Bram Kragten 12bb3f5796 Use close dialog function to close device registry detail dialog (#8269) 2021-01-28 20:59:37 +01:00
Bram Kragten ff62fdb69d hide config links in demo (#8267) 2021-01-28 20:59:15 +01:00
Bram Kragten 4ebf32cb1f Move try tss button to bottom (#8266) 2021-01-28 20:58:59 +01:00
Thomas Lovén 5afb8a77a9 Make input_text entity row usable when value is "unknown" (#8258) 2021-01-28 20:58:43 +01:00
Jaroslav Hanslík 48ed33af95 Typo in texts (#8265) 2021-01-28 20:58:26 +01:00
Jaroslav Hanslík 4a64cd4464 Typo in texts (#8264) 2021-01-28 20:58:12 +01:00
Paulus Schoutsen 8ae1a1b558 Fix tts (#8261) 2021-01-28 20:57:56 +01:00
Philip Allgaier ef1dd8b761 Add check to prevent undefined access during action validation (#8257) 2021-01-28 20:57:41 +01:00
Bram Kragten 3766f44787 Bumped version to 20210127.2 2021-01-28 20:57:15 +01:00
Bram Kragten 101067d018 Fix race condition in zwave migration (#8268) 2021-01-28 20:20:07 +01:00
Bram Kragten 448d19bfbb Use close dialog function to close device registry detail dialog (#8269) 2021-01-28 20:18:13 +01:00
Bram Kragten c1caad6d43 hide config links in demo (#8267) 2021-01-28 20:16:00 +01:00
Bram Kragten a653bf5b0d Move try tss button to bottom (#8266) 2021-01-28 20:10:15 +01:00
J. Nick Koston afdb369e04 Separate fan speeds into percentages and presets modes (#8216) 2021-01-28 09:24:18 -06:00
Thomas Lovén f02841409c Make input_text entity row usable when value is "unknown" (#8258) 2021-01-28 14:05:07 +01:00
Kendell R d4000cf662 Allow theming of header edit background color (#8246)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-01-28 12:00:40 +01:00
Jaroslav Hanslík e1cf5919fa Typo in texts (#8265) 2021-01-28 11:55:00 +01:00
Jaroslav Hanslík d33e8d77c2 Typo in texts (#8264) 2021-01-28 11:54:34 +01:00
GitHub Action 3a522215a9 Translation update 2021-01-28 01:34:39 +00:00
Paulus Schoutsen 7de6ea0879 Fix tts (#8261) 2021-01-27 23:42:06 +01:00
Joakim Sørensen 5ee0250ba5 Add hostname and metrics to Add-ons (#8253) 2021-01-27 22:38:07 +01:00
Philip Allgaier 69d0a22091 Add check to prevent undefined access during action validation (#8257) 2021-01-27 18:39:03 +01:00
Philip Allgaier 4d6c11ce31 Enforce "good" states of binary_sensor as green in history timeline chart (#8145) 2021-01-27 17:22:01 +01:00
Bram Kragten 178605664e Bumped version to 20210127.1 2021-01-27 17:17:48 +01:00
Bram Kragten 12acb5473b Bumped version to 20210127.1 2021-01-27 17:17:20 +01:00
Joakim Sørensen 0cf8004b8d Add twine to release flow (#8254) 2021-01-27 17:14:00 +01:00
Joakim Sørensen 6b0a8eae74 Add twine to release flow (#8254) 2021-01-27 17:13:18 +01:00
Bram Kragten 00412c7216 Merge pull request #8252 from home-assistant/dev 2021-01-27 16:24:07 +01:00
Bram Kragten 6483f23558 Merge branch 'master' into dev 2021-01-27 16:10:48 +01:00
Bram Kragten 4234d51a29 Bumped version to 20210127.0 2021-01-27 16:09:35 +01:00
Bram Kragten 9243d300cc Zwave -> OZW migration (#7765)
Co-authored-by: Joakim Sørensen <joasoe@gmail.com>
2021-01-27 16:01:18 +01:00
Thomas Lovén 2ce70206c6 Markdown format blueprint descriptions in import dialog (#8251) 2021-01-27 16:00:15 +01:00
Bram Kragten b22455d2a5 Add simple try tts dialog (#8245) 2021-01-27 15:58:58 +01:00
Philip Allgaier 4f0bb9f6c3 Add icon support to gauge (#8081) 2021-01-27 13:36:03 +01:00
Philip Allgaier 7dfa1b0942 Convert state history chart to LitElement + add warning if history is disabled (#7994)
Co-authored-by: Ian Richardson <iantrich@gmail.com>
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-01-27 13:13:40 +01:00
Philip Allgaier 093e23c006 Auto capitalize first letter of entity row name (#7962) 2021-01-27 12:38:39 +01:00
Philip Allgaier 5f003ccbe2 Add navigation to dashboard & resources to lovelace editor + icons (#8156) 2021-01-27 12:37:28 +01:00
Philip Allgaier 8d0608610f Entity registry: Translate advanced section + direct link to entity customization (#8181) 2021-01-27 12:35:25 +01:00
Philip Allgaier 8bfe583a20 Convert customize to LitElement + "real" entity picker + option to directly jump to entity (#8180)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-01-27 12:34:31 +01:00
Philip Allgaier 0a3172dfdb More comprehensive YAML config errors + dynamic checks for action configs (#8217) 2021-01-27 12:33:57 +01:00
Thomas Lovén 5c0e151bc2 Add selectors for text and arbitrary objects (#8152) 2021-01-27 11:45:51 +01:00
Ian Richardson e69f36047d add actions to weather-forecast-card (#7659) 2021-01-27 10:59:09 +01:00
Philip Allgaier ed368ddd9d Add support for delay values split into parts + millisecond time input (#7886)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-01-27 10:46:15 +01:00
GitHub Action 8400e90e34 Translation update 2021-01-27 01:32:26 +00:00
Paulus Schoutsen 4cd95b724b Use minified bundle of node vibrant (#7784)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-01-26 23:24:23 +01:00
Shulyaka 46b3836fbd Add number entity support (#7876) 2021-01-26 23:14:23 +01:00
Tim McCormick 9988227d93 Don't assume position of items in array (#7927) 2021-01-26 22:59:58 +01:00
Bram Kragten 9d289bfa34 Add download dump button to zwave_js panel (#8232)
Co-authored-by: Philip Allgaier <mail@spacegaier.de>
2021-01-26 22:20:16 +01:00
Bram Kragten 048de6b388 Fix translation action (#8244) 2021-01-26 17:21:38 +01:00
Philip Allgaier 9b7d8934da Correctly handle "hours to show" for footer graph (#8071)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-01-26 16:15:32 +01:00
Philip Allgaier 0dd9b21c2d Add minutes to hourly weather forecast time display (#8182)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-01-26 16:14:34 +01:00
Philip Allgaier 417184525f Adjust calendar translations to fix loading issue in calendar card (#8235) 2021-01-26 10:42:13 +01:00
Philip Allgaier 0a09ec706f Visual alignment of automation and script more info dialogs (#8234) 2021-01-26 10:25:34 +01:00
Philip Allgaier cf43b26e14 Prevent automation action row issue if event name gets cleared (#8213)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-01-25 20:01:30 +01:00
Philip Allgaier a8b27e224f Allow themeing of media control card (#8209)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-01-25 20:00:50 +01:00
Philip Allgaier dc5d14834d Added missing translations for some automation extra-fields (#8210) 2021-01-25 11:59:44 +01:00
Philip Allgaier 12c935f647 Show entity_picture in logbook (#8118) 2021-01-25 11:48:16 +01:00
Philip Allgaier 7710cb245c Use correct color for shopping list card section header (#8223) 2021-01-25 11:35:04 +01:00
b3nj1 a6b77c0457 entity-filter card allow state_filter.value == 0 (number 0) (#8225) 2021-01-25 10:54:22 +01:00
Philip Allgaier 748a05f355 Use app header color as default selected tab color + allow styling (#8227) 2021-01-25 10:53:53 +01:00
Philip Allgaier 831b9da0cf Various demo tweaks + cleanups (#8229) 2021-01-25 10:52:59 +01:00
Charles Garwood a3339c9d5f Add wizards for adding and removing Z-Wave JS nodes (#8174) 2021-01-25 10:46:50 +01:00
Philip Allgaier d228f38471 Correct URL to automation run modes documentation (#8230) 2021-01-25 01:51:00 +01:00
Philip Allgaier fe13853b8b Allow timestamp attribute formatting + central timestamp formats (#8162) 2021-01-22 20:24:31 +01:00
Philip Allgaier 6f4dbdc959 Improve entity registry errors display (#8208) 2021-01-22 15:50:11 +01:00
Bram Kragten 870f0bcbb1 Fix some dialog close history issues (#8102)
Co-authored-by: Zack Barett <arnett.zackary@gmail.com>
Co-authored-by: Philip Allgaier <mail@spacegaier.de>
2021-01-22 13:33:10 +01:00
Philip Allgaier 599dd81e3c Make "button row" name optional to be consistent with "button card" and "buttons row" (#8189) 2021-01-21 20:03:53 +01:00
Philip Allgaier 9e99d158fd Add error handling to action handling (#8187) 2021-01-21 19:38:19 +01:00
Philip Allgaier 136ebb5a07 Set sensible default tap_action (same logic as button row) (#8186) 2021-01-21 16:44:17 +01:00
Charles Garwood 707338b1aa Add Z-Wave info to device page for zwave_js devices (#8195) 2021-01-21 16:16:24 +01:00
Bram Kragten 7e06bd53b6 Change remote ui portal to account page (#8193) 2021-01-18 16:47:14 +01:00
Joakim Sørensen 08c1b864fc Fix missing addon list when creating partial snapshot (#8176) 2021-01-17 17:34:14 +01:00
Thomas Lovén 16e7a16d12 Fix date picker text color (#7776) 2021-01-16 20:48:18 +01:00
Thomas Lovén 45200da32f Helpful comment. See #8152 (#8160) 2021-01-16 16:40:59 +01:00
Philip Allgaier 84a9ca59ef Add "columns" and "square" to grid card editor (#7888) 2021-01-16 16:25:13 +01:00
Charles Garwood 40f4c35b42 Initial Z-Wave JS Config Panel (#8166) 2021-01-16 16:22:27 +01:00
Philip Allgaier bb77d34017 Tweaks to cloud integration page (#7878) 2021-01-16 16:19:57 +01:00
Philip Allgaier 9659ebe59b Increase font size for alarm keypad number buttons (#8120) 2021-01-16 16:09:52 +01:00
Thomas Lovén 16c914b139 Show default value for selector-less blueprint inputs. (#8163) 2021-01-15 14:30:27 +01:00
Philip Allgaier 142f26add1 Align entities card section header styling (#8113) 2021-01-14 13:05:37 +01:00
Philip Allgaier ef7d2aea8d Fix config update handling for map card editor (#8115) 2021-01-14 12:52:58 +01:00
Philip Allgaier 1aa40cb6df Convert state-card-display to LitElement and use timestamp display (#8150)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-01-14 12:49:29 +01:00
Philip Allgaier cd06b931a9 Allow empty/undefined for counter min & max (#8154) 2021-01-14 12:25:06 +01:00
Mick Vleeshouwer fd2df92000 Change supportsStop to supportsStopTilt (#8153) 2021-01-14 12:24:26 +01:00
Philip Allgaier b565a8b8f7 Allow "idle" media players to be switched of in entity row (#8155) 2021-01-14 11:35:31 +01:00
Philip Allgaier 1aab656705 Make entity-filter and glances demo a bit clearer & cleaner (#8157) 2021-01-14 11:20:18 +01:00
Philip Allgaier 6919d0cde6 Ensure we always show backend error in automation and script editor (#8139)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-01-13 20:45:40 +01:00
Philip Allgaier 95fd8de1bc Adjust person / presence detection texts (#8117) 2021-01-13 20:13:37 +01:00
Philip Allgaier 28ca1a5193 Adjust / theme entity marker background color (#8116) 2021-01-13 17:40:15 +01:00
Joakim Sørensen a1d07e5a00 Sort imports (#8104) 2021-01-13 17:17:12 +01:00
Philip Allgaier 131a7f3782 Clear entity ID during automation duplication (#8140) 2021-01-13 17:12:51 +01:00
Philip Allgaier c28a7d6c10 Center align content of date input (#8134) 2021-01-13 17:10:08 +01:00
J. Nick Koston 6b20bb967b Add support for dhcp discovery (#8149) 2021-01-13 11:07:32 +01:00
Paulus Schoutsen 75f228418d Allow configuring default TTS voice for cloud (#8148) 2021-01-13 11:07:03 +01:00
Joakim Sørensen 41f8b0d19b Add verify-version step to release (#8138) 2021-01-12 10:32:38 +01:00
Paulus Schoutsen cea3b8b010 Show name on ignored entries (#8135) 2021-01-12 09:26:31 +01:00
Nikfinn99 aba0e1f026 Automation - State Condition display 'for:' in frontend (#8124)
* Automation - State Condition display 'for:' in frontend

* rename variable to better name
2021-01-11 17:24:39 +01:00
Philip Allgaier f42587af22 Format URL attribute as link (#8126)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-01-11 15:59:26 +01:00
Joakim Sørensen edcb7e87bb Hide configuration card if the add-on does not expose options or schema (#8131) 2021-01-11 14:43:24 +01:00
Joakim Sørensen 96f0ceeb8c Update supervisor on add-on store refresh (#8132) 2021-01-11 14:42:58 +01:00
Joakim Sørensen a9baa7f1c1 Move netlify preview builds from Azure to Github (#8105)
* Move netlify preview builds from Azure  to Github

* Remove checkout

* Update secret names
2021-01-11 14:03:21 +01:00
Joakim Sørensen b1483287dc Add Core to supervisor/system (#8123) 2021-01-11 13:51:52 +01:00
Thomas Lovén a4657541fc Show location name in auth page, if available. (#8119) 2021-01-11 13:11:30 +01:00
Pikles 56d88b4c56 Add CSS Variables for Menu (#7917) 2021-01-11 12:50:33 +01:00
Joakim Sørensen 02313e4be8 Move translations from Azure to Github (#8129)
* Move translations from Azure to Github

* remove [ci skip]
2021-01-11 12:27:10 +01:00
Joakim Sørensen 2a14a3a4dc Move release builds from Azure to Github (#8128)
* Move release builds from Azure to Github

* Fix syntax issue (YAML is hard)
2021-01-11 12:14:53 +01:00
Philip Allgaier d02a2e8c2e Consistently use isComponentLoaded() helper (#7995) 2021-01-10 19:52:22 +01:00
Bram Kragten 0d281f8437 Fix for undefined stateObj in media player row (#8114) 2021-01-08 09:39:55 -06:00
Thomas Lovén 2b0f43f334 Format blueprint descriptions with markdown (#8109) 2021-01-08 12:22:12 +01:00
Philip Allgaier f9d28fc124 Add blank between temperature and unit in weather card (#8111) 2021-01-08 11:10:39 +01:00
Philip Allgaier da07173471 Prevent YAML mode if no entity is set in card editor (#8110) 2021-01-08 11:07:04 +01:00
Joakim Sørensen 4deeff7029 Prefix supervisor version with supervisor- (#8107) 2021-01-08 10:54:12 +01:00
Bram Kragten 1538fbb102 Bump material elements (#8093) 2021-01-07 10:09:03 +01:00
Philip Allgaier b9259b87eb Beautify ha-attribute <pre> (#8101) 2021-01-06 20:46:39 +01:00
David F. Mulcahey 30997dbc88 Add filtering and zoom to node to the ZHA network visualization (#7913) 2021-01-06 10:37:53 +01:00
Bram Kragten a9d926e80a Bumped version to 20201229.1 2021-01-05 20:38:52 +01:00
Philip Allgaier c41369c89c Add EN fallback text for dismiss button (#8068) 2021-01-05 20:38:36 +01:00
Philip Allgaier 656bef3da9 Prevent relative time text wrapping in more-info-sun (#8051) 2021-01-05 20:38:18 +01:00
Paulus Schoutsen 607eb6d130 Prefer target over environment (#8092) 2021-01-05 16:03:55 +01:00
Philip Allgaier f2e9b3577d Use proper styled confirmation dialog for handled actions (#8077) 2021-01-05 14:04:45 +01:00
Philip Allgaier cb2c6d8560 Convert gallery elements to LitElement (#8088) 2021-01-05 13:29:21 +01:00
dependabot[bot] bfe8346ced Bump ini from 1.3.5 to 1.3.7 (#7949)
Bumps [ini](https://github.com/isaacs/ini) from 1.3.5 to 1.3.7.
- [Release notes](https://github.com/isaacs/ini/releases)
- [Commits](https://github.com/isaacs/ini/compare/v1.3.5...v1.3.7)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-01-05 11:47:56 +01:00
Siemon Geeroms 88da9bb91b Allow to show modal dialogs in iFrame panels (#7971) 2021-01-05 11:38:46 +01:00
Shane Qi 5e2ee1a16c Added Drag & Drop Reordering to Shopping List Card. (#7296) 2021-01-05 11:24:41 +01:00
Philip Allgaier 2fdc746392 Add support for "all" as entity ID in action "service" (#7954) 2021-01-05 11:02:47 +01:00
Philip Allgaier 1667973a66 Improve spacing in entity rows + secondary ellipsis (#8028) 2021-01-05 10:50:40 +01:00
Philip Allgaier 42f0101440 Add gallery demo for plant card (#7989) 2021-01-04 22:10:58 +01:00
Philip Allgaier 13b69bff1b Translate timestamp-display errors + tiny tweaks (#8086)
* Translate timestamp-display errors + tiny tweaks

* Adjust translation key
2021-01-04 20:55:43 +01:00
Philip Allgaier 2c2226dfd6 Slightly increase max attribute value width (#8085) 2021-01-04 19:51:30 +01:00
Philip Allgaier a3fdfe0e15 Add additional entities to gallery more-info-light (#7930) 2021-01-04 18:06:47 +01:00
Philip Allgaier a0de209a55 Aligned gallery more-info with hui-cards (#7931) 2021-01-04 18:05:30 +01:00
Philip Allgaier 0208b50ac7 Disable counter buttons if entity is unavailable (#8084) 2021-01-04 18:02:01 +01:00
Philip Allgaier 4a61779aba Ensure YAML editor gets updated during action change / deletion (#8024)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-01-04 15:01:25 +01:00
Philip Allgaier 05057ade05 Catch navigator.clipboard errors (#7942)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-01-04 14:04:54 +01:00
Abílio Costa 6fb206853c Switch header sizes for ZHA pairing status card (#8078)
Since the top title shows the result of the previous step ("Interview
Complete", "Configuration Complete", etc), while the second title shows
the current step ("Configuring", "Initializing", etc), having the top
title bigger would draw attention to it. So a user could see
"Configuration Complete" and assume pairing is done.

This change makes the second title bigger than the top one, so that the
user focus on the step that is in progress.
2021-01-04 14:03:12 +01:00
Charles Garwood fab68055bf OZW Panel: Don't show an empty wakeup card (#8064) 2021-01-04 10:57:25 +01:00
Philip Allgaier d5a77ef3cd Treat zero as String in number selector (#8070) 2021-01-04 10:53:40 +01:00
Philip Allgaier dcb2605de4 Add Bengali language (#8014) 2021-01-04 10:28:25 +01:00
Philip Allgaier e6d38f4539 Fix incorrect date selection ranges for history and logbook (#8045) 2021-01-04 10:04:14 +01:00
Philip Allgaier 293b56cfa6 Show actual error path/key in YAML card editor (#8076) 2021-01-04 09:58:07 +01:00
Philip Allgaier 775e93d54b Ensure consistent number display for gauge (#8080) 2021-01-04 09:24:51 +01:00
Philip Allgaier 7f840e75df Add EN fallback text for dismiss button (#8068) 2021-01-04 09:18:08 +01:00
Philip Allgaier 2113ea675e Prevent relative time text wrapping in more-info-sun (#8051) 2021-01-04 09:17:17 +01:00
Philip Allgaier 916a5c1a6b Make it clearer that we are looking for the YAML card config 2021-01-04 00:31:42 +01:00
HomeAssistant Azure f684531315 [ci skip] Translation update 2021-01-02 00:41:18 +00:00
HomeAssistant Azure fe8dda8996 [ci skip] Translation update 2020-12-31 00:32:48 +00:00
HomeAssistant Azure 4cd4b328c8 [ci skip] Translation update 2020-12-30 00:32:31 +00:00
Bram Kragten 3d7ee6a4df Merge pull request #8048 from home-assistant/dev 2020-12-29 23:23:58 +01:00
Bram Kragten d844c89b94 Merge branch 'master' into dev 2020-12-29 23:12:14 +01:00
Bram Kragten 177ea2b85a Bumped version to 20201229.0 2020-12-29 23:09:00 +01:00
Marc Mueller 50c5c15f49 Update group reload string (#7938)
* Update group reload string

* Update src/translations/en.json

Co-authored-by: Philip Allgaier <philip.allgaier@gmx.de>

* Update rest reload string

Co-authored-by: Philip Allgaier <philip.allgaier@gmx.de>
2020-12-29 23:03:44 +01:00
Philip Allgaier 1810760dc7 Mark entity ID as optional for button card (#7967) 2020-12-29 23:02:47 +01:00
Joakim Sørensen 4635b92e3f Fix add-on stage icon rendering (#7981) 2020-12-29 22:59:07 +01:00
Jochen Mehlhorn 1c652626eb Fix typo in hassio-supervisor-info.ts (#7907)
Fix typo in "unhealthy state" warning
2020-12-29 22:58:29 +01:00
Philip Allgaier 2000cfb1db Correct tabs for customizations page (#7990) 2020-12-29 22:52:58 +01:00
Philip Allgaier f4d07828e7 Minor follow-up tweaks for PR 7947 (#7955) 2020-12-29 22:52:20 +01:00
Philip Allgaier 95b552671c Fix border radius for labeled slider (#7929) 2020-12-29 22:50:09 +01:00
Philip Allgaier ef3bc3efe1 Do not render "No Area" in device table to reduce clutter (#7986) 2020-12-29 22:46:47 +01:00
Philip Allgaier 371ad899f5 Handle sorting with "undefined" (move always to bottom) (#7985) 2020-12-29 22:45:39 +01:00
plafü 2c54158d84 Fixes typo: 'bring to live'>'bring to life' (#8000) 2020-12-29 22:44:21 +01:00
quthla 5d9e30bbdc Fix translation of home state (#8015) 2020-12-29 22:43:41 +01:00
Marc Randolph e477fd567d Button text is swapped on Lovelace raw YAML exit window (#8038)
Button text is swapped compared to the same thing elsewhere: https://github.com/home-assistant/frontend/blob/d23165d06ae57cddef2529481d1c4d857f3e20b6/src/panels/lovelace/editor/card-editor/hui-dialog-edit-card.ts#L311
2020-12-29 22:38:31 +01:00
Philip Allgaier 6a6c2937fe Ensure consistent spelling of "ID" (#8042) 2020-12-29 22:36:28 +01:00
HomeAssistant Azure 09e17c4da8 [ci skip] Translation update 2020-12-29 00:32:26 +00:00
HomeAssistant Azure fd00469d11 [ci skip] Translation update 2020-12-28 00:32:37 +00:00
HomeAssistant Azure cbbeb795f3 [ci skip] Translation update 2020-12-27 00:32:32 +00:00
HomeAssistant Azure bba40e0da8 [ci skip] Translation update 2020-12-25 00:32:32 +00:00
HomeAssistant Azure d23165d06a [ci skip] Translation update 2020-12-24 00:33:07 +00:00
HomeAssistant Azure 405fef6f03 [ci skip] Translation update 2020-12-23 00:32:49 +00:00
HomeAssistant Azure 588f217826 [ci skip] Translation update 2020-12-22 00:32:27 +00:00
HomeAssistant Azure 3d8b7cf80e [ci skip] Translation update 2020-12-21 00:32:26 +00:00
HomeAssistant Azure c0ef923ad3 [ci skip] Translation update 2020-12-20 00:32:26 +00:00
HomeAssistant Azure 3df44fc71e [ci skip] Translation update 2020-12-19 00:33:33 +00:00
HomeAssistant Azure c1965492d9 [ci skip] Translation update 2020-12-18 00:32:38 +00:00
HomeAssistant Azure 1f56ffde80 [ci skip] Translation update 2020-12-17 00:33:13 +00:00
Georgi Kirichkov f335fdc002 Fixes a typo in hassio-supervisor-info.ts (#7987)
An "a" was missing in installtion
2020-12-16 10:28:21 +01:00
HomeAssistant Azure 0c914b5ec8 [ci skip] Translation update 2020-12-16 00:32:25 +00:00
Philip Allgaier d767b06858 Fix spelling error (#7961)
Fixes https://github.com/home-assistant/frontend/issues/7958
2020-12-15 07:22:06 -06:00
Fabian Affolter d4e49f3944 Update entry (#7978) 2020-12-15 09:49:26 +01:00
HomeAssistant Azure 7dfc5b3faf [ci skip] Translation update 2020-12-15 00:32:22 +00:00
HomeAssistant Azure 8a88033ab9 [ci skip] Translation update 2020-12-14 00:32:42 +00:00
HomeAssistant Azure 7b06b38c94 [ci skip] Translation update 2020-12-13 00:32:43 +00:00
Bram Kragten 5409752817 20201212.0 (#7952)
* [ci skip] Translation update

* Add link to the community forums to find more blueprints (#7947)

* Add link to the community forums to find more blueprints

* Apply suggestions from code review

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>

* Fix `ha-relative-time` usage for tags and sun (#7944)

* Bumped version to 20201212.0

Co-authored-by: HomeAssistant Azure <hello@home-assistant.io>
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
Co-authored-by: Philip Allgaier <mail@spacegaier.de>
2020-12-12 20:57:28 +01:00
Bram Kragten 909f3a3005 Bumped version to 20201212.0 2020-12-12 20:48:44 +01:00
Philip Allgaier 4930532c7b Fix ha-relative-time usage for tags and sun (#7944) 2020-12-12 20:46:56 +01:00
Bram Kragten 8a42e65c6a Add link to the community forums to find more blueprints (#7947)
* Add link to the community forums to find more blueprints

* Apply suggestions from code review

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2020-12-12 20:38:10 +01:00
HomeAssistant Azure 5d4121a9b4 [ci skip] Translation update 2020-12-11 00:32:17 +00:00
Bram Kragten a70e6c49a1 Merge pull request #7940 from home-assistant/dev
20201210.0
2020-12-10 16:56:08 +01:00
Bram Kragten 3d83d5f4b5 Bumped version to 20201210.0 2020-12-10 15:57:13 +01:00
Philip Allgaier f9dece0743 Add copy YAML (automation & script) fallback without navigator.clipboard (#7900) 2020-12-10 15:55:57 +01:00
Bram Kragten ac0871d0e8 Use correct device name in target picker (#7939) 2020-12-10 15:12:33 +01:00
HomeAssistant Azure ffc19e591d [ci skip] Translation update 2020-12-10 00:32:24 +00:00
HomeAssistant Azure c53380ca3d [ci skip] Translation update 2020-12-09 00:32:38 +00:00
Bram Kragten 7c74a2026a Correct tabs helpers page (#7928)
Fixes https://github.com/home-assistant/frontend/issues/7926
2020-12-08 11:47:30 +01:00
HomeAssistant Azure adaed438d9 [ci skip] Translation update 2020-12-08 00:32:29 +00:00
uvjustin baf38305cb Remove use of named groups in regexp (#7921) 2020-12-07 15:18:27 +01:00
HomeAssistant Azure 8254712521 [ci skip] Translation update 2020-12-07 00:33:14 +00:00
HomeAssistant Azure 53214781e3 [ci skip] Translation update 2020-12-06 00:32:39 +00:00
HomeAssistant Azure 88cbbbdf65 [ci skip] Translation update 2020-12-05 00:33:30 +00:00
Bram Kragten c10dca9c7b Merge pull request #7910 from home-assistant/dev 2020-12-04 21:46:54 +01:00
Bram Kragten 7f2ebb4bde Bumped version to 20201204.0 2020-12-04 21:33:37 +01:00
Bram Kragten f1abb60e4a Add message when no matches for selectors, clear config when deleted (#7906) 2020-12-04 20:49:24 +01:00
Bram Kragten e014c7aff6 Entity is not required for button card (#7909)
Fixes https://github.com/home-assistant/frontend/issues/7908
2020-12-04 12:02:58 -06:00
Bram Kragten b79c03433e Don't update device picker while open (#7903) 2020-12-04 12:05:01 +01:00
HomeAssistant Azure 34eb4d974d [ci skip] Translation update 2020-12-04 00:32:26 +00:00
Zack Barett 3264be3c5e Move main function to events on hui-view for custom views to use (#7861) 2020-12-03 18:16:55 -06:00
Bram Kragten 655f4f75fb Change import blueprint fab icon (#7894) 2020-12-03 22:41:13 +01:00
Bram Kragten 4383f31696 Translation logic tweaks (#7901)
* Translation logic tweaks

* Comments
2020-12-03 22:29:52 +01:00
Paulus Schoutsen 99eb15d15e Improve blueprint translations (#7892) 2020-12-03 18:08:14 +01:00
Bram Kragten 2682c6e150 Merge pull request #7891 from home-assistant/dev 2020-12-03 17:22:08 +01:00
Bram Kragten 3a5d854e6d Bumped version to 20201203.0 2020-12-03 17:00:56 +01:00
Bram Kragten 1e90c6387c More BP tweaks (#7884) 2020-12-03 09:59:43 -06:00
Philip Allgaier 2cca25f4d0 Navigate back to area overview after area deletion (#7890) 2020-12-03 16:52:44 +01:00
Bram Kragten 565724d201 Fix translation race condition? (#7885) 2020-12-03 16:52:09 +01:00
Bram Kragten 3e4955becd UI tweaks for BP (#7883)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2020-12-03 14:18:17 +01:00
Philip Allgaier 7b560c727f Adjust text since entities can now be in areas too (#7880) 2020-12-03 14:05:16 +01:00
HomeAssistant Azure 35abd9dfdb [ci skip] Translation update 2020-12-03 00:32:38 +00:00
Bram Kragten b7ccf3e0e5 Merge pull request #7875 from home-assistant/dev 2020-12-02 19:51:36 +01:00
Bram Kragten 0d9ab8fdd0 Bumped version to 20201202.0 2020-12-02 19:31:43 +01:00
Bram Kragten 303f9290a8 Add more device disabled ui (#7874) 2020-12-02 19:31:06 +01:00
Bram Kragten e0c4dc08a1 Tooltip tweak target picker (#7870) 2020-12-02 11:21:51 -06:00
Bram Kragten 8c655883fe Add device disabled reason (#7873) 2020-12-02 18:20:29 +01:00
Bram Kragten ba90785115 Fix card picker cards (#7871) 2020-12-02 17:46:30 +01:00
Bram Kragten 7b392b626b Hardcode history card stub entity to sun.sun (#7760) 2020-12-02 10:16:46 -06:00
Zack Barett 8e4ceb7d48 Fix View Background Colors (#7823) 2020-12-02 17:08:18 +01:00
Philip Allgaier 2ab1c6e9a9 Make media player card work as square + add to gallery + icon pos tweaks (#7727) 2020-12-02 16:57:19 +01:00
Adam Ernst dbdced0971 Improve descriptions for Configuration entries (#7606) 2020-12-02 16:36:21 +01:00
Philip Allgaier 5e481880bd Allow empty entities array with entity-filter (#7854) 2020-12-02 16:34:59 +01:00
Kendell R faec063f34 Add --masonry-view-card-margin (#7395) 2020-12-02 16:22:20 +01:00
Zack Barett bbea38d227 Lovelace Card Editor: Preview Background (#7678) 2020-12-02 16:19:01 +01:00
Kendell R a0ef60de49 Make card picker border follow standards (#7162) 2020-12-02 16:14:46 +01:00
Philip Allgaier 3313572606 Improve password change card (#7756)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2020-12-02 16:00:49 +01:00
Philip Allgaier c4f850cb14 Cleanup created user if person creation is cancelled (#7865) 2020-12-02 15:45:24 +01:00
Erik Montnemery 3bdab738c6 Support disabling devices (#7715)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2020-12-02 15:40:35 +01:00
Philip Allgaier faaef31b9f Show proper error message if username already used (#7866) 2020-12-02 15:35:38 +01:00
Philip Allgaier ca7b8b8b4c Add option to deactivate a user (#7757) 2020-12-02 15:33:11 +01:00
Philip Allgaier 9ca84e0694 Ensure "false" is set as default for "continue_on_timeout" (#7862) 2020-12-02 15:22:17 +01:00
Philip Allgaier daaf2b1796 Ensure push notification description reacts to language change (#7856) 2020-12-02 12:11:47 +01:00
Bram Kragten 25f7cbea5a Add target selector (#7864)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2020-12-02 12:10:31 +01:00
Josh McCarty c485ea9d7b Add number formatting to counter entity state display (#7868) 2020-12-02 11:51:56 +01:00
HomeAssistant Azure 295390c8e9 [ci skip] Translation update 2020-12-02 00:32:30 +00:00
Bram Kragten 3ebf816ce2 Fix height of ha-icon-input (#7767) 2020-12-01 22:51:15 +01:00
HomeAssistant Azure 0e362b851b [ci skip] Translation update 2020-12-01 00:32:47 +00:00
David F. Mulcahey 8d7ba19a08 Add ZHA fab for adding device when filtered by ZHA (#7848) 2020-11-30 11:05:54 +01:00
Paulus Schoutsen 08f4aa9d10 Use balloons as default pic for header. (#7850) 2020-11-30 10:59:04 +01:00
HomeAssistant Azure 98175d5c72 [ci skip] Translation update 2020-11-30 00:32:50 +00:00
Ian Richardson 7d4cad90bc Show attribute value when 0 (#7842)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2020-11-29 22:37:13 +01:00
David F. Mulcahey 335354d962 Enhance ZHA device pairing feedback (#7843)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2020-11-29 22:33:46 +01:00
Bram Kragten fe31d15d27 Add UI for setting an area on entity level (#7837) 2020-11-29 22:00:51 +01:00
Marc Randolph 7ceb6eb50d Apply existing theme variables to unthemed items (#7844) 2020-11-29 16:13:55 +01:00
HomeAssistant Azure 4c4db46aa8 [ci skip] Translation update 2020-11-29 00:32:38 +00:00
Bram Kragten b5724ed343 Make fab blue (#7839) 2020-11-28 17:22:42 +01:00
HomeAssistant Azure cae94175fe [ci skip] Translation update 2020-11-28 00:32:35 +00:00
Kendell R 0494a9d410 Update skeleton height for compact header (#7827)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2020-11-27 19:23:41 +01:00
Philip Allgaier c261b5c1ce Display HA username + flags in user config (#7705) 2020-11-27 09:41:47 +01:00
HomeAssistant Azure c89e17ac00 [ci skip] Translation update 2020-11-27 00:32:35 +00:00
Bram Kragten 50e7410002 Merge pull request #7833 from home-assistant/dev 2020-11-27 00:16:56 +01:00
Bram Kragten c5b0ebf76d Update ha-config-dashboard.ts 2020-11-27 00:01:34 +01:00
Bram Kragten 1d08978d6c Merge branch 'master' into dev 2020-11-26 23:59:34 +01:00
Bram Kragten fc78b6c933 Bumped version to 20201126.0 2020-11-26 23:58:13 +01:00
Philip Allgaier 480a5718fc Remove unusable Polymer mixin from LitElement (vacuum entity row) (#7831) 2020-11-26 23:57:37 +01:00
David F. Mulcahey f093bd115c Add network visualization to the ZHA config panel (#7802)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2020-11-26 23:56:50 +01:00
David F. Mulcahey 8a86beff14 Remove unused ZHA translations and fix ZHA fab labels (#7832) 2020-11-26 23:56:22 +01:00
Philip Allgaier 6020890384 Add state-info ellipsis + fix height (#7740) 2020-11-26 23:54:30 +01:00
Philip Allgaier 124aa947e2 Show pointer cursor for input + scene entity rows (#7830) 2020-11-26 20:32:15 +01:00
Bram Kragten e1add14453 Add UI for new selectors (#7822) 2020-11-26 18:38:01 +01:00
Philip Allgaier e3293837a8 Blueprints: Added missing labels & tooltips, optimize input display + text tweaks (#7748) 2020-11-26 18:14:57 +01:00
HomeAssistant Azure 5cb2743780 [ci skip] Translation update 2020-11-26 00:32:25 +00:00
Joakim Sørensen 6f0c79ec25 Introduce supervisor object (#7800)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2020-11-25 18:22:05 +01:00
Paulus Schoutsen 7de7d1d926 Fix init page message color (#7780)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2020-11-25 15:22:22 +01:00
Bram Kragten 89175f8e85 Add description and device class (#7816)
https://github.com/home-assistant/core/pull/43321
2020-11-25 15:10:08 +01:00
Paulus Schoutsen fc48c59eb0 Clean up some types (#7801)
Co-authored-by: Zack Barett <arnett.zackary@gmail.com>
2020-11-25 12:31:51 +01:00
Paulus Schoutsen 51332bc7e7 Add frontend url (#7797) 2020-11-25 12:26:11 +01:00
Josh McCarty 7403405d12 Additional number formatting (#7763) 2020-11-25 11:37:58 +01:00
Paulus Schoutsen 1d13947e71 Use Record type (#7798) 2020-11-25 10:40:32 +01:00
HomeAssistant Azure f6cb1ffe20 [ci skip] Translation update 2020-11-25 00:32:19 +00:00
Paulus Schoutsen 6d92b5651a Bump @web/dev-server (#7792) 2020-11-24 12:04:22 +01:00
Bram Kragten 3ea5bb2a6c Fix filtering hidden columns entity table (#7786) 2020-11-24 12:03:06 +01:00
Joakim Sørensen 1d367eca69 Add ignored job_conditions to list of unsupported reasons (#7790) 2020-11-24 11:48:19 +01:00
HomeAssistant Azure d4bf3a2ec3 [ci skip] Translation update 2020-11-24 00:32:27 +00:00
Joakim Sørensen 0ef8881660 Add unhealthy dialog and SU restart button (#7781) 2020-11-23 17:24:26 +01:00
Joakim Sørensen d7d1121f7d Force enable interface if configured (#7785) 2020-11-23 17:22:23 +01:00
Joakim Sørensen 7f089c309f Change order and wording snapshot actions (#7677)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2020-11-23 17:08:10 +01:00
Philip Allgaier 4dcc0bb66c Reduce "wasted" screen space (#7655) 2020-11-23 13:44:47 +01:00
Paulus Schoutsen 0049be7feb Allow developing with @web/dev-server (#7782) 2020-11-23 13:05:18 +01:00
Paulus Schoutsen 39ff641be9 Close webpack compiler on prod build (#7779)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2020-11-23 12:48:21 +01:00
Paulus Schoutsen e2fed24995 Clean up node-vibrant (#7777) 2020-11-23 12:47:47 +01:00
Philip Allgaier c0aa353f83 Load missing component titles for ha-related-items (#7771) 2020-11-23 10:48:32 +01:00
Ian Richardson d8521be63d use correct condition for display of tilt controls (#7769)
Co-authored-by: Philip Allgaier <philip.allgaier@gmx.de>
2020-11-23 10:45:53 +01:00
Paulus Schoutsen 6d4569c89d Drop webpackChunkName (#7778) 2020-11-23 10:39:40 +01:00
Paulus Schoutsen cd07553b59 Upgrade Rollup babel plugin (#7773) 2020-11-23 10:21:45 +01:00
HomeAssistant Azure 641bfcc9f7 [ci skip] Translation update 2020-11-23 00:32:47 +00:00
HomeAssistant Azure 6c01371958 [ci skip] Translation update 2020-11-22 00:33:21 +00:00
Paulus Schoutsen 7b00260b1a Remove duplicate translation key (#7766) 2020-11-21 23:33:19 +01:00
Paulus Schoutsen 875142373e Remove unused files and automate translation fragments (#7764) 2020-11-21 22:44:44 +01:00
Philip Allgaier ba505b15ef Ensure new grid card options don't break editor (#7762) 2020-11-21 21:40:27 +01:00
Donnie 17d227b142 Stop appending 'Configuration' to the end of every config page in navigation commands. Fix spinner regression (#7753) 2020-11-21 08:31:40 -08:00
Philip Allgaier e7e192ffe3 Align gallery code + fix icon in entity-icon demo (#7754) 2020-11-21 14:28:25 +01:00
Philip Allgaier c53ec6e12d Ensure more consistent lovelace config errors (#7755) 2020-11-21 14:08:43 +01:00
Philip Allgaier aad6492a6a Show error if no entity specified for history card (#7752) 2020-11-21 13:50:56 +01:00
HomeAssistant Azure fd5b125c2d [ci skip] Translation update 2020-11-21 00:32:15 +00:00
Paulus Schoutsen 5acee76c70 Show validation errors in UI (#7725) 2020-11-20 15:37:56 +01:00
Ian Richardson 10916fa82a Convert ha-relative-time to TypeScript/LitElement (#7709)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2020-11-20 15:17:34 +01:00
Bram Kragten f69951a523 Fix automation editor (#7746) 2020-11-20 15:16:27 +01:00
Philip Allgaier 38ba85e89d Make gallery config visible in dark mode + fix config alignment (#7741) 2020-11-20 15:13:19 +01:00
Patrick Decat 97023921b8 Add missing "gate" cover device_class (#7744) 2020-11-20 15:10:25 +01:00
Bram Kragten f835810f0a Add entity and device selectors (#7735) 2020-11-20 13:26:03 +01:00
HomeAssistant Azure 46f5589530 [ci skip] Translation update 2020-11-20 00:32:27 +00:00
Thomas Lovén ff9840c8ef Use mwc-tabs in conditional card editor (#7716) 2020-11-19 23:06:06 +01:00
Philip Allgaier 0c197558a1 Close notification drawer when last entry is dismissed (#7724) 2020-11-19 22:53:21 +01:00
Joakim Sørensen c409ba149d Supervisor network changes (#7676) 2020-11-19 22:51:29 +01:00
Joakim Sørensen 0b896ddfb1 Check snapshot size before upload (#7733) 2020-11-19 22:49:29 +01:00
Philip Allgaier 45721eb4fe Adjust FAB for blueprint overview + align translations (#7730) 2020-11-19 22:48:34 +01:00
Bram Kragten 1289bd03b2 Remove conference banner (#7731) 2020-11-19 10:46:52 +01:00
HomeAssistant Azure c1ba8ba6b8 [ci skip] Translation update 2020-11-19 00:32:32 +00:00
Bram Kragten 4973d8f629 WIP initial Blueprint UI (#7695)
* WIP initial Blueprint UI

* Review comments

* localize
2020-11-18 12:19:59 +01:00
Philip Allgaier 3aff4c96c4 Add Georgian language (Kartuli) (#7721) 2020-11-18 10:39:35 +01:00
Paulus Schoutsen 4005bc8985 Clarify title 2020-11-18 08:46:42 +00:00
Paulus Schoutsen 62e9792c39 Add note about building the frontend 2020-11-18 08:44:45 +00:00
HomeAssistant Azure 33183cc595 [ci skip] Translation update 2020-11-18 00:32:28 +00:00
Josh McCarty 394d552856 Use ha-dialog for dialog-system-log-details, add close icon button (#7513) 2020-11-17 22:29:20 +01:00
Bram Kragten aa4f0929e0 Bumped version to 20201111.2 2020-11-17 22:24:30 +01:00
Thomas Lovén f99b9215e3 Fix date picker row alignment (#7704) 2020-11-17 22:23:21 +01:00
Paulus Schoutsen c51d621fee Allow dismissing (#7712) 2020-11-17 22:22:39 +01:00
Paulus Schoutsen 7499892bc2 Don't support safari 10 in minifaction for latest support (#7719) 2020-11-17 22:00:28 +01:00
Paulus Schoutsen cbddebeaa8 Allow dismissing (#7712) 2020-11-17 16:14:37 +01:00
Philip Allgaier bbe4c95109 Display entity ID for read-only entities (#7690) 2020-11-17 16:08:47 +01:00
Philip Allgaier 4c6f9f0dd8 Fix empty entity pickers for "Time" trigger and condition (#7689) 2020-11-17 15:51:18 +01:00
Philip Allgaier 90f7dba793 Use proper night time icon consistently for sun integration (#7681) 2020-11-17 15:48:54 +01:00
Philip Allgaier 7c492338a2 Improve gallery hui-gauge-card (#7682) 2020-11-17 15:46:33 +01:00
Philip Allgaier 530f494df8 Improve gallery hui-light-card (#7684) 2020-11-17 15:46:00 +01:00
Joakim Sørensen 8fd1f35c59 Move data_entry_flow_progressed event subscriber (#7700) 2020-11-17 15:12:45 +01:00
Joakim Sørensen af1518e924 Use stepid title if present for progress flow (#7710) 2020-11-17 14:33:13 +01:00
Thomas Lovén 473e381d75 Fix date picker row alignment (#7704) 2020-11-17 09:43:25 +01:00
Paulus Schoutsen 7d3acc747d Guard passing invalid tag to customElements.whenDefined (#7696) 2020-11-17 09:40:35 +01:00
HomeAssistant Azure bf7424a67c [ci skip] Translation update 2020-11-17 00:32:25 +00:00
Bram Kragten 3fb35871c7 Bumped version to 20201111.1 2020-11-16 22:02:28 +01:00
Donnie d6d20cd704 Quick Bar should only capitalize letters in Command Palette (#7671) 2020-11-16 22:02:02 +01:00
Bram Kragten 9cc6a6b885 Fix number format in state-card-display (#7703) 2020-11-16 22:01:48 +01:00
Philip Allgaier ee0be7b6d0 Add missing 1px to prevent slider pin cutoff (#7657) 2020-11-16 22:01:32 +01:00
Joakim Sørensen a856337eae Devcontainer (#7697)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2020-11-16 16:53:47 +01:00
Bram Kragten 6cf47ba4eb Fix number format in state-card-display (#7703) 2020-11-16 12:45:52 +01:00
Donnie 3b7a189708 Quick Bar should only capitalize letters in Command Palette (#7671) 2020-11-16 12:45:20 +01:00
Philip Allgaier 79c542b76a Add missing 1px to prevent slider pin cutoff (#7657) 2020-11-16 12:44:58 +01:00
Bram Kragten 7e6153ba7d Merge pull request #7654 from home-assistant/dev 2020-11-11 15:40:04 +01:00
841 changed files with 56012 additions and 30766 deletions
+13
View File
@@ -0,0 +1,13 @@
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.148.1/containers/python-3/.devcontainer/base.Dockerfile
FROM mcr.microsoft.com/vscode/devcontainers/python:0-3.9
ENV \
DEBIAN_FRONTEND=noninteractive \
DEVCONTAINER=true \
PATH=$PATH:./node_modules/.bin
# Install nvm
COPY .nvmrc /tmp/.nvmrc
RUN \
su vscode -c \
"source /usr/local/share/nvm/nvm.sh && nvm install $(cat /tmp/.nvmrc) 2>&1"
+34
View File
@@ -0,0 +1,34 @@
{
"name": "Home Assistant Frontend",
"build": {
"dockerfile": "Dockerfile",
"context": ".."
},
"appPort": "8124:8123",
"context": "..",
"postCreateCommand": "script/bootstrap",
"extensions": [
"github.vscode-pull-request-github",
"dbaeumer.vscode-eslint",
"ms-vscode.vscode-typescript-tslint-plugin",
"esbenp.prettier-vscode",
"bierner.lit-html",
"runem.lit-plugin",
"ms-python.vscode-pylance"
],
"settings": {
"terminal.integrated.shell.linux": "/bin/bash",
"files.eol": "\n",
"editor.tabSize": 2,
"editor.formatOnPaste": false,
"editor.formatOnSave": true,
"editor.formatOnType": true,
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"files.trimTrailingWhitespace": true
}
}
@@ -74,12 +74,12 @@ DO NOT DELETE ANY TEXT from this template! Otherwise, your issue may be closed w
```
## Problem-relevant configuration
## Problem-relevant frontend configuration
<!--
An example configuration that caused the problem for you. Fill this out even
if it seems unimportant to you. Please be sure to remove personal information
like passwords, private URLs and other credentials.
An example configuration that caused the problem for you, e.g. the YAML configuration
of the used cards. Fill this out even if it seems unimportant to you. Please be sure
to remove personal information like passwords, private URLs and other credentials.
-->
```yaml
@@ -89,7 +89,7 @@ DO NOT DELETE ANY TEXT from this template! Otherwise, your issue may be closed w
## Javascript errors shown in your browser console/inspector
<!--
If you come across any javascript or other error logs, e.g., in your browser
If you come across any Javascript or other error logs, e.g. in your browser
console/inspector please provide them.
-->
+138
View File
@@ -0,0 +1,138 @@
name: Report a bug with the UI, Frontend or Lovelace
about: Report an issue related to the Home Assistant frontend.
labels: bug
title: ""
issue_body: true
body:
- type: markdown
attributes:
value: |
Make sure you are running the [latest version of Home Assistant][releases] before reporting an issue.
If you have a feature or enhancement request for the frontend, please [start an discussion][fr] instead of creating an issue.
**Please not not report issues for custom Lovelace cards.**
[fr]: https://github.com/home-assistant/frontend/discussions
[releases]: https://github.com/home-assistant/home-assistant/releases
- type: checkboxes
attributes:
label: Checklist
description: Please verify that you've followed these steps
options:
- label: I have updated to the latest available Home Assistant version.
required: true
- label: I have cleared the cache of my browser.
required: true
- label: I have tried a different browser to see if it is related to my browser.
required: true
- type: markdown
attributes:
value: |
## The problem
- type: textarea
validations:
required: true
attributes:
label: Describe the issue you are experiencing
description: Provide a clear and concise description of what the bug is.
- type: textarea
validations:
required: true
attributes:
label: Describe the behavior you expected
description: Describe what you expected to happen or it should look/behave.
- type: textarea
validations:
required: true
attributes:
label: Steps to reproduce the issue
description: |
Please tell us exactly how to reproduce your issue.
Provide clear and concise step by step instructions and add code snippets if needed.
value: |
1.
2.
3.
...
- type: markdown
attributes:
value: |
## Environment
- type: input
validations:
required: true
attributes:
label: What version of Home Assistant Core has the issue?
placeholder: core-
description: >
Can be found in the Configuration panel -> Info.
- type: input
attributes:
label: What was the last working version of Home Assistant Core?
placeholder: core-
description: >
If known, otherwise leave blank.
- type: input
attributes:
label: In which browser are you experiencing the issue with?
placeholder: Google Chrome 88.0.4324.150
description: >
Provide the full name and don't forget to add the version!
- type: input
attributes:
label: Which operating system are you using to run this browser?
placeholder: macOS Big Sur (1.11)
description: >
Don't forget to add the version!
- type: markdown
attributes:
value: |
# Details
- type: textarea
attributes:
label: State of relevant entities
description: >
If your issue is about how an entity is shown in the UI, please add the
state and attributes for all situations. You can find this information
at Developer Tools -> States.
value: |
```yaml
# Paste your state here.
```
- type: textarea
attributes:
label: Problem-relevant frontend configuration
description: >
An example configuration that caused the problem for you, e.g., the YAML
configuration of the used cards. Fill this out even if it seems
unimportant to you. Please be sure to remove personal information like
passwords, private URLs and other credentials.
value: |
```yaml
# Paste your YAML here.
```
- type: textarea
attributes:
label: Javascript errors shown in your browser console/inspector
description: >
If you come across any Javascript or other error logs, e.g., in your
browser console/inspector please provide them.
value: |
```txt
# Paste your logs here.
```
- type: markdown
attributes:
value: |
## Additional information
- type: markdown
attributes:
value: |
If you have any additional information for us, use the field below.
Please note, you can attach screenshots or screen recordings here,
by dragging and dropping files in the field below.
+19
View File
@@ -0,0 +1,19 @@
name: Netlify
on:
schedule:
- cron: "0 0 * * *"
jobs:
trigger_builds:
name: Trigger netlify build preview
runs-on: "ubuntu-latest"
steps:
- name: Trigger Cast build
run: curl -X POST -d {} https://api.netlify.com/build_hooks/${{ secrets.NETLIFY_CAST_DEV_BUILD_HOOK }}
- 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 }}
+81
View File
@@ -0,0 +1,81 @@
name: Release
on:
release:
types:
- published
env:
WHEELS_TAG: 3.7-alpine3.11
PYTHON_VERSION: 3.7
NODE_VERSION: 12.1
jobs:
release:
name: Release
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@v2
- name: Verify version
uses: home-assistant/actions/helpers/verify-version@master
- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v2
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Set up Node ${{ env.NODE_VERSION }}
uses: actions/setup-node@v2
with:
node-version: ${{ env.NODE_VERSION }}
- name: Build and release package
run: |
python3 -m pip install twine
export TWINE_USERNAME="__token__"
export TWINE_PASSWORD="${{ secrets.TWINE_TOKEN }}"
script/release
wheels-init:
name: Init wheels build
needs: release
runs-on: ubuntu-latest
steps:
- name: Generate requirements.txt
run: |
# Sleep to give pypi time to populate the new version across mirrors
sleep 240
version=$(echo "${{ github.ref }}" | awk -F"/" '{print $NF}' )
echo "home-assistant-frontend==$version" > ./requirements.txt
- name: Upload requirements.txt
uses: actions/upload-artifact@v2
with:
name: requirements
path: ./requirements.txt
build-wheels:
name: Build wheels for ${{ matrix.arch }}
needs: wheels-init
runs-on: ubuntu-latest
strategy:
matrix:
arch: ["aarch64", "armhf", "armv7", "amd64", "i386"]
steps:
- name: Download requirements.txt
uses: actions/download-artifact@v2
with:
name: requirements
- name: Build wheels
uses: home-assistant/wheels@master
with:
tag: ${{ env.WHEELS_TAG }}
arch: ${{ matrix.arch }}
wheels-host: ${{ secrets.WHEELS_HOST }}
wheels-key: ${{ secrets.WHEELS_KEY }}
wheels-user: wheels
requirements: "requirements.txt"
+65
View File
@@ -0,0 +1,65 @@
name: Translations
on:
schedule:
- cron: "30 0 * * *"
push:
branches:
- dev
paths:
- src/translations/en.json
env:
NODE_VERSION: 12
jobs:
upload:
name: Upload
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@v2
- name: Set up Node ${{ env.NODE_VERSION }}
uses: actions/setup-node@v2
with:
node-version: ${{ env.NODE_VERSION }}
- name: Upload Translations
run: |
export LOKALISE_TOKEN="${{ secrets.LOKALISE_TOKEN }}"
./script/translations_upload_base
download:
name: Download
needs: upload
if: github.event_name == 'schedule'
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@v2
- name: Set up Node ${{ env.NODE_VERSION }}
uses: actions/setup-node@v2
with:
node-version: ${{ env.NODE_VERSION }}
- name: Download Translations
run: |
export LOKALISE_TOKEN="${{ secrets.LOKALISE_TOKEN }}"
npm install
./script/translations_download
- name: Initialize git
uses: home-assistant/actions/helpers/git-init@master
with:
name: GitHub Action
email: github-action@users.noreply.github.com
- name: Update translation
run: |
git add translations
git commit -am "Translation update"
git push
+3
View File
@@ -35,3 +35,6 @@ yarn-error.log
#asdf
.tool-versions
# Home Assistant config
/config
-6
View File
@@ -1,6 +0,0 @@
jshint:
enabled: false
eslint:
enabled: true
config_file: .eslintrc-hound.json
+71
View File
@@ -37,6 +37,37 @@
"instanceLimit": 1
}
},
{
"label": "Develop Supervisor panel",
"type": "gulp",
"task": "develop-hassio",
"problemMatcher": {
"owner": "ha-build",
"source": "ha-build",
"fileLocation": "absolute",
"severity": "error",
"pattern": [
{
"regexp": "(SyntaxError): (.+): (.+) \\((\\d+):(\\d+)\\)",
"severity": 1,
"file": 2,
"message": 3,
"line": 4,
"column": 5
}
],
"background": {
"activeOnStart": true,
"beginsPattern": "Changes detected. Starting compilation",
"endsPattern": "Build done @"
}
},
"isBackground": true,
"group": "build",
"runOptions": {
"instanceLimit": 1
}
},
{
"label": "Develop Gallery",
"type": "gulp",
@@ -133,5 +164,45 @@
"instanceLimit": 1
}
},
{
"label": "Run HA Core in devcontainer",
"type": "shell",
"command": "script/core",
"isBackground": true,
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": [],
"runOptions": {
"instanceLimit": 1
}
},
{
"label": "Run HA Core for Supervisor in devcontainer",
"type": "shell",
"command": "HASSIO=${input:supervisorHost} HASSIO_TOKEN=${input:supervisorToken} script/core",
"isBackground": true,
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": [],
"runOptions": {
"instanceLimit": 1
}
}
],
"inputs": [
{
"id": "supervisorHost",
"type": "promptString",
"description": "The IP of the Supervisor host running the Remote API proxy add-on"
},
{
"id": "supervisorToken",
"type": "promptString",
"description": "The token for the Remote API proxy add-on"
}
]
}
+1 -1
View File
@@ -14,7 +14,7 @@ This is the repository for the official [Home Assistant](https://home-assistant.
- Development: [Instructions](https://developers.home-assistant.io/docs/frontend/development/)
- Production build: `script/build_frontend`
- Gallery: `cd gallery && script/develop_gallery`
- Hass.io: [Instructions](https://developers.home-assistant.io/docs/en/hassio_hass.html)
- Supervisor: [Instructions](https://developers.home-assistant.io/docs/supervisor/developing)
## Frontend development
-30
View File
@@ -1,30 +0,0 @@
# https://dev.azure.com/home-assistant
trigger: none
pr: none
schedules:
- cron: "0 0 * * *"
displayName: "build preview"
branches:
include:
- dev
always: true
variables:
- group: netlify
jobs:
- job: 'Netlify_preview'
pool:
vmImage: 'ubuntu-latest'
steps:
- script: |
# Cast
curl -X POST -d {} https://api.netlify.com/build_hooks/${NETLIFY_CAST}
# Demo
curl -X POST -d {} https://api.netlify.com/build_hooks/${NETLIFY_DEMO}
# Gallery
curl -X POST -d {} https://api.netlify.com/build_hooks/${NETLIFY_GALLERY}
displayName: 'Trigger netlify build preview'
-59
View File
@@ -1,59 +0,0 @@
# https://dev.azure.com/home-assistant
trigger:
batch: true
tags:
include:
- "*"
pr: none
variables:
- name: versionWheels
value: '1.10.1-3.7-alpine3.11'
- name: versionNode
value: '12.1'
- group: twine
resources:
repositories:
- repository: azure
type: github
name: 'home-assistant/ci-azure'
endpoint: 'home-assistant'
stages:
- stage: "Validate"
jobs:
- template: templates/azp-job-version.yaml@azure
- stage: "Build"
jobs:
- job: "ReleasePython"
pool:
vmImage: "ubuntu-latest"
steps:
- task: UsePythonVersion@0
displayName: "Use Python 3.7"
inputs:
versionSpec: "3.7"
- task: NodeTool@0
displayName: "Use Node $(versionNode)"
inputs:
versionSpec: "$(versionNode)"
- script: pip install twine wheel
displayName: "Install tools"
- script: |
export TWINE_USERNAME="$(twineUser)"
export TWINE_PASSWORD="$(twinePassword)"
script/release
displayName: "Build and release package"
- stage: "Wheels"
jobs:
- template: templates/azp-job-wheels.yaml@azure
parameters:
builderVersion: '$(versionWheels)'
wheelsRequirement: 'requirement.txt'
preBuild:
- script: |
sleep 240
echo "home-assistant-frontend==$(Build.SourceBranchName)" > requirement.txt
-70
View File
@@ -1,70 +0,0 @@
# https://dev.azure.com/home-assistant
trigger:
batch: true
branches:
include:
- dev
paths:
include:
- translations/en.json
pr: none
schedules:
- cron: "30 0 * * *"
displayName: "frontend translation update"
branches:
include:
- dev
always: true
variables:
- group: translation
resources:
repositories:
- repository: azure
type: github
name: 'home-assistant/ci-azure'
endpoint: 'home-assistant'
jobs:
- job: 'Upload'
pool:
vmImage: 'ubuntu-latest'
steps:
- task: NodeTool@0
displayName: 'Use Node 12.x'
inputs:
versionSpec: '12.x'
- script: |
export LOKALISE_TOKEN="$(lokaliseToken)"
export AZURE_BRANCH="$(Build.SourceBranchName)"
./script/translations_upload_base
displayName: 'Upload Translation'
- job: 'Download'
dependsOn:
- 'Upload'
condition: or(eq(variables['Build.Reason'], 'Schedule'), eq(variables['Build.Reason'], 'Manual'))
pool:
vmImage: 'ubuntu-latest'
steps:
- task: NodeTool@0
displayName: 'Use Node 12.x'
inputs:
versionSpec: '12.x'
- template: templates/azp-step-git-init.yaml@azure
- script: |
export LOKALISE_TOKEN="$(lokaliseToken)"
export AZURE_BRANCH="$(Build.SourceBranchName)"
npm install
./script/translations_download
displayName: 'Download Translation'
- script: |
git checkout dev
git add translation
git commit -am "[ci skip] Translation update"
git push
displayName: 'Update translation'
+39
View File
@@ -0,0 +1,39 @@
# Bundling Home Assistant Frontend
The Home Assistant build pipeline contains various steps to prepare a build.
- Generating icon files to be included
- Generating translation files to be included
- Converting TypeScript, CSS and JSON files to JavaScript
- Bundling
- Minifying the files
- Generating the HTML entrypoint files
- Generating the service worker
- Compressing the files
## Converting files
Currently in Home Assistant we use a bundler to convert TypeScript, CSS and JSON files to JavaScript files that the browser understands.
We currently rely on Webpack but also have experimental Rollup support. Both of these programs bundle the converted files in both production and development.
For development, bundling is optional. We just want to get the right files in the browser.
Responsibilities of the converter during development:
- Convert TypeScript to JavaScript
- Convert CSS to JavaScript that sets the content as the default export
- Convert JSON to JavaScript that sets the content as the default export
- Make sure import, dynamic import and web worker references work
- Add extensions where missing
- Resolve absolute package imports
- Filter out specific imports/packages
- Replace constants with values
In production, the following responsibilities are added:
- Minify HTML
- Bundle multiple imports so that the browser can fetch less files
- Generate a second version that is ES5 compatible
Configuration for all these steps are specified in [bundle.js](bundle.js).
+3 -2
View File
@@ -44,7 +44,7 @@ module.exports.definedVars = ({ isProdBuild, latestBuild, defineOverlay }) => ({
});
module.exports.terserOptions = (latestBuild) => ({
safari10: true,
safari10: !latestBuild,
ecma: latestBuild ? undefined : 5,
output: { comments: false },
});
@@ -117,7 +117,7 @@ BundleConfig {
*/
module.exports.config = {
app({ isProdBuild, latestBuild, isStatsBuild }) {
app({ isProdBuild, latestBuild, isStatsBuild, isWDS }) {
return {
entry: {
service_worker: "./src/entrypoints/service_worker.ts",
@@ -132,6 +132,7 @@ module.exports.config = {
isProdBuild,
latestBuild,
isStatsBuild,
isWDS,
};
},
+3
View File
@@ -6,6 +6,9 @@ module.exports = {
useRollup() {
return process.env.ROLLUP === "1";
},
useWDS() {
return process.env.WDS === "1";
},
isProdBuild() {
return (
process.env.NODE_ENV === "production" || module.exports.isStatsBuild()
+6 -1
View File
@@ -12,6 +12,7 @@ require("./webpack.js");
require("./service-worker.js");
require("./entry-html.js");
require("./rollup.js");
require("./wds.js");
gulp.task(
"develop-app",
@@ -28,7 +29,11 @@ gulp.task(
"build-translations"
),
"copy-static-app",
env.useRollup() ? "rollup-watch-app" : "webpack-watch-app"
env.useWDS()
? "wds-watch-app"
: env.useRollup()
? "rollup-watch-app"
: "webpack-watch-app"
)
);
+17 -3
View File
@@ -19,6 +19,7 @@ const renderTemplate = (pth, data = {}, pathFunc = templatePath) => {
return compiled({
...data,
useRollup: env.useRollup(),
useWDS: env.useWDS(),
renderTemplate,
});
};
@@ -90,10 +91,23 @@ gulp.task("gen-pages-prod", (done) => {
});
gulp.task("gen-index-app-dev", (done) => {
let latestAppJS, latestCoreJS, latestCustomPanelJS;
if (env.useWDS()) {
latestAppJS = "http://localhost:8000/src/entrypoints/app.ts";
latestCoreJS = "http://localhost:8000/src/entrypoints/core.ts";
latestCustomPanelJS =
"http://localhost:8000/src/entrypoints/custom-panel.ts";
} else {
latestAppJS = "/frontend_latest/app.js";
latestCoreJS = "/frontend_latest/core.js";
latestCustomPanelJS = "/frontend_latest/custom-panel.js";
}
const content = renderTemplate("index", {
latestAppJS: "/frontend_latest/app.js",
latestCoreJS: "/frontend_latest/core.js",
latestCustomPanelJS: "/frontend_latest/custom-panel.js",
latestAppJS,
latestCoreJS,
latestCustomPanelJS,
es5AppJS: "/frontend_es5/app.js",
es5CoreJS: "/frontend_es5/core.js",
+5
View File
@@ -85,6 +85,11 @@ gulp.task("copy-translations-app", async () => {
copyTranslations(staticDir);
});
gulp.task("copy-translations-supervisor", async () => {
const staticDir = paths.hassio_output_static;
copyTranslations(staticDir);
});
gulp.task("copy-static-app", async () => {
const staticDir = paths.app_output_static;
// Basic static files
+6
View File
@@ -10,6 +10,8 @@ require("./gen-icons-json.js");
require("./webpack.js");
require("./compress.js");
require("./rollup.js");
require("./gather-static.js");
require("./translations.js");
gulp.task(
"develop-hassio",
@@ -20,6 +22,8 @@ gulp.task(
"clean-hassio",
"gen-icons-json",
"gen-index-hassio-dev",
"build-supervisor-translations",
"copy-translations-supervisor",
env.useRollup() ? "rollup-watch-hassio" : "webpack-watch-hassio"
)
);
@@ -32,6 +36,8 @@ gulp.task(
},
"clean-hassio",
"gen-icons-json",
"build-supervisor-translations",
"copy-translations-supervisor",
env.useRollup() ? "rollup-prod-hassio" : "webpack-prod-hassio",
"gen-index-hassio-prod",
...// Don't compress running tests
+76 -52
View File
@@ -33,21 +33,10 @@ String.prototype.rsplit = function (sep, maxsplit) {
: split;
};
// Panel translations which should be split from the core translations. These
// should mirror the fragment definitions in polymer.json, so that we load
// additional resources at equivalent points.
const TRANSLATION_FRAGMENTS = [
"config",
"history",
"logbook",
"mailbox",
"profile",
"shopping-list",
"page-authorize",
"page-demo",
"page-onboarding",
"developer-tools",
];
// Panel translations which should be split from the core translations.
const TRANSLATION_FRAGMENTS = Object.keys(
require("../../src/translations/en.json").ui.panel
);
function recursiveFlatten(prefix, data) {
let output = {};
@@ -277,6 +266,7 @@ gulp.task(taskName, function () {
TRANSLATION_FRAGMENTS.forEach((fragment) => {
delete data.ui.panel[fragment];
});
delete data.supervisor;
return data;
})
)
@@ -353,6 +343,62 @@ gulp.task(
}
);
gulp.task("build-translation-fragment-supervisor", function () {
return gulp
.src(fullDir + "/*.json")
.pipe(transform((data) => data.supervisor))
.pipe(gulp.dest(workDir + "/supervisor"));
});
gulp.task("build-translation-flatten-supervisor", function () {
return gulp
.src(workDir + "/supervisor/*.json")
.pipe(
transform(function (data) {
// Polymer.AppLocalizeBehavior requires flattened json
return flatten(data);
})
)
.pipe(gulp.dest(outDir));
});
gulp.task("build-translation-write-metadata", function writeMetadata() {
return gulp
.src(
[
path.join(paths.translations_src, "translationMetadata.json"),
workDir + "/testMetadata.json",
workDir + "/translationFingerprints.json",
],
{ allowEmpty: true }
)
.pipe(merge({}))
.pipe(
transform(function (data) {
const newData = {};
Object.entries(data).forEach(([key, value]) => {
// Filter out translations without native name.
if (value.nativeName) {
newData[key] = value;
} else {
console.warn(
`Skipping language ${key}. Native name was not translated.`
);
}
});
return newData;
})
)
.pipe(
transform((data) => ({
fragments: TRANSLATION_FRAGMENTS,
translations: data,
}))
)
.pipe(rename("translationMetadata.json"))
.pipe(gulp.dest(workDir));
});
gulp.task(
"build-translations",
gulp.series(
@@ -364,42 +410,20 @@ gulp.task(
gulp.parallel(...splitTasks),
"build-flattened-translations",
"build-translation-fingerprints",
function writeMetadata() {
return gulp
.src(
[
path.join(paths.translations_src, "translationMetadata.json"),
workDir + "/testMetadata.json",
workDir + "/translationFingerprints.json",
],
{ allowEmpty: true }
)
.pipe(merge({}))
.pipe(
transform(function (data) {
const newData = {};
Object.entries(data).forEach(([key, value]) => {
// Filter out translations without native name.
if (data[key].nativeName) {
newData[key] = data[key];
} else {
console.warn(
`Skipping language ${key}. Native name was not translated.`
);
}
if (data[key]) newData[key] = value;
});
return newData;
})
)
.pipe(
transform((data) => ({
fragments: TRANSLATION_FRAGMENTS,
translations: data,
}))
)
.pipe(rename("translationMetadata.json"))
.pipe(gulp.dest(workDir));
}
"build-translation-write-metadata"
)
);
gulp.task(
"build-supervisor-translations",
gulp.series(
"clean-translations",
"ensure-translations-build-dir",
"build-master-translation",
"build-merged-translations",
"build-translation-fragment-supervisor",
"build-translation-flatten-supervisor",
"build-translation-fingerprints",
"build-translation-write-metadata"
)
);
+11
View File
@@ -0,0 +1,11 @@
// Tasks to run Rollup
const gulp = require("gulp");
const { startDevServer } = require("@web/dev-server");
gulp.task("wds-watch-app", () => {
startDevServer({
config: {
watch: true,
},
});
});
+50 -61
View File
@@ -47,7 +47,7 @@ const runDevServer = ({
);
});
const handler = (done) => (err, stats) => {
const doneHandler = (done) => (err, stats) => {
if (err) {
log.error(err.stack || err);
if (err.details) {
@@ -67,11 +67,20 @@ const handler = (done) => (err, stats) => {
}
};
const prodBuild = (conf) =>
new Promise((resolve) => {
webpack(
conf,
// Resolve promise when done. Because we pass a callback, webpack closes itself
doneHandler(resolve)
);
});
gulp.task("webpack-watch-app", () => {
// we are not calling done, so this command will run forever
// This command will run forever because we don't close compiler
webpack(createAppConfig({ isProdBuild: false, latestBuild: true })).watch(
{ ignored: /build-translations/ },
handler()
doneHandler()
);
gulp.watch(
path.join(paths.translations_src, "en.json"),
@@ -79,15 +88,12 @@ gulp.task("webpack-watch-app", () => {
);
});
gulp.task(
"webpack-prod-app",
() =>
new Promise((resolve) =>
webpack(
bothBuilds(createAppConfig, { isProdBuild: true }),
handler(resolve)
)
)
gulp.task("webpack-prod-app", () =>
prodBuild(
bothBuilds(createAppConfig, {
isProdBuild: true,
})
)
);
gulp.task("webpack-dev-server-demo", () => {
@@ -98,17 +104,12 @@ gulp.task("webpack-dev-server-demo", () => {
});
});
gulp.task(
"webpack-prod-demo",
() =>
new Promise((resolve) =>
webpack(
bothBuilds(createDemoConfig, {
isProdBuild: true,
}),
handler(resolve)
)
)
gulp.task("webpack-prod-demo", () =>
prodBuild(
bothBuilds(createDemoConfig, {
isProdBuild: true,
})
)
);
gulp.task("webpack-dev-server-cast", () => {
@@ -121,41 +122,35 @@ gulp.task("webpack-dev-server-cast", () => {
});
});
gulp.task(
"webpack-prod-cast",
() =>
new Promise((resolve) =>
webpack(
bothBuilds(createCastConfig, {
isProdBuild: true,
}),
handler(resolve)
)
)
gulp.task("webpack-prod-cast", () =>
prodBuild(
bothBuilds(createCastConfig, {
isProdBuild: true,
})
)
);
gulp.task("webpack-watch-hassio", () => {
// we are not calling done, so this command will run forever
// This command will run forever because we don't close compiler
webpack(
createHassioConfig({
isProdBuild: false,
latestBuild: true,
})
).watch({}, handler());
).watch({ ignored: /build-translations/ }, doneHandler());
gulp.watch(
path.join(paths.translations_src, "en.json"),
gulp.series("build-supervisor-translations", "copy-translations-supervisor")
);
});
gulp.task(
"webpack-prod-hassio",
() =>
new Promise((resolve) =>
webpack(
bothBuilds(createHassioConfig, {
isProdBuild: true,
}),
handler(resolve)
)
)
gulp.task("webpack-prod-hassio", () =>
prodBuild(
bothBuilds(createHassioConfig, {
isProdBuild: true,
})
)
);
gulp.task("webpack-dev-server-gallery", () => {
@@ -167,17 +162,11 @@ gulp.task("webpack-dev-server-gallery", () => {
});
});
gulp.task(
"webpack-prod-gallery",
() =>
new Promise((resolve) =>
webpack(
createGalleryConfig({
isProdBuild: true,
latestBuild: true,
}),
handler(resolve)
)
)
gulp.task("webpack-prod-gallery", () =>
prodBuild(
createGalleryConfig({
isProdBuild: true,
latestBuild: true,
})
)
);
+1
View File
@@ -34,6 +34,7 @@ module.exports = {
hassio_dir: path.resolve(__dirname, "../hassio"),
hassio_output_root: path.resolve(__dirname, "../hassio/build"),
hassio_output_static: path.resolve(__dirname, "../hassio/build/static"),
hassio_output_latest: path.resolve(
__dirname,
"../hassio/build/frontend_latest"
@@ -1,5 +1,3 @@
const path = require("path");
module.exports = function (userOptions = {}) {
// Files need to be absolute paths.
// This only works if the file has no exports
+15 -10
View File
@@ -3,7 +3,7 @@ const path = require("path");
const commonjs = require("@rollup/plugin-commonjs");
const resolve = require("@rollup/plugin-node-resolve");
const json = require("@rollup/plugin-json");
const babel = require("rollup-plugin-babel");
const babel = require("@rollup/plugin-babel").babel;
const replace = require("@rollup/plugin-replace");
const visualizer = require("rollup-plugin-visualizer");
const { string } = require("rollup-plugin-string");
@@ -31,6 +31,7 @@ const createRollupConfig = ({
isStatsBuild,
publicPath,
dontHash,
isWDS,
}) => {
return {
/**
@@ -61,6 +62,7 @@ const createRollupConfig = ({
...bundle.babelOptions({ latestBuild }),
extensions,
exclude: bundle.babelExclude(),
babelHelpers: isWDS ? "inline" : "bundled",
}),
string({
// Import certain extensions as strings
@@ -69,19 +71,21 @@ const createRollupConfig = ({
replace(
bundle.definedVars({ isProdBuild, latestBuild, defineOverlay })
),
manifest({
publicPath,
}),
worker(),
dontHashPlugin({ dontHash }),
isProdBuild && terser(bundle.terserOptions(latestBuild)),
isStatsBuild &&
!isWDS &&
manifest({
publicPath,
}),
!isWDS && worker(),
!isWDS && dontHashPlugin({ dontHash }),
!isWDS && isProdBuild && terser(bundle.terserOptions(latestBuild)),
!isWDS &&
isStatsBuild &&
visualizer({
// https://github.com/btd/rollup-plugin-visualizer#options
open: true,
sourcemap: true,
}),
],
].filter(Boolean),
},
/**
* @type { import("rollup").OutputOptions }
@@ -108,12 +112,13 @@ const createRollupConfig = ({
};
};
const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => {
const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild, isWDS }) => {
return createRollupConfig(
bundle.config.app({
isProdBuild,
latestBuild,
isStatsBuild,
isWDS,
})
);
};
+3 -18
View File
@@ -1,7 +1,7 @@
const webpack = require("webpack");
const path = require("path");
const TerserPlugin = require("terser-webpack-plugin");
const ManifestPlugin = require("webpack-manifest-plugin");
const { WebpackManifestPlugin } = require("webpack-manifest-plugin");
const paths = require("./paths.js");
const bundle = require("./bundle");
const log = require("fancy-log");
@@ -36,6 +36,7 @@ const createWebpackConfig = ({
const ignorePackages = bundle.ignorePackages({ latestBuild });
return {
mode: isProdBuild ? "production" : "development",
target: ["web", latestBuild ? "es2017" : "es5"],
devtool: isProdBuild
? "cheap-module-source-map"
: "eval-cheap-module-source-map",
@@ -67,7 +68,7 @@ const createWebpackConfig = ({
],
},
plugins: [
new ManifestPlugin({
new WebpackManifestPlugin({
// Only include the JS of entrypoints
filter: (file) => file.isInitial && !file.name.endsWith(".map"),
}),
@@ -131,22 +132,6 @@ const createWebpackConfig = ({
}
return `${chunk.name}.${chunk.hash.substr(0, 8)}.js`;
},
environment: {
// The environment supports arrow functions ('() => { ... }').
arrowFunction: latestBuild,
// The environment supports BigInt as literal (123n).
bigIntLiteral: false,
// The environment supports const and let for variable declarations.
const: latestBuild,
// The environment supports destructuring ('{ a, b } = obj').
destructuring: latestBuild,
// The environment supports an async import() function to import EcmaScript modules.
dynamicImport: latestBuild,
// The environment supports 'for of' iteration ('for (const x of array) { ... }').
forOf: latestBuild,
// The environment supports ECMAScript Module syntax to import ECMAScript modules (import ... from '...').
module: latestBuild,
},
chunkFilename:
isProdBuild && !isStatsBuild
? "chunk.[chunkhash].js"
+1 -1
View File
@@ -48,7 +48,7 @@ class HcCast extends LitElement {
protected render(): TemplateResult {
if (this.lovelaceConfig === undefined) {
return html` <hass-loading-screen no-toolbar></hass-loading-screen>> `;
return html`<hass-loading-screen no-toolbar></hass-loading-screen>`;
}
const error =
+4
View File
@@ -98,8 +98,12 @@ class HcLayout extends LitElement {
line-height: 32px;
padding: 24px 16px 16px;
display: block;
margin: 0;
}
.hero {
border-radius: 4px 4px 0 0;
}
.subtitle {
font-size: 14px;
color: var(--secondary-text-color);
+1 -1
View File
@@ -1,8 +1,8 @@
import {
customElement,
html,
property,
internalProperty,
property,
TemplateResult,
} from "lit-element";
import { mockHistory } from "../../../../demo/src/stubs/history";
+1 -1
View File
@@ -1,4 +1,4 @@
import "web-animations-js/web-animations-next-lite.min";
import "../../../src/resources/roboto";
import "../../../src/resources/ha-style";
import "../../../src/resources/roboto";
import "./layout/hc-lovelace";
+6 -2
View File
@@ -54,6 +54,8 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
state: "21",
attributes: {
friendly_name: "Living room temperature",
device_class: "temperature",
unit_of_measurement: "°C",
},
},
"sensor.study_temp_rounded": {
@@ -61,6 +63,8 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
state: "23",
attributes: {
friendly_name: "Study temperature",
device_class: "temperature",
unit_of_measurement: "°C",
},
},
"sensor.living_room": {
@@ -261,7 +265,7 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
entity_id: "light.kitchen_lights",
state: "off",
attributes: {
friendly_name: "Kitchen lights",
friendly_name: "Kitchen Lights",
supported_features: 1,
},
},
@@ -484,7 +488,7 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
attributes: {
min_mireds: 111,
max_mireds: 400,
friendly_name: "Garage lights",
friendly_name: "Garage Lights",
supported_features: 55,
},
},
+1
View File
@@ -12,6 +12,7 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({
{
type: "entities",
title: localize("ui.panel.page-demo.config.arsaboo.labels.lights"),
state_color: true,
entities: [
{
entity: "light.kitchen_lights",
+4 -16
View File
@@ -3,22 +3,10 @@ import { Lovelace } from "../../../src/panels/lovelace/types";
import { DemoConfig } from "./types";
export const demoConfigs: Array<() => Promise<DemoConfig>> = [
() =>
import(/* webpackChunkName: "arsaboo" */ "./arsaboo").then(
(mod) => mod.demoArsaboo
),
() =>
import(/* webpackChunkName: "teachingbirds" */ "./teachingbirds").then(
(mod) => mod.demoTeachingbirds
),
() =>
import(/* webpackChunkName: "kernehed" */ "./kernehed").then(
(mod) => mod.demoKernehed
),
() =>
import(/* webpackChunkName: "jimpower" */ "./jimpower").then(
(mod) => mod.demoJimpower
),
() => import("./arsaboo").then((mod) => mod.demoArsaboo),
() => import("./teachingbirds").then((mod) => mod.demoTeachingbirds),
() => import("./kernehed").then((mod) => mod.demoKernehed),
() => import("./jimpower").then((mod) => mod.demoJimpower),
];
// eslint-disable-next-line import/no-mutable-exports
+2 -2
View File
@@ -653,7 +653,7 @@ export const demoEntitiesJimpower: DemoConfig["entities"] = () =>
entity_id: "binary_sensor.smoke_sensor_158d0001b8ddc7",
state: "off",
attributes: {
Density: 0,
density: 0,
battery_level: 59,
friendly_name: "Downstairs Smoke Detector",
device_class: "smoke",
@@ -663,7 +663,7 @@ export const demoEntitiesJimpower: DemoConfig["entities"] = () =>
entity_id: "binary_sensor.smoke_sensor_158d0001b8deba",
state: "off",
attributes: {
Density: 0,
density: 0,
battery_level: 65,
friendly_name: "Upstairs Smoke Detector",
device_class: "smoke",
+6 -181
View File
@@ -3,49 +3,7 @@ import { DemoConfig } from "../types";
export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({
name: "Kingia Castle",
resources: [
// {
// url: "/local/custom_ui/dark-sky-weather-card.js?v=4",
// type: "js",
// },
// {
// url: "/local/custom_ui/mini-media-player-bundle.js?v=0.9.8",
// type: "module",
// },
// {
// url: "/local/custom_ui/tracker-card.js?v=0.1.5",
// type: "js",
// },
// {
// url: "/local/custom_ui/surveillance-card.js?v=0.0.1",
// type: "module",
// },
// {
// url: "/local/custom_ui/mini-graph-card-bundle.js?v=0.1.0",
// type: "module",
// },
// {
// url: "/local/custom_ui/slider-entity-row.js?v=d6da75",
// type: "js",
// },
// {
// url:
// "/local/custom_ui/compact-custom-header/compact-custom-header.js?v=0.2.7",
// type: "js",
// },
// {
// url: "/local/custom_ui/waze-card.js?v=1.1.1",
// type: "js",
// },
// {
// url: "/local/custom_ui/circle-sensor-card.js?v=1.2.0",
// type: "module",
// },
// {
// url: "/local/custom_ui/monster-card.js?v=0.2.3",
// type: "js",
// },
],
resources: [],
views: [
{
cards: [
@@ -603,89 +561,6 @@ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({
},
{
cards: [
// {
// style: {
// "background-image": 'url("/assets/jimpower/cardbackK.png")',
// "background-size": "100% 400px",
// "box-shadow": "3px 3px rgba(0,0,0,0.4)",
// "background-repeat": "no-repeat",
// color: "#999999",
// "border-radius": "20px",
// border: "solid 1px rgba(100,100,100,0.3)",
// "background-color": "rgba(50,50,50,0.3)",
// },
// type: "custom:card-modder",
// card: {
// entity_visibility: "sensor.dark_sky_visibility",
// entity_sun: "sun.sun",
// entity_daily_summary:
// "sensor.bom_gc_forecast_detailed_summary_0",
// entity_temperature: "sensor.bom_temp",
// entity_forecast_high_temp_3:
// "sensor.bom_gc_forecast_max_temp_c_3",
// entity_forecast_high_temp_2:
// "sensor.bom_gc_forecast_max_temp_c_2",
// entity_forecast_high_temp_5:
// "sensor.bom_gc_forecast_max_temp_c_5",
// entity_forecast_high_temp_4:
// "sensor.bom_gc_forecast_max_temp_c_4",
// entity_wind_speed: "sensor.bom_wind_sp",
// entity_forecast_icon_4: "sensor.dark_sky_icon_4",
// entity_forecast_icon_5: "sensor.dark_sky_icon_5",
// entity_forecast_icon_2: "sensor.dark_sky_icon_2",
// entity_forecast_icon_3: "sensor.dark_sky_icon_3",
// entity_forecast_icon_1: "sensor.dark_sky_icon_1",
// entity_forecast_high_temp_1:
// "sensor.bom_gc_forecast_max_temp_c_1",
// entity_wind_bearing: "sensor.bom_wind_bear",
// entity_forecast_low_temp_2:
// "sensor.bom_gc_forecast_min_temp_c_2",
// entity_forecast_low_temp_3:
// "sensor.bom_gc_forecast_min_temp_c_3",
// entity_pressure: "sensor.bom_pres",
// entity_forecast_low_temp_1:
// "sensor.bom_gc_forecast_min_temp_c_1",
// entity_forecast_low_temp_4:
// "sensor.bom_gc_forecast_min_temp_c_4",
// entity_forecast_low_temp_5:
// "sensor.bom_gc_forecast_min_temp_c_5",
// entity_humidity: "sensor.bom_humd",
// type: "custom:dark-sky-weather-card",
// entity_current_conditions: "sensor.dark_sky_icon",
// },
// },
// {
// style: {
// "background-image": 'url("/assets/jimpower/home/waze_5.png")',
// "background-size": "100% 400px",
// "box-shadow": "3px 3px rgba(0,0,0,0.4)",
// "background-repeat": "no-repeat",
// "border-radius": "20px",
// border: "solid 1px rgba(100,100,100,0.3)",
// "background-color": "rgba(50,50,50,0.3)",
// },
// type: "custom:card-modder",
// card: {
// entities: [
// {
// name: "James",
// zone: "zone.home",
// entity: "sensor.james_to_home",
// },
// {
// name: "Tina",
// zone: "zone.home",
// entity: "sensor.tina_to_home",
// },
// {
// name: "Work",
// zone: "zone.powertec",
// entity: "sensor.commute_to_work",
// },
// ],
// type: "custom:waze-card",
// },
// },
{
style: {
"border-radius": "20px",
@@ -722,46 +597,8 @@ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({
],
type: "vertical-stack",
},
// {
// cards: [
// {
// style: {
// "border-radius": "20px",
// color: "#999999",
// "box-shadow": "3px 3px rgba(0,0,0,0.4)",
// border: "solid 1px rgba(100,100,100,0.3)",
// },
// type: "custom:card-modder",
// card: {
// type: "picture-entity",
// entity: "camera.bom_radar",
// },
// },
// // {
// // style: {
// // "background-image": 'url("/assets/jimpower/cardbackK.png")',
// // "background-size": "100% 525px",
// // "box-shadow": "3px 3px rgba(0,0,0,0.4)",
// // "background-repeat": "no-repeat",
// // color: "#999999",
// // "border-radius": "20px",
// // border: "solid 1px rgba(100,100,100,0.3)",
// // "background-color": "rgba(50,50,50,0.3)",
// // },
// // type: "custom:card-modder",
// // card: {
// // title: null,
// // type: "custom:tracker-card",
// // trackers: [
// // "sensor.custom_card_tracker",
// // "sensor.custom_component_tracker",
// // ],
// // },
// // },
// ],
// type: "vertical-stack",
// },
],
path: "home",
icon: "mdi:castle",
name: "Home",
background:
@@ -881,26 +718,13 @@ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({
card: {
image: "/assets/jimpower/security/air_8.jpg",
elements: [
{
image:
"https://www.airvisual.com/assets/aqi/ic-face-1-green.svg",
type: "image",
style: {
width: "80px",
top: "30%",
left: "12%",
transform: "none",
height: "80px",
},
entity: "sensor.us_air_pollution_level_2",
},
{
style: {
color: "hsl(120, 41%, 39%)",
top: "50%",
"font-weight": 600,
"font-size": "20px",
left: "44%",
"font-size": "50px",
left: "30%",
},
type: "state-label",
entity: "sensor.us_air_pollution_level_2",
@@ -920,7 +744,7 @@ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({
style: {
color: "white",
top: "80%",
left: "52%",
left: "48%",
},
type: "state-icon",
entity: "sensor.us_main_pollutant_2",
@@ -1411,6 +1235,7 @@ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({
type: "vertical-stack",
},
],
path: "security",
icon: "hass:shield-home",
name: "Security",
background:
+10 -5
View File
@@ -101,7 +101,12 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
"sensor.zwave_battery_front_door": {
entity_id: "sensor.zwave_battery_front_door",
state: "63",
attributes: { friendly_name: "Battery", icon: "mdi:battery-60" },
attributes: {
friendly_name: "Battery",
icon: "mdi:battery-60",
unit_of_measurement: "%",
device_class: "battery",
},
},
"sensor.oskar_devices": {
entity_id: "sensor.oskar_devices",
@@ -164,7 +169,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
},
"input_select.christmas_pattern": {
entity_id: "input_select.christmas_pattern",
state: "None",
state: "Rainbow",
attributes: {
options: [
"None",
@@ -186,7 +191,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
},
"input_select.christmas_palette": {
entity_id: "input_select.christmas_palette",
state: "None",
state: "Party",
attributes: {
options: [
"None",
@@ -457,7 +462,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
state: "0.0",
attributes: {
unit_of_measurement: "kB/s",
friendly_name: "Nedladdning",
friendly_name: "Downloading",
icon: "mdi:file-download",
},
},
@@ -471,7 +476,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
state: "0.0",
attributes: {
unit_of_measurement: "kB/s",
friendly_name: "Uppladdning",
friendly_name: "Uploading",
icon: "mdi:file-upload",
},
},
+9 -198
View File
@@ -2,44 +2,7 @@ import { DemoConfig } from "../types";
export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
name: "Hem",
resources: [
// {
// url: "/local/custom-lovelace/monster-card.js",
// type: "js",
// },
// {
// url: "/local/custom-lovelace/mini-media-player-bundle.js?v=0.9.8",
// type: "module",
// },
// {
// url: "/local/custom-lovelace/slideshow-card.js?=1.1.0",
// type: "js",
// },
// {
// url: "/local/custom-lovelace/fold-entity-row.js?v=3ae2c4",
// type: "js",
// },
// {
// url: "/local/custom-lovelace/swipe-card/swipe-card.js?v=2.0.0",
// type: "module",
// },
// {
// url: "/local/custom-lovelace/upcoming-media-card/upcoming-media-card.js",
// type: "js",
// },
// {
// url: "/local/custom-lovelace/tracker-card.js?v=0.1.5",
// type: "js",
// },
// {
// url: "/local/custom-lovelace/card-tools.js?v=6ce5d0",
// type: "js",
// },
// {
// url: "/local/custom-lovelace/krisinfo.js?=0.0.1",
// type: "js",
// },
],
resources: [],
views: [
{
cards: [
@@ -64,7 +27,7 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
style: {
color: "white",
top: "93%",
left: "90%",
left: "85%",
},
type: "state-label",
entity: "sensor.battery_oskar",
@@ -87,7 +50,7 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
{
style: {
color: "white",
top: "92%",
top: "93%",
left: "20%",
},
type: "state-label",
@@ -96,8 +59,8 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
{
style: {
color: "white",
top: "92%",
left: "90%",
top: "93%",
left: "85%",
},
type: "state-label",
entity: "sensor.battery_bella",
@@ -105,7 +68,7 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
{
style: {
color: "white",
top: "92%",
top: "93%",
left: "55%",
},
type: "state-label",
@@ -131,78 +94,6 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
type: "entities",
title: "Lock",
},
// {
// filter: {
// exclude: [
// {
// state: "not_home",
// },
// ],
// include: [
// {
// entity_id: "device_tracker.annasiphone",
// },
// {
// entity_id: "device_tracker.iphone_2",
// },
// ],
// },
// type: "custom:monster-card",
// card: {
// show_header_toggle: false,
// type: "entities",
// title: "G\u00e4ster",
// },
// show_empty: false,
// },
// {
// filter: {
// exclude: [
// {
// state: "Inget",
// },
// {
// state: "i.u.",
// },
// ],
// include: [
// {
// entity_id: "sensor.pollen_al",
// },
// {
// entity_id: "sensor.pollen_alm",
// },
// {
// entity_id: "sensor.pollen_salg_vide",
// },
// {
// entity_id: "sensor.pollen_bjork",
// },
// {
// entity_id: "sensor.pollen_bok",
// },
// {
// entity_id: "sensor.pollen_ek",
// },
// {
// entity_id: "sensor.pollen_grabo",
// },
// {
// entity_id: "sensor.pollen_gras",
// },
// {
// entity_id: "sensor.pollen_hassel",
// },
// ],
// },
// type: "custom:monster-card",
// card: {
// show_header_toggle: false,
// type: "entities",
// title: "Pollenniv\u00e5er",
// },
// show_empty: false,
// },
{
cards: [
{
@@ -226,10 +117,6 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
],
type: "vertical-stack",
},
// {
// url: "https://embed.windy.com/embed2.html",
// type: "iframe",
// },
{
entities: [
{
@@ -263,6 +150,7 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
],
type: "glance",
show_state: false,
columns: 4,
},
{
entities: ["sensor.oskar_bluetooth"],
@@ -270,32 +158,6 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
type: "entities",
title: "Occupancy",
},
// {
// filter: {
// exclude: [
// {
// state: false,
// },
// ],
// include: [
// {
// entity_id:
// "binary_sensor.fibaro_system_unknown_type0c02_id1003_sensor_2",
// },
// {
// entity_id:
// "binary_sensor.fibaro_system_unknown_type0c02_id1003_sensor_3",
// },
// ],
// },
// type: "custom:monster-card",
// card: {
// show_header_toggle: false,
// type: "entities",
// title: "Brandvarnare",
// },
// show_empty: false,
// },
{
type: "weather-forecast",
entity: "weather.smhi_vader",
@@ -378,41 +240,9 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
"binary_sensor.windows_server",
"binary_sensor.teamspeak",
"binary_sensor.harmony_hub",
// {
// style: {
// height: "1px",
// width: "85%",
// "margin-left": "auto",
// background: "#62717b",
// "margin-right": "auto",
// },
// type: "divider",
// },
// {
// items: ["sensor.uptime_router", "sensor.installerad_routeros"],
// head: {
// entity: "binary_sensor.router",
// },
// type: "custom:fold-entity-row",
// group_config: {
// icon: "mdi:router",
// },
// },
// {
// items: [
// "sensor.uptime_router_server",
// "sensor.installerad_routeros_server",
// ],
// head: {
// entity: "binary_sensor.router_server",
// },
// type: "custom:fold-entity-row",
// group_config: {
// icon: "mdi:router",
// },
// },
],
show_header_toggle: false,
state_color: true,
type: "entities",
title: "Network",
},
@@ -422,29 +252,10 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
"binary_sensor.ubiquiti_switch",
"binary_sensor.ubiquiti_nvr",
"binary_sensor.entre_kamera",
// {
// items: ["sensor.uptime_ap_1"],
// head: {
// entity: "binary_sensor.accesspunkt_1",
// },
// type: "custom:fold-entity-row",
// group_config: {
// icon: "router-wireless",
// },
// },
// {
// items: ["sensor.uptime_ap_2"],
// head: {
// entity: "binary_sensor.accesspunkt_2",
// },
// type: "custom:fold-entity-row",
// group_config: {
// icon: "router-wireless",
// },
// },
"sensor.total_clients_wireless",
],
show_header_toggle: false,
state_color: true,
type: "entities",
title: "Ubiquiti",
},
+2 -66
View File
@@ -215,6 +215,7 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
card: {
type: "glance",
show_state: false,
columns: 4,
},
state_filter: ["on"],
},
@@ -808,67 +809,6 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
],
type: "vertical-stack",
},
// {
// cards: [
// {
// entities: [
// {
// hide_when_off: true,
// toggle: true,
// type: "custom:slider-entity-row",
// name: "Bedside",
// entity: "light.bedside_lamp",
// },
// {
// hide_when_off: true,
// toggle: true,
// type: "custom:slider-entity-row",
// name: "Bedroom",
// entity: "light.bedroom_ceiling_light",
// },
// {
// hide_when_off: true,
// toggle: true,
// type: "custom:slider-entity-row",
// name: "Isa",
// entity: "light.isa_ceiling_light",
// },
// {
// hide_when_off: true,
// toggle: true,
// type: "custom:slider-entity-row",
// name: "Upstairs hallway",
// entity: "light.upstairs_hallway_ceiling_light_level",
// },
// {
// hide_when_off: true,
// toggle: true,
// type: "custom:slider-entity-row",
// name: "Nightlight",
// entity: "light.gateway_light_34ce008bfc4b",
// },
// {
// hide_when_off: true,
// toggle: true,
// type: "custom:slider-entity-row",
// name: "Walk in closet",
// entity: "light.walk_in_closet_lights",
// },
// {
// hide_when_off: true,
// toggle: false,
// type: "custom:slider-entity-row",
// name: "Stefan",
// entity: "light.stefan_lightstrip",
// },
// ],
// show_header_toggle: false,
// type: "entities",
// title: "Upstairs",
// },
// ],
// type: "vertical-stack",
// },
],
path: "lights",
title: "Lights",
@@ -918,10 +858,6 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
name: "Dafang",
icon: "mdi:webcam",
},
{
name: "IR Hallway",
entity: "sensor.system_ir_blaster",
},
{
name: "IR Bedroom",
entity: "sensor.system_ir_blaster_bedroom",
@@ -940,7 +876,7 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
"sensor.system_ring_chime",
],
type: "glance",
columns: 5,
columns: 4,
show_state: false,
},
{
+1 -1
View File
@@ -9,5 +9,5 @@ export interface DemoConfig {
authorUrl: string;
lovelace: (localize: LocalizeFunc) => LovelaceConfig;
entities: (localize: LocalizeFunc) => Entity[];
theme: () => { [key: string]: string } | null;
theme: () => Record<string, string> | null;
}
+1 -1
View File
@@ -3,8 +3,8 @@ import {
CSSResult,
customElement,
html,
LitElement,
internalProperty,
LitElement,
TemplateResult,
} from "lit-element";
import { CastManager } from "../../../src/cast/cast_manager";
+1 -1
View File
@@ -3,9 +3,9 @@ import {
css,
CSSResult,
html,
internalProperty,
LitElement,
property,
internalProperty,
TemplateResult,
} from "lit-element";
import { until } from "lit-html/directives/until";
+2 -4
View File
@@ -1,13 +1,11 @@
import "../../src/resources/safari-14-attachshadow-patch";
import "@polymer/polymer/lib/elements/dom-if";
import "@polymer/polymer/lib/elements/dom-repeat";
import "../../src/resources/ha-style";
import "../../src/resources/roboto";
import "../../src/resources/safari-14-attachshadow-patch";
import "./ha-demo";
/* polyfill for paper-dropdown */
setTimeout(() => {
import(
/* webpackChunkName: "polyfill-web-animations-next" */ "web-animations-js/web-animations-next-lite.min"
);
import("web-animations-js/web-animations-next-lite.min");
}, 1000);
+1
View File
@@ -1,3 +1,4 @@
// Compat needs to be first import
import "../../src/resources/compatibility";
import { isNavigationClick } from "../../src/common/dom/is-navigation-click";
import { navigate } from "../../src/common/navigate";
+3 -2
View File
@@ -21,15 +21,16 @@ class DemoCard extends PolymerElement {
}
pre {
width: 400px;
margin: 16px;
margin: 0 16px;
overflow: auto;
color: var(--primary-text-color);
}
@media only screen and (max-width: 800px) {
.root {
flex-direction: column;
}
pre {
margin-left: 0;
margin: 16px 0;
}
}
</style>
+3 -3
View File
@@ -2,10 +2,10 @@ 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 "../../../src/components/ha-switch";
import "../../../src/components/ha-formfield";
import "./demo-card";
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() {
+29 -25
View File
@@ -2,58 +2,62 @@ 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/state-summary/state-card-content";
import "../../../src/dialogs/more-info/more-info-content";
import "../../../src/state-summary/state-card-content";
class DemoMoreInfo extends PolymerElement {
static get template() {
return html`
<style>
:host {
.root {
display: flex;
align-items: start;
}
#card {
max-width: 400px;
width: 100vw;
}
ha-card {
width: 333px;
width: 352px;
padding: 20px 24px;
}
state-card-content {
display: block;
margin-bottom: 16px;
}
pre {
width: 400px;
margin: 16px;
margin: 0 16px;
overflow: auto;
color: var(--primary-text-color);
}
@media only screen and (max-width: 800px) {
:host {
.root {
flex-direction: column;
}
pre {
margin-left: 0;
margin: 16px 0;
}
}
</style>
<ha-card>
<state-card-content
state-obj="[[_stateObj]]"
hass="[[hass]]"
in-dialog
></state-card-content>
<div class="root">
<div id="card">
<ha-card>
<state-card-content
state-obj="[[_stateObj]]"
hass="[[hass]]"
in-dialog
></state-card-content>
<more-info-content
hass="[[hass]]"
state-obj="[[_stateObj]]"
></more-info-content>
</ha-card>
<template is="dom-if" if="[[showConfig]]">
<pre>[[_jsonEntity(_stateObj)]]</pre>
</template>
<more-info-content
hass="[[hass]]"
state-obj="[[_stateObj]]"
></more-info-content>
</ha-card>
</div>
<template is="dom-if" if="[[showConfig]]">
<pre>[[_jsonEntity(_stateObj)]]</pre>
</template>
</div>
`;
}
+36 -9
View File
@@ -2,6 +2,8 @@ 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-more-info";
@@ -9,6 +11,10 @@ class DemoMoreInfos 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;
@@ -23,20 +29,31 @@ class DemoMoreInfos extends PolymerElement {
.filters {
margin-left: 60px;
}
ha-formfield {
margin-right: 16px;
}
</style>
<app-toolbar>
<div class="filters">
<ha-switch checked="{{_showConfig}}">Show entity</ha-switch>
<ha-formfield label="Show entities">
<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 class="cards">
<template is="dom-repeat" items="[[entities]]">
<demo-more-info
entity-id="[[item]]"
show-config="[[_showConfig]]"
hass="[[hass]]"
></demo-more-info>
</template>
<div id="container">
<div class="cards">
<template is="dom-repeat" items="[[entities]]">
<demo-more-info
entity-id="[[item]]"
show-config="[[_showConfig]]"
hass="[[hass]]"
></demo-more-info>
</template>
</div>
</div>
`;
}
@@ -51,6 +68,16 @@ class DemoMoreInfos extends PolymerElement {
},
};
}
_showConfigToggled(ev) {
this._showConfig = ev.target.checked;
}
_darkThemeToggled(ev) {
applyThemesOnElement(this.$.container, { themes: {} }, "default", {
dark: ev.target.checked,
});
}
}
customElements.define("demo-more-infos", DemoMoreInfos);
+4 -4
View File
@@ -7,8 +7,8 @@ export const createMediaPlayerEntities = () => [
media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)",
media_artist: "Technohead",
// Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media +
// Select Source + Stop + Clear + Play + Shuffle Set + Browse Media
supported_features: 195135,
// Select Source + Stop + Clear + Play + Shuffle Set
supported_features: 64063,
entity_picture: "/images/album_cover_2.jpg",
media_duration: 300,
media_position: 50,
@@ -24,8 +24,8 @@ export const createMediaPlayerEntities = () => [
media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)",
media_artist: "Technohead",
// Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media +
// Select Source + Stop + Clear + Play + Shuffle Set
supported_features: 64063,
// Select Source + Stop + Clear + Play + Shuffle Set + Browse Media
supported_features: 195135,
entity_picture: "/images/album_cover.jpg",
media_duration: 300,
media_position: 0,
+72
View File
@@ -0,0 +1,72 @@
import { getEntity } from "../../../src/fake_data/entity";
export const createPlantEntities = () => [
getEntity("plant", "lemon_tree", "ok", {
problem: "none",
sensors: {
moisture: "sensor.lemon_tree_moisture",
battery: "sensor.lemon_tree_battery",
temperature: "sensor.lemon_tree_temperature",
conductivity: "sensor.lemon_tree_conductivity",
brightness: "sensor.lemon_tree_brightness",
},
unit_of_measurement_dict: {
temperature: "°C",
moisture: "%",
brightness: "lx",
battery: "%",
conductivity: "μS/cm",
},
moisture: 54,
battery: 95,
temperature: 15.6,
conductivity: 1,
brightness: 12,
max_brightness: 20,
friendly_name: "Lemon Tree",
}),
getEntity("plant", "apple_tree", "ok", {
problem: "brightness",
sensors: {
moisture: "sensor.apple_tree_moisture",
battery: "sensor.apple_tree_battery",
temperature: "sensor.apple_tree_temperature",
conductivity: "sensor.apple_tree_conductivity",
brightness: "sensor.apple_tree_brightness",
},
unit_of_measurement_dict: {
temperature: "°C",
moisture: "%",
brightness: "lx",
battery: "%",
conductivity: "μS/cm",
},
moisture: 54,
battery: 2,
temperature: 15.6,
conductivity: 1,
brightness: 25,
max_brightness: 20,
friendly_name: "Apple Tree",
}),
getEntity("plant", "sunflowers", "ok", {
problem: "moisture, temperature, conductivity",
sensors: {
moisture: "sensor.sunflowers_moisture",
temperature: "sensor.sunflowers_temperature",
conductivity: "sensor.sunflowers_conductivity",
brightness: "sensor.sunflowers_brightness",
},
unit_of_measurement_dict: {
temperature: "°C",
moisture: "%",
brightness: "lx",
conductivity: "μS/cm",
},
moisture: 54,
temperature: 15.6,
conductivity: 1,
brightness: 25,
entity_picture: "/images/sunflowers.jpg",
}),
];
+19 -30
View File
@@ -1,6 +1,11 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import {
customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -71,35 +76,19 @@ const CONFIGS = [
},
];
class DemoAlarmPanelEntity extends PolymerElement {
static get template() {
return html`
<demo-cards
id="demos"
hass="[[hass]]"
configs="[[_configs]]"
></demo-cards>
`;
@customElement("demo-hui-alarm-panel-card")
class DemoAlarmPanelEntity extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
}
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
hass: Object,
};
}
public ready() {
super.ready();
this._setupDemo();
}
private async _setupDemo() {
const hass = provideHass(this.$.demos);
await hass.updateTranslations(null, "en");
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
}
}
+18 -25
View File
@@ -1,6 +1,11 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import {
customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -53,31 +58,19 @@ const CONFIGS = [
},
];
class DemoConditional extends PolymerElement {
static get template() {
return html`
<demo-cards
id="demos"
hass="[[hass]]"
configs="[[_configs]]"
></demo-cards>
`;
@customElement("demo-hui-conditional-card")
class DemoConditional extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
}
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
hass: Object,
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
}
}
+18 -18
View File
@@ -1,6 +1,11 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import {
customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -217,24 +222,19 @@ const CONFIGS = [
},
];
class DemoEntities extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
@customElement("demo-hui-entities-card")
class DemoEntities extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
}
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
}
}
@@ -1,6 +1,11 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import {
customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -20,10 +25,10 @@ const CONFIGS = [
`,
},
{
heading: "With Name",
heading: "With Name (defined in card)",
config: `
- type: button
name: Bedroom
name: Custom Name
entity: light.bed_light
`,
},
@@ -32,7 +37,7 @@ const CONFIGS = [
config: `
- type: button
entity: light.bed_light
icon: mdi:hotel
icon: mdi:tools
`,
},
{
@@ -48,7 +53,7 @@ const CONFIGS = [
config: `
- type: button
entity: light.bed_light
tap_action:
tap_action:
action: toggle
`,
},
@@ -69,31 +74,19 @@ const CONFIGS = [
},
];
class DemoButtonEntity extends PolymerElement {
static get template() {
return html`
<demo-cards
id="demos"
hass="[[hass]]"
configs="[[_configs]]"
></demo-cards>
`;
@customElement("demo-hui-entity-button-card")
class DemoButtonEntity extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
}
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
hass: Object,
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
}
}
@@ -1,6 +1,11 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import {
customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -43,7 +48,7 @@ const ENTITIES = [
const CONFIGS = [
{
heading: "Controller",
heading: "Unfiltered controller",
config: `
- type: entities
entities:
@@ -53,7 +58,7 @@ const CONFIGS = [
`,
},
{
heading: "Basic",
heading: "Filtered entities card",
config: `
- type: entity-filter
entities:
@@ -69,7 +74,27 @@ const CONFIGS = [
`,
},
{
heading: "With card config",
heading: 'With "entities" card config',
config: `
- type: entity-filter
entities:
- device_tracker.demo_anne_therese
- device_tracker.demo_home_boy
- device_tracker.demo_paulus
- light.bed_light
- light.ceiling_lights
- light.kitchen_lights
state_filter:
- "on"
- not_home
card:
type: entities
title: Custom Title
show_header_toggle: false
`,
},
{
heading: 'With "glance" card config',
config: `
- type: entity-filter
entities:
@@ -84,31 +109,27 @@ const CONFIGS = [
- not_home
card:
type: glance
show_state: false
show_state: true
title: Custom Title
`,
},
];
class DemoFilter extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
@customElement("demo-hui-entity-filter-card")
class DemoEntityFilter extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
}
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
}
}
customElements.define("demo-hui-entity-filter-card", DemoFilter);
customElements.define("demo-hui-entity-filter-card", DemoEntityFilter);
+52 -29
View File
@@ -1,12 +1,19 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import {
customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
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", {}),
getEntity("sensor", "brightness_medium", "53", {}),
getEntity("sensor", "brightness_high", "87", {}),
getEntity("plant", "bonsai", "ok", {}),
getEntity("sensor", "not_working", "unavailable", {}),
getEntity("sensor", "outside_humidity", "54", {
@@ -21,16 +28,10 @@ const CONFIGS = [
{
heading: "Basic example",
config: `
- type: gauge
entity: sensor.brightness
`,
},
{
heading: "With title",
config: `
- type: gauge
title: Humidity
entity: sensor.outside_humidity
name: Outside Humidity
`,
},
{
@@ -39,6 +40,7 @@ const CONFIGS = [
- type: gauge
entity: sensor.outside_temperature
unit_of_measurement: C
name: Outside Temperature
`,
},
{
@@ -46,19 +48,45 @@ const CONFIGS = [
config: `
- type: gauge
entity: sensor.brightness
name: Brightness Low
severity:
red: 32
red: 75
green: 0
yellow: 23
yellow: 50
`,
},
{
heading: "Setting Min and Max Values",
heading: "Setting Severity Levels",
config: `
- type: gauge
entity: sensor.brightness_medium
name: Brightness Medium
severity:
red: 75
green: 0
yellow: 50
`,
},
{
heading: "Setting Severity Levels",
config: `
- type: gauge
entity: sensor.brightness_high
name: Brightness High
severity:
red: 75
green: 0
yellow: 50
`,
},
{
heading: "Setting Min (0) and Max (15) Values",
config: `
- type: gauge
entity: sensor.brightness
name: Brightness
min: 0
max: 38
max: 15
`,
},
{
@@ -84,24 +112,19 @@ const CONFIGS = [
},
];
class DemoGaugeEntity extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
@customElement("demo-hui-gauge-card")
class DemoGaugeEntity extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
}
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
}
}
+53 -61
View File
@@ -1,6 +1,11 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import {
customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -77,7 +82,8 @@ const CONFIGS = [
heading: "With title",
config: `
- type: glance
title: This is glance
title: Custom title
columns: 4
entities:
- device_tracker.demo_paulus
- media_player.living_room
@@ -104,9 +110,10 @@ const CONFIGS = [
`,
},
{
heading: "No name",
heading: "No entity names",
config: `
- type: glance
columns: 4
show_name: false
entities:
- device_tracker.demo_paulus
@@ -119,9 +126,10 @@ const CONFIGS = [
`,
},
{
heading: "No state",
heading: "No state labels",
config: `
- type: glance
columns: 4
show_state: false
entities:
- device_tracker.demo_paulus
@@ -134,9 +142,10 @@ const CONFIGS = [
`,
},
{
heading: "No name and no state",
heading: "No names and no state labels",
config: `
- type: glance
columns: 4
show_name: false
show_state: false
entities:
@@ -150,47 +159,24 @@ const CONFIGS = [
`,
},
{
heading: "Custom name, custom icon",
heading: "Custom name + custom icon",
config: `
- type: glance
columns: 4
entities:
- entity: device_tracker.demo_paulus
name: ¯\\_()_/¯
icon: mdi:home-assistant
- media_player.living_room
- sun.sun
- cover.kitchen_window
- entity: light.kitchen_lights
icon: mdi:alarm-light
- lock.kitchen_door
- light.ceiling_lights
`,
},
{
heading: "Custom tap action",
config: `
- type: glance
entities:
- entity: lock.kitchen_door
tap_action:
type: toggle
- entity: light.ceiling_lights
tap_action:
action: call-service
service: light.turn_on
service_data:
entity_id: light.ceiling_lights
- device_tracker.demo_paulus
- media_player.living_room
- sun.sun
- cover.kitchen_window
- light.kitchen_lights
- entity: media_player.living_room
name: ¯\\_()_/¯
icon: mdi:home-assistant
`,
},
{
heading: "Selectively hidden name",
config: `
- type: glance
columns: 4
entities:
- device_tracker.demo_paulus
- entity: media_player.living_room
@@ -199,45 +185,51 @@ const CONFIGS = [
- entity: cover.kitchen_window
name:
- light.kitchen_lights
- entity: lock.kitchen_door
name:
- light.ceiling_lights
`,
},
{
heading: "Primary theme",
heading: "Custom tap action",
config: `
- type: glance
theming: primary
columns: 4
entities:
- device_tracker.demo_paulus
- media_player.living_room
- sun.sun
- cover.kitchen_window
- light.kitchen_lights
- lock.kitchen_door
- light.ceiling_lights
- entity: lock.kitchen_door
name: Custom
tap_action:
type: toggle
- entity: light.ceiling_lights
name: Custom
tap_action:
action: call-service
service: light.turn_on
service_data:
entity_id: light.ceiling_lights
- entity: sun.sun
name: Regular
- entity: light.kitchen_lights
name: Regular
`,
},
];
class DemoPicEntity extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
@customElement("demo-hui-glance-card")
class DemoGlanceEntity extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
}
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
}
}
customElements.define("demo-hui-glance-card", DemoPicEntity);
customElements.define("demo-hui-glance-card", DemoGlanceEntity);
+5 -15
View File
@@ -1,6 +1,4 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import { customElement, html, LitElement, TemplateResult } from "lit-element";
import "../components/demo-cards";
const CONFIGS = [
@@ -37,18 +35,10 @@ const CONFIGS = [
},
];
class DemoIframe extends PolymerElement {
static get template() {
return html` <demo-cards configs="[[_configs]]"></demo-cards> `;
}
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
@customElement("demo-hui-iframe-card")
class DemoIframe extends LitElement {
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
}
}
+37 -23
View File
@@ -1,6 +1,11 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import {
customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -8,29 +13,43 @@ import "../components/demo-cards";
const ENTITIES = [
getEntity("light", "bed_light", "on", {
friendly_name: "Bed Light",
brightness: 130,
brightness: 255,
}),
getEntity("light", "dim", "off", {
getEntity("light", "dim_on", "on", {
friendly_name: "Dining Room",
supported_features: 1,
brightness: 100,
}),
getEntity("light", "dim_off", "off", {
friendly_name: "Dining Room",
supported_features: 1,
}),
getEntity("light", "unavailable", "unavailable", {
friendly_name: "Lost Light",
supported_features: 1,
}),
];
const CONFIGS = [
{
heading: "Basic example",
heading: "Switchable Light",
config: `
- type: light
entity: light.bed_light
`,
},
{
heading: "Dim",
heading: "Dimmable Light On",
config: `
- type: light
entity: light.dim
entity: light.dim_on
`,
},
{
heading: "Dimmable Light Off",
config: `
- type: light
entity: light.dim_off
`,
},
{
@@ -49,24 +68,19 @@ const CONFIGS = [
},
];
class DemoLightEntity extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
@customElement("demo-hui-light-card")
class DemoLightEntity extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
}
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
}
}
+18 -25
View File
@@ -1,6 +1,11 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import {
customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -161,31 +166,19 @@ const CONFIGS = [
},
];
class DemoMap extends PolymerElement {
static get template() {
return html`
<demo-cards
id="demos"
hass="[[hass]]"
configs="[[_configs]]"
></demo-cards>
`;
@customElement("demo-hui-map-card")
class DemoMap extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
}
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
hass: Object,
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
}
}
+19 -18
View File
@@ -1,6 +1,11 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import {
customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { mockTemplate } from "../../../demo/src/stubs/template";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -254,23 +259,19 @@ const CONFIGS = [
},
];
class DemoMarkdown extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
@customElement("demo-hui-markdown-card")
class DemoMarkdown extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
}
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
mockTemplate(hass);
}
}
@@ -1,6 +1,11 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import {
customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
import { createMediaPlayerEntities } from "../data/media_players";
@@ -146,35 +151,33 @@ const CONFIGS = [
entity: media_player.receiver_off
`,
},
{
heading: "Grid Full Size",
config: `
- type: grid
columns: 1
cards:
- type: media-control
entity: media_player.music_paused
`,
},
];
class DemoHuiMediControlCard extends PolymerElement {
static get template() {
return html`
<demo-cards
id="demos"
hass="[[hass]]"
configs="[[_configs]]"
></demo-cards>
`;
@customElement("demo-hui-media-control-card")
class DemoHuiMediaControlCard extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
}
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
hass: Object,
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(createMediaPlayerEntities());
}
}
customElements.define("demo-hui-media-control-card", DemoHuiMediControlCard);
customElements.define("demo-hui-media-control-card", DemoHuiMediaControlCard);
+22 -29
View File
@@ -1,6 +1,11 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import {
customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
import { createMediaPlayerEntities } from "../data/media_players";
@@ -26,9 +31,9 @@ const CONFIGS = [
- entity: media_player.android_cast
name: Screen casting
- entity: media_player.image_display
name: Digital Picture Frame
name: Digital Picture Frame
- entity: media_player.sonos_idle
name: Sonos Idle
name: Sonos Idle
- entity: media_player.idle_browse_media
name: Idle waiting for Browse Media
- entity: media_player.theater_off
@@ -38,7 +43,7 @@ const CONFIGS = [
- entity: media_player.theater_off_static
name: Player Off (cannot be switched on)
- entity: media_player.theater_on_static
name: Player On (cannot be switched off)
name: Player On (cannot be switched off)
- entity: media_player.idle
name: Player Idle
- entity: media_player.playing
@@ -55,33 +60,21 @@ const CONFIGS = [
},
];
class DemoHuiMediaPlayerRows extends PolymerElement {
static get template() {
return html`
<demo-cards
id="demos"
hass="[[hass]]"
configs="[[_configs]]"
></demo-cards>
`;
@customElement("demo-hui-media-player-row")
class DemoHuiMediaPlayerRow extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
}
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
hass: Object,
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(createMediaPlayerEntities());
}
}
customElements.define("demo-hui-media-player-rows", DemoHuiMediaPlayerRows);
customElements.define("demo-hui-media-player-row", DemoHuiMediaPlayerRow);
@@ -1,6 +1,11 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import {
customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -125,26 +130,21 @@ const CONFIGS = [
},
];
class DemoPicElements extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
@customElement("demo-hui-picture-elements-card")
class DemoPictureElements extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
}
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
}
}
customElements.define("demo-hui-picture-elements-card", DemoPicElements);
customElements.define("demo-hui-picture-elements-card", DemoPictureElements);
@@ -1,6 +1,11 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import {
customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -80,26 +85,21 @@ const CONFIGS = [
},
];
class DemoPicEntity extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
@customElement("demo-hui-picture-entity-card")
class DemoPictureEntity extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
}
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
}
}
customElements.define("demo-hui-picture-entity-card", DemoPicEntity);
customElements.define("demo-hui-picture-entity-card", DemoPictureEntity);
@@ -1,6 +1,11 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import {
customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -121,26 +126,21 @@ const CONFIGS = [
},
];
class DemoPicGlance extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
@customElement("demo-hui-picture-glance-card")
class DemoPictureGlance extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
}
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
}
}
customElements.define("demo-hui-picture-glance-card", DemoPicGlance);
customElements.define("demo-hui-picture-glance-card", DemoPictureGlance);
+55
View File
@@ -0,0 +1,55 @@
import {
customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
import { createPlantEntities } from "../data/plants";
const CONFIGS = [
{
heading: "Basic example",
config: `
- type: plant-status
entity: plant.lemon_tree
`,
},
{
heading: "Problem (too bright) + low battery",
config: `
- type: plant-status
entity: plant.apple_tree
`,
},
{
heading: "With picture + multiple problems",
config: `
- type: plant-status
entity: plant.sunflowers
name: Sunflowers Name Overwrite
`,
},
];
@customElement("demo-hui-plant-card")
export class DemoPlantEntity 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(createPlantEntities());
}
}
customElements.define("demo-hui-plant-card", DemoPlantEntity);
@@ -1,6 +1,11 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import {
customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -20,24 +25,19 @@ const CONFIGS = [
},
];
class DemoShoppingListEntity extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
@customElement("demo-hui-shopping-list-card")
class DemoShoppingListEntity extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
}
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.mockAPI("shopping_list", () => [
{ name: "list", id: 1, complete: false },
+18 -18
View File
@@ -1,6 +1,11 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import {
customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { mockHistory } from "../../../demo/src/stubs/history";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
@@ -132,24 +137,19 @@ const CONFIGS = [
},
];
class DemoStack extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
@customElement("demo-hui-stack-card")
class DemoStack extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
}
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
mockHistory(hass);
}
+18 -18
View File
@@ -1,6 +1,11 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import {
customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -74,24 +79,19 @@ const CONFIGS = [
},
];
class DemoThermostatEntity extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
@customElement("demo-hui-thermostat-card")
class DemoThermostatEntity extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
}
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
}
}
+60 -29
View File
@@ -1,12 +1,29 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import {
customElement,
html,
LitElement,
property,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import "../../../src/components/ha-card";
import { SUPPORT_BRIGHTNESS } from "../../../src/data/light";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-more-infos";
import {
SUPPORT_BRIGHTNESS,
SUPPORT_COLOR,
SUPPORT_COLOR_TEMP,
SUPPORT_EFFECT,
SUPPORT_FLASH,
SUPPORT_TRANSITION,
SUPPORT_WHITE_VALUE,
} from "../../../src/data/light";
import "../../../src/dialogs/more-info/more-info-content";
import { getEntity } from "../../../src/fake_data/entity";
import {
MockHomeAssistant,
provideHass,
} from "../../../src/fake_data/provide_hass";
import "../components/demo-more-infos";
const ENTITIES = [
getEntity("light", "bed_light", "on", {
@@ -14,38 +31,52 @@ const ENTITIES = [
}),
getEntity("light", "kitchen_light", "on", {
friendly_name: "Brightness Light",
brightness: 80,
brightness: 200,
supported_features: SUPPORT_BRIGHTNESS,
}),
getEntity("light", "color_temperature_light", "on", {
friendly_name: "White Color Temperature Light",
brightness: 128,
color_temp: 75,
min_mireds: 30,
max_mireds: 150,
supported_features: SUPPORT_BRIGHTNESS + SUPPORT_COLOR_TEMP,
}),
getEntity("light", "color_effectslight", "on", {
friendly_name: "Color Effets Light",
brightness: 255,
hs_color: [30, 100],
white_value: 36,
supported_features:
SUPPORT_BRIGHTNESS +
SUPPORT_EFFECT +
SUPPORT_FLASH +
SUPPORT_COLOR +
SUPPORT_TRANSITION +
SUPPORT_WHITE_VALUE,
effect_list: ["random", "colorloop"],
}),
];
class DemoMoreInfoLight extends PolymerElement {
static get template() {
@customElement("demo-more-info-light")
class DemoMoreInfoLight extends LitElement {
@property() public hass!: MockHomeAssistant;
@query("demo-more-infos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`
<demo-more-infos
hass="[[hass]]"
entities="[[_entities]]"
.hass=${this.hass}
.entities=${ENTITIES.map((ent) => ent.entityId)}
></demo-more-infos>
`;
}
static get properties() {
return {
_entities: {
type: Array,
value: ENTITIES.map((ent) => ent.entityId),
},
};
}
public ready() {
super.ready();
this._setupDemo();
}
private async _setupDemo() {
const hass = provideHass(this);
await hass.updateTranslations(null, "en");
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.addEntities(ENTITIES);
}
}
+3 -4
View File
@@ -1,9 +1,10 @@
import "@material/mwc-button";
import { html, LitElement, TemplateResult } from "lit-element";
import { customElement, html, LitElement, TemplateResult } from "lit-element";
import "../../../src/components/ha-card";
import { ActionHandlerEvent } from "../../../src/data/lovelace";
import { actionHandler } from "../../../src/panels/lovelace/common/directives/action-handler-directive";
@customElement("demo-util-long-press")
export class DemoUtilLongPress extends LitElement {
protected render(): TemplateResult {
return html`
@@ -20,7 +21,7 @@ export class DemoUtilLongPress extends LitElement {
<textarea></textarea>
<div>(try pressing and scrolling too!)</div>
<div>Try pressing and scrolling too!</div>
</ha-card>
`
)}
@@ -62,5 +63,3 @@ export class DemoUtilLongPress extends LitElement {
`;
}
}
customElements.define("demo-util-long-press", DemoUtilLongPress);
+70 -60
View File
@@ -14,54 +14,51 @@ import "../../src/styles/polymer-ha-style";
// eslint-disable-next-line import/extensions
import { DEMOS } from "../build/import-demos";
const fixPath = (path) => path.substr(2, path.length - 5);
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;
}
: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 {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: start;
}
.pickers ha-card {
width: 400px;
display: block;
margin: 16px 8px;
}
.pickers ha-card {
width: 400px;
display: block;
margin: 16px 8px;
}
.pickers ha-card:last-child {
margin-bottom: 16px;
}
.pickers ha-card:last-child {
margin-bottom: 16px;
}
.intro {
margin: -1em 0;
}
.intro {
margin: -1em 0;
}
p a {
color: var(--primary-color);
}
a {
color: var(--primary-text-color);
text-decoration: none;
}
p a {
color: var(--primary-color);
}
a {
color: var(--primary-text-color);
text-decoration: none;
}
</style>
<app-header-layout>
@@ -70,32 +67,42 @@ class HaGallery extends PolymerElement {
<ha-icon-button
icon="hass:arrow-left"
on-click="_backTapped"
class$='[[_computeHeaderButtonClass(_demo)]]'
class$="[[_computeHeaderButtonClass(_demo)]]"
></ha-icon-button>
<div main-title>[[_withDefault(_demo, "Home Assistant Gallery")]]</div>
<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'>
<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.
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.
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.</a>.
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]]'>
<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>
@@ -104,14 +111,14 @@ class HaGallery extends PolymerElement {
</template>
</ha-card>
<ha-card header="More Info demos">
<div class='card-content intro'>
<ha-card header="More Info Demos">
<div class="card-content intro">
<p>
More info screens show up when an entity is clicked.
</p>
</div>
<template is='dom-repeat' items='[[_moreInfoDemos]]'>
<a href='#[[item]]'>
<template is="dom-repeat" items="[[_moreInfoDemos]]">
<a href="#[[item]]">
<paper-item>
<paper-item-body>{{ item }}</paper-item-body>
<ha-icon icon="hass:chevron-right"></ha-icon>
@@ -120,14 +127,14 @@ class HaGallery extends PolymerElement {
</template>
</ha-card>
<ha-card header="Util demos">
<div class='card-content intro'>
<ha-card header="Util Demos">
<div class="card-content intro">
<p>
Test pages for our utility functions.
</p>
</div>
<template is='dom-repeat' items='[[_utilDemos]]'>
<a href='#[[item]]'>
<template is="dom-repeat" items="[[_utilDemos]]">
<a href="#[[item]]">
<paper-item>
<paper-item-body>{{ item }}</paper-item-body>
<ha-icon icon="hass:chevron-right"></ha-icon>
@@ -139,7 +146,10 @@ class HaGallery extends PolymerElement {
</template>
</div>
</app-header-layout>
<notification-manager hass=[[_fakeHass]] id='notifications'></notification-manager>
<notification-manager
hass="[[_fakeHass]]"
id="notifications"
></notification-manager>
`;
}
@@ -15,6 +15,7 @@ import {
HassioAddonInfo,
HassioAddonRepository,
} from "../../../src/data/hassio/addon";
import { Supervisor } from "../../../src/data/supervisor/supervisor";
import { HomeAssistant } from "../../../src/types";
import "../components/hassio-card-content";
import { filterAndSort } from "../components/hassio-filter-addons";
@@ -23,6 +24,8 @@ import { hassioStyle } from "../resources/hassio-style";
class HassioAddonRepositoryEl extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public supervisor!: Supervisor;
@property({ attribute: false }) public repo!: HassioAddonRepository;
@property({ attribute: false }) public addons!: HassioAddonInfo[];
@@ -54,7 +57,11 @@ class HassioAddonRepositoryEl extends LitElement {
return html`
<div class="content">
<p class="description">
No results found in "${repo.name}."
${this.supervisor.localize(
"store.no_results_found",
"repository",
repo.name
)}
</p>
</div>
`;
@@ -83,11 +90,13 @@ class HassioAddonRepositoryEl extends LitElement {
: mdiPuzzle}
.iconTitle=${addon.installed
? addon.update_available
? "New version available"
: "Add-on is installed"
? this.supervisor.localize(
"common.new_version_available"
)
: this.supervisor.localize("addon.installed")
: addon.available
? "Add-on is not installed"
: "Add-on is not available on your system"}
? this.supervisor.localize("addon.not_installed")
: this.supervisor.localize("addon.not_available")}
.iconClass=${addon.installed
? addon.update_available
? "update"
+72 -51
View File
@@ -1,3 +1,4 @@
import "../components/supervisor-connectivity";
import "@material/mwc-icon-button/mwc-icon-button";
import { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
import "@material/mwc-list/mwc-list-item";
@@ -11,17 +12,20 @@ import {
PropertyValues,
} from "lit-element";
import { html, TemplateResult } from "lit-html";
import memoizeOne from "memoize-one";
import { atLeastVersion } from "../../../src/common/config/version";
import { fireEvent } from "../../../src/common/dom/fire_event";
import { navigate } from "../../../src/common/navigate";
import "../../../src/common/search/search-input";
import { extractSearchParam } from "../../../src/common/url/search-params";
import "../../../src/components/ha-button-menu";
import "../../../src/components/ha-svg-icon";
import {
fetchHassioAddonsInfo,
HassioAddonInfo,
HassioAddonRepository,
reloadHassioAddons,
} from "../../../src/data/hassio/addon";
import { extractApiErrorMessage } from "../../../src/data/hassio/common";
import { Supervisor } from "../../../src/data/supervisor/supervisor";
import "../../../src/layouts/hass-loading-screen";
import "../../../src/layouts/hass-tabs-subpage";
import { HomeAssistant, Route } from "../../../src/types";
@@ -49,58 +53,43 @@ const sortRepos = (a: HassioAddonRepository, b: HassioAddonRepository) => {
class HassioAddonStore extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public supervisor!: Supervisor;
@property({ type: Boolean }) public narrow!: boolean;
@property({ attribute: false }) public route!: Route;
@property({ attribute: false }) private _addons?: HassioAddonInfo[];
@property({ attribute: false }) private _repos?: HassioAddonRepository[];
@internalProperty() private _filter?: string;
public async refreshData() {
this._repos = undefined;
this._addons = undefined;
this._filter = undefined;
await reloadHassioAddons(this.hass);
await this._loadData();
}
protected render(): TemplateResult {
const repos: TemplateResult[] = [];
let repos: TemplateResult[] = [];
if (this._repos) {
for (const repo of this._repos) {
const addons = this._addons!.filter(
(addon) => addon.repository === repo.slug
);
if (addons.length === 0) {
continue;
}
repos.push(html`
<hassio-addon-repository
.hass=${this.hass}
.repo=${repo}
.addons=${addons}
.filter=${this._filter!}
></hassio-addon-repository>
`);
}
if (this.supervisor.addon.repositories) {
repos = this.addonRepositories(
this.supervisor.addon.repositories,
this.supervisor.addon.addons,
this._filter
);
}
return html`
<hass-tabs-subpage
.hass=${this.hass}
.localizeFunc=${this.supervisor.localize}
.narrow=${this.narrow}
.route=${this.route}
hassio
main-page
.tabs=${supervisorTabs}
main-page
supervisor
>
<span slot="header">Add-on Store</span>
<span slot="header">
${this.supervisor.localize("panel.store")}
</span>
<ha-button-menu
corner="BOTTOM_START"
slot="toolbar-icon"
@@ -110,15 +99,15 @@ class HassioAddonStore extends LitElement {
<ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>
</mwc-icon-button>
<mwc-list-item>
Repositories
${this.supervisor.localize("store.repositories")}
</mwc-list-item>
<mwc-list-item>
Reload
${this.supervisor.localize("common.reload")}
</mwc-list-item>
${this.hass.userData?.showAdvanced &&
atLeastVersion(this.hass.config.version, 0, 117)
? html`<mwc-list-item>
Registries
${this.supervisor.localize("store.registries")}
</mwc-list-item>`
: ""}
</ha-button-menu>
@@ -139,28 +128,60 @@ class HassioAddonStore extends LitElement {
${!this.hass.userData?.showAdvanced
? html`
<div class="advanced">
Missing add-ons? Enable advanced mode on
<a href="/profile" target="_top">
your profile page
${this.supervisor.localize("store.missing_addons")}
</a>
.
</div>
`
: ""}
<supervisor-connectivity .supervisor=${this.supervisor}>
</supervisor-connectivity>
</hass-tabs-subpage>
`;
}
protected firstUpdated(changedProps: PropertyValues) {
super.firstUpdated(changedProps);
const repositoryUrl = extractSearchParam("repository_url");
navigate(this, "/hassio/store", true);
if (repositoryUrl) {
this._manageRepositories(repositoryUrl);
}
this.addEventListener("hass-api-called", (ev) => this.apiCalled(ev));
this._loadData();
}
private addonRepositories = memoizeOne(
(
repositories: HassioAddonRepository[],
addons: HassioAddonInfo[],
filter?: string
) => {
return repositories.sort(sortRepos).map((repo) => {
const filteredAddons = addons.filter(
(addon) => addon.repository === repo.slug
);
return filteredAddons.length !== 0
? html`
<hassio-addon-repository
.hass=${this.hass}
.repo=${repo}
.addons=${filteredAddons}
.filter=${filter!}
.supervisor=${this.supervisor}
></hassio-addon-repository>
`
: html``;
});
}
);
private _handleAction(ev: CustomEvent<ActionDetail>) {
switch (ev.detail.index) {
case 0:
this._manageRepositories();
this._manageRepositoriesClicked();
break;
case 1:
this.refreshData();
@@ -177,26 +198,26 @@ class HassioAddonStore extends LitElement {
}
}
private async _manageRepositories() {
private _manageRepositoriesClicked() {
this._manageRepositories();
}
private async _manageRepositories(url?: string) {
showRepositoriesDialog(this, {
repos: this._repos!,
loadData: () => this._loadData(),
supervisor: this.supervisor,
url,
});
}
private async _manageRegistries() {
showRegistriesDialog(this);
showRegistriesDialog(this, { supervisor: this.supervisor });
}
private async _loadData() {
try {
const addonsInfo = await fetchHassioAddonsInfo(this.hass);
this._repos = addonsInfo.repositories;
this._repos.sort(sortRepos);
this._addons = addonsInfo.addons;
} catch (err) {
alert(extractApiErrorMessage(err));
}
fireEvent(this, "supervisor-collection-refresh", { collection: "addon" });
fireEvent(this, "supervisor-collection-refresh", {
collection: "supervisor",
});
}
private async _filterChanged(e) {
@@ -7,13 +7,14 @@ import {
CSSResult,
customElement,
html,
internalProperty,
LitElement,
property,
internalProperty,
PropertyValues,
TemplateResult,
} from "lit-element";
import "web-animations-js/web-animations-next-lite.min";
import "../../../../src/components/buttons/ha-progress-button";
import "../../../../src/components/ha-card";
import {
HassioAddonDetails,
@@ -24,16 +25,18 @@ import {
fetchHassioHardwareAudio,
HassioHardwareAudioDevice,
} from "../../../../src/data/hassio/hardware";
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
import { haStyle } from "../../../../src/resources/styles";
import { HomeAssistant } from "../../../../src/types";
import { suggestAddonRestart } from "../../dialogs/suggestAddonRestart";
import { hassioStyle } from "../../resources/hassio-style";
import "../../../../src/components/buttons/ha-progress-button";
@customElement("hassio-addon-audio")
class HassioAddonAudio extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public supervisor!: Supervisor;
@property({ attribute: false }) public addon!: HassioAddonDetails;
@internalProperty() private _error?: string;
@@ -48,12 +51,16 @@ class HassioAddonAudio extends LitElement {
protected render(): TemplateResult {
return html`
<ha-card header="Audio">
<ha-card
.header=${this.supervisor.localize("addon.configuration.audio.header")}
>
<div class="card-content">
${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
<paper-dropdown-menu
label="Input"
.label=${this.supervisor.localize(
"addon.configuration.audio.input"
)}
@iron-select=${this._setInputDevice}
>
<paper-listbox
@@ -64,15 +71,17 @@ class HassioAddonAudio extends LitElement {
${this._inputDevices &&
this._inputDevices.map((item) => {
return html`
<paper-item device=${item.device || ""}
>${item.name}</paper-item
>
<paper-item device=${item.device || ""}>
${item.name}
</paper-item>
`;
})}
</paper-listbox>
</paper-dropdown-menu>
<paper-dropdown-menu
label="Output"
.label=${this.supervisor.localize(
"addon.configuration.audio.output"
)}
@iron-select=${this._setOutputDevice}
>
<paper-listbox
@@ -93,7 +102,7 @@ class HassioAddonAudio extends LitElement {
</div>
<div class="card-actions">
<ha-progress-button @click=${this._saveSettings}>
Save
${this.supervisor.localize("common.save")}
</ha-progress-button>
</div>
</ha-card>
@@ -152,7 +161,7 @@ class HassioAddonAudio extends LitElement {
const noDevice: HassioHardwareAudioDevice = {
device: "default",
name: "Default",
name: this.supervisor.localize("addon.configuration.audio.default"),
};
try {
@@ -189,7 +198,7 @@ class HassioAddonAudio extends LitElement {
try {
await setHassioAddonOption(this.hass, this.addon.slug, data);
if (this.addon?.state === "started") {
await suggestAddonRestart(this, this.hass, this.addon);
await suggestAddonRestart(this, this.hass, this.supervisor, this.addon);
}
} catch {
this._error = "Failed to set addon audio device";
@@ -7,11 +7,12 @@ import {
property,
TemplateResult,
} from "lit-element";
import "../../../../src/components/ha-circular-progress";
import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
import { haStyle } from "../../../../src/resources/styles";
import { HomeAssistant } from "../../../../src/types";
import { hassioStyle } from "../../resources/hassio-style";
import "../../../../src/components/ha-circular-progress";
import "./hassio-addon-audio";
import "./hassio-addon-config";
import "./hassio-addon-network";
@@ -20,34 +21,51 @@ import "./hassio-addon-network";
class HassioAddonConfigDashboard extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public supervisor!: Supervisor;
@property({ attribute: false }) public addon?: HassioAddonDetails;
protected render(): TemplateResult {
if (!this.addon) {
return html`<ha-circular-progress active></ha-circular-progress>`;
}
const hasConfiguration =
(this.addon.options && Object.keys(this.addon.options).length) ||
(this.addon.schema && Object.keys(this.addon.schema).length);
return html`
<div class="content">
<hassio-addon-config
.hass=${this.hass}
.addon=${this.addon}
></hassio-addon-config>
${this.addon.network
${hasConfiguration || this.addon.network || this.addon.audio
? html`
<hassio-addon-network
.hass=${this.hass}
.addon=${this.addon}
></hassio-addon-network>
${hasConfiguration
? html`
<hassio-addon-config
.hass=${this.hass}
.addon=${this.addon}
.supervisor=${this.supervisor}
></hassio-addon-config>
`
: ""}
${this.addon.network
? html`
<hassio-addon-network
.hass=${this.hass}
.addon=${this.addon}
.supervisor=${this.supervisor}
></hassio-addon-network>
`
: ""}
${this.addon.audio
? html`
<hassio-addon-audio
.hass=${this.hass}
.addon=${this.addon}
.supervisor=${this.supervisor}
></hassio-addon-audio>
`
: ""}
`
: ""}
${this.addon.audio
? html`
<hassio-addon-audio
.hass=${this.hass}
.addon=${this.addon}
></hassio-addon-audio>
`
: ""}
: this.supervisor.localize("addon.configuration.no_configuration")}
</div>
`;
}
@@ -1,4 +1,7 @@
import "@material/mwc-button";
import { ActionDetail } from "@material/mwc-list";
import "@material/mwc-list/mwc-list-item";
import { mdiDotsVertical } from "@mdi/js";
import "@polymer/iron-autogrow-textarea/iron-autogrow-textarea";
import {
css,
@@ -12,9 +15,15 @@ import {
query,
TemplateResult,
} from "lit-element";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../src/common/dom/fire_event";
import "../../../../src/components/buttons/ha-progress-button";
import "../../../../src/components/ha-button-menu";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-form/ha-form";
import type { HaFormSchema } from "../../../../src/components/ha-form/ha-form";
import "../../../../src/components/ha-formfield";
import "../../../../src/components/ha-switch";
import "../../../../src/components/ha-yaml-editor";
import type { HaYamlEditor } from "../../../../src/components/ha-yaml-editor";
import {
@@ -23,62 +32,197 @@ import {
setHassioAddonOption,
} from "../../../../src/data/hassio/addon";
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
import { showConfirmationDialog } from "../../../../src/dialogs/generic/show-dialog-box";
import { haStyle } from "../../../../src/resources/styles";
import type { HomeAssistant } from "../../../../src/types";
import { suggestAddonRestart } from "../../dialogs/suggestAddonRestart";
import { hassioStyle } from "../../resources/hassio-style";
const SUPPORTED_UI_TYPES = ["string", "select", "boolean", "integer", "float"];
@customElement("hassio-addon-config")
class HassioAddonConfig extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public addon!: HassioAddonDetails;
@internalProperty() private _error?: string;
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public supervisor!: Supervisor;
@property({ type: Boolean }) private _configHasChanged = false;
@property({ type: Boolean }) private _valid = true;
@query("ha-yaml-editor", true) private _editor!: HaYamlEditor;
@internalProperty() private _canShowSchema = false;
@internalProperty() private _showOptional = false;
@internalProperty() private _error?: string;
@internalProperty() private _options?: Record<string, unknown>;
@internalProperty() private _yamlMode = false;
@query("ha-yaml-editor") private _editor?: HaYamlEditor;
public computeLabel = (entry: HaFormSchema): string => {
return (
this.addon.translations[this.hass.language]?.configuration?.[entry.name]
?.name ||
this.addon.translations.en?.configuration?.[entry.name].name ||
entry.name
);
};
private _filteredShchema = memoizeOne(
(options: Record<string, unknown>, schema: HaFormSchema[]) => {
return schema.filter((entry) => entry.name in options || entry.required);
}
);
protected render(): TemplateResult {
const showForm =
!this._yamlMode && this._canShowSchema && this.addon.schema;
const hasHiddenOptions =
showForm &&
JSON.stringify(this.addon.schema) !==
JSON.stringify(
this._filteredShchema(this.addon.options, this.addon.schema!)
);
return html`
<h1>${this.addon.name}</h1>
<ha-card header="Configuration">
<div class="card-content">
<ha-yaml-editor
@value-changed=${this._configChanged}
></ha-yaml-editor>
${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
${this._valid ? "" : html` <div class="errors">Invalid YAML</div> `}
<ha-card>
<div class="header">
<h2>
${this.supervisor.localize("addon.configuration.options.header")}
</h2>
<div class="card-menu">
<ha-button-menu corner="BOTTOM_START" @action=${this._handleAction}>
<mwc-icon-button slot="trigger">
<ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>
</mwc-icon-button>
<mwc-list-item .disabled=${!this._canShowSchema}>
${this._yamlMode
? this.supervisor.localize(
"addon.configuration.options.edit_in_ui"
)
: this.supervisor.localize(
"addon.configuration.options.edit_in_yaml"
)}
</mwc-list-item>
<mwc-list-item class="warning">
${this.supervisor.localize("common.reset_defaults")}
</mwc-list-item>
</ha-button-menu>
</div>
</div>
<div class="card-actions">
<ha-progress-button class="warning" @click=${this._resetTapped}>
Reset to defaults
</ha-progress-button>
<div class="card-content">
${showForm
? html`<ha-form
.data=${this._options!}
@value-changed=${this._configChanged}
.computeLabel=${this.computeLabel}
.schema=${this._showOptional
? this.addon.schema!
: this._filteredShchema(
this.addon.options,
this.addon.schema!
)}
></ha-form>`
: html` <ha-yaml-editor
@value-changed=${this._configChanged}
></ha-yaml-editor>`}
${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
${!this._yamlMode ||
(this._canShowSchema && this.addon.schema) ||
this._valid
? ""
: html`
<div class="errors">
${this.supervisor.localize(
"addon.configuration.options.invalid_yaml"
)}
</div>
`}
</div>
${hasHiddenOptions
? html`<ha-formfield
class="show-additional"
.label=${this.supervisor.localize(
"addon.configuration.options.show_unused_optional"
)}
>
<ha-switch
@change=${this._toggleOptional}
.checked=${this._showOptional}
>
</ha-switch>
</ha-formfield>`
: ""}
<div class="card-actions right">
<ha-progress-button
@click=${this._saveTapped}
.disabled=${!this._configHasChanged || !this._valid}
>
Save
${this.supervisor.localize("common.save")}
</ha-progress-button>
</div>
</ha-card>
`;
}
protected firstUpdated(changedProps) {
super.firstUpdated(changedProps);
this._canShowSchema = !this.addon.schema!.find(
// @ts-ignore
(entry) => !SUPPORTED_UI_TYPES.includes(entry.type) || entry.multiple
);
this._yamlMode = !this._canShowSchema;
}
protected updated(changedProperties: PropertyValues): void {
super.updated(changedProperties);
if (changedProperties.has("addon")) {
this._editor.setValue(this.addon.options);
this._options = { ...this.addon.options };
}
super.updated(changedProperties);
if (
changedProperties.has("_yamlMode") ||
changedProperties.has("_options")
) {
if (this._yamlMode) {
const editor = this._editor;
if (editor) {
editor.setValue(this._options!);
}
}
}
}
private _handleAction(ev: CustomEvent<ActionDetail>) {
switch (ev.detail.index) {
case 0:
this._yamlMode = !this._yamlMode;
break;
case 1:
this._resetTapped(ev);
break;
}
}
private _toggleOptional() {
this._showOptional = !this._showOptional;
}
private _configChanged(ev): void {
this._configHasChanged = true;
this._valid = ev.detail.isValid;
if (this.addon.schema && this._canShowSchema && !this._yamlMode) {
this._valid = true;
this._configHasChanged = true;
this._options! = ev.detail.value;
} else {
this._configHasChanged = true;
this._valid = ev.detail.isValid;
}
}
private async _resetTapped(ev: CustomEvent): Promise<void> {
@@ -86,10 +230,10 @@ class HassioAddonConfig extends LitElement {
button.progress = true;
const confirmed = await showConfirmationDialog(this, {
title: this.addon.name,
text: "Are you sure you want to reset all your options?",
confirmText: "reset options",
dismissText: "no",
title: this.supervisor.localize("confirm.reset_options.title"),
text: this.supervisor.localize("confirm.reset_options.text"),
confirmText: this.supervisor.localize("common.reset_options"),
dismissText: this.supervisor.localize("common.cancel"),
});
if (!confirmed) {
@@ -111,9 +255,11 @@ class HassioAddonConfig extends LitElement {
};
fireEvent(this, "hass-api-called", eventdata);
} catch (err) {
this._error = `Failed to reset addon configuration, ${extractApiErrorMessage(
err
)}`;
this._error = this.supervisor.localize(
"addon.common.update_available",
"error",
extractApiErrorMessage(err)
);
}
button.progress = false;
}
@@ -122,18 +268,13 @@ class HassioAddonConfig extends LitElement {
const button = ev.currentTarget as any;
button.progress = true;
let data: HassioAddonSetOptionParams;
this._error = undefined;
try {
data = {
options: this._editor.value,
};
} catch (err) {
this._error = err;
return;
}
try {
await setHassioAddonOption(this.hass, this.addon.slug, data);
await setHassioAddonOption(this.hass, this.addon.slug, {
options: this._yamlMode ? this._editor?.value : this._options,
});
this._configHasChanged = false;
const eventdata = {
success: true,
@@ -142,12 +283,14 @@ class HassioAddonConfig extends LitElement {
};
fireEvent(this, "hass-api-called", eventdata);
if (this.addon?.state === "started") {
await suggestAddonRestart(this, this.hass, this.addon);
await suggestAddonRestart(this, this.hass, this.supervisor, this.addon);
}
} catch (err) {
this._error = `Failed to save addon configuration, ${extractApiErrorMessage(
err
)}`;
this._error = this.supervisor.localize(
"addon.configuration.options.failed_to_save",
"error",
extractApiErrorMessage(err)
);
}
button.progress = false;
}
@@ -178,6 +321,36 @@ class HassioAddonConfig extends LitElement {
.syntaxerror {
color: var(--error-color);
}
.card-menu {
float: right;
z-index: 3;
--mdc-theme-text-primary-on-background: var(--primary-text-color);
}
mwc-list-item[disabled] {
--mdc-theme-text-primary-on-background: var(--disabled-text-color);
}
.header {
display: flex;
justify-content: space-between;
}
.header h2 {
color: var(--ha-card-header-color, --primary-text-color);
font-family: var(--ha-card-header-font-family, inherit);
font-size: var(--ha-card-header-font-size, 24px);
letter-spacing: -0.012em;
line-height: 48px;
padding: 12px 16px 16px;
display: block;
margin-block: 0px;
font-weight: normal;
}
.card-actions.right {
justify-content: flex-end;
}
.show-additional {
padding: 16px;
}
`,
];
}
@@ -19,6 +19,7 @@ import {
setHassioAddonOption,
} from "../../../../src/data/hassio/addon";
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
import { haStyle } from "../../../../src/resources/styles";
import { HomeAssistant } from "../../../../src/types";
import { suggestAddonRestart } from "../../dialogs/suggestAddonRestart";
@@ -38,6 +39,8 @@ interface NetworkItemInput extends PaperInputElement {
class HassioAddonNetwork extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public supervisor!: Supervisor;
@property({ attribute: false }) public addon!: HassioAddonDetails;
@internalProperty() private _error?: string;
@@ -55,16 +58,30 @@ class HassioAddonNetwork extends LitElement {
}
return html`
<ha-card header="Network">
<ha-card
.header=${this.supervisor.localize(
"addon.configuration.network.header"
)}
>
<div class="card-content">
${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
<table>
<tbody>
<tr>
<th>Container</th>
<th>Host</th>
<th>Description</th>
<th>
${this.supervisor.localize(
"addon.configuration.network.container"
)}
</th>
<th>
${this.supervisor.localize(
"addon.configuration.network.host"
)}
</th>
<th>
${this.supervisor.localize("common.description")}
</th>
</tr>
${this._config!.map((item) => {
return html`
@@ -73,13 +90,15 @@ class HassioAddonNetwork extends LitElement {
<td>
<paper-input
@value-changed=${this._configChanged}
placeholder="disabled"
placeholder="${this.supervisor.localize(
"addon.configuration.network.disabled"
)}"
.value=${item.host ? String(item.host) : ""}
.container=${item.container}
no-label-float
></paper-input>
</td>
<td>${item.description}</td>
<td>${this._computeDescription(item)}</td>
</tr>
`;
})}
@@ -88,10 +107,10 @@ class HassioAddonNetwork extends LitElement {
</div>
<div class="card-actions">
<ha-progress-button class="warning" @click=${this._resetTapped}>
Reset to defaults
${this.supervisor.localize("common.reset_defaults")}
</ha-progress-button>
<ha-progress-button @click=${this._saveTapped}>
Save
${this.supervisor.localize("common.save")}
</ha-progress-button>
</div>
</ha-card>
@@ -105,6 +124,15 @@ class HassioAddonNetwork extends LitElement {
}
}
private _computeDescription = (item: NetworkItem): string => {
return (
this.addon.translations[this.hass.language]?.network?.[item.container]
?.description ||
this.addon.translations.en?.network?.[item.container]?.description ||
item.description
);
};
private _setNetworkConfig(): void {
const network = this.addon.network || {};
const description = this.addon.network_description || {};
@@ -147,12 +175,14 @@ class HassioAddonNetwork extends LitElement {
};
fireEvent(this, "hass-api-called", eventdata);
if (this.addon?.state === "started") {
await suggestAddonRestart(this, this.hass, this.addon);
await suggestAddonRestart(this, this.hass, this.supervisor, this.addon);
}
} catch (err) {
this._error = `Failed to set addon network configuration, ${extractApiErrorMessage(
err
)}`;
this._error = this.supervisor.localize(
"addon.failed_to_reset",
"error",
extractApiErrorMessage(err)
);
}
button.progress = false;
@@ -181,12 +211,14 @@ class HassioAddonNetwork extends LitElement {
};
fireEvent(this, "hass-api-called", eventdata);
if (this.addon?.state === "started") {
await suggestAddonRestart(this, this.hass, this.addon);
await suggestAddonRestart(this, this.hass, this.supervisor, this.addon);
}
} catch (err) {
this._error = `Failed to set addon network configuration, ${extractApiErrorMessage(
err
)}`;
this._error = this.supervisor.localize(
"addon.failed_to_save",
"error",
extractApiErrorMessage(err)
);
}
button.progress = false;
}
@@ -1,3 +1,4 @@
import "../../../../src/components/ha-card";
import {
css,
CSSResult,
@@ -19,11 +20,14 @@ import "../../../../src/layouts/hass-loading-screen";
import { haStyle } from "../../../../src/resources/styles";
import { HomeAssistant } from "../../../../src/types";
import { hassioStyle } from "../../resources/hassio-style";
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
@customElement("hassio-addon-documentation-tab")
class HassioAddonDocumentationDashboard extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public supervisor!: Supervisor;
@property({ attribute: false }) public addon?: HassioAddonDetails;
@internalProperty() private _error?: string;
@@ -81,9 +85,11 @@ class HassioAddonDocumentationDashboard extends LitElement {
this.addon!.slug
);
} catch (err) {
this._error = `Failed to get addon documentation, ${extractApiErrorMessage(
err
)}`;
this._error = this.supervisor.localize(
"addon.documentation.get_logs",
"error",
extractApiErrorMessage(err)
);
}
}
}
+70 -15
View File
@@ -1,3 +1,4 @@
import "../components/supervisor-connectivity";
import {
mdiCogs,
mdiFileDocument,
@@ -9,17 +10,26 @@ import {
CSSResult,
customElement,
html,
internalProperty,
LitElement,
property,
TemplateResult,
} from "lit-element";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../src/common/dom/fire_event";
import { navigate } from "../../../src/common/navigate";
import { extractSearchParam } from "../../../src/common/url/search-params";
import "../../../src/components/ha-circular-progress";
import {
fetchHassioAddonInfo,
fetchHassioAddonsInfo,
HassioAddonDetails,
} from "../../../src/data/hassio/addon";
import { extractApiErrorMessage } from "../../../src/data/hassio/common";
import { Supervisor } from "../../../src/data/supervisor/supervisor";
import "../../../src/layouts/hass-error-screen";
import "../../../src/layouts/hass-loading-screen";
import "../../../src/layouts/hass-tabs-subpage";
import "../../../src/components/ha-circular-progress";
import type { PageNavigation } from "../../../src/layouts/hass-tabs-subpage";
import { haStyle } from "../../../src/resources/styles";
import { HomeAssistant, Route } from "../../../src/types";
@@ -35,12 +45,16 @@ import "./log/hassio-addon-logs";
class HassioAddonDashboard extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public supervisor!: Supervisor;
@property({ attribute: false }) public route!: Route;
@property({ attribute: false }) public addon?: HassioAddonDetails;
@property({ type: Boolean }) public narrow!: boolean;
@internalProperty() _error?: string;
private _computeTail = memoizeOne((route: Route) => {
const dividerPos = route.path.indexOf("/", 1);
return dividerPos === -1
@@ -55,13 +69,19 @@ class HassioAddonDashboard extends LitElement {
});
protected render(): TemplateResult {
if (this._error) {
return html`<hass-error-screen
.error=${this._error}
></hass-error-screen>`;
}
if (!this.addon) {
return html`<ha-circular-progress active></ha-circular-progress>`;
return html`<hass-loading-screen></hass-loading-screen>`;
}
const addonTabs: PageNavigation[] = [
{
name: "Info",
translationKey: "addon.panel.info",
path: `/hassio/addon/${this.addon.slug}/info`,
iconPath: mdiInformationVariant,
},
@@ -69,7 +89,7 @@ class HassioAddonDashboard extends LitElement {
if (this.addon.documentation) {
addonTabs.push({
name: "Documentation",
translationKey: "addon.panel.documentation",
path: `/hassio/addon/${this.addon.slug}/documentation`,
iconPath: mdiFileDocument,
});
@@ -78,12 +98,12 @@ class HassioAddonDashboard extends LitElement {
if (this.addon.version) {
addonTabs.push(
{
name: "Configuration",
translationKey: "addon.panel.configuration",
path: `/hassio/addon/${this.addon.slug}/config`,
iconPath: mdiCogs,
},
{
name: "Log",
translationKey: "addon.panel.log",
path: `/hassio/addon/${this.addon.slug}/logs`,
iconPath: mdiMathLog,
}
@@ -95,19 +115,23 @@ class HassioAddonDashboard extends LitElement {
return html`
<hass-tabs-subpage
.hass=${this.hass}
.localizeFunc=${this.supervisor.localize}
.narrow=${this.narrow}
.backPath=${this.addon.version ? "/hassio/dashboard" : "/hassio/store"}
.route=${route}
hassio
.tabs=${addonTabs}
supervisor
>
<span slot="header">${this.addon.name}</span>
<hassio-addon-router
.route=${route}
.narrow=${this.narrow}
.hass=${this.hass}
.supervisor=${this.supervisor}
.addon=${this.addon}
></hassio-addon-router>
<supervisor-connectivity .supervisor=${this.supervisor}>
</supervisor-connectivity>
</hass-tabs-subpage>
`;
}
@@ -152,30 +176,61 @@ class HassioAddonDashboard extends LitElement {
}
protected async firstUpdated(): Promise<void> {
await this._routeDataChanged(this.route);
if (this.route.path === "") {
const requestedAddon = extractSearchParam("addon");
if (requestedAddon) {
const addonsInfo = await fetchHassioAddonsInfo(this.hass);
const validAddon = addonsInfo.addons.some(
(addon) => addon.slug === requestedAddon
);
if (!validAddon) {
this._error = this.supervisor.localize("my.error_addon_not_found");
} else {
navigate(this, `/hassio/addon/${requestedAddon}`, true);
}
}
}
this.addEventListener("hass-api-called", (ev) => this._apiCalled(ev));
}
private async _apiCalled(ev): Promise<void> {
const path: string = ev.detail.path;
const pathSplit: string[] = ev.detail.path?.split("/");
if (!path) {
if (!pathSplit || pathSplit.length === 0) {
return;
}
const path: string = pathSplit[pathSplit.length - 1];
if (["uninstall", "install", "update", "start", "stop"].includes(path)) {
fireEvent(this, "supervisor-collection-refresh", {
collection: "supervisor",
});
}
if (path === "uninstall") {
history.back();
window.history.back();
} else {
await this._routeDataChanged(this.route);
await this._routeDataChanged();
}
}
private async _routeDataChanged(routeData: Route): Promise<void> {
const addon = routeData.path.split("/")[1];
protected updated(changedProperties) {
if (changedProperties.has("route") && !this.addon) {
this._routeDataChanged();
}
}
private async _routeDataChanged(): Promise<void> {
const addon = this.route.path.split("/")[1];
if (!addon) {
return;
}
try {
const addoninfo = await fetchHassioAddonInfo(this.hass, addon);
this.addon = addoninfo;
} catch {
} catch (err) {
this._error = `Error fetching addon info: ${extractApiErrorMessage(err)}`;
this.addon = undefined;
}
}
@@ -1,5 +1,6 @@
import { customElement, property } from "lit-element";
import { HassioAddonDetails } from "../../../src/data/hassio/addon";
import { Supervisor } from "../../../src/data/supervisor/supervisor";
import {
HassRouterPage,
RouterOptions,
@@ -17,6 +18,8 @@ class HassioAddonRouter extends HassRouterPage {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public supervisor!: Supervisor;
@property({ attribute: false }) public addon!: HassioAddonDetails;
protected routerOptions: RouterOptions = {
@@ -41,6 +44,7 @@ class HassioAddonRouter extends HassRouterPage {
protected updatePageEl(el) {
el.route = this.routeTail;
el.hass = this.hass;
el.supervisor = this.supervisor;
el.addon = this.addon;
el.narrow = this.narrow;
}
@@ -7,8 +7,9 @@ import {
property,
TemplateResult,
} from "lit-element";
import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
import "../../../../src/components/ha-circular-progress";
import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
import { haStyle } from "../../../../src/resources/styles";
import { HomeAssistant } from "../../../../src/types";
import { hassioStyle } from "../../resources/hassio-style";
@@ -20,6 +21,8 @@ class HassioAddonInfoDashboard extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public supervisor!: Supervisor;
@property({ attribute: false }) public addon?: HassioAddonDetails;
protected render(): TemplateResult {
@@ -32,6 +35,7 @@ class HassioAddonInfoDashboard extends LitElement {
<hassio-addon-info
.narrow=${this.narrow}
.hass=${this.hass}
.supervisor=${this.supervisor}
.addon=${this.addon}
></hassio-addon-info>
</div>
File diff suppressed because it is too large Load Diff
@@ -7,8 +7,9 @@ import {
property,
TemplateResult,
} from "lit-element";
import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
import "../../../../src/components/ha-circular-progress";
import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
import { haStyle } from "../../../../src/resources/styles";
import { HomeAssistant } from "../../../../src/types";
import { hassioStyle } from "../../resources/hassio-style";
@@ -18,6 +19,8 @@ import "./hassio-addon-logs";
class HassioAddonLogDashboard extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public supervisor!: Supervisor;
@property({ attribute: false }) public addon?: HassioAddonDetails;
protected render(): TemplateResult {
@@ -28,6 +31,7 @@ class HassioAddonLogDashboard extends LitElement {
<div class="content">
<hassio-addon-logs
.hass=${this.hass}
.supervisor=${this.supervisor}
.addon=${this.addon}
></hassio-addon-logs>
</div>
+11 -2
View File
@@ -15,6 +15,7 @@ import {
HassioAddonDetails,
} from "../../../../src/data/hassio/addon";
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
import { haStyle } from "../../../../src/resources/styles";
import { HomeAssistant } from "../../../../src/types";
import "../../components/hassio-ansi-to-html";
@@ -24,6 +25,8 @@ import { hassioStyle } from "../../resources/hassio-style";
class HassioAddonLogs extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public supervisor!: Supervisor;
@property({ attribute: false }) public addon!: HassioAddonDetails;
@internalProperty() private _error?: string;
@@ -48,7 +51,9 @@ class HassioAddonLogs extends LitElement {
: ""}
</div>
<div class="card-actions">
<mwc-button @click=${this._refresh}>Refresh</mwc-button>
<mwc-button @click=${this._refresh}>
${this.supervisor.localize("common.refresh")}
</mwc-button>
</div>
</ha-card>
`;
@@ -76,7 +81,11 @@ class HassioAddonLogs extends LitElement {
try {
this._content = await fetchHassioAddonLogs(this.hass, this.addon.slug);
} catch (err) {
this._error = `Failed to get addon logs, ${extractApiErrorMessage(err)}`;
this._error = this.supervisor.localize(
"addon.logs.get_logs",
"error",
extractApiErrorMessage(err)
);
}
}
+1 -1
View File
@@ -44,7 +44,7 @@ class HassioCardContent extends LitElement {
${this.iconImage
? html`
<div class="icon_image ${this.iconClass}">
<img src="${this.iconImage}" title="${this.iconTitle}" />
<img src="${this.iconImage}" .title=${this.iconTitle} />
<div></div>
</div>
`
@@ -27,6 +27,8 @@ declare global {
}
}
const MAX_FILE_SIZE = 1 * 1024 * 1024 * 1024; // 1GB
@customElement("hassio-upload-snapshot")
export class HassioUploadSnapshot extends LitElement {
public hass!: HomeAssistant;
@@ -51,6 +53,20 @@ export class HassioUploadSnapshot extends LitElement {
private async _uploadFile(ev) {
const file = ev.detail.files[0];
if (file.size > MAX_FILE_SIZE) {
showAlertDialog(this, {
title: "Snapshot file is too big",
text: html`The maximum allowed filesize is 1GB.<br />
<a
href="https://www.home-assistant.io/hassio/haos_common_tasks/#restoring-a-snapshot-on-a-new-install"
target="_blank"
>Have a look here on how to restore it.</a
>`,
confirmText: "ok",
});
return;
}
if (!["application/x-tar"].includes(file.type)) {
showAlertDialog(this, {
title: "Unsupported file format",
@@ -0,0 +1,54 @@
import {
css,
CSSResult,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { Supervisor } from "../../../src/data/supervisor/supervisor";
import { haStyle } from "../../../src/resources/styles";
@customElement("supervisor-connectivity")
class SupervisorConnectivity extends LitElement {
@property({ attribute: false }) public supervisor!: Supervisor;
protected render(): TemplateResult {
if (this.supervisor.network.supervisor_internet) {
return html``;
}
return html`<div class="connectivity">
<span>${this.supervisor.localize("common.error.lost_connectivity")}</span>
</div>`;
}
static get styles(): CSSResult[] {
return [
haStyle,
css`
.connectivity {
position: fixed;
bottom: 0;
height: 32px;
width: 100vw;
background-color: var(--error-color);
color: var(--primary-text-color);
font-weight: 500;
display: flex;
align-items: center;
}
span {
padding-left: 16px;
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"supervisor-connectivity": SupervisorConnectivity;
}
}
@@ -0,0 +1,87 @@
import {
css,
CSSResult,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { classMap } from "lit-html/directives/class-map";
import "../../../src/components/ha-bar";
import "../../../src/components/ha-settings-row";
import { roundWithOneDecimal } from "../../../src/util/calculate";
@customElement("supervisor-metric")
class SupervisorMetric extends LitElement {
@property({ type: Number }) public value!: number;
@property({ type: String }) public description!: string;
@property({ type: String }) public tooltip?: string;
protected render(): TemplateResult {
const roundedValue = roundWithOneDecimal(this.value);
return html`<ha-settings-row>
<span slot="heading">
${this.description}
</span>
<div slot="description" .title=${this.tooltip ?? ""}>
<span class="value">
${roundedValue}%
</span>
<ha-bar
class="${classMap({
"target-warning": roundedValue > 50,
"target-critical": roundedValue > 85,
})}"
.value=${this.value}
></ha-bar>
</div>
</ha-settings-row>`;
}
static get styles(): CSSResult {
return css`
ha-settings-row {
padding: 0;
height: 54px;
width: 100%;
}
ha-settings-row > div[slot="description"] {
white-space: normal;
color: var(--secondary-text-color);
display: flex;
justify-content: space-between;
}
ha-bar {
--ha-bar-primary-color: var(
--hassio-bar-ok-color,
var(--success-color)
);
}
.target-warning {
--ha-bar-primary-color: var(
--hassio-bar-warning-color,
var(--warning-color)
);
}
.target-critical {
--ha-bar-primary-color: var(
--hassio-bar-critical-color,
var(--error-color)
);
}
.value {
width: 42px;
padding-right: 4px;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"supervisor-metric": SupervisorMetric;
}
}
+15 -11
View File
@@ -12,7 +12,7 @@ import { atLeastVersion } from "../../../src/common/config/version";
import { navigate } from "../../../src/common/navigate";
import { compare } from "../../../src/common/string/compare";
import "../../../src/components/ha-card";
import { HassioAddonInfo } from "../../../src/data/hassio/addon";
import { Supervisor } from "../../../src/data/supervisor/supervisor";
import { haStyle } from "../../../src/resources/styles";
import { HomeAssistant } from "../../../src/types";
import "../components/hassio-card-content";
@@ -22,26 +22,24 @@ import { hassioStyle } from "../resources/hassio-style";
class HassioAddons extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public addons?: HassioAddonInfo[];
@property({ attribute: false }) public supervisor!: Supervisor;
protected render(): TemplateResult {
return html`
<div class="content">
<h1>Add-ons</h1>
<h1>${this.supervisor.localize("dashboard.addons")}</h1>
<div class="card-group">
${!this.addons?.length
${!this.supervisor.supervisor.addons?.length
? html`
<ha-card>
<div class="card-content">
You don't have any add-ons installed yet. Head over to
<button class="link" @click=${this._openStore}>
the add-on store
${this.supervisor.localize("dashboard.no_addons")}
</button>
to get started!
</div>
</ha-card>
`
: this.addons
: this.supervisor.supervisor.addons
.sort((a, b) => compare(a.name, b.name))
.map(
(addon) => html`
@@ -58,10 +56,16 @@ class HassioAddons extends LitElement {
? mdiArrowUpBoldCircle
: mdiPuzzle}
.iconTitle=${addon.state !== "started"
? "Add-on is stopped"
? this.supervisor.localize(
"dashboard.addon_stopped"
)
: addon.update_available!
? "New version available"
: "Add-on is running"}
? this.supervisor.localize(
"dashboard.addon_new_version"
)
: this.supervisor.localize(
"dashboard.addon_running"
)}
.iconClass=${addon.update_available
? addon.state === "started"
? "update"
+14 -18
View File
@@ -1,3 +1,4 @@
import "../components/supervisor-connectivity";
import {
css,
CSSResult,
@@ -7,11 +8,7 @@ import {
property,
TemplateResult,
} from "lit-element";
import { HassioHassOSInfo } from "../../../src/data/hassio/host";
import {
HassioHomeAssistantInfo,
HassioSupervisorInfo,
} from "../../../src/data/hassio/supervisor";
import { Supervisor } from "../../../src/data/supervisor/supervisor";
import "../../../src/layouts/hass-tabs-subpage";
import { haStyle } from "../../../src/resources/styles";
import { HomeAssistant, Route } from "../../../src/types";
@@ -23,39 +20,38 @@ import "./hassio-update";
class HassioDashboard extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public supervisor!: Supervisor;
@property({ type: Boolean }) public narrow!: boolean;
@property({ attribute: false }) public route!: Route;
@property({ attribute: false }) public supervisorInfo!: HassioSupervisorInfo;
@property({ attribute: false }) public hassInfo!: HassioHomeAssistantInfo;
@property({ attribute: false }) public hassOsInfo!: HassioHassOSInfo;
protected render(): TemplateResult {
return html`
<hass-tabs-subpage
.hass=${this.hass}
.localizeFunc=${this.supervisor.localize}
.narrow=${this.narrow}
hassio
main-page
.route=${this.route}
.tabs=${supervisorTabs}
main-page
supervisor
>
<span slot="header">Dashboard</span>
<span slot="header">
${this.supervisor.localize("panel.dashboard")}
</span>
<div class="content">
<hassio-update
.hass=${this.hass}
.hassInfo=${this.hassInfo}
.supervisorInfo=${this.supervisorInfo}
.hassOsInfo=${this.hassOsInfo}
.supervisor=${this.supervisor}
></hassio-update>
<hassio-addons
.hass=${this.hass}
.addons=${this.supervisorInfo.addons}
.supervisor=${this.supervisor}
></hassio-addons>
</div>
<supervisor-connectivity .supervisor=${this.supervisor}>
</supervisor-connectivity>
</hass-tabs-subpage>
`;
}
+117 -54
View File
@@ -10,56 +10,58 @@ import {
TemplateResult,
} from "lit-element";
import memoizeOne from "memoize-one";
import { atLeastVersion } from "../../../src/common/config/version";
import { fireEvent } from "../../../src/common/dom/fire_event";
import "../../../src/components/buttons/ha-progress-button";
import "../../../src/components/ha-card";
import "../../../src/components/ha-settings-row";
import "../../../src/components/ha-svg-icon";
import {
extractApiErrorMessage,
HassioResponse,
ignoredStatusCodes,
ignoreSupervisorError,
} from "../../../src/data/hassio/common";
import { HassioHassOSInfo } from "../../../src/data/hassio/host";
import {
HassioHomeAssistantInfo,
HassioSupervisorInfo,
} from "../../../src/data/hassio/supervisor";
import { updateCore } from "../../../src/data/supervisor/core";
import {
Supervisor,
supervisorApiWsRequest,
} from "../../../src/data/supervisor/supervisor";
import {
showAlertDialog,
showConfirmationDialog,
} from "../../../src/dialogs/generic/show-dialog-box";
import { haStyle } from "../../../src/resources/styles";
import { HomeAssistant } from "../../../src/types";
import { showDialogSupervisorUpdate } from "../dialogs/update/show-dialog-update";
import { hassioStyle } from "../resources/hassio-style";
const computeVersion = (key: string, version: string): string => {
return key === "os" ? version : `${key}-${version}`;
};
@customElement("hassio-update")
export class HassioUpdate extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public hassInfo?: HassioHomeAssistantInfo;
@property({ attribute: false }) public supervisor!: Supervisor;
@property({ attribute: false }) public hassOsInfo?: HassioHassOSInfo;
@property({ attribute: false }) public supervisorInfo?: HassioSupervisorInfo;
private _pendingUpdates = memoizeOne(
(
core?: HassioHomeAssistantInfo,
supervisor?: HassioSupervisorInfo,
os?: HassioHassOSInfo
): number => {
return [core, supervisor, os].filter(
(value) => !!value && value?.update_available
).length;
}
);
private _pendingUpdates = memoizeOne((supervisor: Supervisor): number => {
return Object.keys(supervisor).filter(
(value) => supervisor[value].update_available
).length;
});
protected render(): TemplateResult {
const updatesAvailable = this._pendingUpdates(
this.hassInfo,
this.supervisorInfo,
this.hassOsInfo
);
if (!this.supervisor) {
return html``;
}
const updatesAvailable = this._pendingUpdates(this.supervisor);
if (!updatesAvailable) {
return html``;
}
@@ -67,33 +69,37 @@ export class HassioUpdate extends LitElement {
return html`
<div class="content">
<h1>
${updatesAvailable > 1
? "Updates Available 🎉"
: "Update Available 🎉"}
${this.supervisor.localize(
"common.update_available",
"count",
updatesAvailable
)}
🎉
</h1>
<div class="card-group">
${this._renderUpdateCard(
"Home Assistant Core",
this.hassInfo!,
"core",
this.supervisor.core,
"hassio/homeassistant/update",
`https://${
this.hassInfo?.version_latest.includes("b") ? "rc" : "www"
this.supervisor.core.version_latest.includes("b") ? "rc" : "www"
}.home-assistant.io/latest-release-notes/`
)}
${this._renderUpdateCard(
"Supervisor",
this.supervisorInfo!,
"supervisor",
this.supervisor.supervisor,
"hassio/supervisor/update",
`https://github.com//home-assistant/hassio/releases/tag/${
this.supervisorInfo!.version_latest
}`
`https://github.com//home-assistant/hassio/releases/tag/${this.supervisor.supervisor.version_latest}`
)}
${this.hassOsInfo
${this.supervisor.host.features.includes("hassos")
? this._renderUpdateCard(
"Operating System",
this.hassOsInfo,
"os",
this.supervisor.os,
"hassio/os/update",
`https://github.com//home-assistant/hassos/releases/tag/${this.hassOsInfo.version_latest}`
`https://github.com//home-assistant/hassos/releases/tag/${this.supervisor.os.version_latest}`
)
: ""}
</div>
@@ -103,6 +109,7 @@ export class HassioUpdate extends LitElement {
private _renderUpdateCard(
name: string,
key: string,
object: HassioHomeAssistantInfo | HassioSupervisorInfo | HassioHassOSInfo,
apiPath: string,
releaseNotesUrl: string
@@ -116,22 +123,39 @@ export class HassioUpdate extends LitElement {
<div class="icon">
<ha-svg-icon .path=${mdiHomeAssistant}></ha-svg-icon>
</div>
<div class="update-heading">${name} ${object.version_latest}</div>
<div class="warning">
You are currently running version ${object.version}
</div>
<div class="update-heading">${name}</div>
<ha-settings-row two-line>
<span slot="heading">
${this.supervisor.localize("common.version")}
</span>
<span slot="description">
${computeVersion(key, object.version!)}
</span>
</ha-settings-row>
<ha-settings-row two-line>
<span slot="heading">
${this.supervisor.localize("common.newest_version")}
</span>
<span slot="description">
${computeVersion(key, object.version_latest!)}
</span>
</ha-settings-row>
</div>
<div class="card-actions">
<a href="${releaseNotesUrl}" target="_blank" rel="noreferrer">
<mwc-button>Release notes</mwc-button>
<mwc-button>
${this.supervisor.localize("common.release_notes")}
</mwc-button>
</a>
<ha-progress-button
.apiPath=${apiPath}
.name=${name}
.key=${key}
.version=${object.version_latest}
@click=${this._confirmUpdate}
>
Update
${this.supervisor.localize("common.update")}
</ha-progress-button>
</div>
</ha-card>
@@ -140,12 +164,36 @@ export class HassioUpdate extends LitElement {
private async _confirmUpdate(ev): Promise<void> {
const item = ev.currentTarget;
if (item.key === "core") {
showDialogSupervisorUpdate(this, {
supervisor: this.supervisor,
name: "Home Assistant Core",
version: this.supervisor.core.version_latest,
snapshotParams: {
name: `core_${this.supervisor.core.version}`,
folders: ["homeassistant"],
homeassistant: true,
},
updateHandler: async () => this._updateCore(),
});
return;
}
item.progress = true;
const confirmed = await showConfirmationDialog(this, {
title: `Update ${item.name}`,
text: `Are you sure you want to update ${item.name} to version ${item.version}?`,
confirmText: "update",
dismissText: "cancel",
title: this.supervisor.localize(
"confirm.update.title",
"name",
item.name
),
text: this.supervisor.localize(
"confirm.update.text",
"name",
item.name,
"version",
computeVersion(item.key, item.version)
),
confirmText: this.supervisor.localize("common.update"),
dismissText: this.supervisor.localize("common.cancel"),
});
if (!confirmed) {
@@ -153,13 +201,24 @@ export class HassioUpdate extends LitElement {
return;
}
try {
await this.hass.callApi<HassioResponse<void>>("POST", item.apiPath);
if (atLeastVersion(this.hass.config.version, 2021, 2, 4)) {
await supervisorApiWsRequest(this.hass.connection, {
method: "post",
endpoint: item.apiPath.replace("hassio", ""),
timeout: null,
});
} else {
await this.hass.callApi<HassioResponse<void>>("POST", item.apiPath);
}
fireEvent(this, "supervisor-collection-refresh", {
collection: item.key,
});
} catch (err) {
// Only show an error if the status code was not expected (user behind proxy)
// or no status at all(connection terminated)
if (err.status_code && !ignoredStatusCodes.has(err.status_code)) {
if (this.hass.connection.connected && !ignoreSupervisorError(err)) {
showAlertDialog(this, {
title: "Update failed",
title: this.supervisor.localize("common.error.update_failed"),
text: extractApiErrorMessage(err),
});
}
@@ -167,6 +226,13 @@ export class HassioUpdate extends LitElement {
item.progress = false;
}
private async _updateCore(): Promise<void> {
await updateCore(this.hass);
fireEvent(this, "supervisor-collection-refresh", {
collection: "core",
});
}
static get styles(): CSSResult[] {
return [
haStyle,
@@ -184,9 +250,6 @@ export class HassioUpdate extends LitElement {
margin-bottom: 0.5em;
color: var(--primary-text-color);
}
.warning {
color: var(--secondary-text-color);
}
.card-content {
height: calc(100% - 47px);
box-sizing: border-box;
@@ -194,13 +257,13 @@ export class HassioUpdate extends LitElement {
.card-actions {
text-align: right;
}
.errors {
color: var(--error-color);
padding: 16px;
}
a {
text-decoration: none;
}
ha-settings-row {
padding: 0;
--paper-item-body-two-line-min-height: 32px;
}
`,
];
}
@@ -3,9 +3,9 @@ import {
CSSResult,
customElement,
html,
internalProperty,
LitElement,
property,
internalProperty,
TemplateResult,
} from "lit-element";
import { createCloseHeading } from "../../../../src/components/ha-dialog";
@@ -11,10 +11,7 @@ export const showHassioMarkdownDialog = (
): void => {
fireEvent(element, "show-dialog", {
dialogTag: "dialog-hassio-markdown",
dialogImport: () =>
import(
/* webpackChunkName: "dialog-hassio-markdown" */ "./dialog-hassio-markdown"
),
dialogImport: () => import("./dialog-hassio-markdown"),
dialogParams,
});
};
@@ -1,5 +1,7 @@
import "@material/mwc-button/mwc-button";
import "@material/mwc-icon-button";
import "@material/mwc-list/mwc-list";
import "@material/mwc-list/mwc-list-item";
import "@material/mwc-tab";
import "@material/mwc-tab-bar";
import { mdiClose } from "@mdi/js";
@@ -16,19 +18,24 @@ import {
} from "lit-element";
import { cache } from "lit-html/directives/cache";
import { fireEvent } from "../../../../src/common/dom/fire_event";
import "../../../../src/components/ha-chips";
import "../../../../src/components/ha-circular-progress";
import "../../../../src/components/ha-dialog";
import "../../../../src/components/ha-expansion-panel";
import "../../../../src/components/ha-formfield";
import "../../../../src/components/ha-header-bar";
import "../../../../src/components/ha-radio";
import type { HaRadio } from "../../../../src/components/ha-radio";
import "../../../../src/components/ha-related-items";
import "../../../../src/components/ha-svg-icon";
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
import {
AccessPoints,
accesspointScan,
NetworkInterface,
updateNetworkInterface,
WifiConfiguration,
} from "../../../../src/data/hassio/network";
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
import {
showAlertDialog,
showConfirmationDialog,
@@ -38,54 +45,54 @@ import { haStyleDialog } from "../../../../src/resources/styles";
import type { HomeAssistant } from "../../../../src/types";
import { HassioNetworkDialogParams } from "./show-dialog-network";
const IP_VERSIONS = ["ipv4", "ipv6"];
@customElement("dialog-hassio-network")
export class DialogHassioNetwork extends LitElement
implements HassDialog<HassioNetworkDialogParams> {
@property({ attribute: false }) public hass!: HomeAssistant;
@internalProperty() private _prosessing = false;
@property({ attribute: false }) public supervisor!: Supervisor;
@internalProperty() private _params?: HassioNetworkDialogParams;
@internalProperty() private _network!: {
interface: string;
data: NetworkInterface;
}[];
@internalProperty() private _accessPoints?: AccessPoints;
@internalProperty() private _curTabIndex = 0;
@internalProperty() private _device?: {
interface: string;
data: NetworkInterface;
};
@internalProperty() private _dirty = false;
@internalProperty() private _interface?: NetworkInterface;
@internalProperty() private _interfaces!: NetworkInterface[];
@internalProperty() private _params?: HassioNetworkDialogParams;
@internalProperty() private _processing = false;
@internalProperty() private _scanning = false;
@internalProperty() private _wifiConfiguration?: WifiConfiguration;
public async showDialog(params: HassioNetworkDialogParams): Promise<void> {
this._params = params;
this._dirty = false;
this._curTabIndex = 0;
this._network = Object.keys(params.network?.interfaces)
.map((device) => ({
interface: device,
data: params.network.interfaces[device],
}))
.sort((a, b) => {
return a.data.primary > b.data.primary ? -1 : 1;
});
this._device = this._network[this._curTabIndex];
this._device.data.nameservers = String(this._device.data.nameservers);
this.supervisor = params.supervisor;
this._interfaces = params.supervisor.network.interfaces.sort((a, b) => {
return a.primary > b.primary ? -1 : 1;
});
this._interface = { ...this._interfaces[this._curTabIndex] };
await this.updateComplete;
}
public closeDialog(): void {
this._params = undefined;
this._prosessing = false;
this._processing = false;
fireEvent(this, "dialog-closed", { dialog: this.localName });
}
protected render(): TemplateResult {
if (!this._params || !this._network) {
if (!this._params || !this._interface) {
return html``;
}
@@ -101,17 +108,17 @@ export class DialogHassioNetwork extends LitElement
<div slot="heading">
<ha-header-bar>
<span slot="title">
Network settings
${this.supervisor.localize("dialog.network.title")}
</span>
<mwc-icon-button slot="actionItems" dialogAction="cancel">
<ha-svg-icon .path=${mdiClose}></ha-svg-icon>
</mwc-icon-button>
</ha-header-bar>
${this._network.length > 1
${this._interfaces.length > 1
? html` <mwc-tab-bar
.activeIndex=${this._curTabIndex}
@MDCTabBar:activated=${this._handleTabActivated}
>${this._network.map(
>${this._interfaces.map(
(device) =>
html`<mwc-tab
.id=${device.interface}
@@ -129,81 +136,330 @@ export class DialogHassioNetwork extends LitElement
private _renderTab() {
return html` <div class="form container">
<ha-formfield label="DHCP">
<ha-radio
@change=${this._handleRadioValueChanged}
value="dhcp"
name="method"
?checked=${this._device!.data.method === "dhcp"}
>
</ha-radio>
</ha-formfield>
<ha-formfield label="Static">
<ha-radio
@change=${this._handleRadioValueChanged}
value="static"
name="method"
?checked=${this._device!.data.method === "static"}
>
</ha-radio>
</ha-formfield>
${this._device!.data.method !== "dhcp"
? html` <paper-input
class="flex-auto"
id="ip_address"
label="IP address/Netmask"
.value="${this._device!.data.ip_address}"
@value-changed=${this._handleInputValueChanged}
></paper-input>
<paper-input
class="flex-auto"
id="gateway"
label="Gateway address"
.value="${this._device!.data.gateway}"
@value-changed=${this._handleInputValueChanged}
></paper-input>
<paper-input
class="flex-auto"
id="nameservers"
label="DNS servers"
.value="${this._device!.data.nameservers as string}"
@value-changed=${this._handleInputValueChanged}
></paper-input>
NB!: If you are changing IP or gateway addresses, you might lose
the connection.`
${IP_VERSIONS.map((version) =>
this._interface![version] ? this._renderIPConfiguration(version) : ""
)}
${this._interface?.type === "wireless"
? html`
<ha-expansion-panel header="Wi-Fi" outlined>
${this._interface?.wifi?.ssid
? html`<p>
${this.supervisor.localize(
"dialog.network.connected_to",
"ssid",
this._interface?.wifi?.ssid
)}
</p>`
: ""}
<mwc-button
class="scan"
@click=${this._scanForAP}
.disabled=${this._scanning}
>
${this._scanning
? html`<ha-circular-progress active size="small">
</ha-circular-progress>`
: this.supervisor.localize("dialog.network.scan_ap")}
</mwc-button>
${this._accessPoints &&
this._accessPoints.accesspoints &&
this._accessPoints.accesspoints.length !== 0
? html`
<mwc-list>
${this._accessPoints.accesspoints
.filter((ap) => ap.ssid)
.map(
(ap) =>
html`
<mwc-list-item
twoline
@click=${this._selectAP}
.activated=${ap.ssid ===
this._wifiConfiguration?.ssid}
.ap=${ap}
>
<span>${ap.ssid}</span>
<span slot="secondary">
${ap.mac} - Strength: ${ap.signal}
</span>
</mwc-list-item>
`
)}
</mwc-list>
`
: ""}
${this._wifiConfiguration
? html`
<div class="radio-row">
<ha-formfield
.label=${this.supervisor.localize(
"dialog.network.open"
)}
>
<ha-radio
@change=${this._handleRadioValueChangedAp}
.ap=${this._wifiConfiguration}
value="open"
name="auth"
.checked=${this._wifiConfiguration.auth ===
undefined ||
this._wifiConfiguration.auth === "open"}
>
</ha-radio>
</ha-formfield>
<ha-formfield
.label=${this.supervisor.localize(
"dialog.network.wep"
)}
>
<ha-radio
@change=${this._handleRadioValueChangedAp}
.ap=${this._wifiConfiguration}
value="wep"
name="auth"
.checked=${this._wifiConfiguration.auth === "wep"}
>
</ha-radio>
</ha-formfield>
<ha-formfield
.label=${this.supervisor.localize(
"dialog.network.wpa"
)}
>
<ha-radio
@change=${this._handleRadioValueChangedAp}
.ap=${this._wifiConfiguration}
value="wpa-psk"
name="auth"
.checked=${this._wifiConfiguration.auth ===
"wpa-psk"}
>
</ha-radio>
</ha-formfield>
</div>
${this._wifiConfiguration.auth === "wpa-psk" ||
this._wifiConfiguration.auth === "wep"
? html`
<paper-input
class="flex-auto"
type="password"
id="psk"
label="Password"
version="wifi"
@value-changed=${this
._handleInputValueChangedWifi}
>
</paper-input>
`
: ""}
`
: ""}
</ha-expansion-panel>
`
: ""}
${this._dirty
? html`<div class="warning">
${this.supervisor.localize("dialog.network.warning")}
</div>`
: ""}
</div>
<div class="buttons">
<mwc-button label="close" @click=${this.closeDialog}> </mwc-button>
<mwc-button @click=${this._updateNetwork} ?disabled=${!this._dirty}>
${this._prosessing
? html`<ha-circular-progress active></ha-circular-progress>`
: "Update"}
<mwc-button
.label=${this.supervisor.localize("common.cancel")}
@click=${this.closeDialog}
>
</mwc-button>
<mwc-button @click=${this._updateNetwork} .disabled=${!this._dirty}>
${this._processing
? html`<ha-circular-progress active size="small">
</ha-circular-progress>`
: this.supervisor.localize("common.save")}
</mwc-button>
</div>`;
}
private async _updateNetwork() {
this._prosessing = true;
let options: Partial<NetworkInterface> = {
method: this._device!.data.method,
};
if (options.method !== "dhcp") {
options = {
...options,
address: this._device!.data.ip_address,
gateway: this._device!.data.gateway,
dns: String(this._device!.data.nameservers).split(","),
};
private _selectAP(event) {
this._wifiConfiguration = event.currentTarget.ap;
this._dirty = true;
}
private async _scanForAP() {
if (!this._interface) {
return;
}
this._scanning = true;
try {
await updateNetworkInterface(this.hass, this._device!.interface, options);
this._accessPoints = await accesspointScan(
this.hass,
this._interface.interface
);
} catch (err) {
showAlertDialog(this, {
title: "Failed to change network settings",
title: "Failed to scan for accesspoints",
text: extractApiErrorMessage(err),
});
this._prosessing = false;
} finally {
this._scanning = false;
}
}
private _renderIPConfiguration(version: string) {
return html`
<ha-expansion-panel
.header=${`IPv${version.charAt(version.length - 1)}`}
outlined
>
<div class="radio-row">
<ha-formfield
.label=${this.supervisor.localize("dialog.network.dhcp")}
>
<ha-radio
@change=${this._handleRadioValueChanged}
.version=${version}
value="auto"
name="${version}method"
.checked=${this._interface![version]?.method === "auto"}
>
</ha-radio>
</ha-formfield>
<ha-formfield
.label=${this.supervisor.localize("dialog.network.static")}
>
<ha-radio
@change=${this._handleRadioValueChanged}
.version=${version}
value="static"
name="${version}method"
.checked=${this._interface![version]?.method === "static"}
>
</ha-radio>
</ha-formfield>
<ha-formfield
.label=${this.supervisor.localize("dialog.network.disabled")}
class="warning"
>
<ha-radio
@change=${this._handleRadioValueChanged}
.version=${version}
value="disabled"
name="${version}method"
.checked=${this._interface![version]?.method === "disabled"}
>
</ha-radio>
</ha-formfield>
</div>
${this._interface![version].method === "static"
? html`
<paper-input
class="flex-auto"
id="address"
.label=${this.supervisor.localize("dialog.network.ip_netmask")}
.version=${version}
.value=${this._toString(this._interface![version].address)}
@value-changed=${this._handleInputValueChanged}
>
</paper-input>
<paper-input
class="flex-auto"
id="gateway"
.label=${this.supervisor.localize("dialog.network.gateway")}
.version=${version}
.value=${this._interface![version].gateway}
@value-changed=${this._handleInputValueChanged}
>
</paper-input>
<paper-input
class="flex-auto"
id="nameservers"
.label=${this.supervisor.localize("dialog.network.dns_servers")}
.version=${version}
.value=${this._toString(this._interface![version].nameservers)}
@value-changed=${this._handleInputValueChanged}
>
</paper-input>
`
: ""}
</ha-expansion-panel>
`;
}
_toArray(data: string | string[]): string[] {
if (Array.isArray(data)) {
if (data && typeof data[0] === "string") {
data = data[0];
}
}
if (!data) {
return [];
}
if (typeof data === "string") {
return data.replace(/ /g, "").split(",");
}
return data;
}
_toString(data: string | string[]): string {
if (!data) {
return "";
}
if (Array.isArray(data)) {
return data.join(", ");
}
return data;
}
private async _updateNetwork() {
this._processing = true;
let interfaceOptions: Partial<NetworkInterface> = {};
IP_VERSIONS.forEach((version) => {
interfaceOptions[version] = {
method: this._interface![version]?.method || "auto",
};
if (this._interface![version]?.method === "static") {
interfaceOptions[version] = {
...interfaceOptions[version],
address: this._toArray(this._interface![version]?.address),
gateway: this._interface![version]?.gateway,
nameservers: this._toArray(this._interface![version]?.nameservers),
};
}
});
if (this._wifiConfiguration) {
interfaceOptions = {
...interfaceOptions,
wifi: {
ssid: this._wifiConfiguration.ssid,
mode: this._wifiConfiguration.mode,
auth: this._wifiConfiguration.auth || "open",
},
};
if (interfaceOptions.wifi!.auth !== "open") {
interfaceOptions.wifi = {
...interfaceOptions.wifi,
psk: this._wifiConfiguration.psk,
};
}
}
interfaceOptions.enabled =
this._wifiConfiguration !== undefined ||
interfaceOptions.ipv4?.method !== "disabled" ||
interfaceOptions.ipv6?.method !== "disabled";
try {
await updateNetworkInterface(
this.hass,
this._interface!.interface,
interfaceOptions
);
} catch (err) {
showAlertDialog(this, {
title: this.supervisor.localize("dialog.network.failed_to_change"),
text: extractApiErrorMessage(err),
});
this._processing = false;
return;
}
this._params?.loadData();
@@ -213,46 +469,78 @@ export class DialogHassioNetwork extends LitElement
private async _handleTabActivated(ev: CustomEvent): Promise<void> {
if (this._dirty) {
const confirm = await showConfirmationDialog(this, {
text:
"You have unsaved changes, these will get lost if you change tabs, do you want to continue?",
confirmText: "yes",
dismissText: "no",
text: this.supervisor.localize("dialog.network.unsaved"),
confirmText: this.supervisor.localize("common.yes"),
dismissText: this.supervisor.localize("common.no"),
});
if (!confirm) {
this.requestUpdate("_device");
this.requestUpdate("_interface");
return;
}
}
this._curTabIndex = ev.detail.index;
this._device = this._network[ev.detail.index];
this._device.data.nameservers = String(this._device.data.nameservers);
this._interface = { ...this._interfaces[ev.detail.index] };
}
private _handleRadioValueChanged(ev: CustomEvent): void {
const value = (ev.target as HaRadio).value as "dhcp" | "static";
const value = (ev.target as any).value as "disabled" | "auto" | "static";
const version = (ev.target as any).version as "ipv4" | "ipv6";
if (!value || !this._device || this._device!.data.method === value) {
if (
!value ||
!this._interface ||
this._interface[version]!.method === value
) {
return;
}
this._dirty = true;
this._device!.data.method = value;
this.requestUpdate("_device");
this._interface[version]!.method = value;
this.requestUpdate("_interface");
}
private _handleRadioValueChangedAp(ev: CustomEvent): void {
const value = ((ev.target as any).value as string) as
| "open"
| "wep"
| "wpa-psk";
this._wifiConfiguration!.auth = value;
this._dirty = true;
this.requestUpdate("_wifiConfiguration");
}
private _handleInputValueChanged(ev: CustomEvent): void {
const value: string | null | undefined = (ev.target as PaperInputElement)
.value;
const version = (ev.target as any).version as "ipv4" | "ipv6";
const id = (ev.target as PaperInputElement).id;
if (!value || !this._device || this._device.data[id] === value) {
if (
!value ||
!this._interface ||
this._toString(this._interface[version]![id]) === this._toString(value)
) {
return;
}
this._dirty = true;
this._interface[version]![id] = value;
}
this._device.data[id] = value;
private _handleInputValueChangedWifi(ev: CustomEvent): void {
const value: string | null | undefined = (ev.target as PaperInputElement)
.value;
const id = (ev.target as PaperInputElement).id;
if (
!value ||
!this._wifiConfiguration ||
this._wifiConfiguration![id] === value
) {
return;
}
this._dirty = true;
this._wifiConfiguration![id] = value;
}
static get styles(): CSSResult[] {
@@ -299,12 +587,16 @@ export class DialogHassioNetwork extends LitElement
--mdc-theme-primary: var(--error-color);
}
mwc-button.scan {
margin-left: 8px;
}
:host([rtl]) app-toolbar {
direction: rtl;
text-align: right;
}
.container {
padding: 20px 24px;
padding: 0 8px 4px;
}
.form {
margin-bottom: 53px;
@@ -322,6 +614,24 @@ export class DialogHassioNetwork extends LitElement
padding-bottom: max(env(safe-area-inset-bottom), 8px);
background-color: var(--mdc-theme-surface, #fff);
}
.warning {
color: var(--error-color);
--primary-color: var(--error-color);
}
div.warning {
margin: 12px 4px -12px;
}
ha-expansion-panel {
--expansion-panel-summary-padding: 0 16px;
margin: 4px 0;
}
paper-input {
padding: 0 14px;
}
mwc-list-item {
--mdc-list-side-padding: 10px;
}
`,
];
}
@@ -1,9 +1,9 @@
import { fireEvent } from "../../../../src/common/dom/fire_event";
import { NetworkInfo } from "../../../../src/data/hassio/network";
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
import "./dialog-hassio-network";
export interface HassioNetworkDialogParams {
network: NetworkInfo;
supervisor: Supervisor;
loadData: () => Promise<void>;
}
@@ -13,10 +13,7 @@ export const showNetworkDialog = (
): void => {
fireEvent(element, "show-dialog", {
dialogTag: "dialog-hassio-network",
dialogImport: () =>
import(
/* webpackChunkName: "dialog-hassio-network" */ "./dialog-hassio-network"
),
dialogImport: () => import("./dialog-hassio-network"),
dialogParams,
});
};
@@ -22,14 +22,18 @@ import {
fetchHassioDockerRegistries,
removeHassioDockerRegistry,
} from "../../../../src/data/hassio/docker";
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
import { showAlertDialog } from "../../../../src/dialogs/generic/show-dialog-box";
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
import type { HomeAssistant } from "../../../../src/types";
import { RegistriesDialogParams } from "./show-dialog-registries";
@customElement("dialog-hassio-registries")
class HassioRegistriesDialog extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public supervisor!: Supervisor;
@property({ attribute: false }) private _registries?: {
registry: string;
username: string;
@@ -55,8 +59,8 @@ class HassioRegistriesDialog extends LitElement {
.heading=${createCloseHeading(
this.hass,
this._addingRegistry
? "Add New Docker Registry"
: "Manage Docker Registries"
? this.supervisor.localize("dialog.registries.title_add")
: this.supervisor.localize("dialog.registries.title_manage")
)}
>
<div class="form">
@@ -66,7 +70,9 @@ class HassioRegistriesDialog extends LitElement {
@value-changed=${this._inputChanged}
class="flex-auto"
name="registry"
label="Registry"
.label=${this.supervisor.localize(
"dialog.registries.registry"
)}
required
auto-validate
></paper-input>
@@ -74,7 +80,9 @@ class HassioRegistriesDialog extends LitElement {
@value-changed=${this._inputChanged}
class="flex-auto"
name="username"
label="Username"
.label=${this.supervisor.localize(
"dialog.registries.username"
)}
required
auto-validate
></paper-input>
@@ -82,7 +90,9 @@ class HassioRegistriesDialog extends LitElement {
@value-changed=${this._inputChanged}
class="flex-auto"
name="password"
label="Password"
.label=${this.supervisor.localize(
"dialog.registries.password"
)}
type="password"
required
auto-validate
@@ -94,7 +104,7 @@ class HassioRegistriesDialog extends LitElement {
)}
@click=${this._addNewRegistry}
>
Add registry
${this.supervisor.localize("dialog.registries.add_registry")}
</mwc-button>
`
: html`${this._registries?.length
@@ -103,11 +113,16 @@ class HassioRegistriesDialog extends LitElement {
<mwc-list-item class="option" hasMeta twoline>
<span>${entry.registry}</span>
<span slot="secondary"
>Username: ${entry.username}</span
>${this.supervisor.localize(
"dialog.registries.username"
)}:
${entry.username}</span
>
<mwc-icon-button
.entry=${entry}
title="Remove"
.title=${this.supervisor.localize(
"dialog.registries.remove"
)}
slot="meta"
@click=${this._removeRegistry}
>
@@ -118,11 +133,17 @@ class HassioRegistriesDialog extends LitElement {
})
: html`
<mwc-list-item>
<span>No registries configured</span>
<span
>${this.supervisor.localize(
"dialog.registries.no_registries"
)}</span
>
</mwc-list-item>
`}
<mwc-button @click=${this._addRegistry}>
Add new registry
${this.supervisor.localize(
"dialog.registries.add_new_registry"
)}
</mwc-button> `}
</div>
</ha-dialog>
@@ -134,8 +155,9 @@ class HassioRegistriesDialog extends LitElement {
this[`_${target.name}`] = target.value;
}
public async showDialog(_dialogParams: any): Promise<void> {
public async showDialog(dialogParams: RegistriesDialogParams): Promise<void> {
this._opened = true;
this.supervisor = dialogParams.supervisor;
await this._loadRegistries();
await this.updateComplete;
}
@@ -178,7 +200,7 @@ class HassioRegistriesDialog extends LitElement {
this._addingRegistry = false;
} catch (err) {
showAlertDialog(this, {
title: "Failed to add registry",
title: this.supervisor.localize("dialog.registries.failed_to_add"),
text: extractApiErrorMessage(err),
});
}
@@ -192,7 +214,7 @@ class HassioRegistriesDialog extends LitElement {
await this._loadRegistries();
} catch (err) {
showAlertDialog(this, {
title: "Failed to remove registry",
title: this.supervisor.localize("dialog.registries.failed_to_remove"),
text: extractApiErrorMessage(err),
});
}
@@ -1,13 +1,18 @@
import { fireEvent } from "../../../../src/common/dom/fire_event";
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
import "./dialog-hassio-registries";
export const showRegistriesDialog = (element: HTMLElement): void => {
export interface RegistriesDialogParams {
supervisor: Supervisor;
}
export const showRegistriesDialog = (
element: HTMLElement,
dialogParams: RegistriesDialogParams
): void => {
fireEvent(element, "show-dialog", {
dialogTag: "dialog-hassio-registries",
dialogImport: () =>
import(
/* webpackChunkName: "dialog-hassio-registries" */ "./dialog-hassio-registries"
),
dialogParams: {},
dialogImport: () => import("./dialog-hassio-registries"),
dialogParams,
});
};

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