Compare commits

...

269 Commits

Author SHA1 Message Date
Zack 2c4e769343 Start Dongles 2022-08-19 13:57:01 -05:00
Franck Nijhof 38fd6108b4 Keep formatted attribute name in attribute selector (#13413) 2022-08-19 08:44:42 -05:00
Franck Nijhof 57fdea19fd Fix trigger state attribute selector (#13410) 2022-08-19 08:44:02 -05:00
Franck Nijhof d2a19e04ef Add state selector (#13411) 2022-08-19 09:20:13 -04:00
Steve Repsher 196456d0c4 Add translations to nightly artifacts (#13409) 2022-08-19 08:57:58 -04:00
Paulus Schoutsen 5c16447eed Fix DOM reuse for trigger/condition/action (#13407) 2022-08-19 08:05:15 -04:00
Paulus Schoutsen f3d92ba0e0 Fix Gallery menu expansion (#13408) 2022-08-18 23:32:56 -04:00
Steve Repsher 088b3587e0 Remove string exception for localize keys (#13354) 2022-08-18 12:43:15 -04:00
Paulus Schoutsen ede9d8a073 Add back learn more labels to automation editor (#13401) 2022-08-18 16:17:19 +00:00
Paulus Schoutsen 8c71885b4c Add support for the file selector (#13382) 2022-08-18 11:43:43 -04:00
Paulus Schoutsen 47b820d28f Collapse automation/script editor sections by default (#13390) 2022-08-18 14:04:35 +00:00
Steve Repsher d7b888f761 Fix localize key types related to form schemas (Group 3) (#13400)
* Fix key type errors for card editors (Round 4)

* Fix key type errors for remaining form schemas
2022-08-17 23:57:26 -04:00
Paulus Schoutsen 12e57dfcae Allow testing describe functionality in the gallery (#13398) 2022-08-17 13:20:35 -05:00
Steve Repsher 9a1fc02755 Fix localize key types related to form schemas (Group 2) (#13342) 2022-08-17 11:01:05 -05:00
Steve Repsher eb4dbef610 Bump husky & lint-staged and prevent translation edits (#13392) 2022-08-17 08:09:07 -04:00
Paulus Schoutsen 33ce27de02 Bumped version to 20220816.0 2022-08-16 17:18:38 -04:00
Paulus Schoutsen 089f531492 Add redirect_uri to each interaction with login flow (#13389) 2022-08-16 17:17:18 -04:00
uvjustin 3aa813e391 Bump hls.js to v1.2.0 (#13383) 2022-08-16 09:10:18 -05:00
Zack Barett a989eb1c66 Fix Target Selector (#13380) 2022-08-13 20:56:15 -05:00
alvinchen1 e0448be24d Add initial field to the helper input_number in UI (#13378) 2022-08-13 17:29:33 -04:00
Franck Nijhof 651cafc464 Allow Markdown and description placeholder usage in data field descriptions (#13377)
Co-authored-by: Zack Barett <zackbarett@hey.com>
2022-08-13 18:57:28 +00:00
Zack Barett 38607a6410 Add UI for Schedule Helper (#13375) 2022-08-12 13:58:08 +00:00
Steve Repsher 9046c0d0bf Merge pull request #13372 from steverep/fix-more-keys
Fix more bad or missing localize keys
2022-08-10 16:48:12 -04:00
Steve Repsher 589cec10f6 Fix bad key for quick bar hint
Also fixed a bunch of lit plugin errors.
2022-08-10 16:25:01 -04:00
Steve Repsher dba9658658 Fix bad key for add entities to view 2022-08-10 15:59:15 -04:00
Steve Repsher d21bdf2807 Add missing key in logbook card 2022-08-10 15:30:58 -04:00
Steve Repsher 3fe5075ad4 Add missing key in input_select helper dialog 2022-08-10 12:32:46 -04:00
Steve Repsher f76a3ea2ce Merge pull request #13364 from steverep/fix-random-bad-keys 2022-08-09 12:50:50 -04:00
Steve Repsher e95a5ebbbf Merge changes in repair flow header 2022-08-09 12:22:47 -04:00
Steve Repsher ae28eb3813 Fix bad key in system information 2022-08-09 12:04:06 -04:00
Steve Repsher b0807cb80c Fix bad keys in repair flow dialog 2022-08-09 11:55:10 -04:00
Steve Repsher 95231554d5 Add missing key for required tag name 2022-08-09 11:01:51 -04:00
Steve Repsher f3b543f46c Fix bad key on error screen 2022-08-08 18:21:39 -04:00
Steve Repsher 9eb81e2211 Add missing key for area not found 2022-08-08 17:59:15 -04:00
Franck Nijhof 1322ff9295 Process description placeholders in titles for repairs flows (#13362) 2022-08-08 17:36:46 -04:00
Zack Barett 5f169b48d9 Merge pull request #13360 from steverep/fix-blueprint-target-types 2022-08-08 12:27:03 -05:00
Steve Repsher 75d05cdb0e Fix key type errors in target picker 2022-08-08 12:12:13 -04:00
Steve Repsher b444d0030f Fix key type errors for blueprints 2022-08-08 12:10:56 -04:00
Zack Barett e241b20378 Merge pull request #13357 from steverep/fix-some-card-keys 2022-08-08 09:38:27 -05:00
Joakim Sørensen ca28feca80 Missing import and refresh correct collection (#13358) 2022-08-08 10:16:13 +02:00
Zack Barett f5d9a7d662 Merge pull request #13353 from home-assistant/Make-sure-we-have-supervisor.addon 2022-08-07 16:56:33 -05:00
Steve Repsher 3d236a8f49 Fix key type errors for cloud TTS 2022-08-07 15:34:26 -04:00
Steve Repsher 0ebeec0db6 Fix key type errors for media player 2022-08-07 15:33:08 -04:00
Steve Repsher d23d774ec1 Fix some key type errors in cards 2022-08-07 15:28:22 -04:00
Joakim Sørensen 0d5b86e2b6 Check store for addons when using my link (#13352) 2022-08-07 12:56:44 -04:00
ludeeus 825558c8db Make sure we have supervisor.addon 2022-08-07 16:53:09 +00:00
Zack Barett 12239d7fe3 Merge pull request #13344 from steverep/update-vaadin 2022-08-05 13:52:56 -05:00
Zack Barett 6eac6aef18 Merge pull request #13284 from raman325/ws_api 2022-08-05 12:56:47 -05:00
Steve Repsher a79d6b6a4d Upgrade vaadin to 23.1.5 for combobox accessibility 2022-08-05 12:31:07 -04:00
Steve Repsher 1d47303127 Separate supervisor localize key exceptions and disallow string (#13317) 2022-08-05 15:39:19 +02:00
Steve Repsher 150bc00c31 Fix localize key types related to form schemas (Group 1) (#13258) 2022-08-05 15:37:54 +02:00
Zack Barett d4232a2256 Bumped version to 20220802.0 (#13328) 2022-08-02 17:06:00 +02:00
Zack Barett f7e348c19b Fix Automation Creation Dialog (#13322) 2022-08-02 13:48:22 +00:00
Zack Barett f44fd35b90 Fix input and number more info dialog (#13321) 2022-08-02 15:26:17 +02:00
Erik Montnemery dac1d76bd2 Offer to remove statistics for entities with unsupported state class (#13325) 2022-08-02 11:12:05 +00:00
Franck Nijhof 0ab823bcf5 Adjust MDI icon for Repairs menu (#13324) 2022-08-02 09:33:36 +02:00
dependabot[bot] 65e952aaeb Bump actions/stale from 5.1.0 to 5.1.1 (#13313)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-02 09:25:58 +02:00
Zack Barett cfdf043444 Add ability to auto open system health with params (#13303)
* Add ability to auto open system health with params

* Update my link

* Update url when opening dialog

* comment
2022-08-02 09:23:30 +02:00
Zack Barett ecc1bf5206 Merge pull request #13319 from home-assistant/fix-device-page-strip 2022-08-01 16:03:50 -05:00
Zack Barett 57d664d87d Merge pull request #13318 from home-assistant/fix-create-helper 2022-08-01 15:47:11 -05:00
Zack cf2cd4043d Fix Device Page name stripping 2022-08-01 15:41:01 -05:00
Bram Kragten 98761cab3f Fix create helper dialog
Fixes #13274
2022-08-01 22:05:52 +02:00
Erik Montnemery 4a622f9424 Tweak suggested_value in HA-form (#13316) 2022-08-01 09:25:33 +00:00
Zack Barett c27e3325d9 Bumped version to 20220728.0 (#13300) 2022-07-28 10:10:28 -05:00
Zack Barett 08efc2fdd1 Update dialog with status line and other stuff (#13293) 2022-07-28 15:10:05 +00:00
Zack Barett 53519ae8ab Fix Dark Mode Map when set in Dashboard card (#13297) 2022-07-28 10:00:37 -05:00
Zack Barett 9baeabed19 Add integration dialog Scroll bar styles (#13299) 2022-07-28 09:54:41 -05:00
Zack Barett f3229bb8a7 Update Show Skipped/ignored in updates/repairs, update dialog of integration startup time (#13296) 2022-07-28 10:39:16 +02:00
Franck Nijhof 86c971b76a Add My support for Repairs (#13294) 2022-07-28 02:07:07 +02:00
Paulus Schoutsen afc69cb270 Render brand icon for repairs based on issue_domain (#13290) 2022-07-27 22:53:59 +02:00
Paulus Schoutsen a379d29a6c Remove non fixable from repair dialog (#13292) 2022-07-27 15:50:07 -05:00
Franck Nijhof 0769b14566 Add Bluetooth as discovery source (#13291) 2022-07-27 20:30:51 +00:00
Paulus Schoutsen 1dc68b72da Pass translation placeholders to repair title translations (#13289) 2022-07-27 19:30:22 +00:00
Bram Kragten c08be957ce Merge branch 'master' into dev 2022-07-27 12:23:47 +02:00
Bram Kragten 140e269697 Bumped version to 20220727.0 2022-07-27 12:22:43 +02:00
Bram Kragten 7c18d5aa0e Use subscribe to fetch repair issues (#13285) 2022-07-27 12:15:31 +02:00
Yosi Levy 1acdc9cd6c Various RTL fixes (#13268) 2022-07-27 11:40:50 +02:00
Yosi Levy 7501849044 Fix conversation RTL + text-alignment (#13264) 2022-07-27 11:39:44 +02:00
Yosi Levy 26ed13e548 RTL - Humidifier more info location (#13253) 2022-07-27 11:38:35 +02:00
Steve Repsher 086c33d8b3 Fix localize key types to remove user groups exception (#13259) 2022-07-27 11:37:52 +02:00
Zack Barett c73677f15d Move System Information to Repairs (#13281)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2022-07-27 09:23:55 +00:00
Zack Barett f7090583ac Update Repairs to new design (#13276)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2022-07-27 09:06:38 +00:00
Zack Barett 68517018cc Add IP Information Dialog (#13283)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2022-07-27 08:47:03 +00:00
Raman Gupta 62a0a64554 Update zwave_js WS API commands 2022-07-27 00:08:06 -04:00
Zack Barett adf3fa6a0e Use new subscribe mixin for the sidebar to show counts of repairs (#13282)
* Use new subscribe mixin for the sidebar

* spelling
2022-07-26 15:43:46 -05:00
Bram Kragten b443ec0af5 Repair issue fixes (#13272) 2022-07-25 15:37:33 +02:00
Zack Barett b9ae0e72b1 Add Ignore Action + dialog updates (#13254) 2022-07-25 11:36:00 +02:00
Zack Barett 63ea8e6568 Allows for My to support Supported Brands (#13256) 2022-07-25 11:35:13 +02:00
Steve Repsher 5be624f45d Fix localize key types to remove config_entry exception (#13257) 2022-07-22 11:01:44 +02:00
Bram Kragten 38f19b6180 Repair: load translations for all integrations with issues (#13252) 2022-07-21 09:52:44 -05:00
Steve Repsher c99f00ba50 Setup stronger type for localize key (#13244) 2022-07-21 13:13:11 +00:00
Zack Barett ce5776f59d Add Repairs to Settings (#13249)
Co-authored-by: Zack Barett <zackbarett@hey.com>
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2022-07-21 14:48:37 +02:00
Steve Repsher 12ff70020a Fix more bad localize keys (#13250) 2022-07-21 12:17:29 +02:00
Felipe Santos 5d605447a5 Support more icons for media players (#12997)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2022-07-21 12:16:00 +02:00
Michael Irigoyen 6d88d46ce4 Update Material Design Icons to v7.0.96 (#13175)
* Update Material Design Icons to v7.0.96

* Fetch updated MDI packages
2022-07-21 11:43:48 +02:00
Bram Kragten cbe2643146 Use translation_key for repairs (#13246) 2022-07-20 11:52:55 -05:00
Steve Repsher d332b8ab14 Fix bad localize keys (#13245)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2022-07-20 18:34:09 +02:00
Bram Kragten adfef05110 Bump contrast and brightness of dark mode map (#13243) 2022-07-20 14:15:34 +00:00
Yosi Levy ca6a7bfbe2 Additional RTL energy fixes (#13182) 2022-07-20 14:37:48 +02:00
Franck Nijhof a22f96a481 Migrate repairs to repairs API (#13242) 2022-07-20 14:34:57 +02:00
Yosi Levy 688109524d RTL fixes - media, attributes (#13241) 2022-07-20 11:14:50 +02:00
Sven Serlier 62dd7111ce Update design.home-assistant.io (#13240)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2022-07-20 08:24:52 +00:00
Zack Barett 1267575f62 Merge pull request #13235 from home-assistant/fix-builds
Stringify Python version to use 3.10 vs 3.1
2022-07-19 23:25:39 -05:00
Paulus Schoutsen b7da4dc68f Stringify Python version to use 3.10 vs 3.1 2022-07-19 21:11:21 -07:00
Zack Barett 826474518f Merge pull request #13228 from voydz/lovelace-map-autofit 2022-07-19 16:44:48 -05:00
Maximilian Ertl a4b92fef3a Correctly set "allow-downloads" flag on iframes (#13218) 2022-07-19 20:59:45 +02:00
Bram Kragten d41159591c Change map styles to "Voyager" (#13227) 2022-07-19 20:56:50 +02:00
Felix Rudat cb256bc386 Add auto_fit config option to lovelace map card 2022-07-19 17:45:48 +02:00
Zack Barett bd50d6a6a3 Start the Repairs dashboard (#13192)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2022-07-19 14:25:47 +00:00
Bram Kragten 05418fc83b Fix dev tools event (#13225) 2022-07-19 11:17:13 +00:00
Franck Nijhof 8b675cdbba Remove unused mypy config from pyproject (#13224) 2022-07-19 13:14:20 +02:00
Franck Nijhof 36b4909950 Bump Python to 3.10 (#13223) 2022-07-19 12:28:57 +02:00
Raman Gupta 157b3ba5f2 Clean up zwave_js device actions logic (#13185) 2022-07-19 12:11:05 +02:00
Yosi Levy 72443b4f24 RTL card fixes (#13207) 2022-07-19 12:09:50 +02:00
J. Nick Koston 1c7d3fe610 Sync frontend recoverable states with core (#13197) 2022-07-19 12:09:20 +02:00
Franck Nijhof 6ac4560b36 Use YAML in developer tools events (#13222) 2022-07-19 12:07:51 +02:00
Zack Barett 9309a4c7bc Update language when ZHA or Zwave arent installed (re: supported brands) (#13191) 2022-07-19 11:44:02 +02:00
Franck Nijhof b582a4d014 Bump node to 16 (#13221) 2022-07-19 11:39:08 +02:00
Zack Barett e4d233afa8 Filter Integration in Target and Area selectors + clean up some code (#13202) 2022-07-18 22:07:55 +02:00
dependabot[bot] b131b255ec Bump actions/stale from 3.0.13 to 5.1.0 (#13212)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-07-18 17:09:37 +02:00
Kendell R 20bdb9ff35 Use theme default font for charts (#13210)
* Use theme default font for charts

* Prettier
2022-07-18 15:35:51 +02:00
Zack Barett 666ef7a978 Merge pull request #13199 from home-assistant/2022.7-hotfix 2022-07-14 17:26:04 -05:00
Zack 24a97347df Version Bump 2022-07-14 17:13:02 -05:00
Zack Barett 0825d5c64e Fix Suggested Value in HA-Form (#13173) 2022-07-14 17:11:53 -05:00
Erik Montnemery 535e752ec7 Correct display of barometric pressure and rain (#13183) 2022-07-14 17:11:39 -05:00
Zack Barett b611a58fce Add support for Supported Brands (#13184) 2022-07-13 17:51:17 +02:00
Zack Barett 24e54554ad Fix History Graph Name not being friendly (#13179) 2022-07-13 11:48:52 +02:00
Erik Montnemery d23fca4dd1 Correct display of barometric pressure and rain (#13183) 2022-07-12 16:13:29 -05:00
Paulus Schoutsen 729e2f5248 Use entity name in device info page (#13165)
* Use entity name in device info page

* Adjust to new format

* Use latest API

* Fix types

* Fix CI?

Co-authored-by: Zack <zackbarett@hey.com>
2022-07-11 19:07:07 -07:00
Zack Barett 437723c6a6 Fix Number Selector Label (#13178) 2022-07-12 01:05:47 +00:00
Bram Kragten a30c8205b1 Update dialog styles (#13132) 2022-07-11 15:07:10 -05:00
puddly c50cf78bb4 Allow stale ZHA coordinator entries to be deleted (#13154)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2022-07-11 13:14:37 -05:00
Zack Barett be52ba0ea9 Fix Suggested Value in HA-Form (#13173) 2022-07-11 09:46:32 -05:00
Yosi Levy 55e9ebc4d2 Energy panel/cards - RTL fixes (#13171) 2022-07-11 15:47:22 +02:00
Bram Kragten 29c3fb0f92 Fix energy demo (#13172) 2022-07-11 08:44:34 -05:00
Erik Montnemery 7c3cd9d88d Merge pull request #13170 from home-assistant/number_customize_units
Allow customizing number unit of measurement
2022-07-11 14:49:47 +02:00
Erik 4881d699e3 Allow customizing number unit of measurement 2022-07-11 14:05:23 +02:00
Joakim Sørensen 414db83359 Hide homeassistant from partial restore if no version (#13168) 2022-07-11 12:55:43 +02:00
Joakim Sørensen cd4f6e19f4 Await backup restore (#13167) 2022-07-11 12:50:37 +02:00
Joakim Sørensen da709cbbd1 Add hacs_repository my redirect (#13153) 2022-07-11 09:06:45 +02:00
Bram Kragten ee9ca16eb5 Merge pull request #13138 from home-assistant/dev 2022-07-07 15:29:24 +02:00
Raman Gupta 399efca411 Only show firmware update warning if no firmware update is in progress (#13068) 2022-07-07 15:03:11 +02:00
Bram Kragten f8bccf9e79 Bumped version to 20220707.0 2022-07-07 15:02:08 +02:00
Bram Kragten 87aab72b63 opti search params history 2022-07-07 15:01:29 +02:00
Bram Kragten 24688ba18e fix reload history when no selection made (#13137) 2022-07-07 14:58:51 +02:00
Zack Barett e8086b6a6f Refactor History Panel Code a bit (#13129)
Co-authored-by: D3v01dZA <caltona1@gmail.com>
2022-07-07 14:27:28 +02:00
Bram Kragten 4358437278 Merge pull request #13126 from home-assistant/dev
20220706.0
2022-07-06 18:56:12 +02:00
Zack Barett e0a9c57a54 Bumped version to 20220706.0 (#13125) 2022-07-06 18:55:52 +02:00
Bram Kragten e63953ecbc Fix scene editor (#13123) 2022-07-06 13:50:37 +00:00
Bram Kragten 72af200190 Remove localstorage from history, use url (#13122) 2022-07-06 08:44:28 -05:00
Zack Barett 2094ae534b Fix History Panel when no entities are found (#13103) 2022-07-06 01:05:40 +02:00
Bram Kragten f6d6fd179f Merge pull request #13099 from home-assistant/dev
Bump to 20220705.0
2022-07-05 18:55:51 +02:00
Zack Barett 153ebb2a20 Bumped version to 20220705.0 (#13098) 2022-07-05 18:40:26 +02:00
Michael Irigoyen 5d58e52eea Update MDI to v6.9.96 (#13096) 2022-07-05 18:38:24 +02:00
Zack Barett 5038f9c3c6 Some Updates to the History Panel (#13095) 2022-07-05 15:31:17 +00:00
Zack Barett b285fda61b Move Sign out back to Account Card for Cloud (#13094) 2022-07-05 17:15:09 +02:00
D3v01dZA 6cd38472cd Multiple entities on history panel bugfix and additional improvements (#13045)
Co-authored-by: Zack Barett <zackbarett@hey.com>
2022-07-05 09:23:38 -05:00
Erik Montnemery 8fd5f53f96 Exclude config and diagnostic entities from scenes (#13072)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2022-07-05 08:49:05 -05:00
Zack Barett b70eee77ef Remove config Path from about (#13049)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2022-07-05 09:41:40 +00:00
Raman Gupta 4148b8c7aa Get rid of dupe HTML in zwave_js firmware upload dialog (#13067) 2022-07-05 11:26:38 +02:00
Zack Barett e22dd0c49d Fix Energy Compare Translations (#13055)
* Fix Energy Compare Translations

* Fix alert
2022-07-05 11:23:46 +02:00
dependabot[bot] 30a254f98f Bump actions/checkout from 2 to 3 (#13075)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-07-05 10:45:41 +02:00
dependabot[bot] 8fcb3a017b Bump actions/setup-python from 2 to 4 (#13078)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-07-05 10:28:53 +02:00
dependabot[bot] 5e29c7efa9 Bump dessant/lock-threads from 2.0.1 to 3.0.0 (#13077)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-07-05 10:26:46 +02:00
dependabot[bot] b6cc3e3ef0 Bump actions/setup-node from 2 to 3 (#13080)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-07-05 10:04:22 +02:00
dependabot[bot] 184bdc0c85 Bump github/codeql-action from 1 to 2 (#13081)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-07-05 10:04:02 +02:00
Sven Serlier f7fb731dc8 Add dependabot (#13073)
Co-authored-by: Franck Nijhof <frenck@frenck.nl>
2022-07-05 09:40:57 +02:00
Raman Gupta 77977f64a3 Don't check zwave_js node firmware update capabilities (#13066) 2022-07-03 13:45:19 -07:00
Franck Nijhof e8da573ba2 Only upload nighlty wheel build artifact (#13064) 2022-07-02 08:49:27 -05:00
Franck Nijhof 07332bf155 Fix version detection in build env to allow for nightly builds (#13062) 2022-07-02 00:22:54 +02:00
Franck Nijhof f3c7583bf7 Add nightly frontend builds (#13061) 2022-07-01 14:30:40 -07:00
Paulus Schoutsen 1cc02415d3 Fix rendering config entry titles (#13060)
* Fix rendering config entry titles

* Fix location height

* Only fetch installation type when user not created yet
2022-07-01 13:52:56 -05:00
Paulus Schoutsen 6ca3f06ea0 Merge pull request #13057 from home-assistant/hide-sun-onboarding
Do not show the sun during onboarding
2022-07-01 11:05:45 -07:00
Paulus Schoutsen 198e2b7bdf Do not show the sun during onboarding 2022-07-01 10:23:23 -07:00
Zack Barett 5a68e2c977 Merge pull request #13053 from home-assistant/improve-autocompletion
improve autocompletion
2022-06-30 14:57:42 -05:00
Sven Serlier d9d29db560 Fix demo labels (#13033) 2022-06-30 21:51:23 +02:00
Zack Barett 124c6dc2b8 Merge pull request #13048 from emufan/patch-1 2022-06-30 14:49:52 -05:00
Bram Kragten 0f3886e053 improve autocompletion 2022-06-30 21:41:58 +02:00
Paulus Schoutsen 68bb3558b4 Merge pull request #13051 from home-assistant/dev 2022-06-30 10:22:17 -07:00
Zack Barett b8bd15aa33 Bumped version to 20220630.0 (#13050) 2022-06-30 10:17:43 -07:00
Zack Barett ed39aa6a7c Merge pull request #13046 from Nardol/no_fix_em_dash 2022-06-30 08:20:41 -05:00
Zack Barett 405cae9b5f Merge pull request #13047 from piitaya/feat/ha-code-editor-autocomplete-icon-option 2022-06-30 08:19:54 -05:00
emufan 3ca2cbb3f9 Update ha-config-integrations.ts 2022-06-30 15:03:34 +02:00
piitaya c295ae56ab separate autocomplete options in ha-code-editor 2022-06-30 11:27:04 +02:00
piitaya 830364721b separate autocomplete options in ha-code-editor 2022-06-30 11:22:30 +02:00
Patrick ZAJDA 19089213e3 Add em dash instead of blank when no fix is needed in statistics
Signed-off-by: Patrick ZAJDA <patrick@zajda.fr>
2022-06-30 11:12:35 +02:00
Zack Barett b633067e5c Merge pull request #13041 from home-assistant/Fix-About-Page 2022-06-29 17:50:43 -05:00
Zack 1b8874cbd4 Fix Translation on About Page 2022-06-29 15:27:54 -05:00
Zack Barett a5f8ce85ba Merge pull request #13032 from home-assistant/dev 2022-06-29 11:25:03 -05:00
Bram Kragten 56eacf5733 20220629.0 (#13030)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
Co-authored-by: ImgBotApp <ImgBotHelp@gmail.com>
Co-authored-by: foreign-sub <51928805+foreign-sub@users.noreply.github.com>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
Co-authored-by: Philip Allgaier <philip.allgaier@gmx.de>
Co-authored-by: Philip Allgaier <mail@spacegaier.de>
Co-authored-by: Zack Barett <zackbarett@hey.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: Raman Gupta <7243222+raman325@users.noreply.github.com>
Co-authored-by: wizmo2 <wizmo.home@yahoo.com>
Co-authored-by: Yosi Levy <37745463+yosilevy@users.noreply.github.com>
Co-authored-by: RoboMagus <68224306+RoboMagus@users.noreply.github.com>
Co-authored-by: loeffelpan <34661317+loeffelpan@users.noreply.github.com>
Co-authored-by: Steve Repsher <steverep@users.noreply.github.com>
Co-authored-by: Joakim Sørensen <ludeeus@ludeeus.dev>
Co-authored-by: Allen Porter <allen@thebends.org>
Co-authored-by: Erik Montnemery <erik@montnemery.com>
Co-authored-by: Josh McCarty <josh@joshmccarty.com>
Co-authored-by: imgbot[bot] <31301654+imgbot[bot]@users.noreply.github.com>
Co-authored-by: Brandon Rothweiler <brandonrothweiler@gmail.com>
Co-authored-by: James Baker <j.baker@outlook.com>
Co-authored-by: Sven Serlier <85389871+wrt54g@users.noreply.github.com>
Co-authored-by: Alessandro Ghedini <alessandro@ghedini.me>
Co-authored-by: Emanuele <55278049+elax46@users.noreply.github.com>
Co-authored-by: Pascal Vizeli <pascal.vizeli@syshack.ch>
Co-authored-by: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
Co-authored-by: Kristján Bjarni <kristjanbjarni@gmail.com>
Co-authored-by: D3v01dZA <caltona1@gmail.com>
2022-06-29 11:22:17 -05:00
Bram Kragten 9324061d05 Add auto completion for mdi icons to code editor (#13022)
Co-authored-by: Zack Barett <zackbarett@hey.com>
2022-06-29 16:12:16 +00:00
Bram Kragten eafcbdc65b Merge branch 'master' into dev 2022-06-29 18:02:42 +02:00
Zack Barett 0175522c17 Bumped version to 20220629.0 (#13029) 2022-06-29 18:00:50 +02:00
D3v01dZA cff3f51d34 Multiple entities on history panel (#9946)
Co-authored-by: Zack Barett <zackbarett@hey.com>
2022-06-29 10:39:38 -05:00
Kristján Bjarni 0f580a91c9 Add optional label for gauge segment (#12960)
Co-authored-by: Zack Barett <zackbarett@hey.com>
2022-06-29 10:36:18 -05:00
Erik Montnemery 389f50b29a Merge pull request #13028 from home-assistant/add_wind_speed_units
Support knots and ft/s in weather wind speed
2022-06-29 16:34:53 +02:00
J. Nick Koston b689bb8fcf Pause the logbook stream when scrolled (#13026) 2022-06-29 13:49:44 +00:00
Erik 36f067ede4 Support knots and ft/s in weather wind speed 2022-06-29 15:47:28 +02:00
Zack Barett c2178622dd Add Switch as X Icon and Threshold Icon (#13024) 2022-06-29 09:03:04 +02:00
Zack Barett 014448e7ea Update about page (#12653)
Co-authored-by: Philip Allgaier <mail@spacegaier.de>
2022-06-29 08:56:30 +02:00
Zack Barett 08eff0509a Fix General Config Zone update on Mobile (#13011)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2022-06-29 08:54:19 +02:00
Marc Mueller 62d0882e82 Support editable installs (#12838)
* Support editable installs

* Update setup.cfg
2022-06-28 17:25:21 -07:00
Sven Serlier 86a574dbbd Fix demo labes (#13025) 2022-06-28 18:22:31 +00:00
Yosi Levy 71ac4620c5 RTL Fixes (#13023) 2022-06-28 09:28:34 -05:00
Zack Barett f611049517 Remove Restart Moved Tip (#13009) 2022-06-27 10:55:12 +02:00
Zack Barett 45fa8c272f Remove TTS moved Tip (#13010) 2022-06-27 10:54:49 +02:00
Yosi Levy 28a1c97571 Various card RTL fixes (#13006) 2022-06-24 10:18:39 -05:00
Zack Barett d9a5ae0cf1 Bumped version to 20220624.0 (#13008) 2022-06-24 03:10:20 +00:00
Raman Gupta c03849d30b Only show zwave_js firmware action if no other updates in progress (#13002)
* Only show zwave_js firmware update action if no other updates in progress

* readability
2022-06-23 19:19:20 -05:00
Pascal Vizeli 535fe2686b Use new wheels builder (#13001) 2022-06-23 14:11:06 +02:00
Emanuele 709bc87a36 Fix missing translatable energy texts (#12877)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
Co-authored-by: Philip Allgaier <philip.allgaier@gmx.de>
2022-06-23 11:02:02 +02:00
Raman Gupta 2812b467ec Add manual firmware update support for zwave-js devices (#12910)
* Subscribe to zwave_js node status updates in device panel

* Add typing for message

* Add manual firmware update support for zwave-js devices

* Tweaks based on upstream changes

* Tweaks

* remove unused CSS

* Update zwave_js.ts

* Tweaks after somet esting

* Bold device name instead of italic, catch abort errors and show the message

* Incorporate new commands tweak the UI and messaging

* Add a warning about firmware updates potentially bricking a device, and use Promise.all where possible

* Better typing so we can clean up code

* Additional tweaks

* Remove commented out code

* change style a bit

* prettier

* Be more precise with progress because it always helps the user if they can see progress

* nit

* Update src/translations/en.json

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Bram's review

* Only ask for firmware target if there is more than one available

* Only offer another firmware update if the original firmware update failed

* Only offer firmware upgrade if node is ready and pass firmware capabilities into dialog so we don't have to make call again

* Use ha-form

* Add comment

* Switch schema name

* Import icon

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2022-06-23 10:51:52 +02:00
Erik Montnemery 7d118a5715 Allow customizing weather units (#12947)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
Co-authored-by: foreign-sub <51928805+foreign-sub@users.noreply.github.com>
2022-06-23 10:48:39 +02:00
Alessandro Ghedini 8bd7370a02 Show moisture/flood alerts in the Area card (#12978) 2022-06-22 19:26:32 -05:00
Alessandro Ghedini 9fa8a96d09 Show humidity sensor values in Area card (#12976) 2022-06-22 10:45:38 -05:00
Alessandro Ghedini 508d1fffef Show icon for temperature values in Area card (#12977) 2022-06-22 10:42:39 -05:00
J. Nick Koston 3633daa814 Use websocket endpoint to fetch config entries (#12964) 2022-06-21 13:10:39 -05:00
Sven Serlier 05346ae9fc Fix demo labels (#12984) 2022-06-21 10:36:42 -05:00
Yosi Levy ea667cf0b9 RTL safari fix (#12963) 2022-06-21 11:03:22 +02:00
James Baker 048ac3965e Fix grammar in NFC settings tab description (#12979) 2022-06-20 17:24:07 +00:00
Brandon Rothweiler 276b6f4d1f Fix a bug in the climate entity more info card (#12973) 2022-06-20 14:29:42 +02:00
Paulus Schoutsen e765d7749c Update text around updating cloud entity exposed defaults (#12954) 2022-06-20 14:28:32 +02:00
imgbot[bot] 9a3b4d6df2 [ImgBot] Optimize images (#12985)
*Total -- 298.28kb -> 241.37kb (19.08%)

/gallery/public/images/logo-with-text.png -- 66.64kb -> 46.13kb (30.79%)
/gallery/public/images/clearspace.png -- 43.46kb -> 31.99kb (26.39%)
/gallery/public/images/using-our-logo.png -- 32.47kb -> 24.92kb (23.24%)
/gallery/public/images/logo-variants.png -- 34.86kb -> 26.78kb (23.18%)
/gallery/public/images/logo.png -- 27.30kb -> 21.50kb (21.25%)
/gallery/public/images/sunflowers.jpg -- 93.54kb -> 90.05kb (3.73%)

Signed-off-by: ImgBotApp <ImgBotHelp@gmail.com>

Co-authored-by: ImgBotApp <ImgBotHelp@gmail.com>
2022-06-20 14:27:18 +02:00
Raman Gupta 529e27992e Subscribe to zwave_js node status updates in device panel (#12916)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2022-06-19 14:22:02 -05:00
Bram Kragten 6c5cf2a0ec Fix energy panel in Demo (#12906) 2022-06-16 10:00:52 -05:00
Josh McCarty a4cb270f09 Ensures inputmode is set properly for alarm code inputs (#12953) 2022-06-16 09:58:29 -05:00
Bram Kragten 5160a1f55c Don't make dialog boxes fullscreen on mobile (#12928) 2022-06-14 11:04:09 -07:00
Allen Porter 6a3a0db338 Fix application credentials description when loaded from config flow (#12940) 2022-06-14 11:03:40 -07:00
Erik Montnemery 765d4eb3b4 Revert Use unit system definitions for weather units (#10657) (#12946) 2022-06-14 11:03:05 -07:00
Erik Montnemery cc09e24d66 Fix customizing sensor units (#12948) 2022-06-14 11:02:15 -07:00
Joakim Sørensen e7848262ea Split store and installed calls (#12921)
* Split store and installed calls

* Fix issue when installing

* Remove supervisor.addons usage

* one more

* Update core

* Comments
2022-06-11 11:04:54 +02:00
Paulus Schoutsen 0926202eca Clean up unused var (#12930) 2022-06-10 21:10:33 -07:00
Yosi Levy e83af02410 Use ha-list-item in config updates (#12922) 2022-06-10 16:43:31 +02:00
Raman Gupta 74d6a52fa9 Remove unused zwave_js functions (#12915) 2022-06-10 16:43:04 +02:00
Allen Porter 5baa975632 Add application credentials description placeholder (#12869) 2022-06-10 16:42:20 +02:00
Joakim Sørensen 4ad49ef07f Move to supervisor store API (#12911)
* Move to supervisor store API

* Add supervisorApiCall helper to simplify functions

* Do not consider ESPHome as custom repository

* Home Assistant Community Add-ons is not custom
2022-06-08 15:28:40 +02:00
Yosi Levy bc47ecaa57 Various RTL fixes (#12857) 2022-06-08 10:46:39 +02:00
Steve Repsher 2bd617ce6e Add container list and ARIA to create helper listbox (#12885) 2022-06-07 19:52:48 +02:00
loeffelpan dbaf955525 Fix extra space in energy-dist-card (#12905) 2022-06-07 15:18:52 +00:00
RoboMagus 578ff5b53f Energy Dashboard: Align total cost with 'previous cost' column. (#12883)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2022-06-07 15:11:47 +00:00
Yosi Levy e386942ea7 Quick bar keyboard shortcut international support (#12892) 2022-06-07 17:07:22 +02:00
Paulus Schoutsen 2fdd50f45f Add announce: true when sending TTS from media browser (#12866) 2022-06-07 16:43:34 +02:00
wizmo2 4b36770adf Fix overlapped tiles and hidden title for 2:3 aspect ratio media classes (#12853) 2022-06-07 16:42:37 +02:00
Raman Gupta 54377225ec Add zwave_js device statistics (#12794)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2022-06-07 16:40:28 +02:00
Philip Allgaier f020add6be Make translation README a bit clearer (#12901) 2022-06-07 16:36:19 +02:00
J. Nick Koston b1a3996cf1 Fix multiple races in logbook subscriptions (#12878)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2022-06-06 22:23:56 -07:00
J. Nick Koston a47a0ed716 Fix history charts not auto refreshing with cached history (#12873)
* Fix history charts refreshing with cached history

Fixes #12859

* return a new array

* Revert "return a new array"

This reverts commit 2b0e265185.
2022-06-06 16:05:22 -07:00
J. Nick Koston 91cd584b4b Request tiny thumbnails for cameras in the entity selector to reduce memory pressure (#12880) 2022-06-05 22:12:54 -07:00
J. Nick Koston 75562efb79 Add counter to logbook continuous domains (#12888) 2022-06-05 21:46:56 -07:00
J. Nick Koston f464bcfc14 Filter entities that will never have entries in the logbook card editor (#12876) 2022-06-05 21:45:58 -07:00
Bram Kragten f8af66d310 20220601.0 (#12843) 2022-06-01 21:57:23 +02:00
Bram Kragten 4922e575f8 Bumped version to 20220601.0 2022-06-01 21:43:38 +02:00
Bram Kragten ac08daa64e Don't fix width of label when not virtualized (#12842)
Don't fix width of label when not virtualized
2022-06-01 21:42:27 +02:00
Bram Kragten 97f082a384 Merge pull request #12835 from home-assistant/dev 2022-05-31 16:09:03 +02:00
Bram Kragten ced37aab4c Make hardware item non interactive 2022-05-31 15:57:19 +02:00
Bram Kragten 1938fb89e6 Bumped version to 20220531.0 2022-05-31 15:51:20 +02:00
Bram Kragten 6842c479d6 Add compare data to individual devices graph (#12829) 2022-05-31 08:36:04 -05:00
Bram Kragten 881f6b0531 Use icon button for compare on narrow (#12831) 2022-05-31 08:29:32 -05:00
Pawel a564ceb9e3 Add proper label for gas energy stats (#12828)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2022-05-31 08:51:27 +00:00
J. Nick Koston 077fa3f6b2 Fix live logbook starting empty (#12833) 2022-05-30 22:03:51 -07:00
J. Nick Koston ceda911670 Virtualize history panel (#12824) 2022-05-30 10:30:04 -10:00
Bram Kragten afd41e79f0 Make blueprint picker wider (#12830) 2022-05-30 12:53:39 -05:00
Yosi Levy 10f63180eb RTL Auth fix (#12746) 2022-05-30 17:57:37 +02:00
Zack Barett e54802bd87 Actually add Cloud URL Translation.... (#12813) 2022-05-30 17:56:24 +02:00
wizmo2 c1d6b51065 Scale oversized brand thumbnails in media browser (#12820)
* resize brand icons

* add newline JIC

* Now Prettier

* created brand-image style
2022-05-30 17:19:48 +02:00
Joakim Sørensen ab65ce819f Fallback to 0 for undefined offsets (#12823) 2022-05-30 09:57:28 -05:00
Xor 1e011bfe34 Fix color of plant entity when state is problem (#12821) 2022-05-30 09:57:00 -05:00
Joakim Sørensen 5951f5c5c4 Prefer CSS variables in custom panel entrypoint (#12818) 2022-05-30 09:56:13 -05:00
Bram Kragten d810cae194 20220526.0 (#12807)
* Fix setting _externalAccess (#12584)

* Fix enter key support for generic dialog box (#12600)

* Revert #10991 (#12618)

* Replace host-context with css properties

* Remove "Lovelace" from Github issue templates (#12614)

* Remove "Lovelace" from Github issue templates

* Changes from review

* Get full core logs from core (#12639)

* Move YAML to first tab of Developer Tools (#12589)

* Add configuration panel for Application Credentials (#12344)

Co-authored-by: Zack Barett <zackbarett@hey.com>
Co-authored-by: Zack <zackbarett@hey.com>

* Add label for Fix issue column header in statistics developer tools (#12597)

Signed-off-by: Patrick ZAJDA <patrick@zajda.fr>

* RTL settings clickable list item fix (#12595)

* Update src/state/translations-mixin.ts

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

* Update following review

* Update Translations to create helper (#12656)

* Hide Cloud URL - Add Copy Icon (#12655)

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

* Show script traces in logbook (#12643)

* Rtl menu fix (#12561)

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

* Use  /

* Update var name

* Use FabBase

* Update ha-fab.ts

* Switch logbook calls to use the new websocket (#12665)

* Add calendar trigger offsets in automation editor (#12486)

* Add calendar trigger offsets in automation editor

* Use duration selector for offset

* Fix typing for offsets/duratons

* Fix strict error handling in Markdown card templates (#12661)

* Add missing label to search icon (#12671)

* Update Lokalise URL (#12684)

* Teach logbook about additional context data (#12667)

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>

* Guard for missing backup integration (#12696)

* Rtl changes (#12693)

* Return focus after dialogs close (#11999)

* Bumped version to 20220516.0

* Fix float-end for LTR (#12707)

* Add my support for Application Credentials (#12709)

* Show manage cloud link to config (#12673)

* Add guard logic from PR home-assistant#12181 to input select row (#12703)

* Refactor logbook data fetch logic into reusable class (#12701)

* Add logbook to device info page (#12714)

* Add logbook to area info page (#12715)

* Add missing labels in energy dashboard settings (#12722)

Signed-off-by: Patrick ZAJDA <patrick@zajda.fr>

* Delete focus targets for replaced dialogs (#12724)

* Convert history calls to use new websocket endpoint (#12662)

* Add support for OAuth2 callback via My (#12718)

* Support requesting translations for multiple integrations in one request (#12704)

* Support requesting translations for multiple integrations in one request

- Requires https://github.com/home-assistant/core/pull/71979

* onboarding as well

* integrations -> integration

* fix cache

* short return if they are all loaded

* reduce

* reduce

* reduce

* Show the integration brand icon when there is no entity in logbook (#12713)

* Support requesting multiple integration manifests in a single request (#12706)

* Support requesting multiple integration manifests in a single request

* only fetch if there are some to actually fetch

* handle empty

* not truthy, wrong language

* Do not copy params

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

* Import all date-fns from modules (#12717)

* Fix 'loading_log' string (#12712)

* Bumped version to 20220518.0

* Fix python to js timestamp conversions in logbook traces (#12677)

- The websocket version needs the time converted from
  where python stores the decimal

* Update Material Design Icons to v6.7.96 (#12111)

* Various RTL fixes (#12721)

* Select + target picker Rtl fixes (#12711)

* Add error handling for application credentials removal (#12686)

* Update zwave_js data collection URL (#12666)

* Use device_id instead of config entry id and node id for zwave_js (#12658)

* Use device_id instead of config entry id and node id for zwave_js

* Add additional cleanup from #12642

* Revert removal of multiple config entries check

* Update src/panels/config/devices/device-detail/integration-elements/zwave_js/ha-device-actions-zwave_js.ts

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>

* Update src/panels/config/devices/device-detail/integration-elements/zwave_js/ha-device-info-zwave_js.ts

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>

* Get attributes from first state when using minimal responses (#12732)

* Pass device ID to logbook if available (#12728)

* Compute the icon based on the logbook state and not the current state (#12725)

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

* Add option to compare energy graphs with previous period (#12723)

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

* Add an application credentials display name (#12720)

Co-authored-by: Bram Kragten <mail@bramkragten.nl>
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>

* Fixes logbook (#12740)

* Bumped version to 20220521.0

* Stop closed event when selecting datadisk (#12749)

* Use logbook livestream when requesting a time window that includes the future (#12744)

* Fetch supervisor info directly (#12751)

* Remove kernel and agent versions from about page (#12750)

* Move `preload_stream` setting to entity settings (#12730)

Co-authored-by: Zack <zackbarett@hey.com>

* Use new localized context state and source in logbook (#12742)

* Use new logbook streaming websocket api for cases where we need end_time (#12753)

* Open Application Credentials from integration configuration flow (#12708)

* Make entities and devices independent in the scene editor (#11046)

Co-authored-by: Zack Barett <zackbarett@hey.com>
Co-authored-by: Erik <erik@montnemery.com>
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>

* Allow setting `device_class` "outlet" again through entity settings (#12669)

* Allow setting `device_class` "outlet" again through UI

* Fixes

* Null check deviceClass and adjust used translation

* Bumped version to 20220523.0 (#12756)

* Adjust logbook stream consumer to handle new metadata (#12755)

* Adjust path to version info in issue template (#12760)

* Add My HA link to about page to Github issue template (#12761)

* RTL updates (#12745)

* Add compare to energy sources table (#12762)

* Fix (#12764)

* Change service_data to just data (#12628)

* Simplify OAuth2 authorize callback URL (#12765)

* Hide hidden media player entities in media panel (#12766)

* Add integration filter to Device Selector (#12680)

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

* Integration filter for Area Selector (#12682)

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

* Bump Version to 20220524.0 (#12769)

* Move zwave_js node comments from device config to info page (#12625)

Co-authored-by: Zack Barett <zackbarett@hey.com>
Co-authored-by: Bram Kragten <mail@bramkragten.nl>

* Various RTL fixes

* Add controller statistics to zwave_js config dashboard (#12668)

* Move Logbook and make device page better (#12763)

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

* Move metadata to pyproject.toml (#12770)

* Add aria-haspopup to button menus (#12758)

Co-authored-by: Zack Barett <zackbarett@hey.com>

* Hardware MVP (#12773)

* Fix "unavailable" handling for climate state rendering (#12778)

* Ensure state is vertically centered in more-info (#12780)

* Update zwave_js/network_status WS API (#12735)

* Bumped version to 20220525.0 (#12779)

* Use dynamic weather domain icon + icon alignment fix weather more-info (#12781)

* Fix typo in credentials removal dialog (#12784)

* Handle history api being passed entity ids as CSV (#12787)

* Fix history cache when there is cacheConfig (#12788)

* Use Hardware Integration for System Menu (#12789)

* Fix Media Player More info cramped controls (#12790)

* Fix Switch as X unable to change to a new type (#12797)

* Fix Zwave Alerts on device page (#12785)

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

* Set Error if entity is unavailable (#12791)

* Dynamically determine the correct action config struct (#12798)

* Remove import

* Hide Cloud information a bit more (#12802)

* Add dynamic header/footer config determination and update struct (#12795)

* Bump HAWS to 7.1.0 (#12804)

* Update Narrow Order on Device Page (#12801)

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

* Fix combo box inside dialog (#12805)

* Bumped version to 20220526.0

Co-authored-by: Joakim Sørensen <ludeeus@ludeeus.dev>
Co-authored-by: Steve Repsher <steverep@users.noreply.github.com>
Co-authored-by: Zack Barett <zackbarett@hey.com>
Co-authored-by: Yosi Levy <yosilevy@gmail.com>
Co-authored-by: Philip Allgaier <mail@spacegaier.de>
Co-authored-by: Allen Porter <allen@thebends.org>
Co-authored-by: Patrick ZAJDA <patrick@zajda.fr>
Co-authored-by: Yosi Levy <37745463+yosilevy@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: Franck Nijhof <git@frenck.dev>
Co-authored-by: Sven <85389871+wrt54g@users.noreply.github.com>
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
Co-authored-by: breakthestatic <breakthestatic@gmail.com>
Co-authored-by: RoboMagus <68224306+RoboMagus@users.noreply.github.com>
Co-authored-by: Michael Irigoyen <michael@irigoyen.dev>
Co-authored-by: Raman Gupta <7243222+raman325@users.noreply.github.com>
Co-authored-by: David F. Mulcahey <david.mulcahey@me.com>
Co-authored-by: Erik <erik@montnemery.com>
Co-authored-by: Thomas Lovén <thomasloven@gmail.com>
Co-authored-by: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
2022-05-26 23:41:27 +02:00
369 changed files with 10943 additions and 4875 deletions
+8
View File
@@ -0,0 +1,8 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: weekly
time: "06:00"
open-pull-requests-limit: 10
+9 -9
View File
@@ -11,7 +11,7 @@ on:
- master
env:
NODE_VERSION: 14
NODE_VERSION: 16
NODE_OPTIONS: --max_old_space_size=6144
jobs:
@@ -19,9 +19,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out files from GitHub
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Set up Node ${{ env.NODE_VERSION }}
uses: actions/setup-node@v2
uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
cache: yarn
@@ -43,9 +43,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out files from GitHub
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Set up Node ${{ env.NODE_VERSION }}
uses: actions/setup-node@v2
uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
cache: yarn
@@ -62,9 +62,9 @@ jobs:
needs: [lint, test]
steps:
- name: Check out files from GitHub
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Set up Node ${{ env.NODE_VERSION }}
uses: actions/setup-node@v2
uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
cache: yarn
@@ -81,9 +81,9 @@ jobs:
needs: [lint, test]
steps:
- name: Check out files from GitHub
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Set up Node ${{ env.NODE_VERSION }}
uses: actions/setup-node@v2
uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
cache: yarn
+4 -4
View File
@@ -23,7 +23,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
@@ -36,14 +36,14 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
uses: github/codeql-action/autobuild@v2
# ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@@ -57,4 +57,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
uses: github/codeql-action/analyze@v2
+3 -3
View File
@@ -6,7 +6,7 @@ on:
- dev
env:
NODE_VERSION: 14
NODE_VERSION: 16
NODE_OPTIONS: --max_old_space_size=6144
jobs:
@@ -14,9 +14,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out files from GitHub
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Set up Node ${{ env.NODE_VERSION }}
uses: actions/setup-node@v2
uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
cache: yarn
+1 -1
View File
@@ -9,7 +9,7 @@ jobs:
lock:
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@v2.0.1
- uses: dessant/lock-threads@v3.0.0
with:
github-token: ${{ github.token }}
issue-lock-inactive-days: "30"
+73
View File
@@ -0,0 +1,73 @@
name: Nightly
on:
workflow_dispatch:
schedule:
- cron: "0 1 * * *"
env:
PYTHON_VERSION: "3.10"
NODE_VERSION: 16
NODE_OPTIONS: --max_old_space_size=6144
permissions:
actions: none
jobs:
nightly:
name: Nightly
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout the repository
uses: actions/checkout@v3
- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Set up Node ${{ env.NODE_VERSION }}
uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
cache: yarn
- name: Install dependencies
run: yarn install
- name: Download translations
run: ./script/translations_download
env:
LOKALISE_TOKEN: ${{ secrets.LOKALISE_TOKEN }}
- name: Bump version
run: script/version_bump.js nightly
- name: Build nightly Python wheels
run: |
pip install build
yarn install
script/build_frontend
rm -rf dist home_assistant_frontend.egg-info
python3 -m build
- name: Archive translations
run: tar -czvf translations.tar.gz translations
- name: Upload build artifacts
uses: actions/upload-artifact@v3
with:
name: wheels
path: dist/home_assistant_frontend*.whl
if-no-files-found: error
- name: Upload translations
uses: actions/upload-artifact@v3
with:
name: translations
path: translations.tar.gz
if-no-files-found: error
+10 -32
View File
@@ -6,8 +6,8 @@ on:
- published
env:
PYTHON_VERSION: 3.8
NODE_VERSION: 14
PYTHON_VERSION: "3.10"
NODE_VERSION: 16
NODE_OPTIONS: --max_old_space_size=6144
# Set default workflow permissions
@@ -21,21 +21,21 @@ jobs:
name: Release
runs-on: ubuntu-latest
permissions:
contents: write # Required to upload release assets
contents: write # Required to upload release assets
steps:
- name: Checkout the repository
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Verify version
uses: home-assistant/actions/helpers/verify-version@master
- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v2
uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Set up Node ${{ env.NODE_VERSION }}
uses: actions/setup-node@v2
uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
cache: yarn
@@ -74,33 +74,11 @@ jobs:
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"]
tag:
- "3.9-alpine3.14"
steps:
- name: Download requirements.txt
uses: actions/download-artifact@v2
with:
name: requirements
- name: Build wheels
uses: home-assistant/wheels@master
uses: home-assistant/wheels@2022.06.7
with:
tag: ${{ matrix.tag }}
arch: ${{ matrix.arch }}
wheels-host: ${{ secrets.WHEELS_HOST }}
abi: cp310
tag: musllinux_1_2
arch: amd64
wheels-key: ${{ secrets.WHEELS_KEY }}
wheels-user: wheels
requirements: "requirements.txt"
+1 -1
View File
@@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: 90 days stale policy
uses: actions/stale@v3.0.13
uses: actions/stale@v5.1.1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 90
+2 -2
View File
@@ -8,7 +8,7 @@ on:
- src/translations/en.json
env:
NODE_VERSION: 14
NODE_VERSION: 16
jobs:
upload:
@@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Upload Translations
run: |
+4
View File
@@ -0,0 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
lint-staged --relative --shell "/bin/bash"
+1 -1
View File
@@ -1 +1 @@
14
16
+1 -1
View File
@@ -27,7 +27,7 @@ module.exports = {
version() {
const version = fs
.readFileSync(path.resolve(paths.polymer_dir, "pyproject.toml"), "utf8")
.match(/version\W+=\W"(\d{8}\.\d)"/);
.match(/version\W+=\W"(\d{8}\.\d(?:\.dev)?)"/);
if (!version) {
throw Error("Version not found");
}
+9
View File
@@ -156,3 +156,12 @@ gulp.task("gen-icons-json", (done) => {
done();
});
gulp.task("gen-dummy-icons-json", (done) => {
if (!fs.existsSync(OUTPUT_DIR)) {
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
}
fs.writeFileSync(path.resolve(OUTPUT_DIR, "iconList.json"), "[]");
done();
});
+3
View File
@@ -9,6 +9,7 @@ require("./compress.js");
require("./rollup.js");
require("./gather-static.js");
require("./translations.js");
require("./gen-icons-json.js");
gulp.task(
"develop-hassio",
@@ -17,6 +18,7 @@ gulp.task(
process.env.NODE_ENV = "development";
},
"clean-hassio",
"gen-dummy-icons-json",
"gen-index-hassio-dev",
"build-supervisor-translations",
"copy-translations-supervisor",
@@ -33,6 +35,7 @@ gulp.task(
process.env.NODE_ENV = "production";
},
"clean-hassio",
"gen-dummy-icons-json",
"build-supervisor-translations",
"copy-translations-supervisor",
"build-locale-data",
+30 -1
View File
@@ -1 +1,30 @@
[]
[
{
"path": "M20,20H7A2,2 0 0,1 5,18V8.94L2.23,5.64C2.09,5.47 2,5.24 2,5A1,1 0 0,1 3,4H20A2,2 0 0,1 22,6V18A2,2 0 0,1 20,20M8.5,7A0.5,0.5 0 0,0 8,7.5V8.5A0.5,0.5 0 0,0 8.5,9H18.5A0.5,0.5 0 0,0 19,8.5V7.5A0.5,0.5 0 0,0 18.5,7H8.5M8.5,11A0.5,0.5 0 0,0 8,11.5V12.5A0.5,0.5 0 0,0 8.5,13H18.5A0.5,0.5 0 0,0 19,12.5V11.5A0.5,0.5 0 0,0 18.5,11H8.5M8.5,15A0.5,0.5 0 0,0 8,15.5V16.5A0.5,0.5 0 0,0 8.5,17H13.5A0.5,0.5 0 0,0 14,16.5V15.5A0.5,0.5 0 0,0 13.5,15H8.5Z",
"name": "android-messages"
},
{
"path": "M4,6H2V20A2,2 0 0,0 4,22H18V20H4V6M20,2H8A2,2 0 0,0 6,4V16A2,2 0 0,0 8,18H20A2,2 0 0,0 22,16V4A2,2 0 0,0 20,2M20,12L17.5,10.5L15,12V4H20V12Z",
"name": "book-variant-multiple"
},
{
"path": "M21,14H3V4H21M21,2H3C1.89,2 1,2.89 1,4V16A2,2 0 0,0 3,18H10L8,21V22H16V21L14,18H21A2,2 0 0,0 23,16V4C23,2.89 22.1,2 21,2Z",
"name": "desktop-mac"
},
{
"path": "M21,14V4H3V14H21M21,2A2,2 0 0,1 23,4V16A2,2 0 0,1 21,18H14L16,21V22H8V21L10,18H3C1.89,18 1,17.1 1,16V4C1,2.89 1.89,2 3,2H21M4,5H15V10H4V5M16,5H20V7H16V5M20,8V13H16V8H20M4,11H9V13H4V11M10,11H15V13H10V11Z",
"name": "desktop-mac-dashboard"
},
{
"path": "M22,24L16.75,19L17.38,21H4.5A2.5,2.5 0 0,1 2,18.5V3.5A2.5,2.5 0 0,1 4.5,1H19.5A2.5,2.5 0 0,1 22,3.5V24M12,6.8C9.32,6.8 7.44,7.95 7.44,7.95C8.47,7.03 10.27,6.5 10.27,6.5L10.1,6.33C8.41,6.36 6.88,7.53 6.88,7.53C5.16,11.12 5.27,14.22 5.27,14.22C6.67,16.03 8.75,15.9 8.75,15.9L9.46,15C8.21,14.73 7.42,13.62 7.42,13.62C7.42,13.62 9.3,14.9 12,14.9C14.7,14.9 16.58,13.62 16.58,13.62C16.58,13.62 15.79,14.73 14.54,15L15.25,15.9C15.25,15.9 17.33,16.03 18.73,14.22C18.73,14.22 18.84,11.12 17.12,7.53C17.12,7.53 15.59,6.36 13.9,6.33L13.73,6.5C13.73,6.5 15.53,7.03 16.56,7.95C16.56,7.95 14.68,6.8 12,6.8M9.93,10.59C10.58,10.59 11.11,11.16 11.1,11.86C11.1,12.55 10.58,13.13 9.93,13.13C9.29,13.13 8.77,12.55 8.77,11.86C8.77,11.16 9.28,10.59 9.93,10.59M14.1,10.59C14.75,10.59 15.27,11.16 15.27,11.86C15.27,12.55 14.75,13.13 14.1,13.13C13.46,13.13 12.94,12.55 12.94,11.86C12.94,11.16 13.45,10.59 14.1,10.59Z",
"name": "discord"
},
{
"path": "M8.06,7.78C7.5,7.78 7.17,7.73 7.08,7.64L6.66,13.73C7.19,14.05 7.88,14.3 8.72,14.5C9.56,14.71 10.78,14.77 12.38,14.67C13.97,14.58 15.63,14.23 17.34,13.64L16.55,4.22C15.67,5.09 14.38,5.91 12.66,6.66C11.13,7.31 9.81,7.69 8.72,7.78H8.06M7.97,5.34C7.28,5.94 7,6.34 7.13,6.56C7.22,6.78 7.7,6.84 8.58,6.75C9.67,6.66 10.91,6.31 12.28,5.72C13.22,5.31 14.03,4.88 14.72,4.41C15.41,3.94 15.88,3.55 16.13,3.23C16.38,2.92 16.47,2.7 16.41,2.58C16.34,2.42 16.03,2.34 15.47,2.34C14.34,2.34 12.94,2.7 11.25,3.42C9.81,4.05 8.72,4.69 7.97,5.34M17.34,2.2C17.41,2.33 17.44,2.47 17.44,2.63L18.61,17C18.61,18.73 18,20.09 16.83,21.07C15.64,22.05 14.03,22.55 12,22.55C10,22.55 8.4,22.04 7.2,21C6,20 5.39,18.64 5.39,16.92L6.09,6.47C6.09,6.22 6.2,5.94 6.42,5.63C6.64,5.31 6.84,5.06 7.03,4.88L7.36,4.59C8.33,3.78 9.5,3.08 10.88,2.5C11.81,2.08 12.73,1.77 13.62,1.57C14.5,1.37 15.3,1.3 16,1.38C16.71,1.46 17.16,1.73 17.34,2.2Z",
"name": "google-home"
},
{
"path": "M19.25,19H4.75V3H19.25M14,22H10V21H14M18,0H6A3,3 0 0,0 3,3V21A3,3 0 0,0 6,24H18A3,3 0 0,0 21,21V3A3,3 0 0,0 18,0Z",
"name": "tablet-android"
}
]
+14 -14
View File
@@ -59,7 +59,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
attributes: {
hidden: true,
radius: 50,
friendly_name: "Skolan",
friendly_name: "School",
icon: "mdi:school",
},
},
@@ -137,7 +137,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
state: "73",
attributes: {
unit_of_measurement: "%",
friendly_name: "oskar batteri",
friendly_name: "Oskar battery",
device_class: "battery",
},
},
@@ -146,7 +146,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
state: "88",
attributes: {
unit_of_measurement: "%",
friendly_name: "bella batteri",
friendly_name: "Bella battery",
device_class: "battery",
},
},
@@ -154,7 +154,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
entity_id: "binary_sensor.unifi_camera",
state: "off",
attributes: {
friendly_name: "R\u00f6relsesensor kamera",
friendly_name: "Motion sensor camera",
icon: "mdi:walk",
},
},
@@ -707,7 +707,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
},
],
cloudiness: 25,
friendly_name: "V\u00e4der",
friendly_name: "Weather",
},
},
"binary_sensor.ubiquiti_switch": {
@@ -731,7 +731,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
round_trip_time_max: "0.626",
round_trip_time_mdev: "",
round_trip_time_min: "0.358",
friendly_name: "Entr\u00e9 kamera",
friendly_name: "Entrance camera",
device_class: "connectivity",
icon: "mdi:cctv",
},
@@ -797,7 +797,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
attributes: {
battery_level: 34,
on: true,
friendly_name: "altan_motion_sensor",
friendly_name: "Porch motion sensor",
device_class: "motion",
},
},
@@ -807,7 +807,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
attributes: {
battery_level: 88,
on: true,
friendly_name: "Altand\u00f6rren sensor",
friendly_name: "Back door sensor",
device_class: "opening",
icon: "mdi:door",
},
@@ -818,7 +818,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
attributes: {
battery_level: 74,
on: true,
friendly_name: "badrumssensor",
friendly_name: "Bathroom motion sensor",
device_class: "motion",
},
},
@@ -829,7 +829,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
battery_level: 47,
on: true,
dark: true,
friendly_name: "R\u00f6relsesensor k\u00e4llaren 1",
friendly_name: "Basement motion sensor",
device_class: "motion",
icon: "mdi:walk",
},
@@ -841,7 +841,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
battery_level: 60,
on: true,
dark: true,
friendly_name: "R\u00f6relsesensor tv\u00e4ttstugan",
friendly_name: "Laundy room motion sensor",
device_class: "motion",
icon: "mdi:walk",
},
@@ -863,7 +863,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
attributes: {
battery_level: 60,
on: true,
friendly_name: "R\u00f6relsesensor skafferiet",
friendly_name: "Pantry motion sensor",
device_class: "motion",
icon: "mdi:walk",
},
@@ -875,7 +875,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
battery_level: 60,
on: true,
dark: true,
friendly_name: "R\u00f6relsesensor k\u00e4llaren 2",
friendly_name: "Stair motion sensor",
device_class: "motion",
icon: "mdi:walk",
},
@@ -887,7 +887,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
battery_level: 47,
on: true,
dark: true,
friendly_name: "B\u00e4nksensor",
friendly_name: "Bench sensor",
device_class: "motion",
},
},
+1 -1
View File
@@ -277,7 +277,7 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
],
show_header_toggle: false,
type: "entities",
title: "Bandbredd",
title: "Bandwidth",
},
// {
// title: "Updater",
+34 -5
View File
@@ -1,5 +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";
import {
@@ -7,9 +6,14 @@ import {
provideHass,
} from "../../src/fake_data/provide_hass";
import { HomeAssistantAppEl } from "../../src/layouts/home-assistant";
import "../../src/resources/compatibility";
import { HomeAssistant } from "../../src/types";
import { selectedDemoConfig } from "./configs/demo-configs";
import { mockAuth } from "./stubs/auth";
import { mockConfigEntries } from "./stubs/config_entries";
import { mockEnergy } from "./stubs/energy";
import { energyEntities } from "./stubs/entities";
import { mockEntityRegistry } from "./stubs/entity_registry";
import { mockEvents } from "./stubs/events";
import { mockFrontend } from "./stubs/frontend";
import { mockHistory } from "./stubs/history";
@@ -20,9 +24,6 @@ import { mockShoppingList } from "./stubs/shopping_list";
import { mockSystemLog } from "./stubs/system_log";
import { mockTemplate } from "./stubs/template";
import { mockTranslations } from "./stubs/translations";
import { mockEnergy } from "./stubs/energy";
import { mockConfig } from "./stubs/config";
import { energyEntities } from "./stubs/entities";
class HaDemo extends HomeAssistantAppEl {
protected async _initializeHass() {
@@ -51,8 +52,36 @@ class HaDemo extends HomeAssistantAppEl {
mockMediaPlayer(hass);
mockFrontend(hass);
mockEnergy(hass);
mockConfig(hass);
mockPersistentNotification(hass);
mockConfigEntries(hass);
mockEntityRegistry(hass, [
{
config_entry_id: "co2signal",
device_id: "co2signal",
area_id: null,
disabled_by: null,
entity_id: "sensor.co2_intensity",
name: null,
icon: null,
platform: "co2signal",
hidden_by: null,
entity_category: null,
has_entity_name: false,
},
{
config_entry_id: "co2signal",
device_id: "co2signal",
area_id: null,
disabled_by: null,
entity_id: "sensor.grid_fossil_fuel_percentage",
name: null,
icon: null,
platform: "co2signal",
hidden_by: null,
entity_category: null,
has_entity_name: false,
},
]);
hass.addEntities(energyEntities());
-41
View File
@@ -1,41 +0,0 @@
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
export const mockConfig = (hass: MockHomeAssistant) => {
hass.mockAPI("config/config_entries/entry", () => [
{
entry_id: "co2signal",
domain: "co2signal",
title: "CO2 Signal",
source: "user",
state: "loaded",
supports_options: false,
supports_unload: true,
pref_disable_new_entities: false,
pref_disable_polling: false,
disabled_by: null,
reason: null,
},
]);
hass.mockWS("config/entity_registry/list", () => [
{
config_entry_id: "co2signal",
device_id: "co2signal",
area_id: null,
disabled_by: null,
entity_id: "sensor.co2_intensity",
name: null,
icon: null,
platform: "co2signal",
},
{
config_entry_id: "co2signal",
device_id: "co2signal",
area_id: null,
disabled_by: null,
entity_id: "sensor.grid_fossil_fuel_percentage",
name: null,
icon: null,
platform: "co2signal",
},
]);
};
+20
View File
@@ -0,0 +1,20 @@
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
export const mockConfigEntries = (hass: MockHomeAssistant) => {
hass.mockWS("config_entries/get", () => [
{
entry_id: "co2signal",
domain: "co2signal",
title: "CO2 Signal",
source: "user",
state: "loaded",
supports_options: false,
supports_remove_device: false,
supports_unload: true,
pref_disable_new_entities: false,
pref_disable_polling: false,
disabled_by: null,
reason: null,
},
]);
};
+3 -1
View File
@@ -4,4 +4,6 @@ import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
export const mockEntityRegistry = (
hass: MockHomeAssistant,
data: EntityRegistryEntry[] = []
) => hass.mockWS("config/entity_registry/list", () => data);
) => {
hass.mockWS("config/entity_registry/list", () => data);
};
+1
View File
@@ -466,6 +466,7 @@ export const mockHistory = (mockHass: MockHomeAssistant) => {
return results;
}
);
mockHass.mockWS("recorder/get_statistics_metadata", () => []);
mockHass.mockWS("history/list_statistic_ids", () => []);
mockHass.mockWS(
"history/statistics_during_period",
Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 25 KiB

+2 -2
View File
@@ -8,7 +8,7 @@ module.exports = [
{
category: "lovelace",
// Label for in the sidebar
header: "Lovelace",
header: "Dashboards",
// Specify order of pages. Any pages in the category folder but not listed here will
// automatically be added after the pages listed here.
pages: ["introduction"],
@@ -34,7 +34,7 @@ module.exports = [
},
{
category: "misc",
header: "Miscelaneous",
header: "Miscellaneous",
},
{
category: "brand",
+4 -3
View File
@@ -5,7 +5,7 @@ import { html, css, LitElement, PropertyValues } from "lit";
import { customElement, property, query } from "lit/decorators";
import "../../src/components/ha-icon-button";
import "../../src/managers/notification-manager";
import "../../src/components/ha-expansion-panel";
import { HaExpansionPanel } from "../../src/components/ha-expansion-panel";
import { haStyle } from "../../src/resources/styles";
import { PAGES, SIDEBAR } from "../build/import-pages";
import { dynamicElement } from "../../src/common/dom/dynamic-element-directive";
@@ -174,9 +174,10 @@ class HaGallery extends LitElement {
const menuItem = this.shadowRoot!.querySelector(
`a[href="#${this._page}"]`
)!;
// Make sure section is expanded
if (menuItem.parentElement instanceof HTMLDetailsElement) {
menuItem.parentElement.open = true;
if (menuItem.parentElement instanceof HaExpansionPanel) {
menuItem.parentElement.expanded = true;
}
}
@@ -1,7 +1,9 @@
import { dump } from "js-yaml";
import { html, css, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import { customElement, property, state } from "lit/decorators";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-yaml-editor";
import { Action } from "../../../../src/data/script";
import { describeAction } from "../../../../src/data/script_i18n";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
@@ -103,16 +105,38 @@ const ACTIONS = [
},
];
const initialAction: Action = {
service: "light.turn_on",
target: {
entity_id: "light.kitchen",
},
};
@customElement("demo-automation-describe-action")
export class DemoAutomationDescribeAction extends LitElement {
@property({ attribute: false }) hass!: HomeAssistant;
@state() _action = initialAction;
protected render(): TemplateResult {
if (!this.hass) {
return html``;
}
return html`
<ha-card header="Actions">
<div class="action">
<span>
${this._action
? describeAction(this.hass, this._action)
: "<invalid YAML>"}
</span>
<ha-yaml-editor
label="Action Config"
.defaultValue=${initialAction}
@value-changed=${this._dataChanged}
></ha-yaml-editor>
</div>
${ACTIONS.map(
(conf) => html`
<div class="action">
@@ -132,6 +156,11 @@ export class DemoAutomationDescribeAction extends LitElement {
hass.addEntities(ENTITIES);
}
private _dataChanged(ev: CustomEvent): void {
ev.stopPropagation();
this._action = ev.detail.isValid ? ev.detail.value : undefined;
}
static get styles() {
return css`
ha-card {
@@ -147,6 +176,9 @@ export class DemoAutomationDescribeAction extends LitElement {
span {
margin-right: 16px;
}
ha-yaml-editor {
width: 50%;
}
`;
}
}
@@ -1,7 +1,9 @@
import { dump } from "js-yaml";
import { html, css, LitElement, TemplateResult } from "lit";
import { customElement } from "lit/decorators";
import { customElement, state } from "lit/decorators";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-yaml-editor";
import { Condition } from "../../../../src/data/automation";
import { describeCondition } from "../../../../src/data/automation_i18n";
const conditions = [
@@ -17,11 +19,32 @@ const conditions = [
{ condition: "template" },
];
const initialCondition: Condition = {
condition: "state",
entity_id: "light.kitchen",
state: "on",
};
@customElement("demo-automation-describe-condition")
export class DemoAutomationDescribeCondition extends LitElement {
@state() _condition = initialCondition;
protected render(): TemplateResult {
return html`
<ha-card header="Conditions">
<div class="condition">
<span>
${this._condition
? describeCondition(this._condition)
: "<invalid YAML>"}
</span>
<ha-yaml-editor
label="Condition Config"
.defaultValue=${initialCondition}
@value-changed=${this._dataChanged}
></ha-yaml-editor>
</div>
${conditions.map(
(conf) => html`
<div class="condition">
@@ -34,6 +57,11 @@ export class DemoAutomationDescribeCondition extends LitElement {
`;
}
private _dataChanged(ev: CustomEvent): void {
ev.stopPropagation();
this._condition = ev.detail.isValid ? ev.detail.value : undefined;
}
static get styles() {
return css`
ha-card {
@@ -49,6 +77,9 @@ export class DemoAutomationDescribeCondition extends LitElement {
span {
margin-right: 16px;
}
ha-yaml-editor {
width: 50%;
}
`;
}
}
@@ -1,7 +1,9 @@
import { dump } from "js-yaml";
import { html, css, LitElement, TemplateResult } from "lit";
import { customElement } from "lit/decorators";
import { customElement, state } from "lit/decorators";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-yaml-editor";
import { Trigger } from "../../../../src/data/automation";
import { describeTrigger } from "../../../../src/data/automation_i18n";
const triggers = [
@@ -20,11 +22,28 @@ const triggers = [
{ platform: "event" },
];
const initialTrigger: Trigger = {
platform: "state",
entity_id: "light.kitchen",
};
@customElement("demo-automation-describe-trigger")
export class DemoAutomationDescribeTrigger extends LitElement {
@state() _trigger = initialTrigger;
protected render(): TemplateResult {
return html`
<ha-card header="Triggers">
<div class="trigger">
<span>
${this._trigger ? describeTrigger(this._trigger) : "<invalid YAML>"}
</span>
<ha-yaml-editor
label="Trigger Config"
.defaultValue=${initialTrigger}
@value-changed=${this._dataChanged}
></ha-yaml-editor>
</div>
${triggers.map(
(conf) => html`
<div class="trigger">
@@ -37,6 +56,11 @@ export class DemoAutomationDescribeTrigger extends LitElement {
`;
}
private _dataChanged(ev: CustomEvent): void {
ev.stopPropagation();
this._trigger = ev.detail.isValid ? ev.detail.value : undefined;
}
static get styles() {
return css`
ha-card {
@@ -52,6 +76,9 @@ export class DemoAutomationDescribeTrigger extends LitElement {
span {
margin-right: 16px;
}
ha-yaml-editor {
width: 50%;
}
`;
}
}
@@ -0,0 +1,5 @@
---
title: Expansion Panel
---
Expansion panel following all the ARIA guidelines.
@@ -0,0 +1,157 @@
import { mdiPacMan } from "@mdi/js";
import { css, html, LitElement, TemplateResult } from "lit";
import { customElement } from "lit/decorators";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-expansion-panel";
import "../../../../src/components/ha-markdown";
import "../../components/demo-black-white-row";
import { LONG_TEXT } from "../../data/text";
const SHORT_TEXT = LONG_TEXT.substring(0, 113);
const SAMPLES: {
template: (slot: string, leftChevron: boolean) => TemplateResult;
}[] = [
{
template(slot, leftChevron) {
return html`
<ha-expansion-panel
slot=${slot}
.leftChevron=${leftChevron}
header="Attr header"
>
${SHORT_TEXT}
</ha-expansion-panel>
`;
},
},
{
template(slot, leftChevron) {
return html`
<ha-expansion-panel
slot=${slot}
.leftChevron=${leftChevron}
header="Attr header"
secondary="Attr secondary"
>
${SHORT_TEXT}
</ha-expansion-panel>
`;
},
},
{
template(slot, leftChevron) {
return html`
<ha-expansion-panel
slot=${slot}
.leftChevron=${leftChevron}
.header=${"Prop header"}
>
${SHORT_TEXT}
</ha-expansion-panel>
`;
},
},
{
template(slot, leftChevron) {
return html`
<ha-expansion-panel
slot=${slot}
.leftChevron=${leftChevron}
.header=${"Prop header"}
.secondary=${"Prop secondary"}
>
${SHORT_TEXT}
</ha-expansion-panel>
`;
},
},
{
template(slot, leftChevron) {
return html`
<ha-expansion-panel
slot=${slot}
.leftChevron=${leftChevron}
.header=${"Prop header"}
>
<span slot="secondary">Slot Secondary</span>
${SHORT_TEXT}
</ha-expansion-panel>
`;
},
},
{
template(slot, leftChevron) {
return html`
<ha-expansion-panel slot=${slot} .leftChevron=${leftChevron}>
<span slot="header">Slot header</span>
${SHORT_TEXT}
</ha-expansion-panel>
`;
},
},
{
template(slot, leftChevron) {
return html`
<ha-expansion-panel slot=${slot} .leftChevron=${leftChevron}>
<span slot="header">Slot header with actions</span>
<ha-icon-button
slot="icons"
label="Some Action"
.path=${mdiPacMan}
></ha-icon-button>
${SHORT_TEXT}
</ha-expansion-panel>
`;
},
},
{
template(slot, leftChevron) {
return html`
<ha-expansion-panel
slot=${slot}
.leftChevron=${leftChevron}
header="Attr Header with actions"
>
<ha-icon-button
slot="icons"
label="Some Action"
.path=${mdiPacMan}
></ha-icon-button>
${SHORT_TEXT}
</ha-expansion-panel>
`;
},
},
];
@customElement("demo-components-ha-expansion-panel")
export class DemoHaExpansionPanel extends LitElement {
protected render(): TemplateResult {
return html`
${SAMPLES.map(
(sample) => html`
<demo-black-white-row>
${["light", "dark"].map((slot) =>
sample.template(slot, slot === "dark")
)}
</demo-black-white-row>
`
)}
`;
}
static get styles() {
return css`
ha-expansion-panel {
margin: -16px;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"demo-components-ha-expansion-panel": DemoHaExpansionPanel;
}
}
+5
View File
@@ -136,6 +136,11 @@ const SCHEMAS: {
schema: [
{ name: "addon", selector: { addon: {} } },
{ name: "entity", selector: { entity: {} } },
{
name: "State",
selector: { state: { entity_id: "" } },
context: { filter_entity: "entity" },
},
{
name: "Attribute",
selector: { attribute: { entity_id: "" } },
@@ -115,6 +115,10 @@ const SCHEMAS: {
name: "One of each",
input: {
entity: { name: "Entity", selector: { entity: {} } },
state: {
name: "State",
selector: { state: { entity_id: "alarm_control_panel.alarm" } },
},
attribute: {
name: "Attribute",
selector: { attribute: { entity_id: "" } },
+2 -2
View File
@@ -31,7 +31,7 @@ const ENTITIES = [
friendly_name: "Office Light",
}),
getEntity("fan", "kitchen", "on", {
friendly_name: "Second Office Fan",
friendly_name: "Kitchen Fan",
}),
getEntity("binary_sensor", "kitchen_door", "on", {
friendly_name: "Office Door",
@@ -102,7 +102,7 @@ class DemoArea extends LitElement {
picture: "/images/office.jpg",
},
{
name: "Second Office",
name: "Kitchen",
area_id: "kitchen",
picture: "/images/kitchen.png",
},
@@ -1,11 +1,11 @@
---
title: Introduction
---
Lovelace has many different cards. Each card allows the user to tell
Dashboards have many different cards. Each card allows the user to tell
a different story about what is going on in their house. These cards
are very customizable, as no household is the same.
This gallery helps our developers and designers to see all the
different states that each card can be in.
Check [the Lovelace documentation](https://www.home-assistant.io/lovelace) for instructions on how to get started with Lovelace.
Check [the Dashboards documentation](https://www.home-assistant.io/dashboards/) for instructions on how to get started with Dashboards.
@@ -194,6 +194,7 @@ const createEntityRegistryEntries = (
name: null,
icon: null,
platform: "updater",
has_entity_name: false,
},
];
+1 -1
View File
@@ -69,7 +69,7 @@ const ENTITIES = [
effect_list: ["random", "colorloop"],
}),
getEntity("light", "color_RGB_light", "on", {
friendly_name: "Color Effets Light",
friendly_name: "Color Effects Light",
brightness: 255,
rgb_color: [30, 100, 255],
supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
@@ -6,10 +6,8 @@ import { atLeastVersion } from "../../../src/common/config/version";
import { navigate } from "../../../src/common/navigate";
import { caseInsensitiveStringCompare } from "../../../src/common/string/compare";
import "../../../src/components/ha-card";
import {
HassioAddonInfo,
HassioAddonRepository,
} from "../../../src/data/hassio/addon";
import { HassioAddonRepository } from "../../../src/data/hassio/addon";
import { StoreAddon } from "../../../src/data/supervisor/store";
import { Supervisor } from "../../../src/data/supervisor/supervisor";
import { HomeAssistant } from "../../../src/types";
import "../components/hassio-card-content";
@@ -23,20 +21,16 @@ class HassioAddonRepositoryEl extends LitElement {
@property({ attribute: false }) public repo!: HassioAddonRepository;
@property({ attribute: false }) public addons!: HassioAddonInfo[];
@property({ attribute: false }) public addons!: StoreAddon[];
@property() public filter!: string;
private _getAddons = memoizeOne(
(addons: HassioAddonInfo[], filter?: string) => {
if (filter) {
return filterAndSort(addons, filter);
}
return addons.sort((a, b) =>
caseInsensitiveStringCompare(a.name, b.name)
);
private _getAddons = memoizeOne((addons: StoreAddon[], filter?: string) => {
if (filter) {
return filterAndSort(addons, filter);
}
);
return addons.sort((a, b) => caseInsensitiveStringCompare(a.name, b.name));
});
protected render(): TemplateResult {
const repo = this.repo;
@@ -87,10 +81,10 @@ class HassioAddonRepositoryEl extends LitElement {
? this.supervisor.localize(
"common.new_version_available"
)
: this.supervisor.localize("addon.installed")
: this.supervisor.localize("addon.state.installed")
: addon.available
? this.supervisor.localize("addon.not_installed")
: this.supervisor.localize("addon.not_available")}
? this.supervisor.localize("addon.state.not_installed")
: this.supervisor.localize("addon.state.not_available")}
.iconClass=${addon.installed
? addon.update_available
? "update"
+6 -6
View File
@@ -14,15 +14,15 @@ 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/components/search-input";
import { extractSearchParam } from "../../../src/common/url/search-params";
import "../../../src/components/ha-button-menu";
import "../../../src/components/ha-icon-button";
import "../../../src/components/search-input";
import {
HassioAddonInfo,
HassioAddonRepository,
reloadHassioAddons,
} from "../../../src/data/hassio/addon";
import { StoreAddon } from "../../../src/data/supervisor/store";
import { Supervisor } from "../../../src/data/supervisor/supervisor";
import "../../../src/layouts/hass-loading-screen";
import "../../../src/layouts/hass-subpage";
@@ -66,10 +66,10 @@ class HassioAddonStore extends LitElement {
protected render(): TemplateResult {
let repos: TemplateResult[] = [];
if (this.supervisor.addon.repositories) {
if (this.supervisor.store.repositories) {
repos = this.addonRepositories(
this.supervisor.addon.repositories,
this.supervisor.addon.addons,
this.supervisor.store.repositories,
this.supervisor.store.addons,
this._filter
);
}
@@ -145,7 +145,7 @@ class HassioAddonStore extends LitElement {
private addonRepositories = memoizeOne(
(
repositories: HassioAddonRepository[],
addons: HassioAddonInfo[],
addons: StoreAddon[],
filter?: string
) =>
repositories.sort(sortRepos).map((repo) => {
@@ -336,7 +336,7 @@ class HassioAddonConfig extends LitElement {
fireEvent(this, "hass-api-called", eventdata);
} catch (err: any) {
this._error = this.supervisor.localize(
"addon.common.update_available",
"addon.failed_to_reset",
"error",
extractApiErrorMessage(err)
);
@@ -81,7 +81,7 @@ class HassioAddonDocumentationDashboard extends LitElement {
);
} catch (err: any) {
this._error = this.supervisor.localize(
"addon.documentation.get_logs",
"addon.documentation.get_documentation",
"error",
extractApiErrorMessage(err)
);
+23 -19
View File
@@ -12,15 +12,17 @@ import { navigate } from "../../../src/common/navigate";
import { extractSearchParam } from "../../../src/common/url/search-params";
import "../../../src/components/ha-circular-progress";
import {
fetchAddonInfo,
fetchHassioAddonInfo,
fetchHassioAddonsInfo,
HassioAddonDetails,
} from "../../../src/data/hassio/addon";
import { extractApiErrorMessage } from "../../../src/data/hassio/common";
import {
fetchHassioSupervisorInfo,
setSupervisorOption,
} from "../../../src/data/hassio/supervisor";
addStoreRepository,
fetchSupervisorStore,
StoreAddonDetails,
} from "../../../src/data/supervisor/store";
import { Supervisor } from "../../../src/data/supervisor/supervisor";
import { showConfirmationDialog } from "../../../src/dialogs/generic/show-dialog-box";
import "../../../src/layouts/hass-error-screen";
@@ -45,7 +47,9 @@ class HassioAddonDashboard extends LitElement {
@property({ attribute: false }) public route!: Route;
@property({ attribute: false }) public addon?: HassioAddonDetails;
@property({ attribute: false }) public addon?:
| HassioAddonDetails
| StoreAddonDetails;
@property({ type: Boolean }) public narrow!: boolean;
@@ -71,7 +75,7 @@ class HassioAddonDashboard extends LitElement {
></hass-error-screen>`;
}
if (!this.addon) {
if (!this.addon || !this.supervisor?.addon) {
return html`<hass-loading-screen></hass-loading-screen>`;
}
@@ -173,10 +177,10 @@ class HassioAddonDashboard extends LitElement {
const requestedAddon = extractSearchParam("addon");
const requestedAddonRepository = extractSearchParam("repository_url");
if (requestedAddonRepository) {
const supervisorInfo = await fetchHassioSupervisorInfo(this.hass);
const storeInfo = await fetchSupervisorStore(this.hass);
if (
!supervisorInfo.addons_repositories.find(
(repo) => repo === requestedAddonRepository
!storeInfo.repositories.find(
(repo) => repo.source === requestedAddonRepository
)
) {
if (
@@ -197,12 +201,7 @@ class HassioAddonDashboard extends LitElement {
}
try {
await setSupervisorOption(this.hass, {
addons_repositories: [
...supervisorInfo.addons_repositories,
requestedAddonRepository,
],
});
await addStoreRepository(this.hass, requestedAddonRepository);
} catch (err: any) {
this._error = extractApiErrorMessage(err);
}
@@ -210,8 +209,8 @@ class HassioAddonDashboard extends LitElement {
}
if (requestedAddon) {
const addonsInfo = await fetchHassioAddonsInfo(this.hass);
const validAddon = addonsInfo.addons.some(
const store = await fetchSupervisorStore(this.hass);
const validAddon = store.addons.some(
(addon) => addon.slug === requestedAddon
);
if (!validAddon) {
@@ -239,12 +238,14 @@ class HassioAddonDashboard extends LitElement {
if (["uninstall", "install", "update", "start", "stop"].includes(path)) {
fireEvent(this, "supervisor-collection-refresh", {
collection: "supervisor",
collection: "addon",
});
}
if (path === "uninstall") {
window.history.back();
} else if (path === "install") {
this.addon = await fetchHassioAddonInfo(this.hass, this.addon!.slug);
} else {
await this._routeDataChanged();
}
@@ -262,8 +263,11 @@ class HassioAddonDashboard extends LitElement {
return;
}
try {
const addoninfo = await fetchHassioAddonInfo(this.hass, addon);
this.addon = addoninfo;
if (!this.supervisor.addon) {
const addonsInfo = await fetchHassioAddonsInfo(this.hass);
fireEvent(this, "supervisor-update", { addon: addonsInfo });
}
this.addon = await fetchAddonInfo(this.hass, this.supervisor, addon);
} catch (err: any) {
this._error = `Error fetching addon info: ${extractApiErrorMessage(err)}`;
this.addon = undefined;
+4 -1
View File
@@ -1,5 +1,6 @@
import { customElement, property } from "lit/decorators";
import { HassioAddonDetails } from "../../../src/data/hassio/addon";
import { StoreAddonDetails } from "../../../src/data/supervisor/store";
import { Supervisor } from "../../../src/data/supervisor/supervisor";
import {
HassRouterPage,
@@ -20,7 +21,9 @@ class HassioAddonRouter extends HassRouterPage {
@property({ attribute: false }) public supervisor!: Supervisor;
@property({ attribute: false }) public addon!: HassioAddonDetails;
@property({ attribute: false }) public addon!:
| HassioAddonDetails
| StoreAddonDetails;
protected routerOptions: RouterOptions = {
defaultPage: "info",
+26 -16
View File
@@ -59,7 +59,10 @@ import {
fetchHassioStats,
HassioStats,
} from "../../../../src/data/hassio/common";
import { StoreAddon } from "../../../../src/data/supervisor/store";
import {
StoreAddon,
StoreAddonDetails,
} from "../../../../src/data/supervisor/store";
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
import {
showAlertDialog,
@@ -100,7 +103,9 @@ class HassioAddonInfo extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public addon!: HassioAddonDetails;
@property({ attribute: false }) public addon!:
| HassioAddonDetails
| StoreAddonDetails;
@property({ attribute: false }) public supervisor!: Supervisor;
@@ -143,7 +148,7 @@ class HassioAddonInfo extends LitElement {
></update-available-card>
`
: ""}
${!this.addon.protected
${"protected" in this.addon && !this.addon.protected
? html`
<ha-alert
alert-type="error"
@@ -518,7 +523,7 @@ class HassioAddonInfo extends LitElement {
: ""}
</div>
<div>
${this.addon.state === "started"
${this.addon.version && this.addon.state === "started"
? html`<ha-settings-row ?three-line=${this.narrow}>
<span slot="heading">
${this.supervisor.localize("addon.dashboard.hostname")}
@@ -669,7 +674,7 @@ class HassioAddonInfo extends LitElement {
}
private async _loadData(): Promise<void> {
if (this.addon.state === "started") {
if ("state" in this.addon && this.addon.state === "started") {
this._metrics = await fetchHassioStats(
this.hass,
`addons/${this.addon.slug}`
@@ -717,18 +722,22 @@ class HassioAddonInfo extends LitElement {
}
private get _computeIsRunning(): boolean {
return this.addon?.state === "started";
return (this.addon as HassioAddonDetails)?.state === "started";
}
private get _pathWebui(): string | null {
return (
this.addon.webui &&
this.addon.webui.replace("[HOST]", document.location.hostname)
return (this.addon as HassioAddonDetails).webui!.replace(
"[HOST]",
document.location.hostname
);
}
private get _computeShowWebUI(): boolean | "" | null {
return !this.addon.ingress && this.addon.webui && this._computeIsRunning;
return (
!this.addon.ingress &&
(this.addon as HassioAddonDetails).webui &&
this._computeIsRunning
);
}
private _openIngress(): void {
@@ -754,7 +763,8 @@ class HassioAddonInfo extends LitElement {
private async _startOnBootToggled(): Promise<void> {
this._error = undefined;
const data: HassioAddonSetOptionParams = {
boot: this.addon.boot === "auto" ? "manual" : "auto",
boot:
(this.addon as HassioAddonDetails).boot === "auto" ? "manual" : "auto",
};
try {
await setHassioAddonOption(this.hass, this.addon.slug, data);
@@ -776,7 +786,7 @@ class HassioAddonInfo extends LitElement {
private async _watchdogToggled(): Promise<void> {
this._error = undefined;
const data: HassioAddonSetOptionParams = {
watchdog: !this.addon.watchdog,
watchdog: !(this.addon as HassioAddonDetails).watchdog,
};
try {
await setHassioAddonOption(this.hass, this.addon.slug, data);
@@ -798,7 +808,7 @@ class HassioAddonInfo extends LitElement {
private async _autoUpdateToggled(): Promise<void> {
this._error = undefined;
const data: HassioAddonSetOptionParams = {
auto_update: !this.addon.auto_update,
auto_update: !(this.addon as HassioAddonDetails).auto_update,
};
try {
await setHassioAddonOption(this.hass, this.addon.slug, data);
@@ -820,7 +830,7 @@ class HassioAddonInfo extends LitElement {
private async _protectionToggled(): Promise<void> {
this._error = undefined;
const data: HassioAddonSetSecurityParams = {
protected: !this.addon.protected,
protected: !(this.addon as HassioAddonDetails).protected,
};
try {
await setHassioAddonSecurity(this.hass, this.addon.slug, data);
@@ -842,7 +852,7 @@ class HassioAddonInfo extends LitElement {
private async _panelToggled(): Promise<void> {
this._error = undefined;
const data: HassioAddonSetOptionParams = {
ingress_panel: !this.addon.ingress_panel,
ingress_panel: !(this.addon as HassioAddonDetails).ingress_panel,
};
try {
await setHassioAddonOption(this.hass, this.addon.slug, data);
@@ -870,7 +880,7 @@ class HassioAddonInfo extends LitElement {
showHassioMarkdownDialog(this, {
title: this.supervisor.localize("addon.dashboard.changelog"),
content: extractChangelog(this.addon, content),
content: extractChangelog(this.addon as HassioAddonDetails, content),
});
} catch (err: any) {
showAlertDialog(this, {
+4 -5
View File
@@ -98,9 +98,8 @@ export class HassioBackups extends LitElement {
if (backup.content.addons.length !== 0) {
for (const addon of backup.content.addons) {
content.push(
this.supervisor.supervisor.addons.find(
(entry) => entry.slug === addon
)?.name || addon
this.supervisor.addon.addons.find((entry) => entry.slug === addon)
?.name || addon
);
}
}
@@ -177,7 +176,7 @@ export class HassioBackups extends LitElement {
: supervisorTabs(this.hass)}
.hass=${this.hass}
.localizeFunc=${this.supervisor.localize}
.searchLabel=${this.supervisor.localize("search")}
.searchLabel=${this.supervisor.localize("backup.search")}
.noDataText=${this.supervisor.localize("backup.no_backups")}
.narrow=${this.narrow}
.route=${this.route}
@@ -241,7 +240,7 @@ export class HassioBackups extends LitElement {
: html`
<ha-icon-button
.label=${this.supervisor.localize(
"snapshot.delete_selected"
"backup.delete_selected"
)}
.path=${mdiDelete}
id="delete-btn"
@@ -1,8 +1,8 @@
import Fuse from "fuse.js";
import { HassioAddonInfo } from "../../../src/data/hassio/addon";
import { StoreAddon } from "../../../src/data/supervisor/store";
export function filterAndSort(addons: HassioAddonInfo[], filter: string) {
const options: Fuse.IFuseOptions<HassioAddonInfo> = {
export function filterAndSort(addons: StoreAddon[], filter: string) {
const options: Fuse.IFuseOptions<StoreAddon> = {
keys: ["name", "description", "slug"],
isCaseSensitive: false,
minMatchCharLength: 2,
@@ -96,7 +96,7 @@ export class SupervisorBackupContent extends LitElement {
: ["ssl", "share", "media", "addons/local"]
);
this.addons = _computeAddons(
this.backup ? this.backup.addons : this.supervisor?.supervisor.addons
this.backup ? this.backup.addons : this.supervisor?.addon.addons
);
this.backupType = this.backup?.type || "full";
this.backupName = this.backup?.name || "";
@@ -168,23 +168,24 @@ export class SupervisorBackupContent extends LitElement {
: ""}
${this.backupType === "partial"
? html`<div class="partial-picker">
<ha-formfield
.label=${html`<supervisor-formfield-label
label="Home Assistant"
.iconPath=${mdiHomeAssistant}
.version=${this.backup
? this.backup.homeassistant
: this.hass.config.version}
>
</supervisor-formfield-label>`}
>
<ha-checkbox
.checked=${this.homeAssistant}
@change=${this.toggleHomeAssistant}
>
</ha-checkbox>
</ha-formfield>
${this.backup?.homeassistant
? html`<ha-formfield
.label=${html`<supervisor-formfield-label
label="Home Assistant"
.iconPath=${mdiHomeAssistant}
.version=${this.backup
? this.backup.homeassistant
: this.hass.config.version}
>
</supervisor-formfield-label>`}
>
<ha-checkbox
.checked=${this.homeAssistant}
@change=${this.toggleHomeAssistant}
>
</ha-checkbox>
</ha-formfield>`
: ""}
${foldersSection?.templates.length
? html`
<ha-formfield
+2 -2
View File
@@ -24,7 +24,7 @@ class HassioAddons extends LitElement {
? html` <h1>${this.supervisor.localize("dashboard.addons")}</h1> `
: ""}
<div class="card-group">
${!this.supervisor.supervisor.addons?.length
${!this.supervisor.addon.addons.length
? html`
<ha-card outlined>
<div class="card-content">
@@ -34,7 +34,7 @@ class HassioAddons extends LitElement {
</div>
</ha-card>
`
: this.supervisor.supervisor.addons
: this.supervisor.addon.addons
.sort((a, b) => caseInsensitiveStringCompare(a.name, b.name))
.map(
(addon) => html`
@@ -201,26 +201,24 @@ class HassioBackupDialog
}
if (!this._dialogParams?.onboarding) {
this.hass!.callApi(
"POST",
try {
await this.hass!.callApi(
"POST",
`hassio/${
atLeastVersion(this.hass!.config.version, 2021, 9)
? "backups"
: "snapshots"
}/${this._backup!.slug}/restore/partial`,
backupDetails
).then(
() => {
this.closeDialog();
},
(error) => {
this._error = error.body.message;
}
);
`hassio/${
atLeastVersion(this.hass!.config.version, 2021, 9)
? "backups"
: "snapshots"
}/${this._backup!.slug}/restore/partial`,
backupDetails
);
this.closeDialog();
} catch (error: any) {
this._error = error.body.message;
}
} else {
fireEvent(this, "restoring");
fetch(`/api/hassio/backups/${this._backup!.slug}/restore/partial`, {
await fetch(`/api/hassio/backups/${this._backup!.slug}/restore/partial`, {
method: "POST",
body: JSON.stringify(backupDetails),
});
@@ -4,7 +4,7 @@ import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import { createCloseHeading } from "../../../../src/components/ha-dialog";
import "../../../../src/components/ha-form/ha-form";
import { HaFormSchema } from "../../../../src/components/ha-form/types";
import type { SchemaUnion } from "../../../../src/components/ha-form/types";
import "../../../../src/components/ha-icon-button";
import "../../../../src/components/ha-settings-row";
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
@@ -19,7 +19,7 @@ import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
import type { HomeAssistant } from "../../../../src/types";
import { RegistriesDialogParams } from "./show-dialog-registries";
const SCHEMA: HaFormSchema[] = [
const SCHEMA = [
{
name: "registry",
required: true,
@@ -35,7 +35,7 @@ const SCHEMA: HaFormSchema[] = [
required: true,
selector: { text: { type: "password" } },
},
];
] as const;
@customElement("dialog-hassio-registries")
class HassioRegistriesDialog extends LitElement {
@@ -135,8 +135,8 @@ class HassioRegistriesDialog extends LitElement {
`;
}
private _computeLabel = (schema: HaFormSchema) =>
this.supervisor.localize(`dialog.registries.${schema.name}`) || schema.name;
private _computeLabel = (schema: SchemaUnion<typeof SCHEMA>) =>
this.supervisor.localize(`dialog.registries.${schema.name}`);
private _valueChanged(ev: CustomEvent) {
this._input = ev.detail.value;
@@ -15,15 +15,18 @@ import "../../../../src/components/ha-circular-progress";
import { createCloseHeading } from "../../../../src/components/ha-dialog";
import "../../../../src/components/ha-icon-button";
import {
fetchHassioAddonsInfo,
HassioAddonInfo,
HassioAddonRepository,
} from "../../../../src/data/hassio/addon";
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
import { setSupervisorOption } from "../../../../src/data/hassio/supervisor";
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
import type { HomeAssistant } from "../../../../src/types";
import { HassioRepositoryDialogParams } from "./show-dialog-repositories";
import {
addStoreRepository,
fetchStoreRepositories,
removeStoreRepository,
} from "../../../../src/data/supervisor/store";
@customElement("dialog-hassio-repositories")
class HassioRepositoriesDialog extends LitElement {
@@ -58,7 +61,13 @@ class HassioRepositoriesDialog extends LitElement {
private _filteredRepositories = memoizeOne((repos: HassioAddonRepository[]) =>
repos
.filter((repo) => repo.slug !== "core" && repo.slug !== "local")
.filter(
(repo) =>
repo.slug !== "core" && // The core add-ons repository
repo.slug !== "local" && // Locally managed add-ons
repo.slug !== "a0d7b954" && // Home Assistant Community Add-ons
repo.slug !== "5c53de3b" // The ESPHome repository
)
.sort((a, b) => caseInsensitiveStringCompare(a.name, b.name))
);
@@ -78,7 +87,7 @@ class HassioRepositoriesDialog extends LitElement {
const repositories = this._filteredRepositories(this._repositories);
const usedRepositories = this._filteredUsedRepositories(
repositories,
this._dialogParams.supervisor.supervisor.addons
this._dialogParams.supervisor.addon.addons
);
return html`
<ha-dialog
@@ -215,9 +224,7 @@ class HassioRepositoriesDialog extends LitElement {
private async _loadData(): Promise<void> {
try {
const addonsinfo = await fetchHassioAddonsInfo(this.hass);
this._repositories = addonsinfo.repositories;
this._repositories = await fetchStoreRepositories(this.hass);
fireEvent(this, "supervisor-collection-refresh", { collection: "addon" });
} catch (err: any) {
@@ -231,14 +238,9 @@ class HassioRepositoriesDialog extends LitElement {
return;
}
this._processing = true;
const repositories = this._filteredRepositories(this._repositories!);
const newRepositories = repositories.map((repo) => repo.source);
newRepositories.push(input.value);
try {
await setSupervisorOption(this.hass, {
addons_repositories: newRepositories,
});
await addStoreRepository(this.hass, input.value);
await this._loadData();
input.value = "";
@@ -250,19 +252,8 @@ class HassioRepositoriesDialog extends LitElement {
private async _removeRepository(ev: Event) {
const slug = (ev.currentTarget as any).slug;
const repositories = this._filteredRepositories(this._repositories!);
const repository = repositories.find((repo) => repo.slug === slug);
if (!repository) {
return;
}
const newRepositories = repositories
.map((repo) => repo.source)
.filter((repo) => repo !== repository.source);
try {
await setSupervisorOption(this.hass, {
addons_repositories: newRepositories,
});
await removeStoreRepository(this.hass, slug);
await this._loadData();
} catch (err: any) {
this._error = extractApiErrorMessage(err);
+8 -3
View File
@@ -22,6 +22,7 @@ import {
Supervisor,
SupervisorObject,
supervisorCollection,
SupervisorKeys,
} from "../../src/data/supervisor/supervisor";
import { ProvideHassLitMixin } from "../../src/mixins/provide-hass-lit-mixin";
import { urlSyncMixin } from "../../src/state/url-sync-mixin";
@@ -124,9 +125,13 @@ export class SupervisorBaseElement extends urlSyncMixin(
this.supervisor = {
...this.supervisor,
localize: await computeLocalize(this.constructor.prototype, language, {
[language]: data,
}),
localize: await computeLocalize<SupervisorKeys>(
this.constructor.prototype,
language,
{
[language]: data,
}
),
};
}
+1 -1
View File
@@ -26,7 +26,7 @@ import {
import {
UNHEALTHY_REASON_URL,
UNSUPPORTED_REASON_URL,
} from "../../../src/panels/config/system-health/ha-config-system-health";
} from "../../../src/panels/config/repairs/dialog-system-information";
import { haStyle } from "../../../src/resources/styles";
import { HomeAssistant } from "../../../src/types";
import { bytesToString } from "../../../src/util/bytes-to-string";
+9 -2
View File
@@ -1,4 +1,11 @@
module.exports = {
"*.{js,ts}": 'eslint --ignore-pattern "**/build-scripts/**/*.js" --fix',
"!(/translations)*.{js,ts,json,css,md,html}": "prettier --write",
"*.{js,ts}": [
"prettier --write",
'eslint --ignore-pattern "**/build-scripts/**/*.js" --fix',
],
"!(/translations)*.{json,css,md,html}": "prettier --write",
"translations/*/*.json": (files) =>
'printf "%s\n" "These files should not be modified. Instead, make the necessary modifications in src/translations/en.json. Please see translations/README.md for details." ' +
files.join(" ") +
" >&2 && exit 1",
};
+12 -12
View File
@@ -16,6 +16,9 @@
"lint:lit": "lit-analyzer \"**/src/**/*.ts\" --format markdown --outFile result.md",
"lint": "yarn run lint:eslint && yarn run lint:prettier && yarn run lint:types",
"format": "yarn run format:eslint && yarn run format:prettier",
"postinstall": "husky install",
"prepack": "pinst --disable",
"postpack": "pinst --enable",
"test": "instant-mocha --webpack-config ./test/webpack.config.js --require ./test/setup.js \"test/**/*.ts\""
},
"author": "Paulus Schoutsen <Paulus@PaulusSchoutsen.nl> (http://paulusschoutsen.nl)",
@@ -46,6 +49,7 @@
"@fullcalendar/daygrid": "5.9.0",
"@fullcalendar/interaction": "5.9.0",
"@fullcalendar/list": "5.9.0",
"@fullcalendar/timegrid": "5.9.0",
"@lit-labs/motion": "^1.0.2",
"@lit-labs/virtualizer": "patch:@lit-labs/virtualizer@0.7.0-pre.2#./.yarn/patches/@lit-labs/virtualizer/event-target-shim.patch",
"@material/chips": "14.0.0-canary.261f2db59.0",
@@ -72,8 +76,8 @@
"@material/mwc-textfield": "0.25.3",
"@material/mwc-top-app-bar-fixed": "^0.25.3",
"@material/top-app-bar": "14.0.0-canary.261f2db59.0",
"@mdi/js": "6.7.96",
"@mdi/svg": "6.7.96",
"@mdi/js": "7.0.96",
"@mdi/svg": "7.0.96",
"@polymer/app-layout": "^3.1.0",
"@polymer/iron-flex-layout": "^3.0.1",
"@polymer/iron-icon": "^3.0.1",
@@ -89,8 +93,8 @@
"@polymer/paper-tooltip": "^3.0.1",
"@polymer/polymer": "3.4.1",
"@thomasloven/round-slider": "0.5.4",
"@vaadin/combo-box": "^23.0.10",
"@vaadin/vaadin-themable-mixin": "^23.0.10",
"@vaadin/combo-box": "^23.1.5",
"@vaadin/vaadin-themable-mixin": "^23.1.5",
"@vibrant/color": "^3.2.1-alpha.1",
"@vibrant/core": "^3.2.1-alpha.1",
"@vibrant/quantizer-mmcq": "^3.2.1-alpha.1",
@@ -107,7 +111,7 @@
"deep-freeze": "^0.0.1",
"fuse.js": "^6.0.0",
"google-timezones-json": "^1.0.2",
"hls.js": "^1.1.5",
"hls.js": "^1.2.0",
"home-assistant-js-websocket": "^7.1.0",
"idb-keyval": "^5.1.3",
"intl-messageformat": "^9.9.1",
@@ -202,9 +206,9 @@
"gulp-rename": "^2.0.0",
"gulp-zopfli-green": "^3.0.1",
"html-minifier": "^4.0.0",
"husky": "^1.3.1",
"husky": "^8.0.1",
"instant-mocha": "^1.3.1",
"lint-staged": "^11.1.2",
"lint-staged": "^13.0.3",
"lit-analyzer": "^1.2.1",
"lodash.template": "^4.5.0",
"magic-string": "^0.25.7",
@@ -213,6 +217,7 @@
"mocha": "^8.4.0",
"object-hash": "^2.0.3",
"open": "^7.0.4",
"pinst": "^3.0.0",
"prettier": "^2.4.1",
"require-dir": "^1.2.0",
"rollup": "^2.8.2",
@@ -245,11 +250,6 @@
"@lit/reactive-element": "1.2.1"
},
"main": "src/home-assistant.js",
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"prettier": {
"trailingComma": "es5",
"arrowParens": "always"
+1 -6
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "home-assistant-frontend"
version = "20220526.0"
version = "20220816.0"
license = {text = "Apache-2.0"}
description = "The Home Assistant frontend"
readme = "README.md"
@@ -23,8 +23,3 @@ include-package-data = true
[tool.setuptools.packages.find]
include = ["hass_frontend*"]
[tool.mypy]
python_version = 3.4
show_error_codes = true
strict = true
+10 -1
View File
@@ -24,10 +24,15 @@ function auto(version) {
return patch(version);
}
function nightly() {
return `${today()}.dev`;
}
const methods = {
patch,
today,
auto,
nightly,
};
async function main(args) {
@@ -57,7 +62,11 @@ async function main(args) {
console.log("Current version:", version);
console.log("New version:", newVersion);
fs.writeFileSync("pyproject.toml", setup.replace(version, newVersion), "utf-8");
fs.writeFileSync(
"pyproject.toml",
setup.replace(version, newVersion),
"utf-8"
);
if (!commit) {
return;
+2
View File
@@ -0,0 +1,2 @@
# Setuptools v62.3 doesn't support editable installs with just 'pyproject.toml' (PEP 660).
# Keep this file until it does!
+7 -2
View File
@@ -314,7 +314,8 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
}
private _computeStepDescription(step: DataEntryFlowStepForm) {
const resourceKey = `ui.panel.page-authorize.form.providers.${step.handler[0]}.step.${step.step_id}.description`;
const resourceKey =
`ui.panel.page-authorize.form.providers.${step.handler[0]}.step.${step.step_id}.description` as const;
const args: string[] = [];
const placeholders = step.description_placeholders || {};
Object.keys(placeholders).forEach((key) => {
@@ -359,7 +360,11 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
}
this._submitting = true;
const postData = { ...this._stepData, client_id: this.clientId };
const postData = {
...this._stepData,
client_id: this.clientId,
redirect_uri: this.redirectUri,
};
try {
const response = await fetch(`/auth/login_flow/${this._step.flow_id}`, {
+3 -2
View File
@@ -47,7 +47,7 @@ import {
mdiRobotVacuum,
mdiScriptText,
mdiSineWave,
mdiTextToSpeech,
mdiMicrophoneMessage,
mdiThermometer,
mdiThermostat,
mdiTimerOutline,
@@ -74,8 +74,9 @@ export const FIXED_DOMAIN_ICONS = {
camera: mdiVideo,
climate: mdiThermostat,
configurator: mdiCog,
conversation: mdiTextToSpeech,
conversation: mdiMicrophoneMessage,
counter: mdiCounter,
demo: mdiHomeAssistant,
fan: mdiFan,
google_assistant: mdiGoogleAssistant,
group: mdiGoogleCirclesCommunities,
+14 -1
View File
@@ -1,7 +1,7 @@
import memoizeOne from "memoize-one";
import { FrontendLocaleData } from "../../data/translation";
import { useAmPm } from "./use_am_pm";
import { polyfillsLoaded } from "../translations/localize";
import { useAmPm } from "./use_am_pm";
if (__BUILD__ === "latest" && polyfillsLoaded) {
await polyfillsLoaded;
@@ -64,3 +64,16 @@ const formatTimeWeekdayMem = memoizeOne(
}
)
);
// 21:15
export const formatTime24h = (dateObj: Date) =>
formatTime24hMem().format(dateObj);
const formatTime24hMem = memoizeOne(
() =>
new Intl.DateTimeFormat(undefined, {
hour: "numeric",
minute: "2-digit",
hour12: false,
})
);
+5 -1
View File
@@ -76,7 +76,11 @@ class Storage {
public setValue(storageKey: string, value: any): any {
this._storage[storageKey] = value;
try {
window.localStorage.setItem(storageKey, JSON.stringify(value));
if (value === undefined) {
window.localStorage.removeItem(storageKey);
} else {
window.localStorage.setItem(storageKey, JSON.stringify(value));
}
} catch (err: any) {
// Safari in private mode doesn't allow localstorage
}
+8 -13
View File
@@ -5,8 +5,7 @@ export type LeafletModuleType = typeof import("leaflet");
export type LeafletDrawModuleType = typeof import("leaflet-draw");
export const setupLeafletMap = async (
mapElement: HTMLElement,
darkMode?: boolean
mapElement: HTMLElement
): Promise<[Map, LeafletModuleType, TileLayer]> => {
if (!mapElement.parentNode) {
throw new Error("Cannot setup Leaflet map on disconnected element");
@@ -23,7 +22,7 @@ export const setupLeafletMap = async (
mapElement.parentNode.appendChild(style);
map.setView([52.3731339, 4.8903147], 13);
const tileLayer = createTileLayer(Leaflet, Boolean(darkMode)).addTo(map);
const tileLayer = createTileLayer(Leaflet).addTo(map);
return [map, Leaflet, tileLayer];
};
@@ -31,23 +30,19 @@ export const setupLeafletMap = async (
export const replaceTileLayer = (
leaflet: LeafletModuleType,
map: Map,
tileLayer: TileLayer,
darkMode: boolean
tileLayer: TileLayer
): TileLayer => {
map.removeLayer(tileLayer);
tileLayer = createTileLayer(leaflet, darkMode);
tileLayer = createTileLayer(leaflet);
tileLayer.addTo(map);
return tileLayer;
};
const createTileLayer = (
leaflet: LeafletModuleType,
darkMode: boolean
): TileLayer =>
const createTileLayer = (leaflet: LeafletModuleType): TileLayer =>
leaflet.tileLayer(
`https://{s}.basemaps.cartocdn.com/${
darkMode ? "dark_all" : "light_all"
}/{z}/{x}/{y}${leaflet.Browser.retina ? "@2x.png" : ".png"}`,
`https://basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}${
leaflet.Browser.retina ? "@2x.png" : ".png"
}`,
{
attribution:
'&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>, &copy; <a href="https://carto.com/attributions">CARTO</a>',
+51 -1
View File
@@ -8,6 +8,8 @@ import {
mdiCalendar,
mdiCast,
mdiCastConnected,
mdiCastOff,
mdiChartSankey,
mdiCheckCircleOutline,
mdiClock,
mdiCloseCircleOutline,
@@ -24,6 +26,15 @@ import {
mdiPowerPlug,
mdiPowerPlugOff,
mdiRestart,
mdiSpeaker,
mdiSpeakerOff,
mdiSpeakerPause,
mdiSpeakerPlay,
mdiSwapHorizontal,
mdiTelevision,
mdiTelevisionOff,
mdiTelevisionPause,
mdiTelevisionPlay,
mdiToggleSwitchVariant,
mdiToggleSwitchVariantOff,
mdiWeatherNight,
@@ -125,7 +136,40 @@ export const domainIconWithoutDefault = (
}
case "media_player":
return compareState === "playing" ? mdiCastConnected : mdiCast;
switch (stateObj?.attributes.device_class) {
case "speaker":
switch (compareState) {
case "playing":
return mdiSpeakerPlay;
case "paused":
return mdiSpeakerPause;
case "off":
return mdiSpeakerOff;
default:
return mdiSpeaker;
}
case "tv":
switch (compareState) {
case "playing":
return mdiTelevisionPlay;
case "paused":
return mdiTelevisionPause;
case "off":
return mdiTelevisionOff;
default:
return mdiTelevision;
}
default:
switch (compareState) {
case "playing":
case "paused":
return mdiCastConnected;
case "off":
return mdiCastOff;
default:
return mdiCast;
}
}
case "switch":
switch (stateObj?.attributes.device_class) {
@@ -153,6 +197,12 @@ export const domainIconWithoutDefault = (
? FIXED_DOMAIN_ICONS[domain]
: mdiWeatherNight;
case "switch_as_x":
return mdiSwapHorizontal;
case "threshold":
return mdiChartSankey;
case "update":
return compareState === "on"
? updateIsInstalling(stateObj as UpdateEntity)
+89
View File
@@ -0,0 +1,89 @@
import { HassEntity } from "home-assistant-js-websocket";
import { computeStateDomain } from "./compute_state_domain";
import { UNAVAILABLE_STATES } from "../../data/entity";
const FIXED_DOMAIN_STATES = {
alarm_control_panel: [
"armed_away",
"armed_custom_bypass",
"armed_home",
"armed_night",
"armed_vacation",
"arming",
"disarmed",
"disarming",
"pending",
"triggered",
],
automation: ["on", "off"],
binary_sensor: ["on", "off"],
button: [],
calendar: ["on", "off"],
camera: ["idle", "recording", "streaming"],
cover: ["closed", "closing", "open", "opening"],
device_tracker: ["home", "not_home"],
fan: ["on", "off"],
humidifier: ["on", "off"],
input_boolean: ["on", "off"],
input_button: [],
light: ["on", "off"],
lock: ["jammed", "locked", "locking", "unlocked", "unlocking"],
media_player: ["idle", "off", "paused", "playing", "standby"],
person: ["home", "not_home"],
remote: ["on", "off"],
scene: [],
schedule: ["on", "off"],
script: ["on", "off"],
siren: ["on", "off"],
sun: ["above_horizon", "below_horizon"],
switch: ["on", "off"],
update: ["on", "off"],
vacuum: ["cleaning", "docked", "error", "idle", "paused", "returning"],
weather: [
"clear-night",
"cloudy",
"exceptional",
"fog",
"hail",
"lightning-rainy",
"lightning",
"partlycloudy",
"pouring",
"rainy",
"snowy-rainy",
"snowy",
"sunny",
"windy-variant",
"windy",
],
};
export const getStates = (state: HassEntity): string[] => {
const domain = computeStateDomain(state);
const result: string[] = [];
if (domain in FIXED_DOMAIN_STATES) {
result.push(...FIXED_DOMAIN_STATES[domain]);
} else {
// If not fixed, we at least know the current state
result.push(state.state);
}
// Dynamic values based on the entities
switch (domain) {
case "climate":
result.push(...state.attributes.hvac_modes);
break;
case "input_select":
case "select":
result.push(...state.attributes.options);
break;
case "water_heater":
result.push(...state.attributes.operation_list);
break;
}
// All entities can have unavailable states
result.push(...UNAVAILABLE_STATES);
return [...new Set(result)];
};
@@ -0,0 +1,88 @@
import { html } from "lit";
import { getConfigEntries } from "../../data/config_entries";
import { showConfirmationDialog } from "../../dialogs/generic/show-dialog-box";
import { showZWaveJSAddNodeDialog } from "../../panels/config/integrations/integration-panels/zwave_js/show-dialog-zwave_js-add-node";
import type { HomeAssistant } from "../../types";
import { documentationUrl } from "../../util/documentation-url";
import { isComponentLoaded } from "../config/is_component_loaded";
import { fireEvent } from "../dom/fire_event";
import { navigate } from "../navigate";
export const protocolIntegrationPicked = async (
element: HTMLElement,
hass: HomeAssistant,
slug: string
) => {
if (slug === "zwave_js") {
const entries = await getConfigEntries(hass, {
domain: "zwave_js",
});
if (!entries.length) {
// If the component isn't loaded, ask them to load the integration first
showConfirmationDialog(element, {
text: hass.localize(
"ui.panel.config.integrations.config_flow.missing_zwave_zigbee",
{
integration: "Z-Wave",
supported_hardware_link: html`<a
href=${documentationUrl(hass, "/docs/z-wave/controllers")}
target="_blank"
rel="noreferrer"
>${hass.localize(
"ui.panel.config.integrations.config_flow.supported_hardware"
)}</a
>`,
}
),
confirmText: hass.localize(
"ui.panel.config.integrations.config_flow.proceed"
),
confirm: () => {
fireEvent(element, "handler-picked", {
handler: "zwave_js",
});
},
});
return;
}
showZWaveJSAddNodeDialog(element, {
entry_id: entries[0].entry_id,
});
} else if (slug === "zha") {
// If the component isn't loaded, ask them to load the integration first
if (!isComponentLoaded(hass, "zha")) {
showConfirmationDialog(element, {
text: hass.localize(
"ui.panel.config.integrations.config_flow.missing_zwave_zigbee",
{
integration: "Zigbee",
supported_hardware_link: html`<a
href=${documentationUrl(
hass,
"/integrations/zha/#known-working-zigbee-radio-modules"
)}
target="_blank"
rel="noreferrer"
>${hass.localize(
"ui.panel.config.integrations.config_flow.supported_hardware"
)}</a
>`,
}
),
confirmText: hass.localize(
"ui.panel.config.integrations.config_flow.proceed"
),
confirm: () => {
fireEvent(element, "handler-picked", {
handler: "zha",
});
},
});
return;
}
navigate("/config/zha/add");
}
};
+1 -1
View File
@@ -5,6 +5,6 @@ export const clamp = (value: number, min: number, max: number) =>
export const conditionalClamp = (value: number, min?: number, max?: number) => {
let result: number;
result = min ? Math.max(value, min) : value;
result = max ? Math.min(value, max) : value;
result = max ? Math.min(result, max) : result;
return result;
};
+3 -1
View File
@@ -70,7 +70,9 @@ export const iconColorCSS = css`
}
}
ha-state-icon[data-domain="plant"][data-state="problem"],
ha-state-icon[data-domain="plant"][data-state="problem"] {
color: var(--state-icon-error-color);
}
/* Color the icon if unavailable */
ha-state-icon[data-state="unavailable"] {
+30 -4
View File
@@ -3,10 +3,36 @@ import { shouldPolyfill as shouldPolyfillPluralRules } from "@formatjs/intl-plur
import { shouldPolyfill as shouldPolyfillRelativeTime } from "@formatjs/intl-relativetimeformat/lib/should-polyfill";
import { shouldPolyfill as shouldPolyfillDateTime } from "@formatjs/intl-datetimeformat/lib/should-polyfill";
import IntlMessageFormat from "intl-messageformat";
import { Resources } from "../../types";
import { Resources, TranslationDict } from "../../types";
import { getLocalLanguage } from "../../util/common-translation";
export type LocalizeFunc = (key: string, ...args: any[]) => string;
// Exclude some patterns from key type checking for now
// These are intended to be removed as errors are fixed
// Fixing component category will require tighter definition of types from backend and/or web socket
export type LocalizeKeys =
| FlattenObjectKeys<Omit<TranslationDict, "supervisor">>
| `panel.${string}`
| `state.${string}`
| `state_attributes.${string}`
| `state_badge.${string}`
| `ui.${string}`
| `component.${string}`;
// Tweaked from https://www.raygesualdo.com/posts/flattening-object-keys-with-typescript-types
export type FlattenObjectKeys<
T extends Record<string, any>,
Key extends keyof T = keyof T
> = Key extends string
? T[Key] extends Record<string, unknown>
? `${Key}.${FlattenObjectKeys<T[Key]>}`
: `${Key}`
: never;
export type LocalizeFunc<Keys extends string = LocalizeKeys> = (
key: Keys,
...args: any[]
) => string;
interface FormatType {
[format: string]: any;
}
@@ -65,12 +91,12 @@ export const polyfillsLoaded =
* }
*/
export const computeLocalize = async (
export const computeLocalize = async <Keys extends string = LocalizeKeys>(
cache: any,
language: string,
resources: Resources,
formats?: FormatsType
): Promise<LocalizeFunc> => {
): Promise<LocalizeFunc<Keys>> => {
if (polyfillsLoaded) {
await polyfillsLoaded;
}
+19
View File
@@ -1,3 +1,4 @@
import { LitElement } from "lit";
import { HomeAssistant } from "../../types";
export function computeRTL(hass: HomeAssistant) {
@@ -15,3 +16,21 @@ export function computeRTLDirection(hass: HomeAssistant) {
export function emitRTLDirection(rtl: boolean) {
return rtl ? "rtl" : "ltr";
}
export function computeDirectionStyles(isRTL: boolean, element: LitElement) {
const direction: string = emitRTLDirection(isRTL);
setDirectionStyles(direction, element);
}
export function setDirectionStyles(direction: string, element: LitElement) {
element.style.direction = direction;
element.style.setProperty("--direction", direction);
element.style.setProperty(
"--float-start",
direction === "ltr" ? "left" : "right"
);
element.style.setProperty(
"--float-end",
direction === "ltr" ? "right" : "left"
);
}
+33
View File
@@ -11,6 +11,8 @@ import { classMap } from "lit/directives/class-map";
import { styleMap } from "lit/directives/style-map";
import { clamp } from "../../common/number/clamp";
export const MIN_TIME_BETWEEN_UPDATES = 60 * 5 * 1000;
interface Tooltip extends TooltipModel<any> {
top: string;
left: string;
@@ -37,6 +39,26 @@ export default class HaChartBase extends LitElement {
@state() private _hiddenDatasets: Set<number> = new Set();
private _releaseCanvas() {
// release the canvas memory to prevent
// safari from running out of memory.
if (this.chart) {
this.chart.destroy();
}
}
public disconnectedCallback() {
this._releaseCanvas();
super.disconnectedCallback();
}
public connectedCallback() {
super.connectedCallback();
if (this.hasUpdated) {
this._setupChart();
}
}
protected firstUpdated() {
this._setupChart();
this.data.datasets.forEach((dataset, index) => {
@@ -166,6 +188,10 @@ export default class HaChartBase extends LitElement {
ChartConstructor.defaults.color = computedStyles.getPropertyValue(
"--secondary-text-color"
);
ChartConstructor.defaults.font.family =
computedStyles.getPropertyValue("--mdc-typography-body1-font-family") ||
computedStyles.getPropertyValue("--mdc-typography-font-family") ||
"Roboto, Noto, sans-serif";
this.chart = new ChartConstructor(ctx, {
type: this.chartType,
@@ -304,6 +330,9 @@ export default class HaChartBase extends LitElement {
width: 16px;
flex-shrink: 0;
box-sizing: border-box;
margin-inline-end: 6px;
margin-inline-start: initial;
direction: var(--direction);
}
.chartTooltip .bullet {
align-self: baseline;
@@ -312,6 +341,9 @@ export default class HaChartBase extends LitElement {
:host([rtl]) .chartTooltip .bullet {
margin-right: inherit;
margin-left: 6px;
margin-inline-end: inherit;
margin-inline-start: 6px;
direction: var(--direction);
}
.chartTooltip {
padding: 8px;
@@ -348,6 +380,7 @@ export default class HaChartBase extends LitElement {
.chartTooltip .title {
text-align: center;
font-weight: 500;
direction: ltr;
}
.chartTooltip .footer {
font-weight: 500;
@@ -8,7 +8,7 @@ import {
} from "../../common/number/format_number";
import { LineChartEntity, LineChartState } from "../../data/history";
import { HomeAssistant } from "../../types";
import "./ha-chart-base";
import { MIN_TIME_BETWEEN_UPDATES } from "./ha-chart-base";
const safeParseFloat = (value) => {
const parsed = parseFloat(value);
@@ -28,11 +28,13 @@ class StateHistoryChartLine extends LitElement {
@property({ type: Boolean }) public isSingleDevice = false;
@property({ attribute: false }) public endTime?: Date;
@property({ attribute: false }) public endTime!: Date;
@state() private _chartData?: ChartData<"line">;
@state() private _chartOptions?: ChartOptions<"line">;
@state() private _chartOptions?: ChartOptions;
private _chartTime: Date = new Date();
protected render() {
return html`
@@ -57,6 +59,7 @@ class StateHistoryChartLine extends LitElement {
locale: this.hass.locale,
},
},
suggestedMax: this.endTime,
ticks: {
maxRotation: 0,
sampleSize: 5,
@@ -120,7 +123,13 @@ class StateHistoryChartLine extends LitElement {
locale: numberFormatToLocale(this.hass.locale),
};
}
if (changedProps.has("data")) {
if (
changedProps.has("data") ||
this._chartTime <
new Date(this.endTime.getTime() - MIN_TIME_BETWEEN_UPDATES)
) {
// If the line is more than 5 minutes old, re-gen it
// so the X axis grows even if there is no new data
this._generateData();
}
}
@@ -130,28 +139,12 @@ class StateHistoryChartLine extends LitElement {
const computedStyles = getComputedStyle(this);
const entityStates = this.data;
const datasets: ChartDataset<"line">[] = [];
let endTime: Date;
if (entityStates.length === 0) {
return;
}
endTime =
this.endTime ||
// Get the highest date from the last date of each device
new Date(
Math.max(
...entityStates.map((devSts) =>
new Date(
devSts.states[devSts.states.length - 1].last_changed
).getTime()
)
)
);
if (endTime > new Date()) {
endTime = new Date();
}
this._chartTime = new Date();
const endTime = this.endTime;
const names = this.names || {};
entityStates.forEach((states) => {
const domain = states.domain;
@@ -9,7 +9,7 @@ import { numberFormatToLocale } from "../../common/number/format_number";
import { computeRTL } from "../../common/util/compute_rtl";
import { TimelineEntity } from "../../data/history";
import { HomeAssistant } from "../../types";
import "./ha-chart-base";
import { MIN_TIME_BETWEEN_UPDATES } from "./ha-chart-base";
import type { TimeLineData } from "./timeline-chart/const";
/** Binary sensor device classes for which the static colors for on/off are NOT inverted.
@@ -83,6 +83,8 @@ export class StateHistoryChartTimeline extends LitElement {
@property({ attribute: false }) public data: TimelineEntity[] = [];
@property() public narrow!: boolean;
@property() public names: boolean | Record<string, string> = false;
@property() public unit?: string;
@@ -91,12 +93,18 @@ export class StateHistoryChartTimeline extends LitElement {
@property({ type: Boolean }) public isSingleDevice = false;
@property({ attribute: false }) public endTime?: Date;
@property({ type: Boolean }) public chunked = false;
@property({ attribute: false }) public startTime!: Date;
@property({ attribute: false }) public endTime!: Date;
@state() private _chartData?: ChartData<"timeline">;
@state() private _chartOptions?: ChartOptions<"timeline">;
private _chartTime: Date = new Date();
protected render() {
return html`
<ha-chart-base
@@ -110,6 +118,7 @@ export class StateHistoryChartTimeline extends LitElement {
public willUpdate(changedProps: PropertyValues) {
if (!this.hasUpdated) {
const narrow = this.narrow;
this._chartOptions = {
maintainAspectRatio: false,
parsing: false,
@@ -123,6 +132,8 @@ export class StateHistoryChartTimeline extends LitElement {
locale: this.hass.locale,
},
},
suggestedMin: this.startTime,
suggestedMax: this.endTime,
ticks: {
autoSkip: true,
maxRotation: 0,
@@ -153,11 +164,18 @@ export class StateHistoryChartTimeline extends LitElement {
drawTicks: false,
},
ticks: {
display: this.data.length !== 1,
display:
this.chunked || !this.isSingleDevice || this.data.length !== 1,
},
afterSetDimensions: (y) => {
y.maxWidth = y.chart.width * 0.18;
},
afterFit: (scaleInstance) => {
if (this.chunked) {
// ensure all the chart labels are the same width
scaleInstance.width = narrow ? 105 : 185;
}
},
position: computeRTL(this.hass) ? "right" : "left",
},
},
@@ -195,7 +213,13 @@ export class StateHistoryChartTimeline extends LitElement {
locale: numberFormatToLocale(this.hass.locale),
};
}
if (changedProps.has("data")) {
if (
changedProps.has("data") ||
this._chartTime <
new Date(this.endTime.getTime() - MIN_TIME_BETWEEN_UPDATES)
) {
// If the line is more than 5 minutes old, re-gen it
// so the X axis grows even if there is no new data
this._generateData();
}
}
@@ -208,34 +232,9 @@ export class StateHistoryChartTimeline extends LitElement {
stateHistory = [];
}
const startTime = new Date(
stateHistory.reduce(
(minTime, stateInfo) =>
Math.min(minTime, new Date(stateInfo.data[0].last_changed).getTime()),
new Date().getTime()
)
);
// end time is Math.max(startTime, last_event)
let endTime =
this.endTime ||
new Date(
stateHistory.reduce(
(maxTime, stateInfo) =>
Math.max(
maxTime,
new Date(
stateInfo.data[stateInfo.data.length - 1].last_changed
).getTime()
),
startTime.getTime()
)
);
if (endTime > new Date()) {
endTime = new Date();
}
this._chartTime = new Date();
const startTime = this.startTime;
const endTime = this.endTime;
const labels: string[] = [];
const datasets: ChartDataset<"timeline">[] = [];
const names = this.names || {};
+146 -36
View File
@@ -1,3 +1,4 @@
import "@lit-labs/virtualizer";
import {
css,
CSSResultGroup,
@@ -6,12 +7,29 @@ import {
PropertyValues,
TemplateResult,
} from "lit";
import { customElement, property } from "lit/decorators";
import { customElement, property, state, eventOptions } from "lit/decorators";
import { isComponentLoaded } from "../../common/config/is_component_loaded";
import { HistoryResult } from "../../data/history";
import {
HistoryResult,
LineChartUnit,
TimelineEntity,
} from "../../data/history";
import type { HomeAssistant } from "../../types";
import "./state-history-chart-line";
import "./state-history-chart-timeline";
import { restoreScroll } from "../../common/decorators/restore-scroll";
const CANVAS_TIMELINE_ROWS_CHUNK = 10; // Split up the canvases to avoid hitting the render limit
const chunkData = (inputArray: any[], chunks: number) =>
inputArray.reduce((results, item, idx) => {
const chunkIdx = Math.floor(idx / chunks);
if (!results[chunkIdx]) {
results[chunkIdx] = [];
}
results[chunkIdx].push(item);
return results;
}, []);
@customElement("state-history-charts")
class StateHistoryCharts extends LitElement {
@@ -19,8 +37,13 @@ class StateHistoryCharts extends LitElement {
@property({ attribute: false }) public historyData!: HistoryResult;
@property() public narrow!: boolean;
@property({ type: Boolean }) public names = false;
@property({ type: Boolean, attribute: "virtualize", reflect: true })
public virtualize = false;
@property({ attribute: false }) public endTime?: Date;
@property({ type: Boolean, attribute: "up-to-now" }) public upToNow = false;
@@ -29,59 +52,104 @@ class StateHistoryCharts extends LitElement {
@property({ type: Boolean }) public isLoadingData = false;
@state() private _computedStartTime!: Date;
@state() private _computedEndTime!: Date;
// @ts-ignore
@restoreScroll(".container") private _savedScrollPos?: number;
@eventOptions({ passive: true })
protected render(): TemplateResult {
if (!isComponentLoaded(this.hass, "history")) {
return html` <div class="info">
return html`<div class="info">
${this.hass.localize("ui.components.history_charts.history_disabled")}
</div>`;
}
if (this.isLoadingData && !this.historyData) {
return html` <div class="info">
return html`<div class="info">
${this.hass.localize("ui.components.history_charts.loading_history")}
</div>`;
}
if (this._isHistoryEmpty()) {
return html` <div class="info">
return html`<div class="info">
${this.hass.localize("ui.components.history_charts.no_history_found")}
</div>`;
}
const computedEndTime = this.upToNow
? new Date()
: this.endTime || new Date();
const now = new Date();
return html`
${this.historyData.timeline.length
? html`
<state-history-chart-timeline
.hass=${this.hass}
.data=${this.historyData.timeline}
.endTime=${computedEndTime}
.noSingle=${this.noSingle}
.names=${this.names}
></state-history-chart-timeline>
`
: html``}
${this.historyData.line.map(
(line) => html`
<state-history-chart-line
.hass=${this.hass}
.unit=${line.unit}
.data=${line.data}
.identifier=${line.identifier}
.isSingleDevice=${!this.noSingle &&
line.data &&
line.data.length === 1}
.endTime=${computedEndTime}
.names=${this.names}
></state-history-chart-line>
`
)}
`;
this._computedEndTime =
this.upToNow || !this.endTime || this.endTime > now ? now : this.endTime;
this._computedStartTime = new Date(
this.historyData.timeline.reduce(
(minTime, stateInfo) =>
Math.min(minTime, new Date(stateInfo.data[0].last_changed).getTime()),
new Date().getTime()
)
);
const combinedItems = this.historyData.timeline.length
? (this.virtualize
? chunkData(this.historyData.timeline, CANVAS_TIMELINE_ROWS_CHUNK)
: [this.historyData.timeline]
).concat(this.historyData.line)
: this.historyData.line;
return this.virtualize
? html`<div class="container ha-scrollbar" @scroll=${this._saveScrollPos}>
<lit-virtualizer
scroller
class="ha-scrollbar"
.items=${combinedItems}
.renderItem=${this._renderHistoryItem}
>
</lit-virtualizer>
</div>`
: html`${combinedItems.map((item, index) =>
this._renderHistoryItem(item, index)
)}`;
}
private _renderHistoryItem = (
item: TimelineEntity[] | LineChartUnit,
index: number
): TemplateResult => {
if (!item || index === undefined) {
return html``;
}
if (!Array.isArray(item)) {
return html`<div class="entry-container">
<state-history-chart-line
.hass=${this.hass}
.unit=${item.unit}
.data=${item.data}
.identifier=${item.identifier}
.isSingleDevice=${!this.noSingle &&
this.historyData.line?.length === 1}
.endTime=${this._computedEndTime}
.names=${this.names}
></state-history-chart-line>
</div> `;
}
return html`<div class="entry-container">
<state-history-chart-timeline
.hass=${this.hass}
.data=${item}
.startTime=${this._computedStartTime}
.endTime=${this._computedEndTime}
.isSingleDevice=${!this.noSingle &&
this.historyData.timeline?.length === 1}
.names=${this.names}
.narrow=${this.narrow}
.chunked=${this.virtualize}
></state-history-chart-timeline>
</div> `;
};
protected shouldUpdate(changedProps: PropertyValues): boolean {
return !(changedProps.size === 1 && changedProps.has("hass"));
}
@@ -96,6 +164,11 @@ class StateHistoryCharts extends LitElement {
return !this.isLoadingData && historyDataEmpty;
}
@eventOptions({ passive: true })
private _saveScrollPos(e: Event) {
this._savedScrollPos = (e.target as HTMLDivElement).scrollTop;
}
static get styles(): CSSResultGroup {
return css`
:host {
@@ -103,11 +176,48 @@ class StateHistoryCharts extends LitElement {
/* height of single timeline chart = 60px */
min-height: 60px;
}
:host([virtualize]) {
height: 100%;
}
.info {
text-align: center;
line-height: 60px;
color: var(--secondary-text-color);
}
.container {
max-height: var(--history-max-height);
}
.entry-container {
width: 100%;
}
.entry-container:hover {
z-index: 1;
}
:host([virtualize]) .entry-container {
padding-left: 1px;
padding-right: 1px;
}
.container,
lit-virtualizer {
height: 100%;
width: 100%;
}
lit-virtualizer {
contain: size layout !important;
}
state-history-chart-timeline,
state-history-chart-line {
width: 100%;
}
`;
}
}
@@ -172,8 +172,7 @@ export abstract class HaDeviceAutomationPicker<
static get styles(): CSSResultGroup {
return css`
ha-select {
width: 100%;
margin-top: 4px;
display: block;
}
`;
}
@@ -58,7 +58,7 @@ class HaEntityAttributePicker extends LitElement {
return html`
<ha-combo-box
.hass=${this.hass}
.value=${this.value || ""}
.value=${this.value ? formatAttributeName(this.value) : ""}
.autofocus=${this.autofocus}
.label=${this.label ??
this.hass.localize(
@@ -0,0 +1,107 @@
import { HassEntity } from "home-assistant-js-websocket";
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, property, query } from "lit/decorators";
import { computeStateDisplay } from "../../common/entity/compute_state_display";
import { PolymerChangedEvent } from "../../polymer-types";
import { getStates } from "../../common/entity/get_states";
import { HomeAssistant } from "../../types";
import "../ha-combo-box";
import type { HaComboBox } from "../ha-combo-box";
export type HaEntityPickerEntityFilterFunc = (entityId: HassEntity) => boolean;
@customElement("ha-entity-state-picker")
class HaEntityStatePicker extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property() public entityId?: string;
@property({ type: Boolean }) public autofocus = false;
@property({ type: Boolean }) public disabled = false;
@property({ type: Boolean }) public required = false;
@property({ type: Boolean, attribute: "allow-custom-value" })
public allowCustomValue;
@property() public label?: string;
@property() public value?: string;
@property() public helper?: string;
@property({ type: Boolean }) private _opened = false;
@query("ha-combo-box", true) private _comboBox!: HaComboBox;
protected shouldUpdate(changedProps: PropertyValues) {
return !(!changedProps.has("_opened") && this._opened);
}
protected updated(changedProps: PropertyValues) {
if (changedProps.has("_opened") && this._opened) {
const state = this.entityId ? this.hass.states[this.entityId] : undefined;
(this._comboBox as any).items =
this.entityId && state
? getStates(state).map((key) => ({
value: key,
label: computeStateDisplay(
this.hass.localize,
state,
this.hass.locale,
key
),
}))
: [];
}
}
protected render(): TemplateResult {
if (!this.hass) {
return html``;
}
return html`
<ha-combo-box
.hass=${this.hass}
.value=${this.value
? this.entityId && this.hass.states[this.entityId]
? computeStateDisplay(
this.hass.localize,
this.hass.states[this.entityId],
this.hass.locale,
this.value
)
: this.value
: ""}
.autofocus=${this.autofocus}
.label=${this.label ??
this.hass.localize("ui.components.entity.entity-state-picker.state")}
.disabled=${this.disabled || !this.entityId}
.required=${this.required}
.helper=${this.helper}
.allowCustomValue=${this.allowCustomValue}
item-value-path="value"
item-label-path="label"
@opened-changed=${this._openedChanged}
@value-changed=${this._valueChanged}
>
</ha-combo-box>
`;
}
private _openedChanged(ev: PolymerChangedEvent<boolean>) {
this._opened = ev.detail.value;
}
private _valueChanged(ev: PolymerChangedEvent<string>) {
this.value = ev.detail.value;
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-entity-state-picker": HaEntityStatePicker;
}
}
+5
View File
@@ -12,8 +12,10 @@ import { property, state } from "lit/decorators";
import { ifDefined } from "lit/directives/if-defined";
import { styleMap } from "lit/directives/style-map";
import { computeActiveState } from "../../common/entity/compute_active_state";
import { computeDomain } from "../../common/entity/compute_domain";
import { computeStateDomain } from "../../common/entity/compute_state_domain";
import { iconColorCSS } from "../../common/style/icon_color_css";
import { cameraUrlWithWidthHeight } from "../../data/camera";
import type { HomeAssistant } from "../../types";
import "../ha-state-icon";
@@ -93,6 +95,9 @@ export class StateBadge extends LitElement {
if (this.hass) {
imageUrl = this.hass.hassUrl(imageUrl);
}
if (computeDomain(stateObj.entity_id) === "camera") {
imageUrl = cameraUrlWithWidthHeight(imageUrl, 80, 80);
}
hostStyle.backgroundImage = `url(${imageUrl})`;
this._showIcon = false;
} else if (stateObj.state === "on") {
+9 -10
View File
@@ -4,8 +4,7 @@ import { customElement, property, query, state } from "lit/decorators";
import { isComponentLoaded } from "../common/config/is_component_loaded";
import { fireEvent } from "../common/dom/fire_event";
import { stringCompare } from "../common/string/compare";
import { HassioAddonInfo } from "../data/hassio/addon";
import { fetchHassioSupervisorInfo } from "../data/hassio/supervisor";
import { fetchHassioAddonsInfo, HassioAddonInfo } from "../data/hassio/addon";
import { showAlertDialog } from "../dialogs/generic/show-dialog-box";
import { PolymerChangedEvent } from "../polymer-types";
import { HomeAssistant } from "../types";
@@ -78,27 +77,27 @@ class HaAddonPicker extends LitElement {
private async _getAddons() {
try {
if (isComponentLoaded(this.hass, "hassio")) {
const supervisorInfo = await fetchHassioSupervisorInfo(this.hass);
this._addons = supervisorInfo.addons.sort((a, b) =>
stringCompare(a.name, b.name)
);
const addonsInfo = await fetchHassioAddonsInfo(this.hass);
this._addons = addonsInfo.addons
.filter((addon) => addon.version)
.sort((a, b) => stringCompare(a.name, b.name));
} else {
showAlertDialog(this, {
title: this.hass.localize(
"ui.componencts.addon-picker.error.no_supervisor.title"
"ui.components.addon-picker.error.no_supervisor.title"
),
text: this.hass.localize(
"ui.componencts.addon-picker.error.no_supervisor.description"
"ui.components.addon-picker.error.no_supervisor.description"
),
});
}
} catch (err: any) {
showAlertDialog(this, {
title: this.hass.localize(
"ui.componencts.addon-picker.error.fetch_addons.title"
"ui.components.addon-picker.error.fetch_addons.title"
),
text: this.hass.localize(
"ui.componencts.addon-picker.error.fetch_addons.description"
"ui.components.addon-picker.error.fetch_addons.description"
),
});
}
+1
View File
@@ -76,6 +76,7 @@ class HaAttributes extends LitElement {
css`
.attribute-container {
margin-bottom: 8px;
direction: ltr;
}
.data-entry {
display: flex;
+9 -9
View File
@@ -1,13 +1,18 @@
import "@material/mwc-list/mwc-list-item";
import "./ha-select";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../common/dom/fire_event";
import { stopPropagation } from "../common/dom/stop_propagation";
import { stringCompare } from "../common/string/compare";
import { Blueprint, Blueprints, fetchBlueprints } from "../data/blueprint";
import {
Blueprint,
BlueprintDomain,
Blueprints,
fetchBlueprints,
} from "../data/blueprint";
import { HomeAssistant } from "../types";
import "./ha-select";
@customElement("ha-blueprint-picker")
class HaBluePrintPicker extends LitElement {
@@ -17,7 +22,7 @@ class HaBluePrintPicker extends LitElement {
@property() public value = "";
@property() public domain = "automation";
@property() public domain: BlueprintDomain = "automation";
@property() public blueprints?: Blueprints;
@@ -51,7 +56,7 @@ class HaBluePrintPicker extends LitElement {
return html`
<ha-select
.label=${this.label ||
this.hass.localize("ui.components.blueprint-picker.label")}
this.hass.localize("ui.components.blueprint-picker.select_blueprint")}
fixedMenuPosition
naturalMenuWidth
.value=${this.value}
@@ -59,11 +64,6 @@ class HaBluePrintPicker extends LitElement {
@selected=${this._blueprintChanged}
@closed=${stopPropagation}
>
<mwc-list-item value="">
${this.hass.localize(
"ui.components.blueprint-picker.select_blueprint"
)}
</mwc-list-item>
${this._processedBlueprints(this.blueprints).map(
(blueprint) => html`
<mwc-list-item .value=${blueprint.path}>
+1 -2
View File
@@ -67,8 +67,7 @@ export class HaChip extends LitElement {
color: var(--ha-chip-icon-color, var(--ha-chip-text-color));
}
.mdc-chip.mdc-chip--selected .mdc-chip__checkmark,
.mdc-chip.no-text
.mdc-chip__icon--leading:not(.mdc-chip__icon--leading-hidden) {
.mdc-chip .mdc-chip__icon--leading:not(.mdc-chip__icon--leading-hidden) {
margin-right: -4px;
margin-inline-start: -4px;
margin-inline-end: 4px;
+5 -30
View File
@@ -1,17 +1,13 @@
import { ListItemBase } from "@material/mwc-list/mwc-list-item-base";
import { styles } from "@material/mwc-list/mwc-list-item.css";
import { css, CSSResult, html } from "lit";
import { css, CSSResultGroup, html } from "lit";
import { customElement, property, query } from "lit/decorators";
import { HaListItem } from "./ha-list-item";
@customElement("ha-clickable-list-item")
export class HaClickableListItem extends ListItemBase {
export class HaClickableListItem extends HaListItem {
@property() public href?: string;
@property({ type: Boolean }) public disableHref = false;
// property used only in css
@property({ type: Boolean, reflect: true }) public rtl = false;
@property({ type: Boolean, reflect: true }) public openNewTab = false;
@query("a") private _anchor!: HTMLAnchorElement;
@@ -39,18 +35,10 @@ export class HaClickableListItem extends ListItemBase {
});
}
static get styles(): CSSResult[] {
static get styles(): CSSResultGroup {
return [
styles,
super.styles,
css`
:host {
padding-left: 0px;
padding-right: 0px;
}
:host([graphic="avatar"]:not([twoLine])),
:host([graphic="icon"]:not([twoLine])) {
height: 48px;
}
a {
width: 100%;
height: 100%;
@@ -60,19 +48,6 @@ export class HaClickableListItem extends ListItemBase {
padding-right: var(--mdc-list-side-padding, 20px);
overflow: hidden;
}
span.material-icons:first-of-type {
margin-inline-start: 0px !important;
margin-inline-end: var(
--mdc-list-item-graphic-margin,
16px
) !important;
direction: var(--direction);
}
span.material-icons:last-of-type {
margin-inline-start: auto !important;
margin-inline-end: 0px !important;
direction: var(--direction);
}
`,
];
}
+72 -9
View File
@@ -2,6 +2,7 @@ import type {
Completion,
CompletionContext,
CompletionResult,
CompletionSource,
} from "@codemirror/autocomplete";
import type { EditorView, KeyBinding, ViewUpdate } from "@codemirror/view";
import { HassEntities } from "home-assistant-js-websocket";
@@ -11,6 +12,7 @@ import memoizeOne from "memoize-one";
import { fireEvent } from "../common/dom/fire_event";
import { loadCodeMirror } from "../resources/codemirror.ondemand";
import { HomeAssistant } from "../types";
import "./ha-icon";
declare global {
interface HASSDomEvents {
@@ -26,6 +28,12 @@ const saveKeyBinding: KeyBinding = {
},
};
const renderIcon = (completion: Completion) => {
const icon = document.createElement("ha-icon");
icon.icon = completion.label;
return icon;
};
@customElement("ha-code-editor")
export class HaCodeEditor extends ReactiveElement {
public codemirror?: EditorView;
@@ -41,12 +49,17 @@ export class HaCodeEditor extends ReactiveElement {
@property({ type: Boolean, attribute: "autocomplete-entities" })
public autocompleteEntities = false;
@property({ type: Boolean, attribute: "autocomplete-icons" })
public autocompleteIcons = false;
@property() public error = false;
@state() private _value = "";
private _loadedCodeMirror?: typeof import("../resources/codemirror");
private _iconList?: Completion[];
public set value(value: string) {
this._value = value;
}
@@ -151,13 +164,22 @@ export class HaCodeEditor extends ReactiveElement {
),
];
if (!this.readOnly && this.autocompleteEntities && this.hass) {
extensions.push(
this._loadedCodeMirror.autocompletion({
override: [this._entityCompletions.bind(this)],
maxRenderedOptions: 10,
})
);
if (!this.readOnly) {
const completionSources: CompletionSource[] = [];
if (this.autocompleteEntities && this.hass) {
completionSources.push(this._entityCompletions.bind(this));
}
if (this.autocompleteIcons) {
completionSources.push(this._mdiCompletions.bind(this));
}
if (completionSources.length > 0) {
extensions.push(
this._loadedCodeMirror.autocompletion({
override: completionSources,
maxRenderedOptions: 10,
})
);
}
}
this.codemirror = new this._loadedCodeMirror.EditorView({
@@ -187,7 +209,7 @@ export class HaCodeEditor extends ReactiveElement {
private _entityCompletions(
context: CompletionContext
): CompletionResult | null | Promise<CompletionResult | null> {
const entityWord = context.matchBefore(/[a-z_]{3,}\./);
const entityWord = context.matchBefore(/[a-z_]{3,}\.\w*/);
if (
!entityWord ||
@@ -205,7 +227,48 @@ export class HaCodeEditor extends ReactiveElement {
return {
from: Number(entityWord.from),
options: states,
span: /^\w*.\w*$/,
span: /^[a-z_]{3,}\.\w*$/,
};
}
private _getIconItems = async (): Promise<Completion[]> => {
if (!this._iconList) {
let iconList: {
name: string;
keywords: string[];
}[];
if (__SUPERVISOR__) {
iconList = [];
} else {
iconList = (await import("../../build/mdi/iconList.json")).default;
}
this._iconList = iconList.map((icon) => ({
type: "variable",
label: `mdi:${icon.name}`,
detail: icon.keywords.join(", "),
info: renderIcon,
}));
}
return this._iconList;
};
private async _mdiCompletions(
context: CompletionContext
): Promise<CompletionResult | null> {
const match = context.matchBefore(/mdi:\S*/);
if (!match || (match.from === match.to && !context.explicit)) {
return null;
}
const iconItems = await this._getIconItems();
return {
from: Number(match.from),
options: iconItems,
span: /^mdi:\S*$/,
};
}
+18 -11
View File
@@ -11,7 +11,7 @@ export const createCloseHeading = (
hass: HomeAssistant,
title: string | TemplateResult
) => html`
<span class="header_title">${title}</span>
<div class="header_title">${title}</div>
<ha-icon-button
.label=${hass.localize("ui.dialogs.generic.close")}
.path=${mdiClose}
@@ -40,10 +40,13 @@ export class HaDialog extends DialogBase {
z-index: var(--dialog-z-index, 7);
-webkit-backdrop-filter: var(--dialog-backdrop-filter, none);
backdrop-filter: var(--dialog-backdrop-filter, none);
--mdc-dialog-box-shadow: var(--dialog-box-shadow, none);
--mdc-typography-headline6-font-weight: 400;
--mdc-typography-headline6-font-size: 1.574rem;
}
.mdc-dialog__actions {
justify-content: var(--justify-action-buttons, flex-end);
padding-bottom: max(env(safe-area-inset-bottom), 8px);
padding-bottom: max(env(safe-area-inset-bottom), 24px);
}
.mdc-dialog__actions span:nth-child(1) {
flex: var(--secondary-action-button-flex, unset);
@@ -54,17 +57,23 @@ export class HaDialog extends DialogBase {
.mdc-dialog__container {
align-items: var(--vertial-align-dialog, center);
}
.mdc-dialog__title {
padding: 24px 24px 0 24px;
}
.mdc-dialog__actions {
padding: 0 24px 24px 24px;
}
.mdc-dialog__title::before {
display: block;
height: 20px;
height: 0px;
}
.mdc-dialog .mdc-dialog__content {
position: var(--dialog-content-position, relative);
padding: var(--dialog-content-padding, 20px 24px);
padding: var(--dialog-content-padding, 24px);
}
:host([hideactions]) .mdc-dialog .mdc-dialog__content {
padding-bottom: max(
var(--dialog-content-padding, 20px),
var(--dialog-content-padding, 24px),
env(safe-area-inset-bottom)
);
}
@@ -72,10 +81,7 @@ export class HaDialog extends DialogBase {
position: var(--dialog-surface-position, relative);
top: var(--dialog-surface-top);
min-height: var(--mdc-dialog-min-height, auto);
border-radius: var(
--ha-dialog-border-radius,
var(--ha-card-border-radius, 4px)
);
border-radius: var(--ha-dialog-border-radius, 28px);
}
:host([flexContent]) .mdc-dialog .mdc-dialog__content {
display: flex;
@@ -89,8 +95,9 @@ export class HaDialog extends DialogBase {
color: inherit;
}
.header_title {
margin-right: 40px;
margin-inline-end: 40px;
margin-right: 32px;
margin-inline-end: 32px;
margin-inline-start: initial;
direction: var(--direction);
}
.header_button {
+74 -29
View File
@@ -14,11 +14,13 @@ import { nextRender } from "../common/util/render-status";
import "./ha-svg-icon";
@customElement("ha-expansion-panel")
class HaExpansionPanel extends LitElement {
export class HaExpansionPanel extends LitElement {
@property({ type: Boolean, reflect: true }) expanded = false;
@property({ type: Boolean, reflect: true }) outlined = false;
@property({ type: Boolean, reflect: true }) leftChevron = false;
@property() header?: string;
@property() secondary?: string;
@@ -29,23 +31,42 @@ class HaExpansionPanel extends LitElement {
protected render(): TemplateResult {
return html`
<div
id="summary"
@click=${this._toggleContainer}
@keydown=${this._toggleContainer}
role="button"
tabindex="0"
aria-expanded=${this.expanded}
aria-controls="sect1"
>
<slot class="header" name="header">
${this.header}
<slot class="secondary" name="secondary">${this.secondary}</slot>
</slot>
<ha-svg-icon
.path=${mdiChevronDown}
class="summary-icon ${classMap({ expanded: this.expanded })}"
></ha-svg-icon>
<div class="top">
<div
id="summary"
@click=${this._toggleContainer}
@keydown=${this._toggleContainer}
@focus=${this._focusChanged}
@blur=${this._focusChanged}
role="button"
tabindex="0"
aria-expanded=${this.expanded}
aria-controls="sect1"
>
${this.leftChevron
? html`
<ha-svg-icon
.path=${mdiChevronDown}
class="summary-icon ${classMap({ expanded: this.expanded })}"
></ha-svg-icon>
`
: ""}
<slot name="header">
<div class="header">
${this.header}
<slot class="secondary" name="secondary">${this.secondary}</slot>
</div>
</slot>
${!this.leftChevron
? html`
<ha-svg-icon
.path=${mdiChevronDown}
class="summary-icon ${classMap({ expanded: this.expanded })}"
></ha-svg-icon>
`
: ""}
</div>
<slot name="icons"></slot>
</div>
<div
class="container ${classMap({ expanded: this.expanded })}"
@@ -61,6 +82,7 @@ class HaExpansionPanel extends LitElement {
}
protected willUpdate(changedProps: PropertyValues) {
super.willUpdate(changedProps);
if (changedProps.has("expanded") && this.expanded) {
this._showContent = this.expanded;
}
@@ -72,6 +94,9 @@ class HaExpansionPanel extends LitElement {
}
private async _toggleContainer(ev): Promise<void> {
if (ev.defaultPrevented) {
return;
}
if (ev.type === "keydown" && ev.key !== "Enter" && ev.key !== " ") {
return;
}
@@ -98,12 +123,28 @@ class HaExpansionPanel extends LitElement {
fireEvent(this, "expanded-changed", { expanded: this.expanded });
}
private _focusChanged(ev) {
this.shadowRoot!.querySelector(".top")!.classList.toggle(
"focused",
ev.type === "focus"
);
}
static get styles(): CSSResultGroup {
return css`
:host {
display: block;
}
.top {
display: flex;
align-items: center;
}
.top.focused {
background: var(--input-fill-color);
}
:host([outlined]) {
box-shadow: none;
border-width: 1px;
@@ -115,7 +156,17 @@ class HaExpansionPanel extends LitElement {
border-radius: var(--ha-card-border-radius, 4px);
}
.summary-icon {
margin-left: 8px;
}
:host([leftchevron]) .summary-icon {
margin-left: 0;
margin-right: 8px;
}
#summary {
flex: 1;
display: flex;
padding: var(--expansion-panel-summary-padding, 0 8px);
min-height: 48px;
@@ -126,15 +177,8 @@ class HaExpansionPanel extends LitElement {
outline: none;
}
#summary:focus {
background: var(--input-fill-color);
}
.summary-icon {
transition: transform 150ms cubic-bezier(0.4, 0, 0.2, 1);
margin-left: auto;
margin-inline-start: auto;
margin-inline-end: initial;
direction: var(--direction);
}
@@ -142,6 +186,11 @@ class HaExpansionPanel extends LitElement {
transform: rotate(180deg);
}
.header,
::slotted([slot="header"]) {
flex: 1;
}
.container {
padding: var(--expansion-panel-content-padding, 0 8px);
overflow: hidden;
@@ -153,10 +202,6 @@ class HaExpansionPanel extends LitElement {
height: auto;
}
.header {
display: block;
}
.secondary {
display: block;
color: var(--secondary-text-color);
+8
View File
@@ -19,6 +19,14 @@ export class HaFab extends FabBase {
direction: var(--direction);
}
`,
// safari workaround - must be explicit
document.dir === "rtl"
? css`
:host .mdc-fab--extended .mdc-fab__icon {
direction: rtl;
}
`
: css``,
];
}
@@ -6,7 +6,10 @@ export const computeInitialHaFormData = (
): Record<string, any> => {
const data = {};
schema.forEach((field) => {
if (field.description?.suggested_value) {
if (
field.description?.suggested_value !== undefined &&
field.description?.suggested_value !== null
) {
data[field.name] = field.description.suggested_value;
} else if ("default" in field) {
data[field.name] = field.default;
@@ -47,6 +50,7 @@ export const computeInitialHaFormData = (
"text" in selector ||
"addon" in selector ||
"attribute" in selector ||
"file" in selector ||
"icon" in selector ||
"theme" in selector
) {
+4 -3
View File
@@ -3,15 +3,15 @@ import {
CSSResultGroup,
html,
LitElement,
TemplateResult,
PropertyValues,
TemplateResult,
} from "lit";
import { customElement, property, query } from "lit/decorators";
import { fireEvent } from "../../common/dom/fire_event";
import { HaCheckbox } from "../ha-checkbox";
import { HaFormElement, HaFormIntegerData, HaFormIntegerSchema } from "./types";
import "../ha-slider";
import { HaTextField } from "../ha-textfield";
import { HaFormElement, HaFormIntegerData, HaFormIntegerSchema } from "./types";
@customElement("ha-form-integer")
export class HaFormInteger extends LitElement implements HaFormElement {
@@ -105,7 +105,8 @@ export class HaFormInteger extends LitElement implements HaFormElement {
}
return (
this.schema.description?.suggested_value ||
(this.schema.description?.suggested_value !== undefined &&
this.schema.description?.suggested_value !== null) ||
this.schema.default ||
this.schema.valueMin ||
0
@@ -205,6 +205,9 @@ export class HaFormMultiSelect extends LitElement implements HaFormElement {
ha-formfield {
display: block;
padding-right: 16px;
padding-inline-end: 16px;
padding-inline-start: initial;
direction: var(--direction);
}
ha-textfield {
display: block;
@@ -216,6 +219,9 @@ export class HaFormMultiSelect extends LitElement implements HaFormElement {
right: 1em;
top: 1em;
cursor: pointer;
inset-inline-end: 1em;
inset-inline-start: initial;
direction: var(--direction);
}
:host([opened]) ha-svg-icon {
color: var(--primary-color);
+6 -6
View File
@@ -35,20 +35,20 @@ export class HaForm extends LitElement implements HaFormElement {
@property({ attribute: false }) public data!: HaFormDataContainer;
@property({ attribute: false }) public schema!: HaFormSchema[];
@property({ attribute: false }) public schema!: readonly HaFormSchema[];
@property() public error?: Record<string, string>;
@property({ type: Boolean }) public disabled = false;
@property() public computeError?: (schema: HaFormSchema, error) => string;
@property() public computeError?: (schema: any, error) => string;
@property() public computeLabel?: (
schema: HaFormSchema,
data?: HaFormDataContainer
schema: any,
data: HaFormDataContainer
) => string;
@property() public computeHelper?: (schema: HaFormSchema) => string;
@property() public computeHelper?: (schema: any) => string | undefined;
public focus() {
const root = this.shadowRoot?.querySelector(".root");
@@ -168,7 +168,7 @@ export class HaForm extends LitElement implements HaFormElement {
return this.computeHelper ? this.computeHelper(schema) : "";
}
private _computeError(error, schema: HaFormSchema | HaFormSchema[]) {
private _computeError(error, schema: HaFormSchema | readonly HaFormSchema[]) {
return this.computeError ? this.computeError(error, schema) : error;
}
+13 -4
View File
@@ -31,7 +31,7 @@ export interface HaFormGridSchema extends HaFormBaseSchema {
type: "grid";
name: "";
column_min_width?: string;
schema: HaFormSchema[];
schema: readonly HaFormSchema[];
}
export interface HaFormSelector extends HaFormBaseSchema {
@@ -53,12 +53,15 @@ export interface HaFormIntegerSchema extends HaFormBaseSchema {
export interface HaFormSelectSchema extends HaFormBaseSchema {
type: "select";
options: Array<[string, string]>;
options: ReadonlyArray<readonly [string, string]>;
}
export interface HaFormMultiSelectSchema extends HaFormBaseSchema {
type: "multi_select";
options: Record<string, string> | string[] | Array<[string, string]>;
options:
| Record<string, string>
| readonly string[]
| ReadonlyArray<readonly [string, string]>;
}
export interface HaFormFloatSchema extends HaFormBaseSchema {
@@ -78,6 +81,12 @@ export interface HaFormTimeSchema extends HaFormBaseSchema {
type: "positive_time_period_dict";
}
// Type utility to unionize a schema array by flattening any grid schemas
export type SchemaUnion<
SchemaArray extends readonly HaFormSchema[],
Schema = SchemaArray[number]
> = Schema extends HaFormGridSchema ? SchemaUnion<Schema["schema"]> : Schema;
export interface HaFormDataContainer {
[key: string]: HaFormData;
}
@@ -100,7 +109,7 @@ export type HaFormMultiSelectData = string[];
export type HaFormTimeData = HaDurationData;
export interface HaFormElement extends LitElement {
schema: HaFormSchema | HaFormSchema[];
schema: HaFormSchema | readonly HaFormSchema[];
data?: HaFormDataContainer | HaFormData;
label?: string;
}
+28 -4
View File
@@ -14,6 +14,7 @@ const getAngle = (value: number, min: number, max: number) => {
export interface LevelDefinition {
level: number;
stroke: string;
label?: string;
}
@customElement("ha-gauge")
@@ -38,22 +39,31 @@ export class Gauge extends LitElement {
@state() private _updated = false;
@state() private _segment_label? = "";
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
// Wait for the first render for the initial animation to work
afterNextRender(() => {
this._updated = true;
this._angle = getAngle(this.value, this.min, this.max);
this._segment_label = this.getSegmentLabel();
this._rescale_svg();
});
}
protected updated(changedProperties: PropertyValues) {
super.updated(changedProperties);
if (!this._updated || !changedProperties.has("value")) {
if (
!this._updated ||
(!changedProperties.has("value") &&
!changedProperties.has("label") &&
!changedProperties.has("_segment_label"))
) {
return;
}
this._angle = getAngle(this.value, this.min, this.max);
this._segment_label = this.getSegmentLabel();
this._rescale_svg();
}
@@ -118,9 +128,11 @@ export class Gauge extends LitElement {
</svg>
<svg class="text">
<text class="value-text">
${this.valueText || formatNumber(this.value, this.locale)} ${
this.label
}
${
this._segment_label
? this._segment_label
: this.valueText || formatNumber(this.value, this.locale)
} ${this._segment_label ? "" : this.label}
</text>
</svg>`;
}
@@ -137,6 +149,18 @@ export class Gauge extends LitElement {
);
}
private getSegmentLabel() {
if (this.levels) {
this.levels.sort((a, b) => a.level - b.level);
for (let i = this.levels.length - 1; i >= 0; i--) {
if (this.value >= this.levels[i].level) {
return this.levels[i].label;
}
}
}
return "";
}
static get styles() {
return css`
:host {
+96 -1
View File
@@ -29,7 +29,102 @@ interface DeprecatedIcon {
};
}
const mdiDeprecatedIcons: DeprecatedIcon = {};
const mdiDeprecatedIcons: DeprecatedIcon = {
"android-messages": {
newName: "message-text",
removeIn: "2022.10",
},
"book-variant-multiple": {
newName: "bookmark-box-multiple",
removeIn: "2022.10",
},
"desktop-mac": {
newName: "monitor",
removeIn: "2022.10",
},
"desktop-mac-dashboard": {
newName: "monitor-dashboard",
removeIn: "2022.10",
},
discord: {
removeIn: "2022.10",
},
"diving-scuba": {
newName: "diving-scuba-mask",
removeIn: "2022.10",
},
"email-send": {
newName: "email-arrow-right",
removeIn: "2022.10",
},
"email-send-outline": {
newName: "email-arrow-right-outline",
removeIn: "2022.10",
},
"email-receive": {
newName: "email-arrow-left",
removeIn: "2022.10",
},
"email-receive-outline": {
newName: "email-arrow-left-outline",
removeIn: "2022.10",
},
"format-textdirection-r-to-l": {
newName: "format-pilcrow-arrow-left",
removeIn: "2022.10",
},
"format-textdirection-l-to-r": {
newName: "format-pilcrow-arrow-right",
removeIn: "2022.10",
},
"google-controller": {
newName: "controller",
removeIn: "2022.10",
},
"google-controller-off": {
newName: "controller-off",
removeIn: "2022.10",
},
"google-home": {
removeIn: "2022.10",
},
lecturn: {
newName: "lectern",
removeIn: "2022.10",
},
receipt: {
newName: "receipt-text",
removeIn: "2022.10",
},
"receipt-outline": {
newName: "receipt-text-outline",
removeIn: "2022.10",
},
"tablet-android": {
newName: "tablet",
removeIn: "2022.10",
},
"text-to-speech": {
newName: "microphone-message",
removeIn: "2022.10",
},
"text-to-speech-off": {
newName: "microphone-message-off",
removeIn: "2022.10",
},
"timeline-help": {
newName: "timeline-question",
removeIn: "2022.10",
},
"timeline-help-outline": {
newName: "timeline-question-outline",
removeIn: "2022.10",
},
"vector-point": {
newName: "vector-point-select",
removeIn: "2022.10",
},
};
const chunks: Chunks = {};
+42
View File
@@ -0,0 +1,42 @@
import { ListItemBase } from "@material/mwc-list/mwc-list-item-base";
import { styles } from "@material/mwc-list/mwc-list-item.css";
import { css, CSSResultGroup } from "lit";
import { customElement } from "lit/decorators";
@customElement("ha-list-item")
export class HaListItem extends ListItemBase {
static get styles(): CSSResultGroup {
return [
styles,
css`
:host {
padding-left: var(--mdc-list-side-padding, 20px);
padding-right: var(--mdc-list-side-padding, 20px);
}
:host([graphic="avatar"]:not([twoLine])),
:host([graphic="icon"]:not([twoLine])) {
height: 48px;
}
span.material-icons:first-of-type {
margin-inline-start: 0px !important;
margin-inline-end: var(
--mdc-list-item-graphic-margin,
16px
) !important;
direction: var(--direction);
}
span.material-icons:last-of-type {
margin-inline-start: auto !important;
margin-inline-end: 0px !important;
direction: var(--direction);
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-list-item": HaListItem;
}
}
+5
View File
@@ -52,6 +52,11 @@ export class HaSelect extends SelectBase {
inset-inline-end: initial;
direction: var(--direction);
}
.mdc-select--filled.mdc-select--with-leading-icon .mdc-floating-label {
inset-inline-start: 48px;
inset-inline-end: initial;
direction: var(--direction);
}
.mdc-select .mdc-select__anchor {
padding-inline-start: 12px;
padding-inline-end: 0px;

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