Compare commits

..

498 Commits

Author SHA1 Message Date
Aidan Timson 1bf6be0b10 Fix devtools header 2025-09-09 12:00:34 +01:00
Aidan Timson 12bb32bc37 Safe area: Fix dashboard hui-root 2025-09-09 11:55:11 +01:00
Aidan Timson cc70eb46c9 Safe area: sidebar and notification drawer (#26853)
* sidebar: account for safe-area top inset in menu height/padding; adjust list height calc

* Defaults

* Defaults

* Restore

* Remove test

* Adjust

* Adjust

* Only apply on smaller layouts

* Fix

* Restore

* Restore

* No default in this case

* Restore

* Gain back some space

* Fix

* Adjust

* Tweak

* Calculate when mobile

* Use fallbacks for calculations and others anyway
2025-09-09 11:59:24 +02:00
Aidan Timson dec9d304da Add analog card clock style options (#26711)
* Options

* Add default analog options (or delete) when changing style, hide when digital

* Fix rebase error

* Format

* Update description

Co-authored-by: Norbert Rittel <norbert@rittel.de>

* Flatten config options

* Flatten config options

* Add analog clock style options

Show lines

Adjust

Adjust

Replace 'ticks_style' with 'face_style' and use numbers over numeric

Use numbers over numeric

* Rebase fixes

* Missing translations mapping

* Remove rotated numbers and roman upright options

* Remove rotated numbers and roman upright options

* Edit description

* Update src/translations/en.json

Co-authored-by: Norbert Rittel <norbert@rittel.de>

* Update src/translations/en.json

Co-authored-by: Norbert Rittel <norbert@rittel.de>

---------

Co-authored-by: Norbert Rittel <norbert@rittel.de>
2025-09-09 11:30:19 +03:00
Paul Bottein 7f8e856102 Don't trigger automation shortcuts when a field is focused or text selected (#26965)
* Don't trigger automation shortcut when a field is focused

* Don't trigger automation shortcut when a text is selected
2025-09-09 10:53:06 +03:00
karwosts 4bd60a1366 Mark new automation as dirty if it has initData (#26953) 2025-09-09 10:26:18 +03:00
Wendelin e9ca1758a0 Fix yaml editor save in config-flow (#26963) 2025-09-09 10:25:06 +03:00
Paulus Schoutsen dff3b82f0d Show action being called in action dev tools (#26923) 2025-09-09 08:57:36 +02:00
J. Nick Koston 1b630e7b66 Hide useless configure bluetooth options button for remote scanners (#26960)
This avoids showing the button when all they get is
"Bluetooth configuration for remote adapters is not supported."
2025-09-09 09:46:22 +03:00
Aidan Timson f4238bf291 Safe area: bars (#26816)
* app-bars: apply safe-area insets (top padding and fixed-adjust; add content padding in fixed)

* Set toolbar

* Set bars

* Add left and right insets to root ll

* Add to dev tools header

* Fix

* Apply to subpages (config pages mainly)

* Adjust

* Remove old comment
2025-09-09 08:13:03 +02:00
karwosts ef8cb8b393 Add a bulk delete to devices table (#26914) 2025-09-08 20:33:53 +02:00
karwosts bed161d485 Deduplicate table rendering code in energy sources table (#26918) 2025-09-08 20:14:15 +02:00
renovate[bot] 22e0ef4308 Update dependency eslint to v9.35.0 (#26955)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-08 19:34:36 +02:00
renovate[bot] eb355d110d Update babel monorepo to v7.28.4 (#26954)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-08 19:34:07 +02:00
Wendelin c041c295d5 Use @home-assistant/webawesome (#26942) 2025-09-08 14:25:47 +02:00
Wendelin c582896574 Update missing border radius variables (#26944) 2025-09-08 14:01:24 +02:00
renovate[bot] 3e6b59fe1e Update dependency luxon to v3.7.2 (#26941)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-08 14:43:53 +03:00
Aidan Timson 62714b2b68 Safe area: dialogs (#26814)
* dialogs: apply safe-area insets (content padding, header mobile insets, more-info top margin)

* Set default (40px prio if not set)

* Set default (default padding prio if not set)

* Set default to avoid issues

* Set on container

* Sort

* No longer needed

* No longer needed

* No longer needed

* Remove

* Restore

* Restore

* Move to padding

* Switch to margins, set min and max height

* Set default

* Account for insets to remove extra scrollbars

* Fix content for filter dialog

* Move margins outside of media check

* Use padding instead

* use min-width instead

* Use padding for just top and bottom

* Calculate lit-virtualizer to include safe areas

* Calculate lit-virtualizer to include safe areas

* Fix double scrollbar from previous

* Remove calculation

* Default

* Remove double calculation

* Remove double calculation
2025-09-08 09:39:27 +02:00
renovate[bot] 07fdd5b7af Update vaadinWebComponents monorepo to v24.8.7 (#26936)
* Update vaadinWebComponents monorepo to v24.8.7

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-08 07:35:30 +00:00
Phil White 720f435987 Fix refresh button appending logs instead of clearing them (#26875) 2025-09-08 09:18:05 +02:00
Yosi Levy 52061d6c1a Variour RTL fixes for automation and others (#26891) 2025-09-08 09:13:10 +02:00
Paulus Schoutsen ae35164a57 Update clipboard copy template message in actions dev tool (#26925) 2025-09-08 08:59:11 +02:00
karwosts d1c814bd6b Fix condition action in config flow dialog (#26929) 2025-09-08 08:33:02 +02:00
dependabot[bot] bb50512c89 Bump softprops/action-gh-release from 2.3.2 to 2.3.3 (#26935)
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.3.2 to 2.3.3.
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](https://github.com/softprops/action-gh-release/compare/v2.3.2...v2.3.3)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-version: 2.3.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-08 08:20:26 +02:00
dependabot[bot] 0fae45edc9 Bump actions/setup-node from 4.4.0 to 5.0.0 (#26934)
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 4.4.0 to 5.0.0.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v4.4.0...v5.0.0)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: 5.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-08 08:20:00 +02:00
dependabot[bot] 0a8d3cc8fa Bump actions/setup-python from 5 to 6 (#26933)
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5 to 6.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-08 08:19:11 +02:00
dependabot[bot] db09947a67 Bump actions/labeler from 5.0.0 to 6.0.1 (#26932)
Bumps [actions/labeler](https://github.com/actions/labeler) from 5.0.0 to 6.0.1.
- [Release notes](https://github.com/actions/labeler/releases)
- [Commits](https://github.com/actions/labeler/compare/v5.0.0...v6.0.1)

---
updated-dependencies:
- dependency-name: actions/labeler
  dependency-version: 6.0.1
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-08 08:18:47 +02:00
dependabot[bot] 5eb600726f Bump actions/stale from 9.1.0 to 10.0.0 (#26931)
Bumps [actions/stale](https://github.com/actions/stale) from 9.1.0 to 10.0.0.
- [Release notes](https://github.com/actions/stale/releases)
- [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/stale/compare/v9.1.0...v10.0.0)

---
updated-dependencies:
- dependency-name: actions/stale
  dependency-version: 10.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-08 08:18:07 +02:00
dependabot[bot] 17a2e6e1f6 Bump actions/github-script from 7 to 8 (#26930)
Bumps [actions/github-script](https://github.com/actions/github-script) from 7 to 8.
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](https://github.com/actions/github-script/compare/v7...v8)

---
updated-dependencies:
- dependency-name: actions/github-script
  dependency-version: '8'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-08 08:17:35 +02:00
renovate[bot] 53e7959d54 Update dependency serve to v14.2.5 (#26922)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-07 20:21:02 +02:00
Aidan Timson 5f140c5fc4 Safe area: main content and onboarding (#26813)
* foundation: define --safe-area-content-inset-*; adjust drawer width; apply top inset to pre-login templates

* Set default for var

* Update demo template

* Set default to avoid issues

* Allow for left and right insets or 560px

* Remove
2025-09-07 13:25:29 +03:00
renovate[bot] 688b8e5229 Update dependency @types/leaflet.markercluster to v1.5.6 (#26911)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-06 14:59:59 +00:00
renovate[bot] 34b50b45a3 Update dependency @types/leaflet-draw to v1.0.13 (#26910)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-06 16:49:58 +02:00
karwosts c17c9c6cc9 Remove an unused translation (#26908) 2025-09-05 22:39:01 -04:00
renovate[bot] c9d72b5253 Update dependency @types/culori to v4.0.1 (#26899)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-05 23:22:30 +02:00
renovate[bot] f5dbb28fb2 Update dependency typescript-eslint to v8.42.0 (#26897)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-05 23:22:22 +02:00
renovate[bot] 9acfe5c1cc Update dependency @codemirror/autocomplete to v6.18.7 (#26896)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-05 23:22:15 +02:00
Jan-Philipp Benecke 701cbcfbad Do not show state-card-content in new more-info dialog in gallery (#26889)
* Do not show `state-card-content` in new more-info dialog in gallery

* Use `computeShowNewMoreInfo`
2025-09-05 14:13:28 -04:00
renovate[bot] 38685127d2 Update dependency @codemirror/view to v6.38.2 (#26890)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-05 08:59:04 +03:00
renovate[bot] 4275f6c6b6 Update dependency lint-staged to v16.1.6 (#26885)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-04 19:33:55 +02:00
renovate[bot] bfc186b612 Update dependency @rspack/core to v1.5.2 (#26880)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-04 16:41:29 +02:00
Paul Bottein cb4d92ccf4 Add UI editor for trend graph feature (#26872) 2025-09-04 16:28:02 +02:00
Wendelin 1dc7256fb5 Translate del in shortcut dialog (#26865) 2025-09-04 16:11:38 +02:00
Wendelin 012e710e45 Test-device-tracker-entity-filter (#26881) 2025-09-04 16:08:38 +02:00
Paul Bottein 5abb7d0286 Fix testing condition in iOS (#26879) 2025-09-04 15:50:39 +02:00
Petar Petrov ce74946706 Fix highlighting issue in Energy Sankey card (#26878) 2025-09-04 15:42:39 +02:00
Bram Kragten bf351d67e9 Revert "Rename "Logbook" to "Activity" in user-facing strings" (#26867)
Revert "Rename "Logbook" to "Activity" in user-facing strings (#26619)"

This reverts commit 057fad55e8.
2025-09-04 11:14:16 +02:00
Wendelin b75fa013d2 Fix script with fields fields in tile card button feature (#26866)
Check script fields in tile card button feature
2025-09-04 11:42:41 +03:00
Paul Bottein 2601b0d89c Display area with only sensors in climate view in home dashboard (#26863)
* Display area with only sensors in climate view in home dashboard

* Update home-climate-view-strategy.ts

Co-authored-by: Simon Lamon <32477463+silamon@users.noreply.github.com>

---------

Co-authored-by: Simon Lamon <32477463+silamon@users.noreply.github.com>
2025-09-04 10:11:35 +02:00
Mike Kelly ef8410e121 Add MCF as another volume unit (#26400) 2025-09-04 09:30:13 +03:00
karwosts 37610703c8 Pass first weekday to recorder/statistic_during_period (#26229)
* Pass first weekday to recorder/statistic_during_period

* switch order
2025-09-04 09:27:40 +03:00
Aidan Timson 4efd9bba8a Fix weather more info forecast layout jump when loading data (#26764)
* Fix weather more info forecast layout jump when loading data

* Tabs arent always populated

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-04 06:04:56 +00:00
Paulus Schoutsen e1fe7976d8 Do not allow filtering by automation labels on mobile (#26861) 2025-09-04 08:47:52 +03:00
Simon Lamon 53b96107d9 Home dashboard: Climate fix (#26856)
* Climate dashboard fix

* Update home-climate-view-strategy.ts
2025-09-03 18:55:33 +02:00
Bram Kragten dcbad9e798 Dont align with clipboard on copy/cut automation item (#26855) 2025-09-03 14:19:02 +00:00
karwosts 26b3212c7e Enable sorting step flow menu options in user language (#26845)
* Enable sorting step flow menu options in user language

* Remove menu_sort_values

* Update src/dialogs/config-flow/step-flow-menu.ts

Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>

* prettier

---------

Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
2025-09-03 17:03:09 +03:00
Paul Bottein f3f4bcfe45 Rename history chart to trend graph (#26854)
* Rename history chart to trend graph

* rename element and prettier

---------

Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2025-09-03 10:56:05 +00:00
Bram Kragten 7cfa9de75f Bumped version to 20250903.0 2025-09-03 12:05:19 +02:00
Paul Bottein b66dc8894d Improve responsive support for history graph feature (#26851) 2025-09-03 12:00:49 +02:00
Wendelin 14a7813ab0 Fix automation narrow bottom sheet close animation (#26850) 2025-09-03 09:45:13 +00:00
Bram Kragten 70a2ca281f Update shortcuts dialog (#26849) 2025-09-03 10:01:10 +02:00
Wendelin d982f042fc Update device action button variant based on warning class (#26848) 2025-09-03 07:06:18 +00:00
J. Nick Koston e60f9e326b Show scanner-type-specific power cycle instructions in Bluetooth UI (#26847)
* Show scanner-type-specific power cycle instructions in Bluetooth UI

* preen
2025-09-03 10:03:03 +03:00
J. Nick Koston ba39d189e7 Show scanner current state in the Bluetooth UI (#26792)
* Show scanner current state in the Bluetooth UI

The state updates live as the scanner changes mode, and provides
a warning on what to do if the scanner gets in a bad state

* Show scanner current state in the Bluetooth UI

The state updates live as the scanner changes mode, and provides
a warning on what to do if the scanner gets in a bad state

* cleanup

* switch
2025-09-02 15:20:06 -03:00
Paul Bottein 78867b2cd9 Remove non numerical sensor and binary-sensor for history chart feature (#26838)
* Use hui-graph-base for chart history feature and remove non numerical sensor support

* Fix chart opacity
2025-09-02 14:38:06 -03:00
Paul Bottein 1dff42dc00 Change home summaries color (#26839) 2025-09-02 18:14:46 +02:00
Bram Kragten 0c9b3a0765 Remove expand all/collapse all options from overflow (#26837)
* remove expand all/collapse all options from overflow

* ignore
2025-09-02 16:26:44 +02:00
Bram Kragten 5a109c0ba8 Align box shadows (#26835)
* Align box shadows

* update colors
2025-09-02 14:15:48 +00:00
Bram Kragten f3b214c30a Add new shortcuts to shortcuts dialog (#26836) 2025-09-02 17:08:32 +03:00
Petar Petrov c49d2a0be6 Add support for option descriptions in config flows (#26825)
* Add support for option descriptions in config flows

* use `twoline`

* remove unused classes
2025-09-02 14:03:35 +00:00
Bram Kragten c6c3170c1b Tweak automation row (#26834)
tweak automation row
2025-09-02 13:50:05 +00:00
Paul Bottein 0abb958aea Clean graph in security and climate view in home dashboard (#26833) 2025-09-02 14:58:56 +02:00
Bram Kragten 9d55843629 Fix scrolling items in the bottom into view (#26830) 2025-09-02 14:57:11 +02:00
Bram Kragten b70d309297 add shadow when scrollable in automation bottom sheet, min height 50vh (#26828) 2025-09-02 14:12:48 +02:00
Simon Lamon 5961b71562 Home dashboard: Ensure temperature sensor entity exists (#26831) 2025-09-02 13:03:18 +02:00
karwosts 6942626f60 Differentate service vs device in more-info link (#26823) 2025-09-02 12:44:16 +02:00
Aidan Timson 069c0acdff Fix capitalization on data table filter button (#26829) 2025-09-02 10:44:01 +00:00
Bram Kragten 1f0d83190d prevent keyboard shortcuts with more modifier keys (#26826) 2025-09-02 09:39:54 +02:00
Wendelin 7c6c92c856 Automation-keybindings (#26762)
Co-authored-by: Norbert Rittel <norbert@rittel.de>
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2025-09-01 21:41:50 +02:00
Robert Resch eff352cde1 Warn about system performance when setting camera stream orientation (#26616) 2025-09-01 20:54:48 +02:00
Bram Kragten 62a75c188c Change drag selected styling (#26822) 2025-09-01 20:26:25 +02:00
Paul Bottein 4ffa6b6186 Force energy distribution to display the data of today (#26811) 2025-09-01 18:12:34 +02:00
Paul Bottein 25173cf605 Don't use ha-automation-row-selected to know if the item was selected (#26812) 2025-09-01 18:12:08 +02:00
Paul Bottein 3277d8e80b Add automation testing logic to sidebar (#26815) 2025-09-01 18:08:59 +02:00
Bram Kragten 55864fdc82 Update hover and hightlight states for automation rows (#26820) 2025-09-01 17:40:03 +02:00
Paul Bottein d4d662ba46 Fix automation sidebar z index (#26810) 2025-09-01 17:21:36 +02:00
Paul Bottein 3ea5f508bb Remove box shadow for ha card in automation bottom sheet on mobile (#26817) 2025-09-01 17:19:09 +02:00
Paul Bottein 902a5dd678 Fix tag trigger when using it inside sidebar (#26819) 2025-09-01 17:18:18 +02:00
renovate[bot] 4a3ed62583 Update dependency @types/chromecast-caf-receiver to v6.0.24 (#26500)
* Update dependency @types/chromecast-caf-receiver to v6.0.24

* Use enum strings directly to make TS happy

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
2025-09-01 14:53:10 +00:00
Paul Bottein b4223e9e92 Use summary card in home dashboard (#26775) 2025-09-01 15:13:53 +02:00
Paul Bottein 99955d7818 Fix add dialog on dashboards on mobile (#26807) 2025-09-01 15:39:52 +03:00
Paul Bottein f66768726c Update heading subtitle height to better fix grid (#26808) 2025-09-01 15:39:03 +03:00
Michel van de Wetering 0e4be02b2c Remove Hue bridge v1 image (#26674) 2025-09-01 15:27:50 +03:00
Bram Kragten 6daea23b3c Enable drag and drop on mobile for automations (#26805) 2025-09-01 14:09:13 +02:00
Petar Petrov e21ddcb1e5 Handle negative values in History chart card feature (#26806) 2025-09-01 11:45:52 +00:00
Wendelin ded85d9f27 Automation editor: fix yaml editor and editor switch (#26772) 2025-09-01 13:26:17 +02:00
Petar Petrov eea43494da Use feature-color in History chart feature (#26802) 2025-09-01 11:34:44 +02:00
Norbert Rittel 9cf9ef927d Expand description for disabled services (#26782)
* Expand description for disabled services

* Drop "not added to Home Assistant"
2025-09-01 09:51:22 +03:00
Paulus Schoutsen 779ec4f583 Do not add graphs to every sensor tile card (#26793) 2025-08-31 15:11:41 +00:00
renovate[bot] c541831cd2 Update dependency marked to v16.2.1 (#26785)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-31 17:07:02 +02:00
renovate[bot] fd20c2a554 Update dependency @rspack/core to v1.5.1 (#26790)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-31 17:06:55 +02:00
uptimeZERO_ 14fd29808c Bugfix: Fixed column widths in the dashboard config page (#26777)
Fixed column widths in dashboard config
2025-08-30 11:54:37 +00:00
Aidan Timson 7132ee157f Adjust states set state layout (#26765) 2025-08-30 13:11:41 +02:00
J. Nick Koston 1596b313d5 Add RSSI color gradient to Bluetooth visualization to identify connection problems (#26778) 2025-08-29 14:15:00 -05:00
karwosts 70cd68ded7 Don't use context for media selector with 'accept' (#26773) 2025-08-29 14:08:11 +00:00
renovate[bot] cc91a6185e Update dependency @rspack/core to v1.5.0 (#26768)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-29 17:05:14 +03:00
Paul Bottein 1fd7c84583 Use fixed layout for automation sidebar to have scrollbar on the side (#26751)
Co-authored-by: Wendelin <12148533+wendevlin@users.noreply.github.com>
2025-08-29 14:19:13 +02:00
Paul Bottein 0269540ee9 Add translations for home dashboard (#26763) 2025-08-29 14:17:55 +02:00
renovate[bot] 98390b3843 Update Yarn to v4.9.4 (#26760)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-29 08:12:28 +00:00
Paul Bottein 269628929c Fix alert z-index for automation and script (#26759) 2025-08-29 08:05:05 +00:00
Norbert Rittel 21fcc84afd Improve OAuth setup explanation (#26758)
* Improve OAuth setup explanation

* Add "your"

* Include "application" in headline
2025-08-29 09:59:31 +02:00
Wendelin b86c1db83d Automation editor: fix focus handling (#26755) 2025-08-29 08:39:06 +02:00
renovate[bot] a376670478 Update dependency typescript-eslint to v8.41.0 (#26757)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-29 08:21:34 +02:00
renovate[bot] 72c62079aa Update dependency hls.js to v1.6.11 (#26756)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-29 08:21:28 +02:00
Surya Prakash 9baf875585 Fix: Keep buttons active in picture-glance-card regardless of state (#26718)
* Fix: Keep buttons active in picture-glance-card regardless of state


Fixes #26683

**Problem:**
Buttons in the picture-glance-card were incorrectly disabled when their state was 'unknown', making them unclickable.

**Root Cause:**
The `disabled` property was being set based on the state value, but buttons should remain clickable regardless of their state since their state only reflects the last time they were pressed.

**Solution:**
Set `disabled=${false}` to ensure buttons are always active and clickable.

**Testing:**
Verified that buttons remain active and functional even when state is 'unknown'.

* Fix: Ensure buttons get state-on class when state is unknown

Fixes #26683

The "state-on" class was not being applied when button state was
"unknown" because "unknown" was included in the STATES_OFF set.
This ensures buttons appear active regardless of their state.

* Update hui-picture-glance-card.ts

* Update hui-picture-glance-card.ts

* Update hui-picture-glance-card.ts

* Update hui-picture-glance-card.ts

* Update hui-picture-glance-card.ts

* Update hui-picture-glance-card.ts

* Update hui-picture-glance-card.ts
2025-08-29 08:20:39 +02:00
Jonathan Keslin 175915218f Hide leading zero in clock card hour when using 12-hour time format (#26669)
Remove leading zero on hour on Clock card when displayed with 12-hour time format
2025-08-29 08:42:07 +03:00
Wendelin 25f25243bd Automation editor: overflow changes and style fixes (#26744)
* Fix for width also for blueprint editor

* Fix overflow menus

* Fix option icons

* Fix iOS bottom sheet flickering and drag handle

* Fix mobile padding

* Fix padding in sidebar

* Fix overflow placement

* Add new a11y sort

* Remove overflow in rows

* Fix a11y select row

* Revert "Fix a11y select row"

This reverts commit 54260c4a37.

* Fix option padding on blueprint

---------

Co-authored-by: Paul Bottein <paul.bottein@gmail.com>
2025-08-28 16:42:45 +00:00
karwosts cf8d36b1f3 Hide 'options' from enum more info (#26736)
* Hide 'options' from enum more info

* restrict to specific domain and class
2025-08-28 18:08:33 +02:00
Aidan Timson e3a9d754df Change loading detailed storage to use ha-alert with spinner (#26749)
* Change spinner overlay to use `ha-alert` with messaging

* Use spinner for icon slot
2025-08-28 18:05:53 +02:00
Petar Petrov 7b303a699b Increase disk usage request timeout (#26748) 2025-08-28 16:32:52 +03:00
renovate[bot] ee45eb00f7 Update vaadinWebComponents monorepo to v24.8.6 (#26746)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-28 15:38:03 +03:00
Norbert Rittel 24a6aa2669 Different spelling fixes of user-facing strings (#26745)
* Different spelling fixes of user-facing strings

* Fix menu "Application credentials" menu item name
2025-08-28 15:37:06 +03:00
Aidan Timson 66d011cfb9 Move automation and script ha-alerts to main flow (#26735)
Co-authored-by: Wendelin <12148533+wendevlin@users.noreply.github.com>
2025-08-28 13:53:15 +02:00
Wendelin 35895735cc Fix automation editor drag selected row in/out nested (#26740)
Fix nested sort
2025-08-28 13:30:14 +02:00
Norbert Rittel e71df0b71a Improve section descriptions in Automation editor (#26741)
Replace "listed here" or "list of" with "added here"
2025-08-28 11:05:31 +02:00
Paulus Schoutsen 2a9846c598 Show configured area sensors on climate domain dashboard (#26737) 2025-08-28 08:34:45 +03:00
Paulus Schoutsen b243d56bee Show binary sensors with graphs on the security dashboard (#26738) 2025-08-28 08:26:44 +03:00
J. Nick Koston 6a372a165e Improve network adapter configuration discoverability (#26734) 2025-08-27 09:59:23 -05:00
Paul Bottein a5dad9bc22 Use entity picture for home favorite and update home dashboard icon (#26732)
* Add strategy icon

* Use entity picture for favorite
2025-08-27 16:33:48 +02:00
Bram Kragten 954e0a5f63 Merge branch 'rc' into dev 2025-08-27 15:09:31 +02:00
Bram Kragten 4dbd4eebaa Bumped version to 20250827.0 2025-08-27 15:08:57 +02:00
Aidan Timson 09b01df366 Translate integration filters (#26728)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2025-08-27 13:02:38 +00:00
Paul Bottein a76539c732 Revert background section (#26730) 2025-08-27 12:55:15 +00:00
Aidan Timson c7babe884c Add variables for analog clock background (#26726) 2025-08-27 12:36:33 +00:00
Paul Bottein ce83feec93 Use large section for summay view (#26729) 2025-08-27 12:35:45 +00:00
karwosts 150ee3fb12 Display sum/median labels in area card editor (#26721) 2025-08-27 14:35:10 +02:00
Wendelin 8fd3fcd323 Focus automation sidebar when open via keyboard (#26606)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2025-08-27 11:28:52 +00:00
Wendelin 6e3b3a53e4 Fix close automation bottom sheet on add (#26727) 2025-08-27 13:25:19 +02:00
Wendelin 22966485c7 Automation editor: New choose default styles and style fixes (#26708)
Co-authored-by: Simon Lamon <32477463+silamon@users.noreply.github.com>
2025-08-27 13:20:25 +02:00
karwosts 673ca8ba4b Use media selector for media_player.play_media (#26559) 2025-08-27 12:39:40 +02:00
Paul Bottein c8be25dfc2 Rename overview to home and add widget section (#26715) 2025-08-27 12:35:43 +02:00
Aidan Timson edaaa00038 Analog style for clock card (#26557)
Co-authored-by: Norbert Rittel <norbert@rittel.de>
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2025-08-27 10:15:06 +01:00
Wendelin 2de605d97a Automation editor: expand only root rows per default (#26712)
* Add default expand/collapse

* Readd expanded search param functionality
2025-08-27 12:10:53 +03:00
Paul Bottein 0b11302b1d Align storage life time design with used storage (#26724) 2025-08-27 09:52:56 +02:00
Norbert Rittel ddb224e145 Capitalize "Assist" as feature name in "Assist pipeline" (#26719)
Makes it consistent with all other occurrences of Assist in Frontend.
2025-08-27 08:47:15 +02:00
Paul Bottein 317149e51e Always show compact header for dashboards (#26706) 2025-08-26 20:09:57 +02:00
Norbert Rittel 51840b88b3 Add missing hyphens to compound adjectives with "specific" (#26717) 2025-08-26 18:56:48 +02:00
Norbert Rittel 319a1ad8c6 Fix description of battery energy sensors (#26714) 2025-08-26 16:13:38 +00:00
Wendelin d75c84750d Add indent style to automation action, condition, and option editors (#26709) 2025-08-26 18:10:17 +02:00
renovate[bot] 492a73e345 Update dependency @bundle-stats/plugin-webpack-filter to v4.21.3 (#26713)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-26 18:01:25 +02:00
Wendelin 64bf101c95 Automation Editor: Move overflow actions to sidebar only (#26707)
Move all overflow actions to sidebar
2025-08-26 18:00:54 +02:00
hanwg 876ced25f1 Rename subentry (#26574)
* rename subentry

* rename subentry

* update subentry

* remove title from update subentry

* format

---------

Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
2025-08-26 15:49:51 +00:00
Wendelin 72e3b72854 Fix automation action condition row left-chevron (#26705)
Enhance collapse logic to include condition block types in automation action row
2025-08-26 15:46:36 +00:00
Paulus Schoutsen 6ccd3d3b95 Tweak choose spacing (#26702) 2025-08-26 14:18:17 +00:00
Wendelin 5709cb6aa4 Add collapse/expand all for automations (#26695) 2025-08-26 15:21:03 +02:00
Wendelin 1fe7282b0e Fix action condition (#26703) 2025-08-26 13:56:49 +02:00
Paulus Schoutsen 6d29063b35 Adjust top-level spacing to move description a bit closer (#26698) 2025-08-26 13:16:53 +02:00
Paulus Schoutsen d3e0b94d27 Show tag name in tag trigger (#26697) 2025-08-26 13:16:26 +02:00
Wendelin f4f1f98433 Fix tablet sidebar RTL (#26700) 2025-08-26 13:14:42 +02:00
renovate[bot] 69e3d8e13d Update dependency eslint to v9.34.0 (#26694)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-26 08:12:36 +03:00
Paul Bottein 3ab6e389d3 Fixes dahsboard tab bar on mobile (#26690)
* Don't show toolbar in desktop

* Fix tab bar height

* Fix tab bar height

* rename to Add person

* Fix title

* Fix key
2025-08-25 15:41:49 +00:00
Paul Bottein 9cc85bc928 Fix hold action for picture element card in Android app (#26647)
Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2025-08-25 14:36:20 +00:00
Wendelin 4f4343d6c8 Automation editor mobile bottom sheet (#26680) 2025-08-25 16:27:57 +02:00
Petar Petrov 7ab27d620a Sum & Median Area card sensors (#26681)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-25 16:02:16 +02:00
Petar Petrov fa4ee71803 Fix rounding in energy dashboard (#26689) 2025-08-25 14:00:44 +00:00
renovate[bot] 82026250c5 Update dependency @material/web to v2.4.0 (#26678)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-25 15:28:19 +02:00
Douwe bc7533bb42 Add scene to button tile feature (#26520)
Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
2025-08-25 13:16:31 +00:00
Petar Petrov 7110c0381f Show detailed storage space info (#26686)
* Storage space breakdown

* update mock data

* update format

* new format

* clean up

* fix

* remove useless if

* use ha-tooltip and styleMap

* fix hover
2025-08-25 13:03:58 +00:00
Surya Prakash 87fa05accc Fix: Volume bar cut off at 100% in media player entity row (#26675) 2025-08-25 12:47:17 +00:00
dcapslock 56ee3f82fb Improve states tool performance (#26236)
Co-authored-by: Simon Lamon <32477463+silamon@users.noreply.github.com>
2025-08-25 14:15:56 +02:00
victorigualada 11872b076b Fix ha-alert icon z-index overlapping over other elements (#26685)
* Fix ha-alert icon z-index overlapping over other elements

* directly remove ha-alert's icon z-index property to fix overlapping
2025-08-25 14:14:37 +03:00
Kendell R 620db4238d Indirect -> direct use of @rspack/dev-server (#26665)
* Indirect -> direct use of @rspack/dev-server

* move to devdependencies
2025-08-25 08:26:38 +03:00
Surya Prakash ef78bec48d Fix: Enable tile button feature for entities with 'unknown' state (#26673)
## What this fixes
Fixes #26670

The button tile feature was incorrectly disabled for entities in the 'unknown' state. It should only be disabled for 'unavailable' entities, as a button is always pressable.

## Changes made
Removed `"unknown"` from the disabled states check in `hui-button-card-feature.ts`.

**Before:** Button was disabled if state was `"unavailable"` OR `"unknown"`
**After:** Button is disabled **only** if state is `"unavailable"`
2025-08-25 08:06:06 +03:00
Kendell R 4dcf2287ce Fix invalid CSS (#26677) 2025-08-24 21:26:07 +02:00
renovate[bot] a9766ed66b Update dependency core-js to v3.45.1 (#26667)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-23 22:03:05 +02:00
renovate[bot] 26a83feeb0 Update Yarn to v4.9.3 (#26662)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-23 22:02:49 +02:00
karwosts 8c5dd7cdba Fix shortcuts quickbar translation (#26666) 2025-08-23 22:02:35 +02:00
renovate[bot] 0d025a2355 Update dependency @rsdoctor/rspack-plugin to v1.2.3 (#26659)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-23 10:33:08 +02:00
karwosts 8216778d0c Fix last backup status counter on system page (#26655) 2025-08-23 09:46:43 +02:00
Paul Bottein fe16b689a8 Remove floors from the area overview dashboard (#26622)
* Remove floors from the area overview dashboard

* Refactor summaries view code

* Update src/panels/lovelace/strategies/overview/overview-home-view-strategy.ts

---------

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2025-08-23 07:44:47 +00:00
Aidan Timson 2653f6c874 Move section to other views/dashboards from edit dialog menu (#26621)
* Init

* Pass in lovelace

* Working move

* Finalise
2025-08-23 09:22:34 +02:00
Jan-Philipp Benecke 8093f7f4cb Add background opacity option to section styling (#26651)
* Add background opacity option to section styling

* Run updated prettier
2025-08-22 18:58:47 +02:00
Jan-Philipp Benecke b26c914ff9 Use dedicated translation key for none option in section background type (#26652) 2025-08-22 18:13:04 +02:00
Jan-Philipp Benecke b2fa97b6dc Add background styling options to section settings (#26369)
* Add background styling options to section settings

* Improve

* Fix linter

* Move to subkey

* Use hex instead of rgb to save in yaml

* Remove or
2025-08-22 12:51:07 +02:00
renovate[bot] 8a8bba422a Update dependency typescript-eslint to v8.40.0 (#26648)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-22 08:34:15 +03:00
Aidan Timson 76ca66b1b5 Change home and other zones edit dialog title to location name (#26637)
* Change home and other zone edit dialog title to location name

* Remove no longer used translations

* Use name as context

Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>

* Use name as context

* Add translation

* Use name as context

* Actually use the right key

* Use name as context

---------

Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
2025-08-22 08:13:10 +03:00
Paul Bottein 280dbfc958 Add "add" button to lovelace dashboard toolbar (#26644)
* Add add button to lovelace dashboard toolbar

* Add person and area

* Fix typing
2025-08-21 20:45:57 +02:00
karwosts 0b10ad3e78 No multiline strings in state-history-chart-timeline (#26646) 2025-08-21 17:39:54 +02:00
Bram Kragten c172f0c486 Bumped version to 20250811.1 2025-08-21 16:30:40 +02:00
Dav15Jr 1244ed73a2 Fix Inconsistent naming of "Input button" in Quickbar navigation (#26541)
* Fix Inconsistent naming of "Input button" in Quickbar navigation

* Fix Media Manage button default visibilty.

* undo wrong change
2025-08-21 16:29:44 +02:00
Petar Petrov e2aef205cc Filter hidden entities from group more-info (#26527) 2025-08-21 16:29:43 +02:00
Paul Bottein 7434c9345a Fix related entities click behavior in the more info dialog (#26525) 2025-08-21 16:29:43 +02:00
Bram Kragten 287ff17107 Update tabs when user data changes (#26524) 2025-08-21 16:29:42 +02:00
Wendelin 21309944e5 Fix ha-button icon padding (#26517) 2025-08-21 16:29:41 +02:00
Wendelin c0e39ffd67 Fix handling empty release notes in more-info-update (#26515)
Fix handling empty release nots in more-info-update
2025-08-21 16:29:40 +02:00
Bram Kragten 3da2cb3123 Fix style variable in base chart (#26509) 2025-08-21 16:29:37 +02:00
karwosts 7957bd1f25 Fix search in raw configuration editor (#26496) 2025-08-21 16:29:36 +02:00
Aidan Timson 04d0aa2f22 Remove "hassio" option from commands in quick bar (#26640) 2025-08-21 16:42:40 +03:00
Aidan Timson 4b901101da Fix voice assist setup success step translations (#26638) 2025-08-21 16:41:16 +03:00
Aidan Timson 4960284e2d Media player playback controls card feature (#26608)
* Setup

* Add buttons

* Fix

* Move to function

* Clean

* Cleanup

* Check client size

* Get width from host component

* Fix

* Spacing

* use current target

* Ensure state updates update render

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/panels/lovelace/card-features/hui-media-player-playback-card-feature.ts

* Resolve code suggestion type errors

* Resize observer not needed

---------

Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-21 10:28:43 +00:00
renovate[bot] 11d32300e9 Update dependency eslint-plugin-unused-imports to v4.2.0 (#26641)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-21 13:26:40 +03:00
Philipp Waller f131e93e63 Refactor automation editor event naming for consistency (#26634) 2025-08-21 13:23:59 +03:00
Wendelin 5a540dd889 Use automation sidebar in scripts (#26602) 2025-08-21 12:21:00 +02:00
Drinor Dalipi 3a70310f78 Follow-up: revert ultimate fallback to undefined in computeEntityEntryName (#26629) 2025-08-21 09:50:48 +02:00
Philipp Waller feed58c33e Add show-automation-editor event for custom cards & panels (#26613)
* expose showAutomationEditor functionality in ha-panel-custom

* drop connectedCallback change (leftover from earlier test)

* enhance documentation for showAutomationEditor method

* Add automation editor mixin and event declaration for show-automation-editor
2025-08-21 08:22:05 +03:00
Philipp Waller e5585e13fe Replace deprecated ESLint flag to remove warning (#26630)
replace deprecated ESLint flag
2025-08-21 04:43:17 +00:00
renovate[bot] edd49e1511 Update dependency marked to v16.2.0 (#26632)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-21 06:35:54 +02:00
Copilot 057fad55e8 Rename "Logbook" to "Activity" in user-facing strings (#26619)
* Initial plan

* Update user-facing strings from "Logbook" to "Activity"

Co-authored-by: balloob <1444314+balloob@users.noreply.github.com>

* Apply suggestions from code review

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: balloob <1444314+balloob@users.noreply.github.com>
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2025-08-20 17:36:32 +03:00
Aidan Timson d1a8165de2 Allow clock to show without background (#26554) 2025-08-20 17:31:25 +03:00
Petar Petrov 31c555445d Bar gauge card feature for sensors (#26585)
* Bar graph card feature for sensors

* use state color

* tweak name

* rename and improve colors

* Update src/panels/lovelace/card-features/hui-progress-bar-card-feature.ts

Co-authored-by: Paul Bottein <paul.bottein@gmail.com>

* rename to Bar gauge

* name fix

* Force dark-only themes to be in dark mode (#26583)

* Fetch translations for switch-as-x domains (#26566)

* Fetch translations for switch-as-x domains

* no cache

* Migrate ha-progress-ring to webawesome (#26542)

* Migrate all sl-animation usages to wa-animation (#26544)

* Remove unused shoelace animation import (#26584)

* Bump actions/checkout from 4.2.2 to 5.0.0 (#26586)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.2.2 to 5.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4.2.2...v5.0.0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 5.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Add date card feature (#26531)

* Add date card feature

* Rename from "date" to "date-set"

* Better label

* Fix

* Parse date for datetime

* Show accurate state of show_header_toggle in entities card editor (#26564)

* Add clipboard support to condition card conditions (#26535)

* Add copy paste support to condition card conditions

* Don't clear the clipboard

* Fix selection

* Add cut and duplicate

* Remove

* Fix event

* Fix picture glance mouse pointer (#26391)

* Fix picture glance mouse pointer

* More action updates

* Capitalize "Samba" as the name of the Open Source project (#26590)

Capitalize "Samba" as name for the Open Source project

* Update dependency @codemirror/language to v6.11.3 (#26589)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* Set default forecast option in weather forecast card editor (#26592)

* Add fan oscillate feature (#26519)

* First working fan-oscillate feature

This a first working impl, need at least to do:
- Tooltip not yet "Yes/No"
- Need implementation verification

* Use same strings as more info label for control tooltip

* Add missing label for editor

* Rename some variables

* Add fan features in gallery

* Fix lint:types by applying suggestions from code review

Co-authored-by: Aidan Timson <aidan@timmo.dev>

* fix lint new line after import

* fix typo

Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>

* fix event value type treating

* remove type magic as suggested

Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>

* Update localize.ts

Complete suggestion change to have tooltip

* fix lint by removing unused import

---------

Co-authored-by: Aidan Timson <aidan@timmo.dev>
Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>

* Lock file maintenance (#26598)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* Fix legend in devices-detail-graph (#26596)

* Change Suggest with AI label to Suggest (#26603)

* Automation row remember collapsed status (#26604)

* Summaries overview dashboard (#26594)

* Change categories to lights, climate and security

* Add entities grouped by devices

* Fix garage cover device class

* Rename category to summary

* Add media players categories

* Reduce spacing

* Remove person

* Remove translations

* Use media player cards

* Display entities without device

* Add missing entity category

* Update src/panels/lovelace/strategies/overview/helpers/overview-summaries.ts

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

* Add white space

---------

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

* Fix entities not showing due to JavaScript crash (fixes #25363) (#26599)

* Fix entity UI crash from undefined entity names (fixes #25363)

* Fix mock function type compatibility in test

- Update mock to handle string | undefined parameter
- Maintain test functionality while satisfying type checker

* Simplify approach based on reviewer feedback

- Use String() coercion to preserve numeric entity names (e.g., power strip outlets)
- Single line change instead of complex type validation across multiple files
- Revert stripPrefixFromEntityName to original (no longer needs null handling)
- Remove separate test file, update existing test to expect stringified numbers
- More conservative approach that preserves data rather than replacing with fallbacks

* History chart card feature (#26555)

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

* Fix space in overview welcome caption (#26618)

* Fix space in overview welcome caption

* Update src/panels/lovelace/strategies/overview/overview-home-view-strategy.ts

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Paul Bottein <paul.bottein@gmail.com>
Co-authored-by: karwosts <32912880+karwosts@users.noreply.github.com>
Co-authored-by: Simon Lamon <32477463+silamon@users.noreply.github.com>
Co-authored-by: Wendelin <12148533+wendevlin@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Aidan Timson <aidan@timmo.dev>
Co-authored-by: Norbert Rittel <norbert@rittel.de>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: pcan08 <155250376+pcan08@users.noreply.github.com>
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
Co-authored-by: Drinor Dalipi <45405217+Drinory@users.noreply.github.com>
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2025-08-20 17:27:05 +03:00
Paul Bottein 61bb5d33e5 Add home structure helper to overview dashboard (#26620)
* Use home structure in overview home

* Use home structure in summary views
2025-08-20 14:53:05 +02:00
Paulus Schoutsen 20a3bab5bc Fix space in overview welcome caption (#26618)
* Fix space in overview welcome caption

* Update src/panels/lovelace/strategies/overview/overview-home-view-strategy.ts
2025-08-20 09:48:59 +00:00
Petar Petrov e8d916acd7 History chart card feature (#26555)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2025-08-20 11:30:42 +02:00
Drinor Dalipi 8b73d664b4 Fix entities not showing due to JavaScript crash (fixes #25363) (#26599)
* Fix entity UI crash from undefined entity names (fixes #25363)

* Fix mock function type compatibility in test

- Update mock to handle string | undefined parameter
- Maintain test functionality while satisfying type checker

* Simplify approach based on reviewer feedback

- Use String() coercion to preserve numeric entity names (e.g., power strip outlets)
- Single line change instead of complex type validation across multiple files
- Revert stripPrefixFromEntityName to original (no longer needs null handling)
- Remove separate test file, update existing test to expect stringified numbers
- More conservative approach that preserves data rather than replacing with fallbacks
2025-08-20 06:10:13 +00:00
Paul Bottein 10dcc08068 Summaries overview dashboard (#26594)
* Change categories to lights, climate and security

* Add entities grouped by devices

* Fix garage cover device class

* Rename category to summary

* Add media players categories

* Reduce spacing

* Remove person

* Remove translations

* Use media player cards

* Display entities without device

* Add missing entity category

* Update src/panels/lovelace/strategies/overview/helpers/overview-summaries.ts

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

* Add white space

---------

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2025-08-19 19:48:43 +00:00
Wendelin fc5adc3753 Automation row remember collapsed status (#26604) 2025-08-19 12:13:35 +02:00
Paulus Schoutsen d1db8f456f Change Suggest with AI label to Suggest (#26603) 2025-08-19 11:39:12 +02:00
karwosts 0dd07a395a Fix legend in devices-detail-graph (#26596) 2025-08-19 09:12:30 +03:00
renovate[bot] 589771df5c Lock file maintenance (#26598)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-19 08:44:22 +03:00
pcan08 92812048dc Add fan oscillate feature (#26519)
* First working fan-oscillate feature

This a first working impl, need at least to do:
- Tooltip not yet "Yes/No"
- Need implementation verification

* Use same strings as more info label for control tooltip

* Add missing label for editor

* Rename some variables

* Add fan features in gallery

* Fix lint:types by applying suggestions from code review

Co-authored-by: Aidan Timson <aidan@timmo.dev>

* fix lint new line after import

* fix typo

Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>

* fix event value type treating

* remove type magic as suggested

Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>

* Update localize.ts

Complete suggestion change to have tooltip

* fix lint by removing unused import

---------

Co-authored-by: Aidan Timson <aidan@timmo.dev>
Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
2025-08-18 18:22:41 +03:00
Aidan Timson 9fc14d6627 Set default forecast option in weather forecast card editor (#26592) 2025-08-18 16:18:50 +03:00
renovate[bot] 3da6b85593 Update dependency @codemirror/language to v6.11.3 (#26589)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-18 15:59:13 +03:00
Norbert Rittel d2a4f481be Capitalize "Samba" as the name of the Open Source project (#26590)
Capitalize "Samba" as name for the Open Source project
2025-08-18 14:54:51 +02:00
karwosts e37f67c548 Fix picture glance mouse pointer (#26391)
* Fix picture glance mouse pointer

* More action updates
2025-08-18 13:33:10 +03:00
Aidan Timson e775a6770b Add clipboard support to condition card conditions (#26535)
* Add copy paste support to condition card conditions

* Don't clear the clipboard

* Fix selection

* Add cut and duplicate

* Remove

* Fix event
2025-08-18 13:10:58 +03:00
karwosts 4ba5ef6c37 Show accurate state of show_header_toggle in entities card editor (#26564) 2025-08-18 13:01:25 +03:00
Aidan Timson d528ab06d9 Add date card feature (#26531)
* Add date card feature

* Rename from "date" to "date-set"

* Better label

* Fix

* Parse date for datetime
2025-08-18 09:57:09 +00:00
dependabot[bot] 03a628cfe2 Bump actions/checkout from 4.2.2 to 5.0.0 (#26586)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4.2.2 to 5.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4.2.2...v5.0.0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 5.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-18 12:38:04 +03:00
Wendelin 973851b332 Remove unused shoelace animation import (#26584) 2025-08-18 10:42:59 +03:00
Simon Lamon 4c5795c276 Migrate all sl-animation usages to wa-animation (#26544) 2025-08-18 09:04:42 +02:00
Simon Lamon 41d016d96a Migrate ha-progress-ring to webawesome (#26542) 2025-08-18 09:02:32 +02:00
karwosts 22e647cad4 Fetch translations for switch-as-x domains (#26566)
* Fetch translations for switch-as-x domains

* no cache
2025-08-18 08:40:31 +03:00
karwosts b63dd9dbbf Force dark-only themes to be in dark mode (#26583) 2025-08-18 08:39:32 +03:00
renovate[bot] 3b3b9e269d Update dependency hls.js to v1.6.10 (#26581)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-18 08:33:39 +03:00
renovate[bot] d2c3b9ee83 Update dependency @lokalise/node-api to v15.2.1 (#26577)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-17 15:33:15 +02:00
renovate[bot] 5b50a8692b Update babel monorepo to v7.28.3 (#26576)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-17 15:32:52 +02:00
Paulus Schoutsen 8a8bbee8e0 Update theme color in web app manifests (#26572) 2025-08-17 10:19:40 +02:00
TheJulianJES 28e28d1417 Fix restrict-task-creation workflow (#26569) 2025-08-17 10:19:16 +02:00
renovate[bot] ea77a0f3d6 Update dependency @lokalise/node-api to v15.1.0 (#26567)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-16 21:23:51 +02:00
karwosts 10e09b238a Configurable todo item tap action (#26551)
* Configurable todo item tap action

* string
2025-08-15 16:33:50 +03:00
Simon Lamon f9cd2b66cb Migrate ha-fade-in to webawesome (#26543)
fade in
2025-08-15 13:29:34 +03:00
renovate[bot] b1c0fba8cf Update dependency @rsdoctor/rspack-plugin to v1.2.2 (#26553)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-15 13:27:29 +03:00
Norbert Rittel fdae6257b3 Consistently use "Home Assistant OS" as name (#26552) 2025-08-15 10:02:00 +02:00
Dav15Jr 6a48aea128 Fix media manage button default visibilty (#26548)
* Fix Inconsistent naming of "Input button" in Quickbar navigation

* Fix Media Manage button default visibilty.

---------

Co-authored-by: Simon Lamon <32477463+silamon@users.noreply.github.com>
2025-08-15 07:19:09 +00:00
Norbert Rittel a90b173671 Fix missing sentence-case in a few strings (#26547)
* Fix missing sentence-case in a few strings

* One more

* And another one
2025-08-15 09:10:00 +02:00
Dav15Jr d9971bfaa9 Fix Inconsistent naming of "Input button" in Quickbar navigation (#26541)
* Fix Inconsistent naming of "Input button" in Quickbar navigation

* Fix Media Manage button default visibilty.

* undo wrong change
2025-08-15 07:09:54 +00:00
renovate[bot] 369d56a809 Update dependency typescript-eslint to v8.39.1 (#26546)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-15 09:04:30 +02:00
Norbert Rittel 939a3cdf63 Fix missing sentence-casing in People settings (#26539)
* Fix missing sentence-casing in People settings

* Remove hyphen from "presence detection integration"
2025-08-14 16:56:02 +02:00
Wendelin 208fd0662c Migrate ha-button-toggle-group to webawesome (#26506)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2025-08-14 14:52:39 +02:00
renovate[bot] f133f246cb Update vaadinWebComponents monorepo to v24.8.5 (#25954)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-14 14:51:25 +02:00
Paul Bottein b9b8997d68 Makes ha-combo-box clear value compatible with vaadin 24.8 (#26530) 2025-08-14 14:50:24 +02:00
Aidan Timson 46c4a19a13 Fix conditional card options alignment (#26534) 2025-08-14 12:27:21 +00:00
Paul Bottein 8d63654211 Create overview dashboard with lights, covers, energy and favorite entities (#26504)
* Create overview dashboard with light, cover, energy and favorite

* Add welcome message

* Add person entities

* Add editor for favorite entities

* Don't use property
2025-08-14 11:56:35 +02:00
Wendelin 3bf25f125b Automation editor sidebar (#26413) 2025-08-14 10:06:16 +02:00
Petar Petrov 8c65876413 Filter hidden entities from group more-info (#26527) 2025-08-13 17:50:00 +02:00
Bram Kragten 2ab6d49553 Update tabs when user data changes (#26524) 2025-08-13 16:24:56 +02:00
Aidan Timson 67b0cf0952 Add small amount of extra padding to ohf card in about section (#26523) 2025-08-13 17:23:01 +03:00
Bram Kragten 5138276f8a Show password forgot link on mobile (#26526)
show password forgot link on mobile
2025-08-13 17:22:17 +03:00
Paul Bottein 30e6777529 Fix related entities click behavior in the more info dialog (#26525) 2025-08-13 16:08:52 +02:00
Aidan Timson 1686ab4b9d Add valve position card feature (#26511) 2025-08-13 15:04:44 +03:00
Wendelin b7102c0d7d Fix ha-button icon padding (#26517) 2025-08-13 13:40:44 +02:00
Petar Petrov d41d524850 Show battery in and out energy in Sankey chart (#26490) 2025-08-13 12:40:45 +03:00
renovate[bot] 4f05f6305a Update fullcalendar monorepo to v6.1.19 (#26516)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-13 09:50:40 +02:00
karwosts ba0b1239be Fix search in automation yaml editor (#26513) 2025-08-13 09:07:50 +03:00
Wendelin 708b68f35d Fix handling empty release notes in more-info-update (#26515)
Fix handling empty release nots in more-info-update
2025-08-13 09:03:25 +03:00
Aidan Timson 3108e98b97 Update ha-spinner component to webawesome (#26507) 2025-08-12 21:00:40 +02:00
Bram Kragten ba7609cc2c Fix style variable in base chart (#26509) 2025-08-12 17:14:29 +02:00
Bram Kragten 506fd7d480 center spinner 2025-08-12 15:30:47 +02:00
Bram Kragten 9767ebe1fb Show spinner when loading application credential config (#26510) 2025-08-12 15:25:18 +02:00
Aidan Timson 539e89e7b5 Add valve open/close card feature (#26488)
* Add valve open/close card feature

* Toggle button UI if no assumed state
2025-08-12 14:26:12 +02:00
karwosts a7eef81272 Fix search in raw configuration editor (#26496) 2025-08-12 14:31:38 +03:00
Aidan Timson 7986be103f Fix invalid loading margin while variables are loading (#26495)
* Fix invalid loading margin while variables are loading

* Fix invalid loading margin while variables are loading
2025-08-12 07:53:36 +03:00
karwosts 055e65c45e Add a dashboard condition based on user's location (#26401)
* Add a dashboard condition based on user's location

* Update src/translations/en.json

Co-authored-by: Norbert Rittel <norbert@rittel.de>

* Update src/data/person.ts

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

* Use multiple: true

---------

Co-authored-by: Norbert Rittel <norbert@rittel.de>
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2025-08-12 07:51:31 +03:00
renovate[bot] fe762e9ae4 Update dependency eslint to v9.33.0 (#26502)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-12 07:47:01 +03:00
pcan08 5267c6fdfc Add fan direction feature in gallery (#26499) 2025-08-11 19:40:32 +02:00
Andrew Jackson 8eff913845 Sort subentries within integration devices by title (#26497)
Sort subentries by title
2025-08-11 17:34:36 +00:00
renovate[bot] 1c845d0052 Update dependency lint-staged to v16.1.5 (#26498)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-11 19:22:34 +02:00
pcan08 60a1d25e1e Add fan direction feature (#26467)
* Create fan-direction feature

* Translate direction buttons tooltip

* Update src/translations/en.json

fix fan-direction label to sentence-cased

Co-authored-by: Norbert Rittel <norbert@rittel.de>

* Update src/translations/en.json

Remove usued translation

Co-authored-by: Paul Bottein <paul.bottein@gmail.com>

---------

Co-authored-by: Norbert Rittel <norbert@rittel.de>
Co-authored-by: Paul Bottein <paul.bottein@gmail.com>
2025-08-11 16:32:42 +03:00
Aidan Timson 3439d1d663 Add area, device, floor and formatted state to template editor completions (#26383) 2025-08-11 15:28:34 +02:00
renovate[bot] bf120d9cb2 Update dependency @rsdoctor/rspack-plugin to v1.2.1 (#26492)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-11 14:25:53 +03:00
Paul Bottein b5a024c879 Fix default alarm modes order when customizing it in card feature (#26491) 2025-08-11 14:13:02 +03:00
renovate[bot] 602d754e5e Update dependency typescript to v5.9.2 (#26372)
* Update dependency typescript to v5.9.2

* fix types

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
2025-08-11 11:11:15 +00:00
Wendelin b7c4f4029d Translate service worker update toast notification (#26487)
* Translate service worker update toast notification

* Update src/managers/notification-manager.ts

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

* Update src/managers/notification-manager.ts

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

* Fix toast translation args

---------

Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2025-08-11 14:07:32 +03:00
dependabot[bot] 7fdb5d4862 Bump actions/cache from 4.2.3 to 4.2.4 (#26489)
Bumps [actions/cache](https://github.com/actions/cache) from 4.2.3 to 4.2.4.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v4.2.3...v4.2.4)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-version: 4.2.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-11 13:32:14 +03:00
Aidan Timson bc52ab410c Split repeat building blocks in picker (#26385)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2025-08-11 11:07:41 +02:00
karwosts 3b0220fa92 Support multiple for StateSelector (#25716)
* Support `multiple` for StateSelector

* lint

* Fixup after merge
2025-08-11 11:24:37 +03:00
Bram Kragten 64e00e559f Bumped version to 20250811.0 2025-08-11 09:58:49 +02:00
Wendelin b407bd4c4f Fix first letter uppercase in some buttons (#26485) 2025-08-11 09:58:39 +02:00
Simon Lamon d466abf9c4 Fix diagnostic download (#26466)
Diagnostic fix 2
2025-08-11 09:58:38 +02:00
karwosts 4d98230145 Support button feature for input_button (#26444) 2025-08-11 09:58:38 +02:00
Petar Petrov 8a5bca0eb0 Show sankey chart in vertical layout on mobile (#26439)
* Show sankey chart in vertical layout on mobile

* ts fix
2025-08-11 09:58:37 +02:00
Petar Petrov 1638da858c Font improvements for Sankey chart (#26438)
* Use theme vars for sankey chart font

* improve font size calculation
2025-08-11 09:58:36 +02:00
Wendelin bfb11102cc Fix css var naming --ha-color-border-primary (#26433) 2025-08-11 09:58:35 +02:00
Wendelin a3d3539e82 Fix button start/end slot margins, add reduce-left-padding flag (#26431)
* Fix button start/end slot margins, add reduce-left-padding flag

* Always reduce padding when icons are there

* Revert icon padding changes
2025-08-11 09:58:35 +02:00
Timothy 1fc6cff857 Fix typo in Neutral80 color (#26430) 2025-08-11 09:58:34 +02:00
Wendelin c5d7eb5384 Fix plain button in legacy browsers (#26426) 2025-08-11 09:58:33 +02:00
karwosts 759e6eba35 Fix mqtt config panel (#26422) 2025-08-11 09:58:32 +02:00
karwosts 153129e066 Fix a dangerous button color (#26418) 2025-08-11 09:58:31 +02:00
Aidan Timson 7bf3c7273e Fix Mod-S (Ctrl-S/Cmd-S) support for automation/scene/script YAML editors (#26412)
* Fix automation and script yaml mode Mod-S (Ctrl/Cmd-S) support

* Fix manual script editor

* Fix manual automation editor save

* Fix scene yaml mode
2025-08-11 09:58:31 +02:00
Wendelin 08765e6ce2 Add border radius css var ha prefix (#26411) 2025-08-11 09:58:30 +02:00
Wendelin a60c9f788d Fix first letter uppercase in some buttons (#26485) 2025-08-11 10:47:06 +03:00
karwosts d9c297c06a Improve robustness of UI for if/then action (#26477) 2025-08-11 08:40:42 +03:00
renovate[bot] 3789bebb2b Update dependency @bundle-stats/plugin-webpack-filter to v4.21.2 (#26480)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-11 08:36:51 +03:00
renovate[bot] bbecf5f368 Update dependency hls.js to v1.6.9 (#26474)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-10 14:30:38 +02:00
renovate[bot] e580b30219 Update dependency @rsdoctor/rspack-plugin to v1.2.0 (#26471)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-10 14:30:12 +02:00
renovate[bot] ed8c8ad3e3 Update dependency @rsdoctor/rspack-plugin to v1.1.11 (#26470)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-09 15:19:04 +02:00
Simon Lamon 4f61d5689b Fix diagnostic download (#26466)
Diagnostic fix 2
2025-08-09 15:17:59 +02:00
renovate[bot] 60a18185d7 Update dependency @tsparticles/engine to v3.9.1 (#26420)
* Update dependency @tsparticles/engine to v3.9.1
2025-08-09 06:19:38 +00:00
karwosts e0246b8488 Support button feature for input_button (#26444) 2025-08-09 07:52:14 +02:00
renovate[bot] 1cd0fae84a Update dependency @awesome.me/webawesome to v3.0.0-beta.4 (#26465)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-08 21:42:37 +02:00
renovate[bot] e8a1ebbff4 Update dependency fs-extra to v11.3.1 (#26464)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-08 19:38:28 +02:00
karwosts c5010b8502 Fix some date-range bugs (#26441) 2025-08-08 16:57:54 +03:00
Petar Petrov a7db401b62 Show sankey chart in vertical layout on mobile (#26439)
* Show sankey chart in vertical layout on mobile

* ts fix
2025-08-08 16:37:44 +03:00
Petar Petrov 49c7dad6eb Font improvements for Sankey chart (#26438)
* Use theme vars for sankey chart font

* improve font size calculation
2025-08-08 08:43:59 +02:00
Aidan Timson 521c3d40b7 Add missing button feature translation (#26437) 2025-08-08 08:42:45 +02:00
renovate[bot] 709a1d2ef0 Update dependency typescript-eslint to v8.39.0 (#26446)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-08 08:42:06 +02:00
renovate[bot] 3c5d7b97d1 Update dependency core-js to v3.45.0 (#26449)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-08 08:41:49 +02:00
renovate[bot] 9165c8bc57 Update dependency hls.js to v1.6.8 (#26451)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-08 08:40:58 +02:00
Luke Mondy 0b3e4eab23 Add 'Not' to lovelace visibility conditions (#26408)
:Add 'Not' to lovelace visibility conditions
2025-08-07 16:40:27 +03:00
Wendelin 39d14c943c Revert "Add Mobile team and design has codeowner of the theme colors" (#26432)
Revert "Add Mobile team and design has codeowner of the theme colors (#26428)"

This reverts commit 9588987e30.
2025-08-07 15:06:41 +02:00
Wendelin 09469be93f Fix css var naming --ha-color-border-primary (#26433) 2025-08-07 15:06:13 +02:00
karwosts 6e215870ef Fix mqtt config panel (#26422) 2025-08-07 16:06:07 +03:00
Wendelin d5985dcaaf Fix button start/end slot margins, add reduce-left-padding flag (#26431)
* Fix button start/end slot margins, add reduce-left-padding flag

* Always reduce padding when icons are there

* Revert icon padding changes
2025-08-07 15:05:56 +02:00
Timothy bbd9d8887d Fix typo in Neutral80 color (#26430) 2025-08-07 10:07:12 +00:00
Timothy 9588987e30 Add Mobile team and design has codeowner of the theme colors (#26428) 2025-08-07 09:56:47 +00:00
Wendelin 52c05a4426 Fix plain button in legacy browsers (#26426) 2025-08-07 09:50:05 +02:00
renovate[bot] e8224df4e5 Update dependency echarts to v6 (#26356)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-07 09:41:59 +03:00
karwosts 83a6df1621 Fix a dangerous button color (#26418) 2025-08-07 07:40:31 +02:00
renovate[bot] c46ebc8d3e Update dependency marked to v16.1.2 (#26423)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-07 07:38:25 +02:00
Aidan Timson fca530411f Fix Mod-S (Ctrl-S/Cmd-S) support for automation/scene/script YAML editors (#26412)
* Fix automation and script yaml mode Mod-S (Ctrl/Cmd-S) support

* Fix manual script editor

* Fix manual automation editor save

* Fix scene yaml mode
2025-08-06 18:52:41 +02:00
Norbert Rittel c2c64b9923 Fix summary for Choose building block by counting options (#26409)
* Fix summary for Choose building block

* Sentence-case "If-then" to match label of that building block
2025-08-06 18:52:03 +02:00
renovate[bot] 9968c27a8e Update dependency lint-staged to v16.1.4 (#26414)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-06 18:45:36 +02:00
Wendelin 96796ac5da Add border radius css var ha prefix (#26411) 2025-08-06 12:59:34 +00:00
Bram Kragten 550e4cd4aa Bumped version to 20250806.0 2025-08-06 14:33:58 +02:00
Bram Kragten f6041c5cbb add save button to AI suggestions settings (#26407) 2025-08-06 14:32:48 +02:00
Bram Kragten 42f65c2ca1 Fix buttons in button row (#26405) 2025-08-06 14:32:46 +02:00
Bram Kragten 588f171f7f Update zwave js buttons (#26404) 2025-08-06 14:32:44 +02:00
Bram Kragten bd1445840d Update color variables (#26403) 2025-08-06 14:32:42 +02:00
Bram Kragten 37def6d3e4 add save button to AI suggestions settings (#26407) 2025-08-06 14:27:58 +02:00
Bram Kragten 013d603ba0 Fix buttons in button row (#26405) 2025-08-06 11:15:09 +00:00
Bram Kragten b76407d28d Update zwave js buttons (#26404) 2025-08-06 13:13:22 +02:00
Bram Kragten 4e969ccf09 Update color variables (#26403) 2025-08-06 10:42:21 +00:00
Bram Kragten 97a0903cec Bumped version to 20250805.0 2025-08-05 15:21:31 +02:00
Bram Kragten e2525a3d07 Fix colors in network graphs (#26397) 2025-08-05 15:21:22 +02:00
Bram Kragten 8bc0f5a42c Fix network graph not rendering (#26396) 2025-08-05 15:21:21 +02:00
Jan-Philipp Benecke bf7e8ffd24 Add localization for third-party data reporting in the Z-Wave JS dashboard (#26395)
* Add localization for third-party data reporting in the Z-Wave JS dashboard

* Run prettier
2025-08-05 15:21:21 +02:00
Stefan Agner 255e598c65 Fix System information dialog unhealthy/unsupported list (#26393)
The System Information dialog was not displaying translated list of
unhealthy and unsupported reasons because the wrong translation keys
were used. This commit updates the translation keys to the correct
ones.
2025-08-05 15:21:19 +02:00
karwosts 5e22178225 Fix energy now button (#26384)
Update hui-energy-period-selector.ts
2025-08-05 15:21:19 +02:00
Wendelin 56b7a6abec Improve ha button radius variables (#26382)
* Improve ha button radius variables

* fixes

---------

Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2025-08-05 15:21:18 +02:00
Wendelin f34c4a11af Improve neutral color palette (#26381)
Improve neutral, add docs, removed unused var.
2025-08-05 15:21:17 +02:00
Stefan Agner 102689b711 Remove eMMC specific references in disk life time handling (#26379)
* Remove eMMC specific references in disk life time handling

Remove eMMC specific calculations and references in the disk life
time handling to generalize the code for all disk types. This includes
updating translations and UI components to reflect a more generic
approach to disk life time metrics.

* Assume 30 MB/s as the speed for disk operations

The previous code tried to estimate based on disk type, 30 MB/s for
eMMC devices and 10 MB/s for others. However, this did not work
correctly since the disk_life_time returns null for non-eMMC devices,
leading to 30 MB/s being used for all devices.

Now disk_life_time is not a eMMC indicator anymore. Simply assume a
constant speed of 30 MB/s for all disk operations explicitly.
2025-08-05 15:21:16 +02:00
Wendelin b16d769192 Fix ha-buttons (#26373)
* Fix ha-button supervisor network

* Fix button appearance for entity row

* Fix logs button menu mobile width

* Fix new logs indicator
2025-08-05 15:21:15 +02:00
Jan-Philipp Benecke 29bcacc64e Improve Z-Wave JS config dashboard styling (#26368) 2025-08-05 15:21:14 +02:00
Jan-Philipp Benecke c7f3331373 Do not show AI suggestion button when no inputs in save dialog (#26357) 2025-08-05 15:21:13 +02:00
Squazel c32444b70c Fix picture-glance card icon styling for unavailable/unknown entities (#26352) 2025-08-05 15:21:12 +02:00
Wendelin 11c6b90eb0 Fix dialog secondary button design (#26344) 2025-08-05 15:21:11 +02:00
Simon Lamon f7a17598f0 Fix diagnostic download on integration level (#26341) 2025-08-05 15:21:10 +02:00
Bram Kragten 56967bc0c1 Add support for sub config flows in conversation agent picker (#26336) 2025-08-05 15:21:09 +02:00
Bram Kragten cdfd6431c3 Fix colors in network graphs (#26397) 2025-08-05 15:14:21 +02:00
Bram Kragten c363995718 Fix network graph not rendering (#26396) 2025-08-05 15:05:23 +02:00
Jan-Philipp Benecke 53497aa632 Add localization for third-party data reporting in the Z-Wave JS dashboard (#26395)
* Add localization for third-party data reporting in the Z-Wave JS dashboard

* Run prettier
2025-08-05 13:00:14 +02:00
Stefan Agner 8d89b0e57f Fix System information dialog unhealthy/unsupported list (#26393)
The System Information dialog was not displaying translated list of
unhealthy and unsupported reasons because the wrong translation keys
were used. This commit updates the translation keys to the correct
ones.
2025-08-05 12:10:51 +02:00
Stefan Agner 92cf8b5579 Remove eMMC specific references in disk life time handling (#26379)
* Remove eMMC specific references in disk life time handling

Remove eMMC specific calculations and references in the disk life
time handling to generalize the code for all disk types. This includes
updating translations and UI components to reflect a more generic
approach to disk life time metrics.

* Assume 30 MB/s as the speed for disk operations

The previous code tried to estimate based on disk type, 30 MB/s for
eMMC devices and 10 MB/s for others. However, this did not work
correctly since the disk_life_time returns null for non-eMMC devices,
leading to 30 MB/s being used for all devices.

Now disk_life_time is not a eMMC indicator anymore. Simply assume a
constant speed of 30 MB/s for all disk operations explicitly.
2025-08-05 10:42:42 +02:00
karwosts 6068c32176 Pass narrow through parallel/sequence automation actions (#26386) 2025-08-04 17:51:17 +02:00
karwosts 38893324af Fix energy now button (#26384)
Update hui-energy-period-selector.ts
2025-08-04 16:19:26 +02:00
Wendelin a39ab3c174 Improve ha button radius variables (#26382)
* Improve ha button radius variables

* fixes

---------

Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2025-08-04 12:58:12 +00:00
Christoph 797d2be5bf show spinner on update button (during update installation) (#26110)
* scroll to top when installing an update

* Revert "scroll to top when installing an update"

This reverts commit d0051b0c4c.

* add progress spinner to update button

* refactor disabled logic for update/skip button

* do not run update when disabled button is clicked

* refactor: use new ha-button to show progress

* refactor: move functions to update.ts
2025-08-04 14:51:38 +02:00
Wendelin 99a91e1019 Improve neutral color palette (#26381)
Improve neutral, add docs, removed unused var.
2025-08-04 12:24:42 +00:00
Wendelin 5de8d07ce0 Fix ha-buttons (#26373)
* Fix ha-button supervisor network

* Fix button appearance for entity row

* Fix logs button menu mobile width

* Fix new logs indicator
2025-08-04 14:18:31 +02:00
dependabot[bot] 3a31a4a721 Bump home-assistant/wheels from 2025.03.0 to 2025.07.0 (#26375)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-04 11:01:02 +02:00
Squazel 05f4419a92 Fix picture-glance card icon styling for unavailable/unknown entities (#26352) 2025-08-04 08:16:11 +02:00
renovate[bot] 5ea8feb86b Update rspack monorepo to v1.4.11 (#26365)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-04 06:52:21 +02:00
Jan-Philipp Benecke 8fd70b3ae6 Improve Z-Wave JS config dashboard styling (#26368) 2025-08-04 06:50:35 +02:00
renovate[bot] 343aa40bc8 Update dependency @types/luxon to v3.7.1 (#26351)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-03 08:10:32 +02:00
Jan-Philipp Benecke 6022f9a77e Do not show AI suggestion button when no inputs in save dialog (#26357) 2025-08-03 08:10:06 +02:00
renovate[bot] bd9de0680e Update dependency @types/luxon to v3.7.0 (#26342)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-01 17:09:44 +02:00
Simon Lamon b8000d5bc1 Fix diagnostic download on integration level (#26341) 2025-08-01 13:16:09 +02:00
Wendelin c6efa1127f Fix dialog secondary button design (#26344) 2025-08-01 13:13:42 +02:00
Bram Kragten 688a3d91d3 Add support for sub config flows in conversation agent picker (#26336) 2025-08-01 13:13:28 +02:00
Timothy 68151a2a70 Add Bruno and Timo as codeowners of the external_app folders (#26345) 2025-08-01 11:49:47 +02:00
Bram Kragten b01ab9234b Bumped version to 20250731.0 2025-07-31 16:54:24 +02:00
Wendelin ad39228dea Fix line-height, fix script editor buttons (#26337)
* Fix line-height

* Fix script root buttons
2025-07-31 16:54:03 +02:00
Wendelin 8cc48cdecb Use tilecard button feature editor (#26335)
Use button feature editor
2025-07-31 16:54:02 +02:00
Wendelin 524e89acf0 Revert "Use query params instead of path for media browser navigate ids" (#26333) 2025-07-31 16:54:01 +02:00
Wendelin 48f6b34882 Fix ha-button with missing label and links (#26332) 2025-07-31 16:54:00 +02:00
Bram Kragten 44d9185574 Fix area picker text alignment in voice wizard (#26330) 2025-07-31 16:53:59 +02:00
Joost Lekkerkerker 51ff6c6564 Use underscores in AI task name (#26327) 2025-07-31 16:53:58 +02:00
Franck Nijhof b49b8e3db8 Add weekdays to time trigger (#25908)
* Add weekdays to time trigger

* Update src/translations/en.json

Co-authored-by: Norbert Rittel <norbert@rittel.de>

* Localization changes

---------

Co-authored-by: Norbert Rittel <norbert@rittel.de>
Co-authored-by: Simon Lamon <32477463+silamon@users.noreply.github.com>
2025-07-31 16:53:57 +02:00
Wendelin c2ca556151 Fix line-height, fix script editor buttons (#26337)
* Fix line-height

* Fix script root buttons
2025-07-31 16:52:36 +02:00
Wendelin df86b27af4 Use tilecard button feature editor (#26335)
Use button feature editor
2025-07-31 13:27:40 +02:00
Wendelin eba1f401cc Fix ha-button with missing label and links (#26332) 2025-07-31 12:40:17 +02:00
Wendelin 19c2f9c9e8 Revert "Use query params instead of path for media browser navigate ids" (#26333) 2025-07-31 12:38:52 +02:00
Bram Kragten 4250447d14 Fix area picker text alignment in voice wizard (#26330) 2025-07-31 11:52:33 +02:00
Joost Lekkerkerker 4666197f28 Use underscores in AI task name (#26327) 2025-07-30 21:52:09 +02:00
Franck Nijhof a5ca36c93f Add weekdays to time trigger (#25908)
* Add weekdays to time trigger

* Update src/translations/en.json

Co-authored-by: Norbert Rittel <norbert@rittel.de>

* Localization changes

---------

Co-authored-by: Norbert Rittel <norbert@rittel.de>
Co-authored-by: Simon Lamon <32477463+silamon@users.noreply.github.com>
2025-07-30 19:45:10 +02:00
Norbert Rittel a88950e16c Correct the setup steps for Matter sharing in Google Home app (#26322)
Correct the setup steps in the Google Home app
2025-07-30 19:40:41 +02:00
Bram Kragten c013c5ec64 Merge branch 'rc' into dev 2025-07-30 16:18:36 +02:00
Bram Kragten 53d5d0efbd Merge branch 'master' into rc 2025-07-30 16:18:22 +02:00
Bram Kragten 3577991553 Bumped version to 20250730.0 2025-07-30 16:16:28 +02:00
Wendelin fa758f2bee Redesign ha-button (#25564)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2025-07-30 16:15:18 +02:00
Douwe 6dbfc2f4ed Add new card feature: button (#26165)
Co-authored-by: Simon Lamon <32477463+silamon@users.noreply.github.com>
Co-authored-by: Wendelin <12148533+wendevlin@users.noreply.github.com>
2025-07-30 16:13:05 +02:00
Petar Petrov b355556c07 Improve network graph layout (#26268) 2025-07-30 15:55:06 +02:00
renovate[bot] fec336260e Pin dependency @types/culori to 4.0.0 (#26318)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-30 14:07:48 +02:00
Wendelin 3e67d91d1a Add color palettes (#26271) 2025-07-30 13:51:14 +02:00
karwosts 641e406502 Don't allow view URL to be a number (#26313) 2025-07-29 21:33:58 +02:00
renovate[bot] 073ba22233 Update dependency @bundle-stats/plugin-webpack-filter to v4.21.1 (#26316)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-29 21:29:30 +02:00
Norbert Rittel 6b4a4e6024 Fix doubled plural in "add-on(s) repositories / capabilities" (#26310)
The string "Manage add-on repositories" has it correct, so this makes the fixed ones more consistent, too.
2025-07-29 10:05:13 +02:00
karwosts 7d8b418a81 Fix some instability in ha-selector-object (#26301)
Fix some instability in ha-object-selector
2025-07-29 09:50:58 +03:00
karwosts c14425b2d1 Allow picture card to serve media images (#26291)
* Allow picture card to serve media images

* small adjustments
2025-07-29 09:49:08 +03:00
renovate[bot] 4740a71bdd Update dependency eslint to v9.32.0 (#26309)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-28 22:12:27 +02:00
dependabot[bot] 6d0e0158ea Bump relative-ci/agent-action from 3.0.0 to 3.0.1 (#26307)
Bumps [relative-ci/agent-action](https://github.com/relative-ci/agent-action) from 3.0.0 to 3.0.1.
- [Release notes](https://github.com/relative-ci/agent-action/releases)
- [Commits](https://github.com/relative-ci/agent-action/compare/v3.0.0...v3.0.1)

---
updated-dependencies:
- dependency-name: relative-ci/agent-action
  dependency-version: 3.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-28 17:03:04 +03:00
renovate[bot] e966d6f4f4 Update rspack monorepo to v1.4.10 (#26300)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-27 22:11:12 +02:00
karwosts b99bb60cd0 Cleanup some selectors firing double value-changed events (#26302) 2025-07-27 22:10:30 +02:00
karwosts 080c79234c Fix typo in attribute (#26303)
Update hui-action-editor.ts
2025-07-27 22:09:16 +02:00
karwosts 9ad887942e Add raindrops to lightning-rainy state SVG (#26298) 2025-07-27 07:54:25 +02:00
renovate[bot] 27dfa514e7 Update dependency @lokalise/node-api to v15 (#26297)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-27 07:50:01 +02:00
renovate[bot] ab5c5389e8 Update dependency @rsdoctor/rspack-plugin to v1.1.10 (#26295)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-27 07:49:07 +02:00
renovate[bot] 219679bce9 Update rspack monorepo to v1.4.9 (#26289)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-25 14:43:41 +02:00
Chai aca4a1f86d Fix for integration 'Add entry' unnecessary dialogs (#26285) 2025-07-25 11:42:34 +00:00
Paulus Schoutsen f428d6b3f2 Add download device info button (#26278)
* Add download device info button

* Update src/panels/config/core/ha-config-section-analytics.ts

* Guard download support

* Update src/translations/en.json
2025-07-24 19:53:04 +00:00
renovate[bot] 109c3e86d9 Lock file maintenance (#26230)
* Lock file maintenance

* Remove duplicated packages

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Simon Lamon <32477463+silamon@users.noreply.github.com>
2025-07-24 19:02:51 +00:00
karwosts e4b6c3fd4d Disallow special characters in view URL (#26280)
* Disallow special characters in view URL
2025-07-24 18:58:26 +00:00
renovate[bot] 43f1d9be44 Update dependency typescript-eslint to v8.38.0 (#26283)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-24 20:05:15 +02:00
renovate[bot] 5c405201b2 Update dependency eslint-plugin-lit-a11y to v5.1.1 (#26279)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-24 19:25:39 +03:00
karwosts 9fc91fbbcc Fix spelling, remove errant apostrophe (#26277)
Update en.json
2025-07-24 17:06:57 +03:00
Norbert Rittel d9e3f2c15f Three smaller fixes in user-facing strings (#26276)
- change "eg." to "e.g." (four other occurrences are correct)
- add a comma after "Enter your email address, …" as the noun in the second part of the sentence changes to "we"
- sentence-case "Newest version"
2025-07-24 15:10:35 +03:00
Bram Kragten a885c7e358 Merge branch 'rc' 2025-07-18 10:32:20 +02:00
Bram Kragten fa968f49c1 Bumped version to 20250702.3 2025-07-18 10:32:06 +02:00
Petar Petrov cf3c40f5f7 Fix "Cancel exclusion" button for Z-Wave (#26188) 2025-07-18 10:31:56 +02:00
Petar Petrov 361474885f Fix entity renaming when adding a new device (#26177) 2025-07-18 10:31:55 +02:00
Petar Petrov 1e06046bd6 Fix number format in statistics charts (#26176)
fix number format in statistics charts
2025-07-18 10:31:54 +02:00
Petar Petrov 4c940e62f3 Fix entities link on integration page (#26167) 2025-07-18 10:31:53 +02:00
dcapslock 7631c409e1 Improve performance of Helpers config page (#26153) 2025-07-18 10:31:52 +02:00
Bram Kragten 6a3c58d20f Merge branch 'rc' 2025-07-10 21:35:08 +02:00
Bram Kragten a87afe9fb3 Bumped version to 20250702.2 2025-07-10 21:34:53 +02:00
Christoph 61fe8983f3 do not set "___ADD_NEW___" value in ha-floor-picker (#26102) 2025-07-10 21:34:33 +02:00
Ezra Freedman c10410ade3 Weather card smallest width is not set correctly (#26082)
set result.width, not result.height
2025-07-10 21:34:32 +02:00
Yosi Levy 761fded9e3 RTL fixes for 7-25 (#26074) 2025-07-10 21:34:32 +02:00
karwosts b87fbe7a1e Fix default range icon (#26069) 2025-07-10 21:34:31 +02:00
karwosts 7fdf824e97 Revert changes to persistent notification in sidebar (#25984) 2025-07-10 21:34:30 +02:00
Bram Kragten fe946eb75b Merge branch 'rc' 2025-07-04 13:53:23 +02:00
Bram Kragten 3bfbe4bde6 Bumped version to 20250702.1 2025-07-04 13:53:08 +02:00
c0ffeeca7 7963a97358 Terminology: change controller to adapter (#26051)
* Terminology: change controller to adapter

* Update src/translations/en.json

Co-authored-by: AlCalzone <d.griesel@gmx.net>

* Apply suggestions from code review

---------

Co-authored-by: AlCalzone <d.griesel@gmx.net>
2025-07-04 13:52:37 +02:00
Ezra Freedman 0ed2b5966e Prevent uncaught TypeError on HuiWeatherForecastCard render (#26038) 2025-07-04 13:52:37 +02:00
Paul Bottein 70c5f77aa7 Fix play media action (#26035) 2025-07-04 13:52:36 +02:00
Paul Bottein 1013647249 Fix zoom in statistic chart (#26034) 2025-07-04 13:52:35 +02:00
Paul Bottein 45e9c51525 Reduce media selector size (#26033) 2025-07-04 13:52:34 +02:00
Bram Kragten bbe3a9e0c2 Merge branch 'rc' 2025-07-02 13:45:20 +02:00
Bram Kragten 33ea02208a Bumped version to 20250702.0 2025-07-02 13:44:53 +02:00
Bram Kragten cf531cd935 Disable fullscreen in trigger detail dialog (#26030) 2025-07-02 13:44:27 +02:00
Paul Bottein 232649c0cd Improve styling of the code editor in fullscreen mode (#26029) 2025-07-02 13:44:26 +02:00
Bram Kragten 1db8ef37a2 Dont fetch device actions on first updated (#26028) 2025-07-02 13:44:25 +02:00
Paul Bottein eecd765d09 Fix UI jump when using drag and drop in areas strategy editor (#26026) 2025-07-02 13:44:25 +02:00
Paul Bottein 3d75831623 Add missing domain icon import in area controls (#26023) 2025-07-02 13:44:24 +02:00
Paul Bottein c1934e0b9a Add missing area helper (#26022) 2025-07-02 13:44:23 +02:00
karwosts c0e9c3b9dc Fix glitchy 'show' checkboxes on integration page (#26021) 2025-07-02 13:44:22 +02:00
Paul Bottein c3f0bba4a3 20250701.0 (#26019) 2025-07-01 15:04:35 +02:00
Paul Bottein 0026ee7563 Bumped version to 20250701.0 2025-07-01 15:02:51 +02:00
Paul Bottein 61f1c8cbd4 Force narrow style for action, condition and trigger in blueprint (#26018) 2025-07-01 15:02:17 +02:00
Paul Bottein e0b32ea789 Increase target area in tile card and area card (#26017) 2025-07-01 15:02:17 +02:00
Paul Bottein 96bbfe8a93 Add dashboard title to strategy editor (#26015) 2025-07-01 15:02:16 +02:00
Paul Bottein 93837f01f7 Avoid selector to take to much space in action calls (#26014) 2025-07-01 15:02:15 +02:00
Ezra Freedman d0737082a5 Fix translation in the integration page for entities (#26009)
add call to localize
2025-07-01 15:02:14 +02:00
Paul Bottein 57da4d3499 Fix object selector not displayed (#26007) 2025-07-01 15:02:13 +02:00
Paul Bottein 6e84fee791 Do not display quality scale for custom integrations (#26006) 2025-07-01 15:02:12 +02:00
Paul Bottein 2e223e637b Improve device row in integration page (#26005)
Improve device row in config entry page
2025-07-01 15:02:11 +02:00
Paul Bottein 3e45821fd0 Allow to re-order floors in areas dashboard (#26002)
* Allow to re-order floors in areas dashboard

* Move drag handle to right

* Improve typings

* Only show drag handle if there is at least 2 floors
2025-07-01 15:02:10 +02:00
Simon Lamon b16087d5b5 Fix fullscreen yaml editor (transparency background) (#25989)
Fix fullscreen editor (transparency background)
2025-07-01 15:02:10 +02:00
Norbert Rittel 6300bfb200 Fix grammar of Light, Sensor and Tile card descriptions (#25988)
* Fix grammar of Light, Sensor and Entity card descriptions

* Capitalize "Tile card" as a name

* Apply same change to Entity badge description
2025-07-01 15:02:09 +02:00
Simon Lamon 05a9f69c9e Pass area control service calls through hass (#25986)
Connection logging
2025-07-01 15:02:08 +02:00
Norbert Rittel e306e29d95 Fix sentence-casing, spelling and grammar issues (#25981)
* Fix sentence-casing, spelling and grammar issues

* Add "IP information" to the list

* More sentence-casing issues
2025-07-01 15:02:07 +02:00
Norbert Rittel 619974ffdb Dev Tools: Remove excessive space from "Input date times" (#25973)
Remove excessive space from "input date times"
2025-07-01 15:02:06 +02:00
Paul Bottein 6f753c4909 Use entity format state if only one entity for that domain in the area card (#25964)
Use entity format state if only one entity is area card
2025-07-01 15:02:05 +02:00
Paul Bottein a0a2b5f065 20250627.0 (#25971) 2025-06-27 13:51:57 +02:00
Paul Bottein 5430325127 Bumped version to 20250627.0 2025-06-27 13:50:14 +02:00
Paul Bottein 56d3cf7f1e Use areas dashboard name in the top bar (#25969) 2025-06-27 13:49:10 +02:00
Paul Bottein b39e9c38b9 Bump vaadin to 24.7.9 (#25963) 2025-06-27 13:49:09 +02:00
Bram Kragten c065efc52f Disable fullscreen editor for editors that are already fullscreen (#25959)
* Disabled fullscreen editor for editors that are already fullscreen

* Update ha-code-editor.ts
2025-06-27 13:49:08 +02:00
Paul Bottein caaec7d34d Fix expand icon for entries and sub entries (#25955) 2025-06-27 13:49:07 +02:00
Bram Kragten 76509d8bd4 Bumped version to 20250626.0 2025-06-26 16:44:40 +02:00
Paul Bottein 11fcab87d4 Revert vaadin to 24.7.7 (#25953) 2025-06-26 16:44:05 +02:00
Paul Bottein bfa0b8c0fc Don't limit combo-box dropdown size (#25952) 2025-06-26 16:44:04 +02:00
Bram Kragten f54312a7bc Load title when fetching flow (#25951) 2025-06-26 16:44:03 +02:00
Bram Kragten 8ff3f7733f Fix filtering on device in entities config panel (#25948)
* Fix filtering on device in entities config panel

* fix

* set filters from url twice to catch race...
2025-06-26 16:44:02 +02:00
Paul Bottein def8e8a713 Disable escape key to close edit card dialog (#25947) 2025-06-26 16:44:01 +02:00
Bram Kragten e675283fcc Add label to version number (#25942)
Add label
2025-06-26 16:44:00 +02:00
Bram Kragten 1900cce7f9 make sure header is always shown in data entry flow (#25941) 2025-06-26 16:44:00 +02:00
Bram Kragten 8e2c943dff add version number to integration page (#25940)
* add version number to integration page

* Update ha-config-integration-page.ts
2025-06-26 16:43:59 +02:00
Bram Kragten 8c5beb724f Use different icon for services (#25939) 2025-06-26 16:43:58 +02:00
Paul Bottein b9fb981fb2 Remove alert classes and only use slot sensors for areas dashboard (#25937)
* Remove alert classes and only used slot sensors for areas dashboard

* Rename group to sensors

* Rename group to sensors
2025-06-26 16:43:57 +02:00
Paul Bottein 3456aa96e8 Better handle case when no floors in areas dashboard (#25933) 2025-06-26 16:43:56 +02:00
Eric Stern e88d9a1ffb Fix logbook stream subscription (#25927) 2025-06-26 16:43:56 +02:00
Paulus Schoutsen 0b488e1ffd Fix wrapping of add subentry buttons (#25925) 2025-06-26 16:43:55 +02:00
Paulus Schoutsen 47aea824aa Make the config entry row section wider on mobile (#25924) 2025-06-26 16:43:54 +02:00
Bram Kragten 570c63c50a Prevent overflow of ripple on device row on integration page (#25922) 2025-06-26 16:43:53 +02:00
Bram Kragten d9a3a27245 Dont show internal quality scale (#25921)
dont show internal quality scale
2025-06-26 16:43:52 +02:00
Bram Kragten 3d1a3e2335 Only show own devices when there are devices... (#25920)
only show own devices when there are devices...
2025-06-26 16:43:51 +02:00
Bram Kragten 42815c4d5e Update confirm disable messages (#25919) 2025-06-26 16:43:50 +02:00
686 changed files with 25965 additions and 10964 deletions
+5 -1
View File
@@ -310,7 +310,11 @@ export class DialogMyFeature
.heading=${createCloseHeading(this.hass, this._params.title)}
>
<!-- Dialog content -->
<ha-button @click=${this.closeDialog} slot="secondaryAction">
<ha-button
appearance="plain"
@click=${this.closeDialog}
slot="secondaryAction"
>
${this.hass.localize("ui.common.cancel")}
</ha-button>
<ha-button @click=${this._submit} slot="primaryAction">
+6 -6
View File
@@ -21,12 +21,12 @@ jobs:
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
steps:
- name: Check out files from GitHub
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
with:
ref: dev
- name: Setup Node
uses: actions/setup-node@v4.4.0
uses: actions/setup-node@v5.0.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -35,7 +35,7 @@ jobs:
run: yarn install --immutable
- name: Build Cast
run: yarn run-task build-cast
run: ./node_modules/.bin/gulp build-cast
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -56,12 +56,12 @@ jobs:
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
steps:
- name: Check out files from GitHub
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
with:
ref: master
- name: Setup Node
uses: actions/setup-node@v4.4.0
uses: actions/setup-node@v5.0.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -70,7 +70,7 @@ jobs:
run: yarn install --immutable
- name: Build Cast
run: yarn run-task build-cast
run: ./node_modules/.bin/gulp build-cast
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+13 -13
View File
@@ -24,9 +24,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out files from GitHub
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
- name: Setup Node
uses: actions/setup-node@v4.4.0
uses: actions/setup-node@v5.0.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -35,9 +35,9 @@ jobs:
- name: Check for duplicate dependencies
run: yarn dedupe --check
- name: Build resources
run: yarn run-task gen-icons-json build-translations build-locale-data gather-gallery-pages
run: ./node_modules/.bin/gulp gen-icons-json build-translations build-locale-data gather-gallery-pages
- name: Setup lint cache
uses: actions/cache@v4.2.3
uses: actions/cache@v4.2.4
with:
path: |
node_modules/.cache/prettier
@@ -58,16 +58,16 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out files from GitHub
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
- name: Setup Node
uses: actions/setup-node@v4.4.0
uses: actions/setup-node@v5.0.0
with:
node-version-file: ".nvmrc"
cache: yarn
- name: Install dependencies
run: yarn install --immutable
- name: Build resources
run: yarn run-task gen-icons-json build-translations build-locale-data
run: ./node_modules/.bin/gulp gen-icons-json build-translations build-locale-data
- name: Run Tests
run: yarn run test
build:
@@ -76,16 +76,16 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out files from GitHub
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
- name: Setup Node
uses: actions/setup-node@v4.4.0
uses: actions/setup-node@v5.0.0
with:
node-version-file: ".nvmrc"
cache: yarn
- name: Install dependencies
run: yarn install --immutable
- name: Build Application
run: yarn run-task build-app
run: ./node_modules/.bin/gulp build-app
env:
IS_TEST: "true"
- name: Upload bundle stats
@@ -100,16 +100,16 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out files from GitHub
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
- name: Setup Node
uses: actions/setup-node@v4.4.0
uses: actions/setup-node@v5.0.0
with:
node-version-file: ".nvmrc"
cache: yarn
- name: Install dependencies
run: yarn install --immutable
- name: Build Application
run: yarn run-task build-hassio
run: ./node_modules/.bin/gulp build-hassio
env:
IS_TEST: "true"
- name: Upload bundle stats
+1 -1
View File
@@ -23,7 +23,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
+6 -6
View File
@@ -22,12 +22,12 @@ jobs:
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
steps:
- name: Check out files from GitHub
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
with:
ref: dev
- name: Setup Node
uses: actions/setup-node@v4.4.0
uses: actions/setup-node@v5.0.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -36,7 +36,7 @@ jobs:
run: yarn install --immutable
- name: Build Demo
run: yarn run-task build-demo
run: ./node_modules/.bin/gulp build-demo
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -57,12 +57,12 @@ jobs:
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
steps:
- name: Check out files from GitHub
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
with:
ref: master
- name: Setup Node
uses: actions/setup-node@v4.4.0
uses: actions/setup-node@v5.0.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -71,7 +71,7 @@ jobs:
run: yarn install --immutable
- name: Build Demo
run: yarn run-task build-demo
run: ./node_modules/.bin/gulp build-demo
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+3 -3
View File
@@ -16,10 +16,10 @@ jobs:
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
steps:
- name: Check out files from GitHub
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
- name: Setup Node
uses: actions/setup-node@v4.4.0
uses: actions/setup-node@v5.0.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -28,7 +28,7 @@ jobs:
run: yarn install --immutable
- name: Build Gallery
run: yarn run-task build-gallery
run: ./node_modules/.bin/gulp build-gallery
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+3 -3
View File
@@ -21,10 +21,10 @@ jobs:
if: github.repository == 'home-assistant/frontend' && contains(github.event.pull_request.labels.*.name, 'needs design preview')
steps:
- name: Check out files from GitHub
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
- name: Setup Node
uses: actions/setup-node@v4.4.0
uses: actions/setup-node@v5.0.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -33,7 +33,7 @@ jobs:
run: yarn install --immutable
- name: Build Gallery
run: yarn run-task build-gallery
run: ./node_modules/.bin/gulp build-gallery
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+1 -1
View File
@@ -10,6 +10,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Apply labels
uses: actions/labeler@v5.0.0
uses: actions/labeler@v6.0.1
with:
sync-labels: true
+3 -3
View File
@@ -20,15 +20,15 @@ jobs:
contents: write
steps:
- name: Checkout the repository
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Setup Node
uses: actions/setup-node@v4.4.0
uses: actions/setup-node@v5.0.0
with:
node-version-file: ".nvmrc"
cache: yarn
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Send bundle stats and build information to RelativeCI
uses: relative-ci/agent-action@v3.0.0
uses: relative-ci/agent-action@v3.0.1
with:
key: ${{ secrets[format('RELATIVE_CI_KEY_{0}_{1}', matrix.bundle, matrix.build)] }}
token: ${{ github.token }}
+11 -11
View File
@@ -23,10 +23,10 @@ jobs:
contents: write # Required to upload release assets
steps:
- name: Checkout the repository
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: ${{ env.PYTHON_VERSION }}
@@ -34,7 +34,7 @@ jobs:
uses: home-assistant/actions/helpers/verify-version@master
- name: Setup Node
uses: actions/setup-node@v4.4.0
uses: actions/setup-node@v5.0.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -55,7 +55,7 @@ jobs:
script/release
- name: Upload release assets
uses: softprops/action-gh-release@v2.3.2
uses: softprops/action-gh-release@v2.3.3
with:
files: |
dist/*.whl
@@ -74,7 +74,7 @@ jobs:
echo "home-assistant-frontend==$version" > ./requirements.txt
- name: Build wheels
uses: home-assistant/wheels@2025.03.0
uses: home-assistant/wheels@2025.07.0
with:
abi: cp313
tag: musllinux_1_2
@@ -90,9 +90,9 @@ jobs:
contents: write # Required to upload release assets
steps:
- name: Checkout the repository
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
- name: Setup Node
uses: actions/setup-node@v4.4.0
uses: actions/setup-node@v5.0.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -107,7 +107,7 @@ jobs:
- name: Tar folder
run: tar -czf landing-page/home_assistant_frontend_landingpage-${{ github.event.release.tag_name }}.tar.gz -C landing-page/dist .
- name: Upload release asset
uses: softprops/action-gh-release@v2.3.2
uses: softprops/action-gh-release@v2.3.3
with:
files: landing-page/home_assistant_frontend_landingpage-${{ github.event.release.tag_name }}.tar.gz
@@ -119,9 +119,9 @@ jobs:
contents: write # Required to upload release assets
steps:
- name: Checkout the repository
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
- name: Setup Node
uses: actions/setup-node@v4.4.0
uses: actions/setup-node@v5.0.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -136,6 +136,6 @@ jobs:
- name: Tar folder
run: tar -czf hassio/home_assistant_frontend_supervisor-${{ github.event.release.tag_name }}.tar.gz -C hassio/build .
- name: Upload release asset
uses: softprops/action-gh-release@v2.3.2
uses: softprops/action-gh-release@v2.3.3
with:
files: hassio/home_assistant_frontend_supervisor-${{ github.event.release.tag_name }}.tar.gz
+2 -2
View File
@@ -9,10 +9,10 @@ jobs:
check-authorization:
runs-on: ubuntu-latest
# Only run if this is a Task issue type (from the issue form)
if: github.event.issue.issue_type == 'Task'
if: github.event.issue.type.name == 'Task'
steps:
- name: Check if user is authorized
uses: actions/github-script@v7
uses: actions/github-script@v8
with:
script: |
const issueAuthor = context.payload.issue.user.login;
+1 -1
View File
@@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: 90 days stale policy
uses: actions/stale@v9.1.0
uses: actions/stale@v10.0.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 90
+1 -1
View File
@@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@v4.2.2
uses: actions/checkout@v5.0.0
- name: Upload Translations
run: |
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -6,4 +6,4 @@ enableGlobalCache: false
nodeLinker: node-modules
yarnPath: .yarn/releases/yarn-4.9.2.cjs
yarnPath: .yarn/releases/yarn-4.9.4.cjs
+8
View File
@@ -0,0 +1,8 @@
# People marked here will be automatically requested for a review
# when the code that they own is touched.
# https://github.com/blog/2392-introducing-code-owners
# https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners
# Part of the frontend that mobile developper should review
src/external_app/ @bgoncal @TimoPtr
test/external_app/ @bgoncal @TimoPtr
@@ -1,6 +1,6 @@
import defineProvider from "@babel/helper-define-polyfill-provider";
import { join } from "node:path";
import paths from "../paths";
import paths from "../paths.cjs";
const POLYFILL_DIR = join(paths.root_dir, "src/resources/polyfills");
@@ -1,41 +1,42 @@
import path from "node:path";
import packageJson from "../package.json" assert { type: "json" };
import { version } from "./env.ts";
import paths, { dirname } from "./paths.ts";
const path = require("path");
const env = require("./env.cjs");
const paths = require("./paths.cjs");
const { dependencies } = require("../package.json");
const dependencies = packageJson.dependencies;
const BABEL_PLUGINS = path.join(dirname, "babel-plugins");
const BABEL_PLUGINS = path.join(__dirname, "babel-plugins");
// GitHub base URL to use for production source maps
// Nightly builds use the commit SHA, otherwise assumes there is a tag that matches the version
export const sourceMapURL = () => {
const ref = version().endsWith("dev")
module.exports.sourceMapURL = () => {
const ref = env.version().endsWith("dev")
? process.env.GITHUB_SHA || "dev"
: version();
: env.version();
return `https://raw.githubusercontent.com/home-assistant/frontend/${ref}/`;
};
// Files from NPM Packages that should not be imported
module.exports.ignorePackages = () => [];
// Files from NPM packages that we should replace with empty file
export const emptyPackages = ({ isHassioBuild }) =>
module.exports.emptyPackages = ({ isHassioBuild }) =>
[
import.meta.resolve("@vaadin/vaadin-material-styles/typography.js"),
import.meta.resolve("@vaadin/vaadin-material-styles/font-icons.js"),
require.resolve("@vaadin/vaadin-material-styles/typography.js"),
require.resolve("@vaadin/vaadin-material-styles/font-icons.js"),
// Icons in supervisor conflict with icons in HA so we don't load.
isHassioBuild &&
import.meta.resolve(
require.resolve(
path.resolve(paths.root_dir, "src/components/ha-icon.ts")
),
isHassioBuild &&
import.meta.resolve(
require.resolve(
path.resolve(paths.root_dir, "src/components/ha-icon-picker.ts")
),
].filter(Boolean);
export const definedVars = ({ isProdBuild, latestBuild, defineOverlay }) => ({
module.exports.definedVars = ({ isProdBuild, latestBuild, defineOverlay }) => ({
__DEV__: !isProdBuild,
__BUILD__: JSON.stringify(latestBuild ? "modern" : "legacy"),
__VERSION__: JSON.stringify(version()),
__VERSION__: JSON.stringify(env.version()),
__DEMO__: false,
__SUPERVISOR__: false,
__BACKWARDS_COMPAT__: false,
@@ -52,7 +53,7 @@ export const definedVars = ({ isProdBuild, latestBuild, defineOverlay }) => ({
...defineOverlay,
});
export const htmlMinifierOptions = {
module.exports.htmlMinifierOptions = {
caseSensitive: true,
collapseWhitespace: true,
conservativeCollapse: true,
@@ -64,16 +65,16 @@ export const htmlMinifierOptions = {
},
};
export const terserOptions = ({ latestBuild, isTestBuild }) => ({
module.exports.terserOptions = ({ latestBuild, isTestBuild }) => ({
safari10: !latestBuild,
ecma: latestBuild ? (2015 as const) : (5 as const),
ecma: latestBuild ? 2015 : 5,
module: latestBuild,
format: { comments: false },
sourceMap: !isTestBuild,
});
/** @type {import('@rspack/core').SwcLoaderOptions} */
export const swcOptions = () => ({
module.exports.swcOptions = () => ({
jsc: {
loose: true,
externalHelpers: true,
@@ -85,16 +86,11 @@ export const swcOptions = () => ({
},
});
export const babelOptions = ({
module.exports.babelOptions = ({
latestBuild,
isProdBuild,
isTestBuild,
sw,
}: {
latestBuild?: boolean;
isProdBuild?: boolean;
isTestBuild?: boolean;
sw?: boolean;
}) => ({
babelrc: false,
compact: false,
@@ -141,7 +137,7 @@ export const babelOptions = ({
"@polymer/polymer/lib/utils/html-tag.js": ["html"],
},
strictCSS: true,
htmlMinifier: htmlMinifierOptions,
htmlMinifier: module.exports.htmlMinifierOptions,
failOnError: false, // we can turn this off in case of false positives
},
],
@@ -164,7 +160,7 @@ export const babelOptions = ({
// themselves to prevent self-injection.
plugins: [
[
path.join(BABEL_PLUGINS, "custom-polyfill-plugin.ts"),
path.join(BABEL_PLUGINS, "custom-polyfill-plugin.js"),
{ method: "usage-global" },
],
],
@@ -225,20 +221,8 @@ const publicPath = (latestBuild, root = "") =>
}
*/
export const config = {
app({
isProdBuild,
latestBuild,
isStatsBuild,
isTestBuild,
isWDS,
}: {
isProdBuild?: boolean;
latestBuild?: boolean;
isStatsBuild?: boolean;
isTestBuild?: boolean;
isWDS?: boolean;
}) {
module.exports.config = {
app({ isProdBuild, latestBuild, isStatsBuild, isTestBuild, isWDS }) {
return {
name: "frontend" + nameSuffix(latestBuild),
entry: {
@@ -273,7 +257,7 @@ export const config = {
outputPath: outputPath(paths.demo_output_root, latestBuild),
publicPath: publicPath(latestBuild),
defineOverlay: {
__VERSION__: JSON.stringify(`DEMO-${version()}`),
__VERSION__: JSON.stringify(`DEMO-${env.version()}`),
__DEMO__: true,
},
isProdBuild,
@@ -283,7 +267,7 @@ export const config = {
},
cast({ isProdBuild, latestBuild }) {
const entry: Record<string, string> = {
const entry = {
launcher: path.resolve(paths.cast_dir, "src/launcher/entrypoint.ts"),
media: path.resolve(paths.cast_dir, "src/media/entrypoint.ts"),
};
+34
View File
@@ -0,0 +1,34 @@
const fs = require("fs");
const path = require("path");
const paths = require("./paths.cjs");
const isTrue = (value) => value === "1" || value?.toLowerCase() === "true";
module.exports = {
isProdBuild() {
return (
process.env.NODE_ENV === "production" || module.exports.isStatsBuild()
);
},
isStatsBuild() {
return isTrue(process.env.STATS);
},
isTestBuild() {
return isTrue(process.env.IS_TEST);
},
isNetlify() {
return isTrue(process.env.NETLIFY);
},
version() {
const version = fs
.readFileSync(path.resolve(paths.root_dir, "pyproject.toml"), "utf8")
.match(/version\W+=\W"(\d{8}\.\d(?:\.dev)?)"/);
if (!version) {
throw Error("Version not found");
}
return version[1];
},
isDevContainer() {
return isTrue(process.env.DEV_CONTAINER);
},
};
-21
View File
@@ -1,21 +0,0 @@
import fs from "node:fs";
import path from "node:path";
import paths from "./paths.ts";
const isTrue = (value) => value === "1" || value?.toLowerCase() === "true";
export const isProdBuild = () =>
process.env.NODE_ENV === "production" || isStatsBuild();
export const isStatsBuild = () => isTrue(process.env.STATS);
export const isTestBuild = () => isTrue(process.env.IS_TEST);
export const isNetlify = () => isTrue(process.env.NETLIFY);
export const version = () => {
const pyProjectVersion = fs
.readFileSync(path.resolve(paths.root_dir, "pyproject.toml"), "utf8")
.match(/version\W+=\W"(\d{8}\.\d(?:\.dev)?)"/);
if (!pyProjectVersion) {
throw Error("Version not found");
}
return pyProjectVersion[1];
};
export const isDevContainer = () => isTrue(process.env.DEV_CONTAINER);
+57
View File
@@ -0,0 +1,57 @@
import gulp from "gulp";
import env from "../env.cjs";
import "./clean.js";
import "./compress.js";
import "./entry-html.js";
import "./gather-static.js";
import "./gen-icons-json.js";
import "./locale-data.js";
import "./service-worker.js";
import "./translations.js";
import "./rspack.js";
gulp.task(
"develop-app",
gulp.series(
async function setEnv() {
process.env.NODE_ENV = "development";
},
"clean",
gulp.parallel(
"gen-service-worker-app-dev",
"gen-icons-json",
"gen-pages-app-dev",
"build-translations",
"build-locale-data"
),
"copy-static-app",
"rspack-watch-app"
)
);
gulp.task(
"build-app",
gulp.series(
async function setEnv() {
process.env.NODE_ENV = "production";
},
"clean",
gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"),
"copy-static-app",
"rspack-prod-app",
gulp.parallel("gen-pages-app-prod", "gen-service-worker-app-prod"),
// Don't compress running tests
...(env.isTestBuild() || env.isStatsBuild() ? [] : ["compress-app"])
)
);
gulp.task(
"analyze-app",
gulp.series(
async function setEnv() {
process.env.STATS = "1";
},
"clean",
"rspack-prod-app"
)
);
-54
View File
@@ -1,54 +0,0 @@
import { parallel, series } from "gulp";
import { isStatsBuild, isTestBuild } from "../env.ts";
import { clean } from "./clean.ts";
import { compressApp } from "./compress.ts";
import { genPagesAppDev, genPagesAppProd } from "./entry-html.ts";
import { copyStaticApp } from "./gather-static.ts";
import { genIconsJson } from "./gen-icons-json.ts";
import { buildLocaleData } from "./locale-data.ts";
import { rspackProdApp, rspackWatchApp } from "./rspack.ts";
import {
genServiceWorkerAppDev,
genServiceWorkerAppProd,
} from "./service-worker.ts";
import { buildTranslations } from "./translations.ts";
// develop-app
export const developApp = series(
async () => {
process.env.NODE_ENV = "development";
},
clean,
parallel(
genServiceWorkerAppDev,
genIconsJson,
genPagesAppDev,
buildTranslations,
buildLocaleData
),
copyStaticApp,
rspackWatchApp
);
// build-app
export const buildApp = series(
async () => {
process.env.NODE_ENV = "production";
},
clean,
parallel(genIconsJson, buildTranslations, buildLocaleData),
copyStaticApp,
rspackProdApp,
parallel(genPagesAppProd, genServiceWorkerAppProd),
// Don't compress running tests
...(isTestBuild() || isStatsBuild() ? [] : [compressApp])
);
// analyze-app
export const analyzeApp = series(
async () => {
process.env.STATS = "1";
},
clean,
rspackProdApp
);
+37
View File
@@ -0,0 +1,37 @@
import gulp from "gulp";
import "./clean.js";
import "./entry-html.js";
import "./gather-static.js";
import "./service-worker.js";
import "./translations.js";
import "./rspack.js";
gulp.task(
"develop-cast",
gulp.series(
async function setEnv() {
process.env.NODE_ENV = "development";
},
"clean-cast",
"translations-enable-merge-backend",
gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"),
"copy-static-cast",
"gen-pages-cast-dev",
"rspack-dev-server-cast"
)
);
gulp.task(
"build-cast",
gulp.series(
async function setEnv() {
process.env.NODE_ENV = "production";
},
"clean-cast",
"translations-enable-merge-backend",
gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"),
"copy-static-cast",
"rspack-prod-cast",
"gen-pages-cast-prod"
)
);
-38
View File
@@ -1,38 +0,0 @@
import { parallel, series } from "gulp";
import { cleanCast } from "./clean.ts";
import { genPagesCastDev, genPagesCastProd } from "./entry-html.ts";
import { copyStaticCast } from "./gather-static.ts";
import { genIconsJson } from "./gen-icons-json.ts";
import { buildLocaleData } from "./locale-data.ts";
import { rspackDevServerCast, rspackProdCast } from "./rspack.ts";
import "./service-worker.ts";
import {
buildTranslations,
translationsEnableMergeBackend,
} from "./translations.ts";
// develop-cast
export const developCast = series(
async () => {
process.env.NODE_ENV = "development";
},
cleanCast,
translationsEnableMergeBackend,
parallel(genIconsJson, buildTranslations, buildLocaleData),
copyStaticCast,
genPagesCastDev,
rspackDevServerCast
);
// build-cast
export const buildCast = series(
async () => {
process.env.NODE_ENV = "production";
},
cleanCast,
translationsEnableMergeBackend,
parallel(genIconsJson, buildTranslations, buildLocaleData),
copyStaticCast,
rspackProdCast,
genPagesCastProd
);
+51
View File
@@ -0,0 +1,51 @@
import { deleteSync } from "del";
import gulp from "gulp";
import paths from "../paths.cjs";
import "./translations.js";
gulp.task(
"clean",
gulp.parallel("clean-translations", async () =>
deleteSync([paths.app_output_root, paths.build_dir])
)
);
gulp.task(
"clean-demo",
gulp.parallel("clean-translations", async () =>
deleteSync([paths.demo_output_root, paths.build_dir])
)
);
gulp.task(
"clean-cast",
gulp.parallel("clean-translations", async () =>
deleteSync([paths.cast_output_root, paths.build_dir])
)
);
gulp.task("clean-hassio", async () =>
deleteSync([paths.hassio_output_root, paths.build_dir])
);
gulp.task(
"clean-gallery",
gulp.parallel("clean-translations", async () =>
deleteSync([
paths.gallery_output_root,
paths.gallery_build,
paths.build_dir,
])
)
);
gulp.task(
"clean-landing-page",
gulp.parallel("clean-translations", async () =>
deleteSync([
paths.landingPage_output_root,
paths.landingPage_build,
paths.build_dir,
])
)
);
-31
View File
@@ -1,31 +0,0 @@
import { deleteSync } from "del";
import { parallel } from "gulp";
import paths from "../paths.ts";
import { cleanTranslations } from "./translations.ts";
export const clean = parallel(cleanTranslations, async () =>
deleteSync([paths.app_output_root, paths.build_dir])
);
export const cleanDemo = parallel(cleanTranslations, async () =>
deleteSync([paths.demo_output_root, paths.build_dir])
);
export const cleanCast = parallel(cleanTranslations, async () =>
deleteSync([paths.cast_output_root, paths.build_dir])
);
export const cleanHassio = async () =>
deleteSync([paths.hassio_output_root, paths.build_dir]);
export const cleanGallery = parallel(cleanTranslations, async () =>
deleteSync([paths.gallery_output_root, paths.gallery_build, paths.build_dir])
);
export const cleanLandingPage = parallel(cleanTranslations, async () =>
deleteSync([
paths.landingPage_output_root,
paths.landingPage_build,
paths.build_dir,
])
);
@@ -1,10 +1,10 @@
// Tasks to compress
import { dest, parallel, src } from "gulp";
import { constants } from "node:zlib";
import gulp from "gulp";
import brotli from "gulp-brotli";
import zopfli from "gulp-zopfli-green";
import { constants } from "node:zlib";
import paths from "../paths.ts";
import paths from "../paths.cjs";
const filesGlob = "*.{js,json,css,svg,xml}";
const brotliOptions = {
@@ -16,25 +16,27 @@ const brotliOptions = {
const zopfliOptions = { threshold: 150 };
const compressModern = (rootDir, modernDir, compress) =>
src([`${modernDir}/**/${filesGlob}`, `${rootDir}/sw-modern.js`], {
base: rootDir,
allowEmpty: true,
})
gulp
.src([`${modernDir}/**/${filesGlob}`, `${rootDir}/sw-modern.js`], {
base: rootDir,
allowEmpty: true,
})
.pipe(compress === "zopfli" ? zopfli(zopfliOptions) : brotli(brotliOptions))
.pipe(dest(rootDir));
.pipe(gulp.dest(rootDir));
const compressOther = (rootDir, modernDir, compress) =>
src(
[
`${rootDir}/**/${filesGlob}`,
`!${modernDir}/**/${filesGlob}`,
`!${rootDir}/{sw-modern,service_worker}.js`,
`${rootDir}/{authorize,onboarding}.html`,
],
{ base: rootDir, allowEmpty: true }
)
gulp
.src(
[
`${rootDir}/**/${filesGlob}`,
`!${modernDir}/**/${filesGlob}`,
`!${rootDir}/{sw-modern,service_worker}.js`,
`${rootDir}/{authorize,onboarding}.html`,
],
{ base: rootDir, allowEmpty: true }
)
.pipe(compress === "zopfli" ? zopfli(zopfliOptions) : brotli(brotliOptions))
.pipe(dest(rootDir));
.pipe(gulp.dest(rootDir));
const compressAppModernBrotli = () =>
compressModern(paths.app_output_root, paths.app_output_latest, "brotli");
@@ -64,16 +66,21 @@ const compressHassioOtherBrotli = () =>
const compressHassioOtherZopfli = () =>
compressOther(paths.hassio_output_root, paths.hassio_output_latest, "zopfli");
export const compressApp = parallel(
compressAppModernBrotli,
compressAppOtherBrotli,
compressAppModernZopfli,
compressAppOtherZopfli
gulp.task(
"compress-app",
gulp.parallel(
compressAppModernBrotli,
compressAppOtherBrotli,
compressAppModernZopfli,
compressAppOtherZopfli
)
);
export const compressHassio = parallel(
compressHassioModernBrotli,
compressHassioOtherBrotli,
compressHassioModernZopfli,
compressHassioOtherZopfli
gulp.task(
"compress-hassio",
gulp.parallel(
compressHassioModernBrotli,
compressHassioOtherBrotli,
compressHassioModernZopfli,
compressHassioOtherZopfli
)
);
+54
View File
@@ -0,0 +1,54 @@
import gulp from "gulp";
import "./clean.js";
import "./entry-html.js";
import "./gather-static.js";
import "./gen-icons-json.js";
import "./service-worker.js";
import "./translations.js";
import "./rspack.js";
gulp.task(
"develop-demo",
gulp.series(
async function setEnv() {
process.env.NODE_ENV = "development";
},
"clean-demo",
"translations-enable-merge-backend",
gulp.parallel(
"gen-icons-json",
"gen-pages-demo-dev",
"build-translations",
"build-locale-data"
),
"copy-static-demo",
"rspack-dev-server-demo"
)
);
gulp.task(
"build-demo",
gulp.series(
async function setEnv() {
process.env.NODE_ENV = "production";
},
"clean-demo",
// Cast needs to be backwards compatible and older HA has no translations
"translations-enable-merge-backend",
gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"),
"copy-static-demo",
"rspack-prod-demo",
"gen-pages-demo-prod"
)
);
gulp.task(
"analyze-demo",
gulp.series(
async function setEnv() {
process.env.STATS = "1";
},
"clean",
"rspack-prod-demo"
)
);
-47
View File
@@ -1,47 +0,0 @@
import { parallel, series } from "gulp";
import { clean, cleanDemo } from "./clean.ts";
import { genPagesDemoDev, genPagesDemoProd } from "./entry-html.ts";
import { copyStaticDemo } from "./gather-static.ts";
import { genIconsJson } from "./gen-icons-json.ts";
import { buildLocaleData } from "./locale-data.ts";
import { rspackDevServerDemo, rspackProdDemo } from "./rspack.ts";
import "./service-worker.ts";
import {
buildTranslations,
translationsEnableMergeBackend,
} from "./translations.ts";
// develop-demo
export const developDemo = series(
async function setEnv() {
process.env.NODE_ENV = "development";
},
cleanDemo,
translationsEnableMergeBackend,
parallel(genIconsJson, genPagesDemoDev, buildTranslations, buildLocaleData),
copyStaticDemo,
rspackDevServerDemo
);
// build-demo
export const buildDemo = series(
async function setEnv() {
process.env.NODE_ENV = "production";
},
cleanDemo,
// Cast needs to be backwards compatible and older HA has no translations
translationsEnableMergeBackend,
parallel(genIconsJson, buildTranslations, buildLocaleData),
copyStaticDemo,
rspackProdDemo,
genPagesDemoProd
);
// analyze-demo
export const analyzeDemo = series(
async function setEnv() {
process.env.STATS = "1";
},
clean,
rspackProdDemo
);
@@ -1,10 +1,10 @@
import { LokaliseApi } from "@lokalise/node-api";
import { dest, series, src } from "gulp";
import transform from "gulp-json-transform";
import JSZip from "jszip";
import fs from "fs/promises";
import gulp from "gulp";
import path from "path";
import mapStream from "map-stream";
import fs from "node:fs/promises";
import path from "node:path";
import transform from "gulp-json-transform";
import { LokaliseApi } from "@lokalise/node-api";
import JSZip from "jszip";
const inDir = "translations";
const inDirFrontend = `${inDir}/frontend`;
@@ -12,14 +12,11 @@ const inDirBackend = `${inDir}/backend`;
const srcMeta = "src/translations/translationMetadata.json";
const encoding = "utf8";
const hasHtml = (data) => /<\S*>/i.test(data);
function hasHtml(data) {
return /<\S*>/i.test(data);
}
const recursiveCheckHasHtml = (
file,
data,
errors: string[],
recKey?: string
) => {
function recursiveCheckHasHtml(file, data, errors, recKey) {
Object.keys(data).forEach(function (key) {
if (typeof data[key] === "object") {
const nextRecKey = recKey ? `${recKey}.${key}` : key;
@@ -28,9 +25,9 @@ const recursiveCheckHasHtml = (
errors.push(`HTML found in ${file.path} at key ${recKey}.${key}`);
}
});
};
}
const checkHtml = () => {
function checkHtml() {
const errors = [];
return mapStream(function (file, cb) {
@@ -47,9 +44,9 @@ const checkHtml = () => {
}
cb(error, file);
});
};
}
const convertBackendTranslationsTransform = (data, _file) => {
function convertBackendTranslations(data, _file) {
const output = { component: {} };
if (!data.component) {
return output;
@@ -65,22 +62,25 @@ const convertBackendTranslationsTransform = (data, _file) => {
});
});
return output;
};
}
const convertBackendTranslations = () =>
src([`${inDirBackend}/*.json`])
.pipe(
transform((data, file) => convertBackendTranslationsTransform(data, file))
)
.pipe(dest(inDirBackend));
gulp.task("convert-backend-translations", function () {
return gulp
.src([`${inDirBackend}/*.json`])
.pipe(transform((data, file) => convertBackendTranslations(data, file)))
.pipe(gulp.dest(inDirBackend));
});
const checkTranslationsHtml = () =>
src([`${inDirFrontend}/*.json`, `${inDirBackend}/*.json`]).pipe(checkHtml());
gulp.task("check-translations-html", function () {
return gulp
.src([`${inDirFrontend}/*.json`, `${inDirBackend}/*.json`])
.pipe(checkHtml());
});
const checkAllFilesExist = async () => {
gulp.task("check-all-files-exist", async function () {
const file = await fs.readFile(srcMeta, { encoding });
const meta = JSON.parse(file);
const writings: Promise<void>[] = [];
const writings = [];
Object.keys(meta).forEach((lang) => {
writings.push(
fs.writeFile(`${inDirFrontend}/${lang}.json`, JSON.stringify({}), {
@@ -92,14 +92,14 @@ const checkAllFilesExist = async () => {
);
});
await Promise.allSettled(writings);
};
});
const lokaliseProjects = {
backend: "130246255a974bd3b5e8a1.51616605",
frontend: "3420425759f6d6d241f598.13594006",
};
const fetchLokalise = async () => {
gulp.task("fetch-lokalise", async function () {
let apiKey;
try {
apiKey =
@@ -168,11 +168,14 @@ const fetchLokalise = async () => {
})
)
);
};
});
export const downloadTranslations = series(
fetchLokalise,
convertBackendTranslations,
checkTranslationsHtml,
checkAllFilesExist
gulp.task(
"download-translations",
gulp.series(
"fetch-lokalise",
"convert-backend-translations",
"check-translations-html",
"check-all-files-exist"
)
);
@@ -6,11 +6,12 @@ import {
getPreUserAgentRegexes,
} from "browserslist-useragent-regexp";
import fs from "fs-extra";
import gulp from "gulp";
import { minify } from "html-minifier-terser";
import template from "lodash.template";
import { dirname, extname, resolve } from "node:path";
import { htmlMinifierOptions, terserOptions } from "../bundle.ts";
import paths from "../paths.ts";
import { htmlMinifierOptions, terserOptions } from "../bundle.cjs";
import paths from "../paths.cjs";
// macOS companion app has no way to obtain the Safari version used by WKWebView,
// and it is not in the default user agent string. So we add an additional regex
@@ -33,9 +34,9 @@ const getCommonTemplateVars = () => {
mobileToDesktop: true,
throwOnMissing: true,
});
const minSafariVersion =
browserRegexes.find((regex) => regex.family === "safari")
?.matchedVersions[0][0] ?? 18;
const minSafariVersion = browserRegexes.find(
(regex) => regex.family === "safari"
)?.matchedVersions[0][0];
const minMacOSVersion = SAFARI_TO_MACOS[minSafariVersion];
if (!minMacOSVersion) {
throw Error(
@@ -105,10 +106,10 @@ const genPagesDevTask =
resolve(inputRoot, inputSub, `${page}.template`),
{
...commonVars,
latestEntryJS: (entries as string[]).map(
latestEntryJS: entries.map(
(entry) => `${publicRoot}/frontend_latest/${entry}.js`
),
es5EntryJS: (entries as string[]).map(
es5EntryJS: entries.map(
(entry) => `${publicRoot}/frontend_es5/${entry}.js`
),
latestCustomPanelJS: `${publicRoot}/frontend_latest/custom-panel.js`,
@@ -127,7 +128,7 @@ const genPagesProdTask =
inputRoot,
outputRoot,
outputLatest,
outputES5?: string,
outputES5,
inputSub = "src/html"
) =>
async () => {
@@ -138,18 +139,14 @@ const genPagesProdTask =
? fs.readJsonSync(resolve(outputES5, "manifest.json"))
: {};
const commonVars = getCommonTemplateVars();
const minifiedHTML: Promise<void>[] = [];
const minifiedHTML = [];
for (const [page, entries] of Object.entries(pageEntries)) {
const content = renderTemplate(
resolve(inputRoot, inputSub, `${page}.template`),
{
...commonVars,
latestEntryJS: (entries as string[]).map(
(entry) => latestManifest[`${entry}.js`]
),
es5EntryJS: (entries as string[]).map(
(entry) => es5Manifest[`${entry}.js`]
),
latestEntryJS: entries.map((entry) => latestManifest[`${entry}.js`]),
es5EntryJS: entries.map((entry) => es5Manifest[`${entry}.js`]),
latestCustomPanelJS: latestManifest["custom-panel.js"],
es5CustomPanelJS: es5Manifest["custom-panel.js"],
}
@@ -170,18 +167,20 @@ const APP_PAGE_ENTRIES = {
"index.html": ["core", "app"],
};
export const genPagesAppDev = genPagesDevTask(
APP_PAGE_ENTRIES,
paths.root_dir,
paths.app_output_root
gulp.task(
"gen-pages-app-dev",
genPagesDevTask(APP_PAGE_ENTRIES, paths.root_dir, paths.app_output_root)
);
export const genPagesAppProd = genPagesProdTask(
APP_PAGE_ENTRIES,
paths.root_dir,
paths.app_output_root,
paths.app_output_latest,
paths.app_output_es5
gulp.task(
"gen-pages-app-prod",
genPagesProdTask(
APP_PAGE_ENTRIES,
paths.root_dir,
paths.app_output_root,
paths.app_output_latest,
paths.app_output_es5
)
);
const CAST_PAGE_ENTRIES = {
@@ -191,82 +190,104 @@ const CAST_PAGE_ENTRIES = {
"receiver.html": ["receiver"],
};
export const genPagesCastDev = genPagesDevTask(
CAST_PAGE_ENTRIES,
paths.cast_dir,
paths.cast_output_root
gulp.task(
"gen-pages-cast-dev",
genPagesDevTask(CAST_PAGE_ENTRIES, paths.cast_dir, paths.cast_output_root)
);
export const genPagesCastProd = genPagesProdTask(
CAST_PAGE_ENTRIES,
paths.cast_dir,
paths.cast_output_root,
paths.cast_output_latest,
paths.cast_output_es5
gulp.task(
"gen-pages-cast-prod",
genPagesProdTask(
CAST_PAGE_ENTRIES,
paths.cast_dir,
paths.cast_output_root,
paths.cast_output_latest,
paths.cast_output_es5
)
);
const DEMO_PAGE_ENTRIES = { "index.html": ["main"] };
export const genPagesDemoDev = genPagesDevTask(
DEMO_PAGE_ENTRIES,
paths.demo_dir,
paths.demo_output_root
gulp.task(
"gen-pages-demo-dev",
genPagesDevTask(DEMO_PAGE_ENTRIES, paths.demo_dir, paths.demo_output_root)
);
export const genPagesDemoProd = genPagesProdTask(
DEMO_PAGE_ENTRIES,
paths.demo_dir,
paths.demo_output_root,
paths.demo_output_latest,
paths.demo_output_es5
gulp.task(
"gen-pages-demo-prod",
genPagesProdTask(
DEMO_PAGE_ENTRIES,
paths.demo_dir,
paths.demo_output_root,
paths.demo_output_latest,
paths.demo_output_es5
)
);
const GALLERY_PAGE_ENTRIES = { "index.html": ["entrypoint"] };
export const genPagesGalleryDev = genPagesDevTask(
GALLERY_PAGE_ENTRIES,
paths.gallery_dir,
paths.gallery_output_root
gulp.task(
"gen-pages-gallery-dev",
genPagesDevTask(
GALLERY_PAGE_ENTRIES,
paths.gallery_dir,
paths.gallery_output_root
)
);
export const genPagesGalleryProd = genPagesProdTask(
GALLERY_PAGE_ENTRIES,
paths.gallery_dir,
paths.gallery_output_root,
paths.gallery_output_latest
gulp.task(
"gen-pages-gallery-prod",
genPagesProdTask(
GALLERY_PAGE_ENTRIES,
paths.gallery_dir,
paths.gallery_output_root,
paths.gallery_output_latest
)
);
const LANDING_PAGE_PAGE_ENTRIES = { "index.html": ["entrypoint"] };
export const genPagesLandingPageDev = genPagesDevTask(
LANDING_PAGE_PAGE_ENTRIES,
paths.landingPage_dir,
paths.landingPage_output_root
gulp.task(
"gen-pages-landing-page-dev",
genPagesDevTask(
LANDING_PAGE_PAGE_ENTRIES,
paths.landingPage_dir,
paths.landingPage_output_root
)
);
export const genPagesLandingPageProd = genPagesProdTask(
LANDING_PAGE_PAGE_ENTRIES,
paths.landingPage_dir,
paths.landingPage_output_root,
paths.landingPage_output_latest,
paths.landingPage_output_es5
gulp.task(
"gen-pages-landing-page-prod",
genPagesProdTask(
LANDING_PAGE_PAGE_ENTRIES,
paths.landingPage_dir,
paths.landingPage_output_root,
paths.landingPage_output_latest,
paths.landingPage_output_es5
)
);
const HASSIO_PAGE_ENTRIES = { "entrypoint.js": ["entrypoint"] };
export const genPagesHassioDev = genPagesDevTask(
HASSIO_PAGE_ENTRIES,
paths.hassio_dir,
paths.hassio_output_root,
"src",
paths.hassio_publicPath
gulp.task(
"gen-pages-hassio-dev",
genPagesDevTask(
HASSIO_PAGE_ENTRIES,
paths.hassio_dir,
paths.hassio_output_root,
"src",
paths.hassio_publicPath
)
);
export const genPagesHassioProd = genPagesProdTask(
HASSIO_PAGE_ENTRIES,
paths.hassio_dir,
paths.hassio_output_root,
paths.hassio_output_latest,
paths.hassio_output_es5,
"src"
gulp.task(
"gen-pages-hassio-prod",
genPagesProdTask(
HASSIO_PAGE_ENTRIES,
paths.hassio_dir,
paths.hassio_output_root,
paths.hassio_output_latest,
paths.hassio_output_es5,
"src"
)
);
@@ -1,14 +1,14 @@
// Task to download the latest 00Lokalise translations from the nightly workflow artifacts
// Task to download the latest Lokalise translations from the nightly workflow artifacts
import { createOAuthDeviceAuth } from "@octokit/auth-oauth-device";
import { retry } from "@octokit/plugin-retry";
import { Octokit } from "@octokit/rest";
import { deleteAsync } from "del";
import { series } from "gulp";
import { mkdir, readFile, writeFile } from "fs/promises";
import gulp from "gulp";
import jszip from "jszip";
import { mkdir, readFile, writeFile } from "node:fs/promises";
import path from "node:path";
import process from "node:process";
import path from "path";
import process from "process";
import { extract } from "tar";
const MAX_AGE = 24; // hours
@@ -22,13 +22,12 @@ const TOKEN_FILE = path.posix.join(EXTRACT_DIR, "token.json");
const ARTIFACT_FILE = path.posix.join(EXTRACT_DIR, "artifact.json");
let allowTokenSetup = false;
export const allowSetupFetchNightlyTranslations = (done) => {
gulp.task("allow-setup-fetch-nightly-translations", (done) => {
allowTokenSetup = true;
done();
};
});
export const fetchNightlyTranslations = async () => {
gulp.task("fetch-nightly-translations", async function () {
// Skip all when environment flag is set (assumes translations are already in place)
if (process.env?.SKIP_FETCH_NIGHTLY_TRANSLATIONS) {
console.log("Skipping fetch due to environment signal");
@@ -55,7 +54,7 @@ export const fetchNightlyTranslations = async () => {
// To store file writing promises
const createExtractDir = mkdir(EXTRACT_DIR, { recursive: true });
const writings: Promise<void>[] = [];
const writings = [];
// Authenticate to GitHub using GitHub action token if it exists,
// otherwise look for a saved user token or generate a new one if none
@@ -88,7 +87,7 @@ export const fetchNightlyTranslations = async () => {
});
tokenAuth = await auth({ type: "oauth" });
writings.push(
createExtractDir.then(() =>
createExtractDir.then(
writeFile(TOKEN_FILE, JSON.stringify(tokenAuth, null, 2))
)
);
@@ -132,13 +131,13 @@ export const fetchNightlyTranslations = async () => {
throw Error("Latest nightly workflow run has no translations artifact");
}
writings.push(
createExtractDir.then(() =>
createExtractDir.then(
writeFile(ARTIFACT_FILE, JSON.stringify(latestArtifact, null, 2))
)
);
// Remove the current translations
const deleteCurrent = Promise.all(writings).then(() =>
const deleteCurrent = Promise.all(writings).then(
deleteAsync([`${EXTRACT_DIR}/*`, `!${ARTIFACT_FILE}`, `!${TOKEN_FILE}`])
);
@@ -149,22 +148,24 @@ export const fetchNightlyTranslations = async () => {
artifact_id: latestArtifact.id,
archive_format: "zip",
});
// @ts-ignore OctokitResponse<unknown, 302> doesn't allow to check for 200
if (downloadResponse.status !== 200) {
throw Error("Failure downloading translations artifact");
}
// Artifact is a tarball, but GitHub adds it to a zip file
console.log("Unpacking downloaded translations...");
const zip = await jszip.loadAsync(downloadResponse.data as any);
const zip = await jszip.loadAsync(downloadResponse.data);
await deleteCurrent;
const extractStream = zip.file(/.*/)[0].nodeStream().pipe(extract());
await new Promise((resolve, reject) => {
extractStream.on("close", resolve).on("error", reject);
});
};
});
export const setupAndFetchNightlyTranslations = series(
allowSetupFetchNightlyTranslations,
fetchNightlyTranslations
gulp.task(
"setup-and-fetch-nightly-translations",
gulp.series(
"allow-setup-fetch-nightly-translations",
"fetch-nightly-translations"
)
);
@@ -1,23 +1,19 @@
import fs from "fs";
import { glob } from "glob";
import { parallel, series, watch } from "gulp";
import gulp from "gulp";
import yaml from "js-yaml";
import { marked } from "marked";
import fs from "node:fs";
import path from "node:path";
import paths from "../paths.ts";
import { cleanGallery } from "./clean.ts";
import { genPagesGalleryDev, genPagesGalleryProd } from "./entry-html.ts";
import { copyStaticGallery } from "./gather-static.ts";
import { genIconsJson } from "./gen-icons-json.ts";
import { buildLocaleData } from "./locale-data.ts";
import { rspackDevServerGallery, rspackProdGallery } from "./rspack.ts";
import {
buildTranslations,
translationsEnableMergeBackend,
} from "./translations.ts";
import path from "path";
import paths from "../paths.cjs";
import "./clean.js";
import "./entry-html.js";
import "./gather-static.js";
import "./gen-icons-json.js";
import "./service-worker.js";
import "./translations.js";
import "./rspack.js";
// gather-gallery-pages
export const gatherGalleryPages = async function gatherPages() {
gulp.task("gather-gallery-pages", async function gatherPages() {
const pageDir = path.resolve(paths.gallery_dir, "src/pages");
const files = await glob(path.resolve(pageDir, "**/*"));
@@ -26,7 +22,7 @@ export const gatherGalleryPages = async function gatherPages() {
let content = "export const PAGES = {\n";
const processed = new Set<string>();
const processed = new Set();
for (const file of files) {
if (fs.lstatSync(file).isDirectory()) {
@@ -51,9 +47,7 @@ export const gatherGalleryPages = async function gatherPages() {
if (descriptionContent.startsWith("---")) {
const metadataEnd = descriptionContent.indexOf("---", 3);
metadata = yaml.load(
descriptionContent.substring(3, metadataEnd)
) as any;
metadata = yaml.load(descriptionContent.substring(3, metadataEnd));
descriptionContent = descriptionContent
.substring(metadataEnd + 3)
.trim();
@@ -63,9 +57,7 @@ export const gatherGalleryPages = async function gatherPages() {
if (descriptionContent === "") {
hasDescription = false;
} else {
// eslint-disable-next-line no-await-in-loop
descriptionContent = await marked(descriptionContent);
descriptionContent = descriptionContent.replace(/`/g, "\\`");
descriptionContent = marked(descriptionContent).replace(/`/g, "\\`");
fs.mkdirSync(path.resolve(galleryBuild, category), { recursive: true });
fs.writeFileSync(
path.resolve(galleryBuild, `${pageId}-description.ts`),
@@ -103,10 +95,7 @@ export const gatherGalleryPages = async function gatherPages() {
pagesToProcess[category].add(page);
}
for (const group of Object.values(sidebar) as {
category: string;
pages?: string[];
}[]) {
for (const group of Object.values(sidebar)) {
const toProcess = pagesToProcess[group.category];
delete pagesToProcess[group.category];
@@ -129,7 +118,7 @@ export const gatherGalleryPages = async function gatherPages() {
group.pages = [];
}
for (const page of Array.from(toProcess).sort()) {
group.pages.push(page as string);
group.pages.push(page);
}
}
@@ -137,7 +126,7 @@ export const gatherGalleryPages = async function gatherPages() {
sidebar.push({
category,
header: category,
pages: Array.from(pages as Set<string>).sort(),
pages: Array.from(pages).sort(),
});
}
@@ -148,48 +137,55 @@ export const gatherGalleryPages = async function gatherPages() {
content,
"utf-8"
);
};
});
// develop-gallery
export const developGallery = series(
async function setEnv() {
process.env.NODE_ENV = "development";
},
cleanGallery,
translationsEnableMergeBackend,
parallel(
genIconsJson,
buildTranslations,
buildLocaleData,
gatherGalleryPages
),
copyStaticGallery,
genPagesGalleryDev,
parallel(rspackDevServerGallery, async function watchMarkdownFiles() {
watch(
[
path.resolve(paths.gallery_dir, "src/pages/**/*.markdown"),
path.resolve(paths.gallery_dir, "sidebar.js"),
],
series(gatherGalleryPages)
);
})
gulp.task(
"develop-gallery",
gulp.series(
async function setEnv() {
process.env.NODE_ENV = "development";
},
"clean-gallery",
"translations-enable-merge-backend",
gulp.parallel(
"gen-icons-json",
"build-translations",
"build-locale-data",
"gather-gallery-pages"
),
"copy-static-gallery",
"gen-pages-gallery-dev",
gulp.parallel(
"rspack-dev-server-gallery",
async function watchMarkdownFiles() {
gulp.watch(
[
path.resolve(paths.gallery_dir, "src/pages/**/*.markdown"),
path.resolve(paths.gallery_dir, "sidebar.js"),
],
gulp.series("gather-gallery-pages")
);
}
)
)
);
// build-gallery
export const buildGallery = series(
async function setEnv() {
process.env.NODE_ENV = "production";
},
cleanGallery,
translationsEnableMergeBackend,
parallel(
genIconsJson,
buildTranslations,
buildLocaleData,
gatherGalleryPages
),
copyStaticGallery,
rspackProdGallery,
genPagesGalleryProd
gulp.task(
"build-gallery",
gulp.series(
async function setEnv() {
process.env.NODE_ENV = "production";
},
"clean-gallery",
"translations-enable-merge-backend",
gulp.parallel(
"gen-icons-json",
"build-translations",
"build-locale-data",
"gather-gallery-pages"
),
"copy-static-gallery",
"rspack-prod-gallery",
"gen-pages-gallery-prod"
)
);
@@ -1,8 +1,9 @@
// Gulp task to gather all static files.
import fs from "fs-extra";
import path from "node:path";
import paths from "../paths.ts";
import gulp from "gulp";
import path from "path";
import paths from "../paths.cjs";
const npmPath = (...parts) =>
path.resolve(paths.root_dir, "node_modules", ...parts);
@@ -16,7 +17,7 @@ const genStaticPath =
(...parts) =>
path.resolve(staticDir, ...parts);
const copyTranslations = (staticDir) => {
function copyTranslations(staticDir) {
const staticPath = genStaticPath(staticDir);
// Translation output
@@ -24,23 +25,23 @@ const copyTranslations = (staticDir) => {
polyPath("build/translations/output"),
staticPath("translations")
);
};
}
const copyLocaleData = (staticDir) => {
function copyLocaleData(staticDir) {
const staticPath = genStaticPath(staticDir);
// Locale data output
fs.copySync(polyPath("build/locale-data"), staticPath("locale-data"));
};
}
const copyMdiIcons = (staticDir) => {
function copyMdiIcons(staticDir) {
const staticPath = genStaticPath(staticDir);
// MDI icons output
fs.copySync(polyPath("build/mdi"), staticPath("mdi"));
};
}
const copyPolyfills = (staticDir) => {
function copyPolyfills(staticDir) {
const staticPath = genStaticPath(staticDir);
// For custom panels using ES5 builds that don't use Babel 7+
@@ -69,9 +70,9 @@ const copyPolyfills = (staticDir) => {
npmPath("dialog-polyfill/dialog-polyfill.css"),
staticPath("polyfills/")
);
};
}
const copyFonts = (staticDir) => {
function copyFonts(staticDir) {
const staticPath = genStaticPath(staticDir);
// Local fonts
fs.copySync(
@@ -81,14 +82,14 @@ const copyFonts = (staticDir) => {
filter: (src) => !src.includes(".") || src.endsWith(".woff2"),
}
);
};
}
const copyQrScannerWorker = (staticDir) => {
function copyQrScannerWorker(staticDir) {
const staticPath = genStaticPath(staticDir);
copyFileDir(npmPath("qr-scanner/qr-scanner-worker.min.js"), staticPath("js"));
};
}
const copyMapPanel = (staticDir) => {
function copyMapPanel(staticDir) {
const staticPath = genStaticPath(staticDir);
copyFileDir(
npmPath("leaflet/dist/leaflet.css"),
@@ -102,38 +103,43 @@ const copyMapPanel = (staticDir) => {
npmPath("leaflet/dist/images"),
staticPath("images/leaflet/images/")
);
};
}
const copyZXingWasm = (staticDir) => {
function copyZXingWasm(staticDir) {
const staticPath = genStaticPath(staticDir);
copyFileDir(
npmPath("zxing-wasm/dist/reader/zxing_reader.wasm"),
staticPath("js")
);
};
}
export const copyTranslationsApp = async () => {
gulp.task("copy-locale-data", async () => {
const staticDir = paths.app_output_static;
copyLocaleData(staticDir);
});
gulp.task("copy-translations-app", async () => {
const staticDir = paths.app_output_static;
copyTranslations(staticDir);
};
});
export const copyTranslationsSupervisor = async () => {
gulp.task("copy-translations-supervisor", async () => {
const staticDir = paths.hassio_output_static;
copyTranslations(staticDir);
};
});
export const copyTranslationsLandingPage = async () => {
gulp.task("copy-translations-landing-page", async () => {
const staticDir = paths.landingPage_output_static;
copyTranslations(staticDir);
};
});
export const copyStaticSupervisor = async () => {
gulp.task("copy-static-supervisor", async () => {
const staticDir = paths.hassio_output_static;
copyLocaleData(staticDir);
copyFonts(staticDir);
};
});
export const copyStaticApp = async () => {
gulp.task("copy-static-app", async () => {
const staticDir = paths.app_output_static;
// Basic static files
fs.copySync(polyPath("public"), paths.app_output_root);
@@ -149,9 +155,9 @@ export const copyStaticApp = async () => {
// Qr Scanner assets
copyZXingWasm(staticDir);
copyQrScannerWorker(staticDir);
};
});
export const copyStaticDemo = async () => {
gulp.task("copy-static-demo", async () => {
// Copy app static files
fs.copySync(
polyPath("public/static"),
@@ -165,9 +171,9 @@ export const copyStaticDemo = async () => {
copyTranslations(paths.demo_output_static);
copyLocaleData(paths.demo_output_static);
copyMdiIcons(paths.demo_output_static);
};
});
export const copyStaticCast = async () => {
gulp.task("copy-static-cast", async () => {
// Copy app static files
fs.copySync(polyPath("public/static"), paths.cast_output_static);
// Copy cast static files
@@ -178,9 +184,9 @@ export const copyStaticCast = async () => {
copyTranslations(paths.cast_output_static);
copyLocaleData(paths.cast_output_static);
copyMdiIcons(paths.cast_output_static);
};
});
export const copyStaticGallery = async () => {
gulp.task("copy-static-gallery", async () => {
// Copy app static files
fs.copySync(polyPath("public/static"), paths.gallery_output_static);
// Copy gallery static files
@@ -194,9 +200,9 @@ export const copyStaticGallery = async () => {
copyTranslations(paths.gallery_output_static);
copyLocaleData(paths.gallery_output_static);
copyMdiIcons(paths.gallery_output_static);
};
});
export const copyStaticLandingPage = async () => {
gulp.task("copy-static-landing-page", async () => {
// Copy landing-page static files
fs.copySync(
path.resolve(paths.landingPage_dir, "public"),
@@ -205,4 +211,4 @@ export const copyStaticLandingPage = async () => {
copyFonts(paths.landingPage_output_static);
copyTranslations(paths.landingPage_output_static);
};
});
@@ -1,7 +1,8 @@
import fs from "node:fs";
import path from "node:path";
import fs from "fs";
import gulp from "gulp";
import hash from "object-hash";
import paths from "../paths.ts";
import path from "path";
import paths from "../paths.cjs";
const ICON_PACKAGE_PATH = path.resolve("node_modules/@mdi/svg/");
const META_PATH = path.resolve(ICON_PACKAGE_PATH, "meta.json");
@@ -20,7 +21,7 @@ const getMeta = () => {
encoding,
});
return {
path: svg.match(/ d="([^"]+)"/)?.[1],
path: svg.match(/ d="([^"]+)"/)[1],
name: icon.name,
tags: icon.tags,
aliases: icon.aliases,
@@ -54,14 +55,14 @@ const orderMeta = (meta) => {
};
const splitBySize = (meta) => {
const chunks: any[] = [];
const chunks = [];
const CHUNK_SIZE = 50000;
let curSize = 0;
let startKey;
let icons: any[] = [];
let icons = [];
Object.values(meta).forEach((icon: any) => {
Object.values(meta).forEach((icon) => {
if (startKey === undefined) {
startKey = icon.name;
}
@@ -93,10 +94,10 @@ const findDifferentiator = (curString, prevString) => {
return curString.substring(0, i + 1);
}
}
throw new Error(`Cannot find differentiator; ${curString}; ${prevString}`);
throw new Error("Cannot find differentiator", curString, prevString);
};
export const genIconsJson = (done) => {
gulp.task("gen-icons-json", (done) => {
const meta = getMeta();
const metaAndRemoved = addRemovedMeta(meta);
@@ -105,7 +106,7 @@ export const genIconsJson = (done) => {
if (!fs.existsSync(OUTPUT_DIR)) {
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
}
const parts: any[] = [];
const parts = [];
let lastEnd;
split.forEach((chunk) => {
@@ -152,13 +153,13 @@ export const genIconsJson = (done) => {
);
done();
};
});
export const genDummyIconsJson = (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();
};
});
+45
View File
@@ -0,0 +1,45 @@
import gulp from "gulp";
import env from "../env.cjs";
import "./clean.js";
import "./compress.js";
import "./entry-html.js";
import "./gather-static.js";
import "./gen-icons-json.js";
import "./translations.js";
import "./rspack.js";
gulp.task(
"develop-hassio",
gulp.series(
async function setEnv() {
process.env.NODE_ENV = "development";
},
"clean-hassio",
"gen-dummy-icons-json",
"gen-pages-hassio-dev",
"build-supervisor-translations",
"copy-translations-supervisor",
"build-locale-data",
"copy-static-supervisor",
"rspack-watch-hassio"
)
);
gulp.task(
"build-hassio",
gulp.series(
async function setEnv() {
process.env.NODE_ENV = "production";
},
"clean-hassio",
"gen-dummy-icons-json",
"build-supervisor-translations",
"copy-translations-supervisor",
"build-locale-data",
"copy-static-supervisor",
"rspack-prod-hassio",
"gen-pages-hassio-prod",
...// Don't compress running tests
(env.isTestBuild() ? [] : ["compress-hassio"])
)
);
-45
View File
@@ -1,45 +0,0 @@
import { series } from "gulp";
import { isTestBuild } from "../env.ts";
import { cleanHassio } from "./clean.ts";
import { compressHassio } from "./compress.ts";
import { genPagesHassioDev, genPagesHassioProd } from "./entry-html.ts";
import {
copyStaticSupervisor,
copyTranslationsSupervisor,
} from "./gather-static.ts";
import { genDummyIconsJson } from "./gen-icons-json.ts";
import { buildLocaleData } from "./locale-data.ts";
import { rspackProdHassio, rspackWatchHassio } from "./rspack.ts";
import { buildSupervisorTranslations } from "./translations.ts";
// develop-hassio
export const developHassio = series(
async function setEnv() {
process.env.NODE_ENV = "development";
},
cleanHassio,
genDummyIconsJson,
genPagesHassioDev,
buildSupervisorTranslations,
copyTranslationsSupervisor,
buildLocaleData,
copyStaticSupervisor,
rspackWatchHassio
);
// build-hassio
export const buildHassio = series(
async function setEnv() {
process.env.NODE_ENV = "production";
},
cleanHassio,
genDummyIconsJson,
buildSupervisorTranslations,
copyTranslationsSupervisor,
buildLocaleData,
copyStaticSupervisor,
rspackProdHassio,
genPagesHassioProd,
...// Don't compress running tests
(isTestBuild() ? [] : [compressHassio])
);
+17
View File
@@ -0,0 +1,17 @@
import "./app.js";
import "./cast.js";
import "./clean.js";
import "./compress.js";
import "./demo.js";
import "./download-translations.js";
import "./entry-html.js";
import "./fetch-nightly-translations.js";
import "./gallery.js";
import "./gather-static.js";
import "./gen-icons-json.js";
import "./hassio.js";
import "./landing-page.js";
import "./locale-data.js";
import "./rspack.js";
import "./service-worker.js";
import "./translations.js";
-42
View File
@@ -1,42 +0,0 @@
import { analyzeApp, buildApp, developApp } from "./app";
import { buildCast, developCast } from "./cast";
import { analyzeDemo, buildDemo, developDemo } from "./demo";
import { downloadTranslations } from "./download-translations";
import { setupAndFetchNightlyTranslations } from "./fetch-nightly-translations";
import { buildGallery, developGallery, gatherGalleryPages } from "./gallery";
import { genIconsJson } from "./gen-icons-json";
import { buildHassio, developHassio } from "./hassio";
import { buildLandingPage, developLandingPage } from "./landing-page";
import { buildLocaleData } from "./locale-data";
import { buildTranslations } from "./translations";
export default {
"develop-app": developApp,
"build-app": buildApp,
"analyze-app": analyzeApp,
"develop-cast": developCast,
"build-cast": buildCast,
"develop-demo": developDemo,
"build-demo": buildDemo,
"analyze-demo": analyzeDemo,
"develop-gallery": developGallery,
"build-gallery": buildGallery,
"gather-gallery-pages": gatherGalleryPages,
"develop-hassio": developHassio,
"build-hassio": buildHassio,
"develop-landing-page": developLandingPage,
"build-landing-page": buildLandingPage,
"setup-and-fetch-nightly-translations": setupAndFetchNightlyTranslations,
"download-translations": downloadTranslations,
"build-translations": buildTranslations,
"gen-icons-json": genIconsJson,
"build-locale-data": buildLocaleData,
};
+41
View File
@@ -0,0 +1,41 @@
import gulp from "gulp";
import "./clean.js";
import "./compress.js";
import "./entry-html.js";
import "./gather-static.js";
import "./gen-icons-json.js";
import "./translations.js";
import "./rspack.js";
gulp.task(
"develop-landing-page",
gulp.series(
async function setEnv() {
process.env.NODE_ENV = "development";
},
"clean-landing-page",
"translations-enable-merge-backend",
"build-landing-page-translations",
"copy-translations-landing-page",
"build-locale-data",
"copy-static-landing-page",
"gen-pages-landing-page-dev",
"rspack-watch-landing-page"
)
);
gulp.task(
"build-landing-page",
gulp.series(
async function setEnv() {
process.env.NODE_ENV = "production";
},
"clean-landing-page",
"build-landing-page-translations",
"copy-translations-landing-page",
"build-locale-data",
"copy-static-landing-page",
"rspack-prod-landing-page",
"gen-pages-landing-page-prod"
)
);
-46
View File
@@ -1,46 +0,0 @@
import { series } from "gulp";
import { cleanLandingPage } from "./clean.ts";
import "./compress.ts";
import {
genPagesLandingPageDev,
genPagesLandingPageProd,
} from "./entry-html.ts";
import {
copyStaticLandingPage,
copyTranslationsLandingPage,
} from "./gather-static.ts";
import { buildLocaleData } from "./locale-data.ts";
import { rspackProdLandingPage, rspackWatchLandingPage } from "./rspack.ts";
import {
buildLandingPageTranslations,
translationsEnableMergeBackend,
} from "./translations.ts";
// develop-landing-page
export const developLandingPage = series(
async function setEnv() {
process.env.NODE_ENV = "development";
},
cleanLandingPage,
translationsEnableMergeBackend,
buildLandingPageTranslations,
copyTranslationsLandingPage,
buildLocaleData,
copyStaticLandingPage,
genPagesLandingPageDev,
rspackWatchLandingPage
);
// build-landing-page
export const buildLandingPage = series(
async function setEnv() {
process.env.NODE_ENV = "production";
},
cleanLandingPage,
buildLandingPageTranslations,
copyTranslationsLandingPage,
buildLocaleData,
copyStaticLandingPage,
rspackProdLandingPage,
genPagesLandingPageProd
);
@@ -1,8 +1,8 @@
import { deleteSync } from "del";
import { series } from "gulp";
import { mkdir, readFile, writeFile } from "node:fs/promises";
import { mkdir, readFile, writeFile } from "fs/promises";
import gulp from "gulp";
import { join, resolve } from "node:path";
import paths from "../paths.ts";
import paths from "../paths.cjs";
const formatjsDir = join(paths.root_dir, "node_modules", "@formatjs");
const outDir = join(paths.build_dir, "locale-data");
@@ -31,7 +31,7 @@ const convertToJSON = async (
join(formatjsDir, pkg, subDir, `${language}.js`),
"utf-8"
);
} catch (e: any) {
} catch (e) {
// Ignore if language is missing (i.e. not supported by @formatjs)
if (e.code === "ENOENT" && skipMissing) {
console.warn(`Skipped missing data for language ${lang} from ${pkg}`);
@@ -54,16 +54,16 @@ const convertToJSON = async (
await writeFile(join(outDir, `${pkg}/${lang}.json`), localeData);
};
const cleanLocaleData = async () => deleteSync([outDir]);
gulp.task("clean-locale-data", async () => deleteSync([outDir]));
const createLocaleData = async () => {
gulp.task("create-locale-data", async () => {
const translationMeta = JSON.parse(
await readFile(
resolve(paths.translations_src, "translationMetadata.json"),
"utf-8"
)
);
const conversions: any[] = [];
const conversions = [];
for (const pkg of Object.keys(INTL_POLYFILLS)) {
// eslint-disable-next-line no-await-in-loop
await mkdir(join(outDir, pkg), { recursive: true });
@@ -81,6 +81,9 @@ const createLocaleData = async () => {
)
);
await Promise.all(conversions);
};
});
export const buildLocaleData = series(cleanLocaleData, createLocaleData);
gulp.task(
"build-locale-data",
gulp.series("clean-locale-data", "create-locale-data")
);
@@ -1,13 +1,13 @@
// Tasks to run rspack.
import fs from "fs";
import path from "path";
import log from "fancy-log";
import gulp from "gulp";
import rspack from "@rspack/core";
import { RspackDevServer } from "@rspack/dev-server";
import log from "fancy-log";
import { series, watch } from "gulp";
import fs from "node:fs";
import path from "node:path";
import { isDevContainer, isStatsBuild, isTestBuild } from "../env.ts";
import paths from "../paths.ts";
import env from "../env.cjs";
import paths from "../paths.cjs";
import {
createAppConfig,
createCastConfig,
@@ -15,17 +15,7 @@ import {
createGalleryConfig,
createHassioConfig,
createLandingPageConfig,
} from "../rspack.ts";
import {
copyTranslationsApp,
copyTranslationsLandingPage,
copyTranslationsSupervisor,
} from "./gather-static.ts";
import {
buildLandingPageTranslations,
buildSupervisorTranslations,
buildTranslations,
} from "./translations.ts";
} from "../rspack.cjs";
const bothBuilds = (createConfigFunc, params) => [
createConfigFunc({ ...params, latestBuild: true }),
@@ -39,14 +29,6 @@ const isWsl =
.toLocaleLowerCase()
.includes("microsoft");
interface RunDevServer {
compiler: any;
contentBase: string;
port: number;
listenHost?: string;
proxy?: any;
}
/**
* @param {{
* compiler: import("@rspack/core").Compiler,
@@ -59,12 +41,12 @@ const runDevServer = async ({
compiler,
contentBase,
port,
listenHost,
proxy,
}: RunDevServer) => {
listenHost = undefined,
proxy = undefined,
}) => {
if (listenHost === undefined) {
// For dev container, we need to listen on all hosts
listenHost = isDevContainer() ? "0.0.0.0" : "localhost";
listenHost = env.isDevContainer() ? "0.0.0.0" : "localhost";
}
const server = new RspackDevServer(
{
@@ -86,7 +68,7 @@ const runDevServer = async ({
log("[rspack-dev-server]", `Project is running at http://localhost:${port}`);
};
const doneHandler = (done?: (value?: unknown) => void) => (err, stats) => {
const doneHandler = (done) => (err, stats) => {
if (err) {
log.error(err.stack || err);
if (err.details) {
@@ -115,46 +97,49 @@ const prodBuild = (conf) =>
);
});
export const rspackWatchApp = () => {
gulp.task("rspack-watch-app", () => {
// This command will run forever because we don't close compiler
rspack(
process.env.ES5
? bothBuilds(createAppConfig, { isProdBuild: false })
: createAppConfig({ isProdBuild: false, latestBuild: true })
).watch({ poll: isWsl }, doneHandler());
watch(
gulp.watch(
path.join(paths.translations_src, "en.json"),
series(buildTranslations, copyTranslationsApp)
gulp.series("build-translations", "copy-translations-app")
);
};
});
export const rspackProdApp = () =>
gulp.task("rspack-prod-app", () =>
prodBuild(
bothBuilds(createAppConfig, {
isProdBuild: true,
isStatsBuild: isStatsBuild(),
isTestBuild: isTestBuild(),
isStatsBuild: env.isStatsBuild(),
isTestBuild: env.isTestBuild(),
})
);
)
);
export const rspackDevServerDemo = () =>
gulp.task("rspack-dev-server-demo", () =>
runDevServer({
compiler: rspack(
createDemoConfig({ isProdBuild: false, latestBuild: true })
),
contentBase: paths.demo_output_root,
port: 8090,
});
})
);
export const rspackProdDemo = () =>
gulp.task("rspack-prod-demo", () =>
prodBuild(
bothBuilds(createDemoConfig, {
isProdBuild: true,
isStatsBuild: isStatsBuild(),
isStatsBuild: env.isStatsBuild(),
})
);
)
);
export const rspackDevServerCast = () =>
gulp.task("rspack-dev-server-cast", () =>
runDevServer({
compiler: rspack(
createCastConfig({ isProdBuild: false, latestBuild: true })
@@ -163,16 +148,18 @@ export const rspackDevServerCast = () =>
port: 8080,
// Accessible from the network, because that's how Cast hits it.
listenHost: "0.0.0.0",
});
})
);
export const rspackProdCast = () =>
gulp.task("rspack-prod-cast", () =>
prodBuild(
bothBuilds(createCastConfig, {
isProdBuild: true,
})
);
)
);
export const rspackWatchHassio = () => {
gulp.task("rspack-watch-hassio", () => {
// This command will run forever because we don't close compiler
rspack(
createHassioConfig({
@@ -181,22 +168,23 @@ export const rspackWatchHassio = () => {
})
).watch({ ignored: /build/, poll: isWsl }, doneHandler());
watch(
gulp.watch(
path.join(paths.translations_src, "en.json"),
series(buildSupervisorTranslations, copyTranslationsSupervisor)
gulp.series("build-supervisor-translations", "copy-translations-supervisor")
);
};
});
export const rspackProdHassio = () =>
gulp.task("rspack-prod-hassio", () =>
prodBuild(
bothBuilds(createHassioConfig, {
isProdBuild: true,
isStatsBuild: isStatsBuild(),
isTestBuild: isTestBuild(),
isStatsBuild: env.isStatsBuild(),
isTestBuild: env.isTestBuild(),
})
);
)
);
export const rspackDevServerGallery = () =>
gulp.task("rspack-dev-server-gallery", () =>
runDevServer({
compiler: rspack(
createGalleryConfig({ isProdBuild: false, latestBuild: true })
@@ -204,17 +192,19 @@ export const rspackDevServerGallery = () =>
contentBase: paths.gallery_output_root,
port: 8100,
listenHost: "0.0.0.0",
});
})
);
export const rspackProdGallery = () =>
gulp.task("rspack-prod-gallery", () =>
prodBuild(
createGalleryConfig({
isProdBuild: true,
latestBuild: true,
})
);
)
);
export const rspackWatchLandingPage = () => {
gulp.task("rspack-watch-landing-page", () => {
// This command will run forever because we don't close compiler
rspack(
process.env.ES5
@@ -222,17 +212,21 @@ export const rspackWatchLandingPage = () => {
: createLandingPageConfig({ isProdBuild: false, latestBuild: true })
).watch({ poll: isWsl }, doneHandler());
watch(
gulp.watch(
path.join(paths.translations_src, "en.json"),
series(buildLandingPageTranslations, copyTranslationsLandingPage)
gulp.series(
"build-landing-page-translations",
"copy-translations-landing-page"
)
);
};
});
export const rspackProdLandingPage = () =>
gulp.task("rspack-prod-landing-page", () =>
prodBuild(
bothBuilds(createLandingPageConfig, {
isProdBuild: true,
isStatsBuild: isStatsBuild(),
isTestBuild: isTestBuild(),
isStatsBuild: env.isStatsBuild(),
isTestBuild: env.isTestBuild(),
})
);
)
);
@@ -1,10 +1,11 @@
// Generate service workers
import { deleteAsync } from "del";
import gulp from "gulp";
import { mkdir, readFile, symlink, writeFile } from "node:fs/promises";
import { basename, join, relative } from "node:path";
import { injectManifest } from "workbox-build";
import paths from "../paths.ts";
import paths from "../paths.cjs";
const SW_MAP = {
[paths.app_output_latest]: "modern",
@@ -22,7 +23,7 @@ self.addEventListener('install', (event) => {
});
`.trim() + "\n";
export const genServiceWorkerAppDev = async () => {
gulp.task("gen-service-worker-app-dev", async () => {
await mkdir(paths.app_output_root, { recursive: true });
await Promise.all(
Object.values(SW_MAP).map((build) =>
@@ -31,9 +32,9 @@ export const genServiceWorkerAppDev = async () => {
})
)
);
};
});
export const genServiceWorkerAppProd = () =>
gulp.task("gen-service-worker-app-prod", () =>
Promise.all(
Object.entries(SW_MAP).map(async ([outPath, build]) => {
const manifest = JSON.parse(
@@ -82,4 +83,5 @@ export const genServiceWorkerAppProd = () =>
await symlink(basename(swDest), swOld);
}
})
);
)
);
@@ -2,7 +2,7 @@
import { deleteAsync } from "del";
import { glob } from "glob";
import { src as glupSrc, dest as gulpDest, parallel, series } from "gulp";
import gulp from "gulp";
import rename from "gulp-rename";
import merge from "lodash.merge";
import { createHash } from "node:crypto";
@@ -10,12 +10,9 @@ import { mkdir, readFile } from "node:fs/promises";
import { basename, join } from "node:path";
import { PassThrough, Transform } from "node:stream";
import { finished } from "node:stream/promises";
import { isProdBuild } from "../env.ts";
import paths from "../paths.ts";
import {
allowSetupFetchNightlyTranslations,
fetchNightlyTranslations,
} from "./fetch-nightly-translations.ts";
import env from "../env.cjs";
import paths from "../paths.cjs";
import "./fetch-nightly-translations.js";
const inFrontendDir = "translations/frontend";
const inBackendDir = "translations/backend";
@@ -26,20 +23,18 @@ const TEST_LOCALE = "en-x-test";
let mergeBackend = false;
// translations-enable-merge-backend
export const translationsEnableMergeBackend = parallel(async () => {
mergeBackend = true;
}, allowSetupFetchNightlyTranslations);
gulp.task(
"translations-enable-merge-backend",
gulp.parallel(async () => {
mergeBackend = true;
}, "allow-setup-fetch-nightly-translations")
);
// Transform stream to apply a function on Vinyl JSON files (buffer mode only).
// The provided function can either return a new object, or an array of
// [object, subdirectory] pairs for fragmentizing the JSON.
class CustomJSON extends Transform {
_func: any;
_reviver: any;
constructor(func, reviver: any = null) {
constructor(func, reviver = null) {
super({ objectMode: true });
this._func = func;
this._reviver = reviver;
@@ -61,17 +56,9 @@ class CustomJSON extends Transform {
// Transform stream to merge Vinyl JSON files (buffer mode only).
class MergeJSON extends Transform {
_objects: any[] = [];
_objects = [];
_stem: any;
_startObj: any;
_reviver: any;
_outFile: any;
constructor(stem, startObj = {}, reviver: any = null) {
constructor(stem, startObj = {}, reviver = null) {
super({ objectMode: true, allowHalfOpen: false });
this._stem = stem;
this._startObj = structuredClone(startObj);
@@ -124,12 +111,11 @@ const testReviver = (_key, value) =>
const KEY_REFERENCE = /\[%key:([^%]+)%\]/;
const lokaliseTransform = (data, path, original = data) => {
const output = {};
for (const entry of Object.entries(data)) {
const [key, value] = entry as [string, string];
for (const [key, value] of Object.entries(data)) {
if (typeof value === "object") {
output[key] = lokaliseTransform(value, path, original);
} else {
output[key] = value?.replace(KEY_REFERENCE, (_match, lokalise_key) => {
output[key] = value.replace(KEY_REFERENCE, (_match, lokalise_key) => {
const replace = lokalise_key.split("::").reduce((tr, k) => {
if (!tr) {
throw Error(`Invalid key placeholder ${lokalise_key} in ${path}`);
@@ -146,17 +132,18 @@ const lokaliseTransform = (data, path, original = data) => {
return output;
};
export const cleanTranslations = () => deleteAsync([workDir]);
gulp.task("clean-translations", () => deleteAsync([workDir]));
const makeWorkDir = () => mkdir(workDir, { recursive: true });
const createTestTranslation = () =>
isProdBuild()
env.isProdBuild()
? Promise.resolve()
: glupSrc(EN_SRC)
: gulp
.src(EN_SRC)
.pipe(new CustomJSON(null, testReviver))
.pipe(rename(`${TEST_LOCALE}.json`))
.pipe(gulpDest(workDir));
.pipe(gulp.dest(workDir));
/**
* This task will build a master translation file, to be used as the base for
@@ -168,10 +155,11 @@ const createTestTranslation = () =>
* the Lokalise update to translations/en.json will not happen immediately.
*/
const createMasterTranslation = () =>
glupSrc([EN_SRC, ...(mergeBackend ? [`${inBackendDir}/en.json`] : [])])
gulp
.src([EN_SRC, ...(mergeBackend ? [`${inBackendDir}/en.json`] : [])])
.pipe(new CustomJSON(lokaliseTransform))
.pipe(new MergeJSON("en"))
.pipe(gulpDest(workDir));
.pipe(gulp.dest(workDir));
const FRAGMENTS = ["base"];
@@ -198,12 +186,12 @@ const createTranslations = async () => {
// each locale, then fragmentizes and flattens the data for final output.
const translationFiles = await glob([
`${inFrontendDir}/!(en).json`,
...(isProdBuild() ? [] : [`${workDir}/${TEST_LOCALE}.json`]),
...(env.isProdBuild() ? [] : [`${workDir}/${TEST_LOCALE}.json`]),
]);
const hashStream = new Transform({
objectMode: true,
transform: async (file, _, callback) => {
const hash = isProdBuild()
const hash = env.isProdBuild()
? createHash("md5").update(file.contents).digest("hex")
: "dev";
HASHES.set(file.stem, hash);
@@ -242,7 +230,7 @@ const createTranslations = async () => {
})
)
)
.pipe(gulpDest(outDir));
.pipe(gulp.dest(outDir));
// Send the English master downstream first, then for each other locale
// generate merged JSON data to continue piping. It begins with the master
@@ -252,15 +240,15 @@ const createTranslations = async () => {
// TODO: This is a naive interpretation of BCP47 that should be improved.
// Will be OK for now as long as we don't have anything more complicated
// than a base translation + region.
const masterStream = glupSrc(`${workDir}/en.json`).pipe(
new PassThrough({ objectMode: true })
);
const masterStream = gulp
.src(`${workDir}/en.json`)
.pipe(new PassThrough({ objectMode: true }));
masterStream.pipe(hashStream, { end: false });
const mergesFinished = [finished(masterStream)];
for (const translationFile of translationFiles) {
const locale = basename(translationFile, ".json");
const subtags = locale.split("-");
const mergeFiles: string[] = [];
const mergeFiles = [];
for (let i = 1; i <= subtags.length; i++) {
const lang = subtags.slice(0, i).join("-");
if (lang === TEST_LOCALE) {
@@ -272,9 +260,9 @@ const createTranslations = async () => {
}
}
}
const mergeStream = glupSrc(mergeFiles, { allowEmpty: true }).pipe(
new MergeJSON(locale, enMaster, emptyReviver)
);
const mergeStream = gulp
.src(mergeFiles, { allowEmpty: true })
.pipe(new MergeJSON(locale, enMaster, emptyReviver));
mergesFinished.push(finished(mergeStream));
mergeStream.pipe(hashStream, { end: false });
}
@@ -287,11 +275,12 @@ const createTranslations = async () => {
};
const writeTranslationMetaData = () =>
glupSrc([`${paths.translations_src}/translationMetadata.json`])
gulp
.src([`${paths.translations_src}/translationMetadata.json`])
.pipe(
new CustomJSON((meta) => {
// Add the test translation in development.
if (!isProdBuild()) {
if (!env.isProdBuild()) {
meta[TEST_LOCALE] = { nativeName: "Translation Test" };
}
// Filter out locales without a native name, and add the hashes.
@@ -311,22 +300,28 @@ const writeTranslationMetaData = () =>
};
})
)
.pipe(gulpDest(workDir));
.pipe(gulp.dest(workDir));
export const buildTranslations = series(
parallel(fetchNightlyTranslations, series(cleanTranslations, makeWorkDir)),
createTestTranslation,
createMasterTranslation,
createTranslations,
writeTranslationMetaData
gulp.task(
"build-translations",
gulp.series(
gulp.parallel(
"fetch-nightly-translations",
gulp.series("clean-translations", makeWorkDir)
),
createTestTranslation,
createMasterTranslation,
createTranslations,
writeTranslationMetaData
)
);
export const buildSupervisorTranslations = series(
setFragment("supervisor"),
buildTranslations
gulp.task(
"build-supervisor-translations",
gulp.series(setFragment("supervisor"), "build-translations")
);
export const buildLandingPageTranslations = series(
setFragment("landing-page"),
buildTranslations
gulp.task(
"build-landing-page-translations",
gulp.series(setFragment("landing-page"), "build-translations")
);
@@ -5,11 +5,10 @@ import { version as babelVersion } from "@babel/core";
import presetEnv from "@babel/preset-env";
import compilationTargets from "@babel/helper-compilation-targets";
import coreJSCompat from "core-js-compat";
import { logPlugin } from "@babel/preset-env/lib/debug.js";
// eslint-disable-next-line import/no-relative-packages
import shippedPolyfills from "../node_modules/babel-plugin-polyfill-corejs3/lib/shipped-proposals.js";
import { babelOptions } from "./bundle.ts";
import { babelOptions } from "./bundle.cjs";
const detailsOpen = (heading) =>
`<details>\n<summary><h4>${heading}</h4></summary>\n`;
@@ -51,12 +50,6 @@ for (const buildType of ["Modern", "Legacy"]) {
const babelOpts = babelOptions({ latestBuild: browserslistEnv === "modern" });
const presetEnvOpts = babelOpts.presets[0][1];
if (typeof presetEnvOpts !== "object") {
throw new Error(
"The first preset in babelOptions is not an object. This is unexpected."
);
}
// Invoking preset-env in debug mode will log the included plugins
console.log(detailsOpen(`${buildType} Build Babel Plugins`));
presetEnv.default(dummyAPI, {
+63
View File
@@ -0,0 +1,63 @@
const path = require("path");
module.exports = {
root_dir: path.resolve(__dirname, ".."),
build_dir: path.resolve(__dirname, "../build"),
app_output_root: path.resolve(__dirname, "../hass_frontend"),
app_output_static: path.resolve(__dirname, "../hass_frontend/static"),
app_output_latest: path.resolve(
__dirname,
"../hass_frontend/frontend_latest"
),
app_output_es5: path.resolve(__dirname, "../hass_frontend/frontend_es5"),
demo_dir: path.resolve(__dirname, "../demo"),
demo_output_root: path.resolve(__dirname, "../demo/dist"),
demo_output_static: path.resolve(__dirname, "../demo/dist/static"),
demo_output_latest: path.resolve(__dirname, "../demo/dist/frontend_latest"),
demo_output_es5: path.resolve(__dirname, "../demo/dist/frontend_es5"),
cast_dir: path.resolve(__dirname, "../cast"),
cast_output_root: path.resolve(__dirname, "../cast/dist"),
cast_output_static: path.resolve(__dirname, "../cast/dist/static"),
cast_output_latest: path.resolve(__dirname, "../cast/dist/frontend_latest"),
cast_output_es5: path.resolve(__dirname, "../cast/dist/frontend_es5"),
gallery_dir: path.resolve(__dirname, "../gallery"),
gallery_build: path.resolve(__dirname, "../gallery/build"),
gallery_output_root: path.resolve(__dirname, "../gallery/dist"),
gallery_output_latest: path.resolve(
__dirname,
"../gallery/dist/frontend_latest"
),
gallery_output_static: path.resolve(__dirname, "../gallery/dist/static"),
landingPage_dir: path.resolve(__dirname, "../landing-page"),
landingPage_build: path.resolve(__dirname, "../landing-page/build"),
landingPage_output_root: path.resolve(__dirname, "../landing-page/dist"),
landingPage_output_latest: path.resolve(
__dirname,
"../landing-page/dist/frontend_latest"
),
landingPage_output_es5: path.resolve(
__dirname,
"../landing-page/dist/frontend_es5"
),
landingPage_output_static: path.resolve(
__dirname,
"../landing-page/dist/static"
),
hassio_dir: path.resolve(__dirname, "../hassio"),
hassio_output_root: path.resolve(__dirname, "../hassio/build"),
hassio_output_static: path.resolve(__dirname, "../hassio/build/static"),
hassio_output_latest: path.resolve(
__dirname,
"../hassio/build/frontend_latest"
),
hassio_output_es5: path.resolve(__dirname, "../hassio/build/frontend_es5"),
hassio_publicPath: "/api/hassio/app",
translations_src: path.resolve(__dirname, "../src/translations"),
};
-63
View File
@@ -1,63 +0,0 @@
import path, { dirname as pathDirname } from "node:path";
import { fileURLToPath } from "node:url";
export const dirname = pathDirname(fileURLToPath(import.meta.url));
export default {
root_dir: path.resolve(dirname, ".."),
build_dir: path.resolve(dirname, "../build"),
app_output_root: path.resolve(dirname, "../hass_frontend"),
app_output_static: path.resolve(dirname, "../hass_frontend/static"),
app_output_latest: path.resolve(dirname, "../hass_frontend/frontend_latest"),
app_output_es5: path.resolve(dirname, "../hass_frontend/frontend_es5"),
demo_dir: path.resolve(dirname, "../demo"),
demo_output_root: path.resolve(dirname, "../demo/dist"),
demo_output_static: path.resolve(dirname, "../demo/dist/static"),
demo_output_latest: path.resolve(dirname, "../demo/dist/frontend_latest"),
demo_output_es5: path.resolve(dirname, "../demo/dist/frontend_es5"),
cast_dir: path.resolve(dirname, "../cast"),
cast_output_root: path.resolve(dirname, "../cast/dist"),
cast_output_static: path.resolve(dirname, "../cast/dist/static"),
cast_output_latest: path.resolve(dirname, "../cast/dist/frontend_latest"),
cast_output_es5: path.resolve(dirname, "../cast/dist/frontend_es5"),
gallery_dir: path.resolve(dirname, "../gallery"),
gallery_build: path.resolve(dirname, "../gallery/build"),
gallery_output_root: path.resolve(dirname, "../gallery/dist"),
gallery_output_latest: path.resolve(
dirname,
"../gallery/dist/frontend_latest"
),
gallery_output_static: path.resolve(dirname, "../gallery/dist/static"),
landingPage_dir: path.resolve(dirname, "../landing-page"),
landingPage_build: path.resolve(dirname, "../landing-page/build"),
landingPage_output_root: path.resolve(dirname, "../landing-page/dist"),
landingPage_output_latest: path.resolve(
dirname,
"../landing-page/dist/frontend_latest"
),
landingPage_output_es5: path.resolve(
dirname,
"../landing-page/dist/frontend_es5"
),
landingPage_output_static: path.resolve(
dirname,
"../landing-page/dist/static"
),
hassio_dir: path.resolve(dirname, "../hassio"),
hassio_output_root: path.resolve(dirname, "../hassio/build"),
hassio_output_static: path.resolve(dirname, "../hassio/build/static"),
hassio_output_latest: path.resolve(
dirname,
"../hassio/build/frontend_latest"
),
hassio_output_es5: path.resolve(dirname, "../hassio/build/frontend_es5"),
hassio_publicPath: "/api/hassio/app",
translations_src: path.resolve(dirname, "../src/translations"),
};
@@ -1,25 +1,20 @@
import filterStats from "@bundle-stats/plugin-webpack-filter";
import { RsdoctorRspackPlugin } from "@rsdoctor/rspack-plugin";
import { DefinePlugin, NormalModuleReplacementPlugin } from "@rspack/core";
import { defineConfig } from "@rspack/cli";
import log from "fancy-log";
import { existsSync } from "node:fs";
import path from "node:path";
import { WebpackManifestPlugin } from "rspack-manifest-plugin";
import TerserPlugin from "terser-webpack-plugin";
import { StatsWriterPlugin } from "webpack-stats-plugin";
// @ts-ignore
import WebpackBar from "webpackbar/rspack";
import {
babelOptions,
config,
definedVars,
emptyPackages,
sourceMapURL,
swcOptions,
terserOptions,
} from "./bundle.ts";
import paths from "./paths.ts";
const { existsSync } = require("fs");
const path = require("path");
const rspack = require("@rspack/core");
// eslint-disable-next-line @typescript-eslint/naming-convention
const { RsdoctorRspackPlugin } = require("@rsdoctor/rspack-plugin");
// eslint-disable-next-line @typescript-eslint/naming-convention
const { StatsWriterPlugin } = require("webpack-stats-plugin");
const filterStats = require("@bundle-stats/plugin-webpack-filter");
// eslint-disable-next-line @typescript-eslint/naming-convention
const TerserPlugin = require("terser-webpack-plugin");
// eslint-disable-next-line @typescript-eslint/naming-convention
const { WebpackManifestPlugin } = require("rspack-manifest-plugin");
const log = require("fancy-log");
// eslint-disable-next-line @typescript-eslint/naming-convention
const WebpackBar = require("webpackbar/rspack");
const paths = require("./paths.cjs");
const bundle = require("./bundle.cjs");
class LogStartCompilePlugin {
ignoredFirst = false;
@@ -35,7 +30,7 @@ class LogStartCompilePlugin {
}
}
export const createRspackConfig = ({
const createRspackConfig = ({
name,
entry,
outputPath,
@@ -47,23 +42,12 @@ export const createRspackConfig = ({
isTestBuild,
isHassioBuild,
dontHash,
}: {
name: string;
entry: any;
outputPath: string;
publicPath: string;
defineOverlay?: Record<string, any>;
isProdBuild?: boolean;
latestBuild?: boolean;
isStatsBuild?: boolean;
isTestBuild?: boolean;
isHassioBuild?: boolean;
dontHash?: Set<string>;
}) => {
if (!dontHash) {
dontHash = new Set();
}
return defineConfig({
const ignorePackages = bundle.ignorePackages({ latestBuild });
return {
name,
mode: isProdBuild ? "production" : "development",
target: `browserslist:${latestBuild ? "modern" : "legacy"}`,
@@ -86,7 +70,7 @@ export const createRspackConfig = ({
{
loader: "babel-loader",
options: {
...babelOptions({
...bundle.babelOptions({
latestBuild,
isProdBuild,
isTestBuild,
@@ -98,7 +82,7 @@ export const createRspackConfig = ({
},
{
loader: "builtin:swc-loader",
options: swcOptions(),
options: bundle.swcOptions(),
},
],
resolve: {
@@ -119,7 +103,7 @@ export const createRspackConfig = ({
new TerserPlugin({
parallel: true,
extractComments: true,
terserOptions: terserOptions({ latestBuild, isTestBuild }),
terserOptions: bundle.terserOptions({ latestBuild, isTestBuild }),
}),
],
moduleIds: isProdBuild && !isStatsBuild ? "deterministic" : "named",
@@ -138,7 +122,7 @@ export const createRspackConfig = ({
!chunk.canBeInitial() &&
!new RegExp(
`^.+-work${latestBuild ? "(?:let|er)" : "let"}$`
).test(chunk?.name || ""),
).test(chunk.name),
},
},
plugins: [
@@ -147,11 +131,44 @@ export const createRspackConfig = ({
// Only include the JS of entrypoints
filter: (file) => file.isInitial && !file.name.endsWith(".map"),
}),
new DefinePlugin(
definedVars({ isProdBuild, latestBuild, defineOverlay })
new rspack.DefinePlugin(
bundle.definedVars({ isProdBuild, latestBuild, defineOverlay })
),
new NormalModuleReplacementPlugin(
new RegExp(emptyPackages({ isHassioBuild }).join("|")),
new rspack.IgnorePlugin({
checkResource(resource, context) {
// Only use ignore to intercept imports that we don't control
// inside node_module dependencies.
if (
!context.includes("/node_modules/") ||
// calling define.amd will call require("!!webpack amd options")
resource.startsWith("!!webpack") ||
// loaded by webpack dev server but doesn't exist.
resource === "webpack/hot" ||
resource.startsWith("@swc/helpers")
) {
return false;
}
let fullPath;
try {
fullPath = resource.startsWith(".")
? path.resolve(context, resource)
: require.resolve(resource);
} catch (err) {
console.error(
"Error in Home Assistant ignore plugin",
resource,
context
);
throw err;
}
return ignorePackages.some((toIgnorePath) =>
fullPath.startsWith(toIgnorePath)
);
},
}),
new rspack.NormalModuleReplacementPlugin(
new RegExp(bundle.emptyPackages({ isHassioBuild }).join("|")),
path.resolve(paths.root_dir, "src/util/empty.js")
),
!isProdBuild && new LogStartCompilePlugin(),
@@ -167,9 +184,7 @@ export const createRspackConfig = ({
isProdBuild &&
isStatsBuild &&
new RsdoctorRspackPlugin({
output: {
reportDir: path.join(paths.build_dir, "rsdoctor"),
},
reportDir: path.join(paths.build_dir, "rsdoctor"),
features: ["plugins", "bundle"],
supports: {
generateTileGraph: true,
@@ -204,9 +219,7 @@ export const createRspackConfig = ({
output: {
module: latestBuild,
filename: ({ chunk }) =>
!isProdBuild ||
isStatsBuild ||
(chunk?.name && dontHash.has(chunk.name))
!isProdBuild || isStatsBuild || dontHash.has(chunk.name)
? "[name].js"
: "[name].[contenthash].js",
chunkFilename:
@@ -237,7 +250,7 @@ export const createRspackConfig = ({
// dev tools, and they stay happy getting 404s with valid requests.
return `/unknown${path.resolve("/", info.resourcePath)}`;
}
return new URL(info.resourcePath, sourceMapURL()).href;
return new URL(info.resourcePath, bundle.sourceMapURL()).href;
}
: undefined,
])
@@ -247,51 +260,35 @@ export const createRspackConfig = ({
layers: true,
outputModule: true,
},
});
};
};
export const createAppConfig = ({
const createAppConfig = ({
isProdBuild,
latestBuild,
isStatsBuild,
isTestBuild,
}: {
isProdBuild?: boolean;
latestBuild?: boolean;
isStatsBuild?: boolean;
isTestBuild?: boolean;
}) =>
createRspackConfig(
config.app({ isProdBuild, latestBuild, isStatsBuild, isTestBuild })
bundle.config.app({ isProdBuild, latestBuild, isStatsBuild, isTestBuild })
);
export const createDemoConfig = ({
isProdBuild,
latestBuild,
isStatsBuild,
}: {
isProdBuild?: boolean;
latestBuild?: boolean;
isStatsBuild?: boolean;
}) =>
createRspackConfig(config.demo({ isProdBuild, latestBuild, isStatsBuild }));
const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) =>
createRspackConfig(
bundle.config.demo({ isProdBuild, latestBuild, isStatsBuild })
);
export const createCastConfig = ({ isProdBuild, latestBuild }) =>
createRspackConfig(config.cast({ isProdBuild, latestBuild }));
const createCastConfig = ({ isProdBuild, latestBuild }) =>
createRspackConfig(bundle.config.cast({ isProdBuild, latestBuild }));
export const createHassioConfig = ({
const createHassioConfig = ({
isProdBuild,
latestBuild,
isStatsBuild,
isTestBuild,
}: {
isProdBuild?: boolean;
latestBuild?: boolean;
isStatsBuild?: boolean;
isTestBuild?: boolean;
}) =>
createRspackConfig(
config.hassio({
bundle.config.hassio({
isProdBuild,
latestBuild,
isStatsBuild,
@@ -299,8 +296,18 @@ export const createHassioConfig = ({
})
);
export const createGalleryConfig = ({ isProdBuild, latestBuild }) =>
createRspackConfig(config.gallery({ isProdBuild, latestBuild }));
const createGalleryConfig = ({ isProdBuild, latestBuild }) =>
createRspackConfig(bundle.config.gallery({ isProdBuild, latestBuild }));
export const createLandingPageConfig = ({ isProdBuild, latestBuild }) =>
createRspackConfig(config.landingPage({ isProdBuild, latestBuild }));
const createLandingPageConfig = ({ isProdBuild, latestBuild }) =>
createRspackConfig(bundle.config.landingPage({ isProdBuild, latestBuild }));
module.exports = {
createAppConfig,
createDemoConfig,
createCastConfig,
createHassioConfig,
createGalleryConfig,
createRspackConfig,
createLandingPageConfig,
};
-42
View File
@@ -1,42 +0,0 @@
// run-build.ts
import { series } from "gulp";
import { availableParallelism } from "node:os";
import tasks from "./gulp/index.ts";
process.env.UV_THREADPOOL_SIZE = availableParallelism().toString();
const runGulpTask = async (runTasks: string[]) => {
try {
for (const taskName of runTasks) {
if (tasks[taskName] === undefined) {
console.error(`Gulp task "${taskName}" does not exist.`);
console.log("Available tasks:");
Object.keys(tasks).forEach((task) => {
console.log(` - ${task}`);
});
process.exit(1);
}
}
await new Promise((resolve, reject) => {
series(...runTasks.map((taskName) => tasks[taskName]))((err?: Error) => {
if (err) {
reject(err);
} else {
resolve(null);
}
});
});
process.exit(0);
} catch (error: any) {
console.error(`Error running Gulp task "${runTasks}":`, error);
process.exit(1);
}
};
// Get the task name from command line arguments
// TODO arg validation
const tasksToRun = process.argv.slice(2);
runGulpTask(tasksToRun);
+1 -1
View File
@@ -14,5 +14,5 @@
"name": "Home Assistant Cast",
"short_name": "HA Cast",
"start_url": "/?homescreen=1",
"theme_color": "#03A9F4"
"theme_color": "#009ac7"
}
+1 -1
View File
@@ -6,4 +6,4 @@ set -e
cd "$(dirname "$0")/../.."
yarn run-task build-cast
./node_modules/.bin/gulp build-cast
+1 -1
View File
@@ -6,4 +6,4 @@ set -e
cd "$(dirname "$0")/../.."
yarn run-task develop-cast
./node_modules/.bin/gulp develop-cast
+28 -20
View File
@@ -1,5 +1,3 @@
import "@material/mwc-button/mwc-button";
import type { ActionDetail } from "@material/mwc-list/mwc-list";
import { mdiCast, mdiCastConnected, mdiViewDashboard } from "@mdi/js";
import type { Auth, Connection } from "home-assistant-js-websocket";
@@ -20,6 +18,7 @@ import { atLeastVersion } from "../../../../src/common/config/version";
import { toggleAttribute } from "../../../../src/common/dom/toggle_attribute";
import "../../../../src/components/ha-icon";
import "../../../../src/components/ha-list";
import "../../../../src/components/ha-button";
import "../../../../src/components/ha-list-item";
import "../../../../src/components/ha-svg-icon";
import {
@@ -63,12 +62,20 @@ class HcCast extends LitElement {
<p class="question action-item">
Stay logged in?
<span>
<mwc-button @click=${this._handleSaveTokens}>
<ha-button
appearance="plain"
size="small"
@click=${this._handleSaveTokens}
>
YES
</mwc-button>
<mwc-button @click=${this._handleSkipSaveTokens}>
</ha-button>
<ha-button
appearance="plain"
size="small"
@click=${this._handleSkipSaveTokens}
>
NO
</mwc-button>
</ha-button>
</span>
</p>
`
@@ -78,10 +85,10 @@ class HcCast extends LitElement {
: !this.castManager.status
? html`
<p class="center-item">
<mwc-button raised @click=${this._handleLaunch}>
<ha-svg-icon .path=${mdiCast}></ha-svg-icon>
<ha-button @click=${this._handleLaunch}>
<ha-svg-icon slot="start" .path=${mdiCast}></ha-svg-icon>
Start Casting
</mwc-button>
</ha-button>
</p>
`
: html`
@@ -121,14 +128,22 @@ class HcCast extends LitElement {
<div class="card-actions">
${this.castManager.status
? html`
<mwc-button @click=${this._handleLaunch}>
<ha-svg-icon .path=${mdiCastConnected}></ha-svg-icon>
<ha-button appearance="plain" @click=${this._handleLaunch}>
<ha-svg-icon
slot="start"
.path=${mdiCastConnected}
></ha-svg-icon>
Manage
</mwc-button>
</ha-button>
`
: ""}
<div class="spacer"></div>
<mwc-button @click=${this._handleLogout}>Log out</mwc-button>
<ha-button
variant="danger"
appearance="plain"
@click=${this._handleLogout}
>Log out</ha-button
>
</div>
</hc-layout>
`;
@@ -245,13 +260,6 @@ class HcCast extends LitElement {
color: var(--secondary-text-color);
}
mwc-button ha-svg-icon {
margin-right: 8px;
margin-inline-end: 8px;
margin-inline-start: initial;
height: 18px;
}
ha-list-item ha-icon,
ha-list-item ha-svg-icon {
padding: 12px;
+14 -12
View File
@@ -1,4 +1,3 @@
import "@material/mwc-button";
import { mdiCastConnected, mdiCast } from "@mdi/js";
import type {
Auth,
@@ -28,6 +27,7 @@ import "../../../../src/layouts/hass-loading-screen";
import { registerServiceWorker } from "../../../../src/util/register-service-worker";
import "./hc-layout";
import "../../../../src/components/ha-textfield";
import "../../../../src/components/ha-button";
const seeFAQ = (qid) => html`
See <a href="./faq.html${qid ? `#${qid}` : ""}">the FAQ</a> for more
@@ -83,11 +83,14 @@ export class HcConnect extends LitElement {
Unable to connect to ${tokens!.hassUrl}.
</div>
<div class="card-actions">
<a href="/">
<mwc-button> Retry </mwc-button>
</a>
<ha-button appearance="plain" href="/">Retry</ha-button>
<div class="spacer"></div>
<mwc-button @click=${this._handleLogout}>Log out</mwc-button>
<ha-button
appearance="plain"
variant="danger"
@click=${this._handleLogout}
>Log out</ha-button
>
</div>
</hc-layout>
`;
@@ -128,16 +131,19 @@ export class HcConnect extends LitElement {
${this.error ? html` <p class="error">${this.error}</p> ` : ""}
</div>
<div class="card-actions">
<mwc-button @click=${this._handleDemo}>
<ha-button appearance="plain" @click=${this._handleDemo}>
Show Demo
<ha-svg-icon
slot="end"
.path=${this.castManager.castState === "CONNECTED"
? mdiCastConnected
: mdiCast}
></ha-svg-icon>
</mwc-button>
</ha-button>
<div class="spacer"></div>
<mwc-button @click=${this._handleConnect}>Authorize</mwc-button>
<ha-button appearance="plain" @click=${this._handleConnect}
>Authorize</ha-button
>
</div>
</hc-layout>
`;
@@ -309,10 +315,6 @@ export class HcConnect extends LitElement {
color: darkred;
}
mwc-button ha-svg-icon {
margin-left: 8px;
}
.spacer {
flex: 1;
}
+3 -3
View File
@@ -5,17 +5,17 @@ const castContext = framework.CastReceiverContext.getInstance();
const playerManager = castContext.getPlayerManager();
playerManager.setMessageInterceptor(
framework.messages.MessageType.LOAD,
"LOAD" as framework.messages.MessageType.LOAD,
(loadRequestData) => {
const media = loadRequestData.media;
// Special handling if it came from Google Assistant
if (media.entity) {
media.contentId = media.entity;
media.streamType = framework.messages.StreamType.LIVE;
media.streamType = "LIVE" as framework.messages.StreamType.LIVE;
media.contentType = "application/vnd.apple.mpegurl";
// @ts-ignore
media.hlsVideoSegmentFormat =
framework.messages.HlsVideoSegmentFormat.FMP4;
"fmp4" as framework.messages.HlsVideoSegmentFormat.FMP4;
}
return loadRequestData;
}
+10 -12
View File
@@ -40,7 +40,8 @@ const playDummyMedia = (viewTitle?: string) => {
loadRequestData.media.contentId =
"https://cast.home-assistant.io/images/google-nest-hub.png";
loadRequestData.media.contentType = "image/jpeg";
loadRequestData.media.streamType = framework.messages.StreamType.NONE;
loadRequestData.media.streamType =
"NONE" as framework.messages.StreamType.NONE;
const metadata = new framework.messages.GenericMediaMetadata();
metadata.title = viewTitle;
loadRequestData.media.metadata = metadata;
@@ -89,7 +90,7 @@ const showMediaPlayer = () => {
const options = new framework.CastReceiverOptions();
options.disableIdleTimeout = true;
options.customNamespaces = {
[CAST_NS]: framework.system.MessageType.JSON,
[CAST_NS]: "json" as framework.system.MessageType.JSON,
};
castContext.addCustomMessageListener(
@@ -97,9 +98,7 @@ castContext.addCustomMessageListener(
// @ts-ignore
(ev: ReceivedMessage<HassMessage>) => {
// We received a show Lovelace command, stop media from playing, hide media player and show Lovelace controller
if (
playerManager.getPlayerState() !== framework.messages.PlayerState.IDLE
) {
if (playerManager.getPlayerState() !== "IDLE") {
playerManager.stop();
} else {
showLovelaceController();
@@ -113,7 +112,7 @@ castContext.addCustomMessageListener(
const playerManager = castContext.getPlayerManager();
playerManager.setMessageInterceptor(
framework.messages.MessageType.LOAD,
"LOAD" as framework.messages.MessageType.LOAD,
(loadRequestData) => {
if (
loadRequestData.media.contentId ===
@@ -127,24 +126,23 @@ playerManager.setMessageInterceptor(
// Special handling if it came from Google Assistant
if (media.entity) {
media.contentId = media.entity;
media.streamType = framework.messages.StreamType.LIVE;
media.streamType = "LIVE" as framework.messages.StreamType.LIVE;
media.contentType = "application/vnd.apple.mpegurl";
// @ts-ignore
media.hlsVideoSegmentFormat =
framework.messages.HlsVideoSegmentFormat.FMP4;
"fmp4" as framework.messages.HlsVideoSegmentFormat.FMP4;
}
return loadRequestData;
}
);
playerManager.addEventListener(
framework.events.EventType.MEDIA_STATUS,
"MEDIA_STATUS" as framework.events.EventType.MEDIA_STATUS,
(event) => {
if (
event.mediaStatus?.playerState === framework.messages.PlayerState.IDLE &&
event.mediaStatus?.playerState === "IDLE" &&
event.mediaStatus?.idleReason &&
event.mediaStatus?.idleReason !==
framework.messages.IdleReason.INTERRUPTED
event.mediaStatus?.idleReason !== "INTERRUPTED"
) {
// media finished or stopped, return to default Lovelace
showLovelaceController();
+1 -1
View File
@@ -75,5 +75,5 @@
"name": "Home Assistant Demo",
"short_name": "HA Demo",
"start_url": "/?homescreen=1",
"theme_color": "#03A9F4"
"theme_color": "#009ac7"
}
+1 -1
View File
@@ -6,4 +6,4 @@ set -e
cd "$(dirname "$0")/../.."
yarn run-task build-demo
./node_modules/.bin/gulp build-demo
+1 -1
View File
@@ -6,4 +6,4 @@ set -e
cd "$(dirname "$0")/../.."
yarn run-task develop-demo
./node_modules/.bin/gulp develop-demo
+1 -1
View File
@@ -6,4 +6,4 @@ set -e
cd "$(dirname "$0")/../.."
yarn run-task analyze-demo
./node_modules/.bin/gulp analyze-demo
+8 -5
View File
@@ -89,11 +89,14 @@ export class HADemoCard extends LitElement implements LovelaceCard {
)}
</div>
<div class="actions small-hidden">
<a href="https://www.home-assistant.io" target="_blank">
<ha-button>
${this.hass.localize("ui.panel.page-demo.cards.demo.learn_more")}
</ha-button>
</a>
<ha-button
appearance="plain"
size="small"
href="https://www.home-assistant.io"
target="_blank"
>
${this.hass.localize("ui.panel.page-demo.cards.demo.learn_more")}
</ha-button>
</div>
</ha-card>
`;
+2 -2
View File
@@ -68,7 +68,7 @@
}
#ha-launch-screen .ha-launch-screen-spacer-top {
flex: 1;
margin-top: calc( 2 * max(var(--safe-area-inset-bottom), 48px) + 46px );
margin-top: calc( 2 * max(var(--safe-area-inset-top, 0px), 48px) + 46px );
padding-top: 48px;
}
#ha-launch-screen .ha-launch-screen-spacer-bottom {
@@ -76,7 +76,7 @@
padding-top: 48px;
}
.ohf-logo {
margin: max(var(--safe-area-inset-bottom), 48px) 0;
margin: max(var(--safe-area-inset-bottom, 0px), 48px) 0;
display: flex;
flex-direction: column;
align-items: center;
+9 -7
View File
@@ -56,6 +56,15 @@ export default tseslint.config(
},
},
},
settings: {
"import/resolver": {
webpack: {
config: "./rspack.config.cjs",
},
},
},
rules: {
"class-methods-use-this": "off",
"new-cap": "off",
@@ -178,12 +187,5 @@ export default tseslint.config(
],
"no-use-before-define": "off",
},
settings: {
"import/resolver": {
node: {
extensions: [".ts", ".js"],
},
},
},
}
);
+1 -1
View File
@@ -6,4 +6,4 @@ set -e
cd "$(dirname "$0")/../.."
yarn run-task build-gallery
./node_modules/.bin/gulp build-gallery
+1 -1
View File
@@ -6,4 +6,4 @@ set -e
cd "$(dirname "$0")/../.."
yarn run-task develop-gallery
./node_modules/.bin/gulp develop-gallery
+7 -13
View File
@@ -1,11 +1,11 @@
import "@material/mwc-button/mwc-button";
import type { Button } from "@material/mwc-button";
import type { TemplateResult } from "lit";
import { html, LitElement, css, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import { applyThemesOnElement } from "../../../src/common/dom/apply_themes_on_element";
import { fireEvent } from "../../../src/common/dom/fire_event";
import "../../../src/components/ha-card";
import "../../../src/components/ha-button";
import type { HaButton } from "../../../src/components/ha-button";
@customElement("demo-black-white-row")
class DemoBlackWhiteRow extends LitElement {
@@ -25,12 +25,9 @@ class DemoBlackWhiteRow extends LitElement {
<slot name="light"></slot>
</div>
<div class="card-actions">
<mwc-button
.disabled=${this.disabled}
@click=${this.handleSubmit}
>
<ha-button .disabled=${this.disabled} @click=${this.handleSubmit}>
Submit
</mwc-button>
</ha-button>
</div>
</ha-card>
</div>
@@ -40,12 +37,9 @@ class DemoBlackWhiteRow extends LitElement {
<slot name="dark"></slot>
</div>
<div class="card-actions">
<mwc-button
.disabled=${this.disabled}
@click=${this.handleSubmit}
>
<ha-button .disabled=${this.disabled} @click=${this.handleSubmit}>
Submit
</mwc-button>
</ha-button>
</div>
</ha-card>
${this.value
@@ -74,7 +68,7 @@ class DemoBlackWhiteRow extends LitElement {
}
handleSubmit(ev) {
const content = (ev.target as Button).closest(".content")!;
const content = (ev.target as HaButton).closest(".content")!;
fireEvent(this, "submitted" as any, {
slot: content.classList.contains("light") ? "light" : "dark",
});
+9 -6
View File
@@ -1,10 +1,11 @@
import { LitElement, css, html } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import "../../../src/components/ha-card";
import "../../../src/dialogs/more-info/more-info-content";
import "../../../src/state-summary/state-card-content";
import "../ha-demo-options";
import type { HomeAssistant } from "../../../src/types";
import { computeShowNewMoreInfo } from "../../../src/dialogs/more-info/const";
@customElement("demo-more-info")
class DemoMoreInfo extends LitElement {
@@ -21,11 +22,13 @@ class DemoMoreInfo extends LitElement {
<div class="root">
<div id="card">
<ha-card>
<state-card-content
.stateObj=${state}
.hass=${this.hass}
in-dialog
></state-card-content>
${!computeShowNewMoreInfo(state)
? html`<state-card-content
.stateObj=${state}
.hass=${this.hass}
in-dialog
></state-card-content>`
: nothing}
<more-info-content
.hass=${this.hass}
+1 -1
View File
@@ -1106,7 +1106,7 @@ export default {
friendly_name: "Philips Hue",
entity_picture: null,
description:
"Press the button on the bridge to register Philips Hue with Home Assistant.\n\n![Description image](/static/images/config_philips_hue.jpg)",
"Press the button on the bridge to register Philips Hue with Home Assistant.",
submit_caption: "I have pressed the button",
},
last_changed: "2018-07-19T10:44:46.515160+00:00",
@@ -18,7 +18,6 @@ import { HaDeviceAction } from "../../../../src/panels/config/automation/action/
import { HaEventAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-event";
import { HaIfAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-if";
import { HaParallelAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-parallel";
import { HaPlayMediaAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-play_media";
import { HaRepeatAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-repeat";
import { HaSequenceAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-sequence";
import { HaServiceAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-service";
@@ -32,7 +31,6 @@ const SCHEMAS: { name: string; actions: Action[] }[] = [
{ name: "Service", actions: [HaServiceAction.defaultConfig] },
{ name: "Condition", actions: [HaConditionAction.defaultConfig] },
{ name: "Delay", actions: [HaDelayAction.defaultConfig] },
{ name: "Play media", actions: [HaPlayMediaAction.defaultConfig] },
{ name: "Wait", actions: [HaWaitAction.defaultConfig] },
{ name: "WaitForTrigger", actions: [HaWaitForTriggerAction.defaultConfig] },
{ name: "Repeat", actions: [HaRepeatAction.defaultConfig] },
@@ -147,13 +147,13 @@ The `title ` option should not be used without a description.
<ha-alert alert-type="success">
This is a success alert — check it out!
<mwc-button slot="action" label="Undo"></mwc-button>
<ha-button slot="action">Undo</ha-button>
</ha-alert>
```html
<ha-alert alert-type="success">
This is a success alert — check it out!
<mwc-button slot="action" label="Undo"></mwc-button>
<ha-button slot="action">Undo</ha-button>
</ha-alert>
```
+6 -6
View File
@@ -1,10 +1,10 @@
import "@material/mwc-button/mwc-button";
import type { TemplateResult } from "lit";
import { css, html, LitElement } from "lit";
import { customElement } from "lit/decorators";
import { applyThemesOnElement } from "../../../../src/common/dom/apply_themes_on_element";
import "../../../../src/components/ha-alert";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-button";
import "../../../../src/components/ha-logo-svg";
const alerts: {
@@ -78,13 +78,13 @@ const alerts: {
title: "Error with action",
description: "This is a test error alert with action",
type: "error",
actionSlot: html`<mwc-button slot="action" label="restart"></mwc-button>`,
actionSlot: html`<ha-button size="small" slot="action">restart</ha-button>`,
},
{
title: "Unsaved data",
description: "You have unsaved data",
type: "warning",
actionSlot: html`<mwc-button slot="action" label="save"></mwc-button>`,
actionSlot: html`<ha-button size="small" slot="action">save</ha-button>`,
},
{
title: "Slotted icon",
@@ -108,7 +108,7 @@ const alerts: {
title: "Slotted action",
description: "Alert with slotted action",
type: "info",
actionSlot: html`<mwc-button slot="action" label="action"></mwc-button>`,
actionSlot: html`<ha-button slot="action">action</ha-button>`,
},
{
description: "Dismissable information (RTL)",
@@ -120,7 +120,7 @@ const alerts: {
title: "Error with action",
description: "This is a test error alert with action (RTL)",
type: "error",
actionSlot: html`<mwc-button slot="action" label="restart"></mwc-button>`,
actionSlot: html`<ha-button slot="action">restart</ha-button>`,
rtl: true,
},
{
@@ -211,7 +211,7 @@ export class DemoHaAlert extends LitElement {
max-height: 24px;
width: 24px;
}
mwc-button {
ha-button {
--mdc-theme-primary: var(--primary-text-color);
}
`;
@@ -0,0 +1,67 @@
---
title: Button
---
<style>
.wrapper {
display: flex;
gap: 24px;
}
</style>
# Button `<ha-button>`
## Implementation
### Example Usage
<div class="wrapper">
<ha-button>
simple button
</ha-button>
<ha-button appearance="plain">
plain button
</ha-button>
<ha-button appearance="filled">
filled button
</ha-button>
<ha-button size="small">
small
</ha-button>
</div>
```html
<ha-button> simple button </ha-button>
<ha-button size="small"> small </ha-button>
```
### API
This component is based on the webawesome button component.
Check the [webawesome documentation](https://webawesome.com/docs/components/button/) for more details.
**Slots**
- default slot: Label of the button
` - no default
- `start`: The prefix container (usually for icons).
` - no default
- `end`: The suffix container (usually for icons).
` - no default
**Properties/Attributes**
| Name | Type | Default | Description |
| ---------- | ---------------------------------------------- | -------- | --------------------------------------------------------------------------------- |
| appearance | "accent"/"filled"/"plain" | "accent" | Sets the button appearance. |
| variants | "brand"/"danger"/"neutral"/"warning"/"success" | "brand" | Sets the button color variant. "brand" is default. |
| size | "small"/"medium" | "medium" | Sets the button size. |
| loading | Boolean | false | Shows a loading indicator instead of the buttons label and disable buttons click. |
| disabled | Boolean | false | Disables the button and prevents user interaction. |
**CSS Custom Properties**
- `--ha-button-height` - Height of the button.
- `--ha-button-border-radius` - Border radius of the button. Defaults to `var(--ha-border-radius-pill)`.
+171
View File
@@ -0,0 +1,171 @@
import { mdiHome } from "@mdi/js";
import type { TemplateResult } from "lit";
import { css, html, LitElement } from "lit";
import { customElement } from "lit/decorators";
import { applyThemesOnElement } from "../../../../src/common/dom/apply_themes_on_element";
import { titleCase } from "../../../../src/common/string/title-case";
import "../../../../src/components/ha-button";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-svg-icon";
import { mdiHomeAssistant } from "../../../../src/resources/home-assistant-logo-svg";
const appearances = ["accent", "filled", "plain"];
const variants = ["brand", "danger", "neutral", "warning", "success"];
@customElement("demo-components-ha-button")
export class DemoHaButton extends LitElement {
protected render(): TemplateResult {
return html`
${["light", "dark"].map(
(mode) => html`
<div class=${mode}>
<ha-card header="ha-button in ${mode}">
<div class="card-content">
${variants.map(
(variant) => html`
<div>
${appearances.map(
(appearance) => html`
<ha-button
.appearance=${appearance}
.variant=${variant}
>
<ha-svg-icon
.path=${mdiHomeAssistant}
slot="start"
></ha-svg-icon>
${titleCase(`${variant} ${appearance}`)}
<ha-svg-icon
.path=${mdiHome}
slot="end"
></ha-svg-icon>
</ha-button>
`
)}
</div>
<div>
${appearances.map(
(appearance) => html`
<ha-button
.appearance=${appearance}
.variant=${variant}
size="small"
>
${titleCase(`${variant} ${appearance}`)}
</ha-button>
`
)}
</div>
<div>
${appearances.map(
(appearance) => html`
<ha-button
.appearance=${appearance}
.variant=${variant}
loading
>
<ha-svg-icon
.path=${mdiHomeAssistant}
slot="start"
></ha-svg-icon>
${titleCase(`${variant} ${appearance}`)}
<ha-svg-icon
.path=${mdiHome}
slot="end"
></ha-svg-icon>
</ha-button>
`
)}
</div>
`
)}
${variants.map(
(variant) => html`
<div>
${appearances.map(
(appearance) => html`
<ha-button
.variant=${variant}
.appearance=${appearance}
disabled
>
${titleCase(`${appearance}`)}
</ha-button>
`
)}
</div>
<div>
${appearances.map(
(appearance) => html`
<ha-button
.variant=${variant}
.appearance=${appearance}
size="small"
disabled
>
${titleCase(`${appearance}`)}
</ha-button>
`
)}
</div>
`
)}
</div>
</ha-card>
</div>
`
)}
`;
}
firstUpdated(changedProps) {
super.firstUpdated(changedProps);
applyThemesOnElement(
this.shadowRoot!.querySelector(".dark"),
{
default_theme: "default",
default_dark_theme: "default",
themes: {},
darkMode: true,
theme: "default",
},
undefined,
undefined,
true
);
}
static styles = css`
:host {
display: flex;
justify-content: center;
}
.dark,
.light {
display: block;
background-color: var(--primary-background-color);
padding: 0 50px;
}
.button {
padding: unset;
}
ha-card {
margin: 24px auto;
}
.card-content {
display: flex;
flex-direction: column;
gap: 24px;
}
.card-content div {
display: flex;
gap: 8px;
}
`;
}
declare global {
interface HTMLElementTagNameMap {
"demo-components-ha-button": DemoHaButton;
}
}
-1
View File
@@ -1,5 +1,4 @@
/* eslint-disable lit/no-template-arrow */
import "@material/mwc-button";
import type { TemplateResult } from "lit";
import { html, LitElement } from "lit";
import { customElement, state } from "lit/decorators";
@@ -0,0 +1,32 @@
---
title: Progress Button
---
<style>
.wrapper {
display: flex;
gap: 24px;
}
</style>
# Progress Button `<ha-progress-button>`
### API
This component is a wrapper around `<ha-button>` that adds support for showing progress
**Slots**
- default slot: Label of the button
` - no default
**Properties/Attributes**
| Name | Type | Default | Description |
| ---------- | ---------------------------------------------- | --------- | -------------------------------------------------- |
| label | string | "accent" | Sets the button label. |
| disabled | Boolean | false | Disables the button if true. |
| progress | Boolean | false | Shows a progress indicator on the button. |
| appearance | "accent"/"filled"/"plain" | "accent" | Sets the button appearance. |
| variants | "brand"/"danger"/"neutral"/"warning"/"success" | "brand" | Sets the button color variant. "brand" is default. |
| iconPath | string | undefined | Sets the icon path for the button. |
@@ -0,0 +1,139 @@
import type { TemplateResult } from "lit";
import { css, html, LitElement } from "lit";
import { customElement } from "lit/decorators";
import { applyThemesOnElement } from "../../../../src/common/dom/apply_themes_on_element";
import "../../../../src/components/buttons/ha-progress-button";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-svg-icon";
import { mdiHomeAssistant } from "../../../../src/resources/home-assistant-logo-svg";
@customElement("demo-components-ha-progress-button")
export class DemoHaProgressButton extends LitElement {
protected render(): TemplateResult {
return html`
${["light", "dark"].map(
(mode) => html`
<div class=${mode}>
<ha-card header="ha-progress-button in ${mode}">
<div class="card-content">
<ha-progress-button @click=${this._clickedSuccess}>
Success
</ha-progress-button>
<ha-progress-button @click=${this._clickedFail}>
Fail
</ha-progress-button>
<ha-progress-button size="small" @click=${this._clickedSuccess}>
small
</ha-progress-button>
<ha-progress-button
appearance="filled"
@click=${this._clickedSuccess}
>
filled
</ha-progress-button>
<ha-progress-button
appearance="plain"
@click=${this._clickedSuccess}
>
plain
</ha-progress-button>
<ha-progress-button
variant="warning"
@click=${this._clickedSuccess}
>
warning
</ha-progress-button>
<ha-progress-button
variant="neutral"
@click=${this._clickedSuccess}
label="with icon"
.iconPath=${mdiHomeAssistant}
>
With Icon
</ha-progress-button>
<ha-progress-button progress @click=${this._clickedSuccess}>
progress
</ha-progress-button>
<ha-progress-button disabled @click=${this._clickedSuccess}>
disabled
</ha-progress-button>
</div>
</ha-card>
</div>
`
)}
`;
}
firstUpdated(changedProps) {
super.firstUpdated(changedProps);
applyThemesOnElement(
this.shadowRoot!.querySelector(".dark"),
{
default_theme: "default",
default_dark_theme: "default",
themes: {},
darkMode: true,
theme: "default",
},
undefined,
undefined,
true
);
}
private async _clickedSuccess(ev: CustomEvent): Promise<void> {
console.log("Clicked success");
const button = ev.currentTarget as any;
button.progress = true;
setTimeout(() => {
button.actionSuccess();
button.progress = false;
}, 1000);
}
private async _clickedFail(ev: CustomEvent): Promise<void> {
const button = ev.currentTarget as any;
button.progress = true;
setTimeout(() => {
button.actionError();
button.progress = false;
}, 1000);
}
static styles = css`
:host {
display: flex;
justify-content: center;
}
.dark,
.light {
display: block;
background-color: var(--primary-background-color);
padding: 0 50px;
}
.button {
padding: unset;
}
ha-card {
margin: 24px auto;
}
.card-content {
display: flex;
flex-direction: column;
gap: 24px;
}
.card-content div {
display: flex;
gap: 8px;
}
`;
}
declare global {
interface HTMLElementTagNameMap {
"demo-components-ha-progress-button": DemoHaProgressButton;
}
}
@@ -1,4 +1,3 @@
import "@material/mwc-button";
import type { TemplateResult } from "lit";
import { css, html, LitElement } from "lit";
import { customElement, state } from "lit/decorators";
+37
View File
@@ -11,6 +11,7 @@ import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
import { mockIcons } from "../../../../demo/src/stubs/icons";
import { ClimateEntityFeature } from "../../../../src/data/climate";
import { FanEntityFeature } from "../../../../src/data/fan";
const ENTITIES = [
getEntity("switch", "tv_outlet", "on", {
@@ -100,6 +101,15 @@ const ENTITIES = [
ClimateEntityFeature.FAN_MODE +
ClimateEntityFeature.TARGET_TEMPERATURE_RANGE,
}),
getEntity("fan", "fan_demo", "on", {
friendly_name: "Ceiling fan",
device_class: "fan",
direction: "reverse",
supported_features:
FanEntityFeature.DIRECTION +
FanEntityFeature.SET_SPEED +
FanEntityFeature.OSCILLATE,
}),
];
const CONFIGS = [
@@ -261,6 +271,33 @@ const CONFIGS = [
- type: target-temperature
`,
},
{
heading: "Fan direction feature",
config: `
- type: tile
entity: fan.fan_demo
features:
- type: fan-direction
`,
},
{
heading: "Fan speed feature",
config: `
- type: tile
entity: fan.fan_demo
features:
- type: fan-speed
`,
},
{
heading: "Fan oscillate feature",
config: `
- type: tile
entity: fan.fan_demo
features:
- type: fan-oscillate
`,
},
];
@customElement("demo-lovelace-tile-card")
+8 -4
View File
@@ -1,7 +1,7 @@
import "@material/mwc-button";
import type { TemplateResult } from "lit";
import { css, html, LitElement } from "lit";
import { customElement } from "lit/decorators";
import "../../../../src/components/ha-button";
import "../../../../src/components/ha-card";
import type { ActionHandlerEvent } from "../../../../src/data/lovelace/action_handler";
import { actionHandler } from "../../../../src/panels/lovelace/common/directives/action-handler-directive";
@@ -13,12 +13,16 @@ export class DemoUtilLongPress extends LitElement {
${[1, 2, 3].map(
() => html`
<ha-card>
<mwc-button
<ha-button
appearance="plain"
@action=${this._handleAction}
.actionHandler=${actionHandler({})}
.actionHandler=${actionHandler({
hasHold: true,
hasDoubleClick: true,
})}
>
(long) press me!
</mwc-button>
</ha-button>
<textarea></textarea>
+3
View File
@@ -0,0 +1,3 @@
---
title: Fan
---
+50
View File
@@ -0,0 +1,50 @@
import type { PropertyValues, TemplateResult } from "lit";
import { html, LitElement } from "lit";
import { customElement, property, query } from "lit/decorators";
import "../../../../src/components/ha-card";
import "../../../../src/dialogs/more-info/more-info-content";
import { getEntity } from "../../../../src/fake_data/entity";
import type { MockHomeAssistant } from "../../../../src/fake_data/provide_hass";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-more-infos";
import { FanEntityFeature } from "../../../../src/data/fan";
const ENTITIES = [
getEntity("fan", "fan", "on", {
friendly_name: "Fan",
device_class: "fan",
supported_features:
FanEntityFeature.OSCILLATE +
FanEntityFeature.DIRECTION +
FanEntityFeature.SET_SPEED,
}),
];
@customElement("demo-more-info-fan")
class DemoMoreInfoFan extends LitElement {
@property({ attribute: false }) public hass!: MockHomeAssistant;
@query("demo-more-infos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`
<demo-more-infos
.hass=${this.hass}
.entities=${ENTITIES.map((ent) => ent.entityId)}
></demo-more-infos>
`;
}
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.addEntities(ENTITIES);
}
}
declare global {
interface HTMLElementTagNameMap {
"demo-more-info-fan": DemoMoreInfoFan;
}
}
+4
View File
@@ -0,0 +1,4 @@
import { availableParallelism } from "node:os";
import "./build-scripts/gulp/index.mjs";
process.env.UV_THREADPOOL_SIZE = availableParallelism();
+1 -1
View File
@@ -6,4 +6,4 @@ set -e
cd "$(dirname "$0")/../.."
yarn run-task build-hassio
./node_modules/.bin/gulp build-hassio
+1 -1
View File
@@ -6,4 +6,4 @@ set -e
cd "$(dirname "$0")/../.."
yarn run-task develop-hassio
./node_modules/.bin/gulp develop-hassio
@@ -99,7 +99,8 @@ class HassioAddonNetwork extends LitElement {
: nothing}
<div class="card-actions">
<ha-progress-button
class="warning"
variant="danger"
appearance="plain"
.disabled=${this.disabled}
@click=${this._resetTapped}
>
+72 -53
View File
@@ -25,6 +25,7 @@ import type { CSSResultGroup, TemplateResult } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { ifDefined } from "lit/directives/if-defined";
import memoizeOne from "memoize-one";
import { atLeastVersion } from "../../../../src/common/config/version";
import { fireEvent } from "../../../../src/common/dom/fire_event";
@@ -187,12 +188,13 @@ class HassioAddonInfo extends LitElement {
"addon.dashboard.protection_mode.content"
)}
<ha-button
variant="danger"
slot="action"
.label=${this.supervisor.localize(
"addon.dashboard.protection_mode.enable"
)}
@click=${this._protectionToggled}
>
${this.supervisor.localize(
"addon.dashboard.protection_mode.enable"
)}
</ha-button>
</ha-alert>
`
@@ -692,14 +694,16 @@ class HassioAddonInfo extends LitElement {
? this._computeIsRunning
? html`
<ha-progress-button
class="warning"
variant="danger"
appearance="plain"
@click=${this._stopClicked}
.disabled=${systemManaged && !this.controlEnabled}
>
${this.supervisor.localize("addon.dashboard.stop")}
</ha-progress-button>
<ha-progress-button
class="warning"
variant="danger"
appearance="plain"
@click=${this._restartClicked}
>
${this.supervisor.localize("addon.dashboard.restart")}
@@ -709,10 +713,60 @@ class HassioAddonInfo extends LitElement {
<ha-progress-button
@click=${this._startClicked}
.progress=${this.addon.state === "startup"}
appearance="plain"
>
${this.supervisor.localize("addon.dashboard.start")}
</ha-progress-button>
`
: nothing}
</div>
<div>
${this.addon.version
? html`
<ha-progress-button
variant="danger"
appearance="plain"
@click=${this._uninstallClicked}
.disabled=${systemManaged && !this.controlEnabled}
>
${this.supervisor.localize("addon.dashboard.uninstall")}
</ha-progress-button>
${this.addon.build
? html`
<ha-progress-button
variant="danger"
appearance="plain"
@click=${this._rebuildClicked}
>
${this.supervisor.localize("addon.dashboard.rebuild")}
</ha-progress-button>
`
: nothing}
${this._computeShowWebUI || this._computeShowIngressUI
? html`
<ha-button
href=${ifDefined(
!this._computeShowIngressUI
? this._pathWebui!
: nothing
)}
target=${ifDefined(
!this._computeShowIngressUI ? "_blank" : nothing
)}
rel=${ifDefined(
!this._computeShowIngressUI ? "noopener" : nothing
)}
@click=${!this._computeShowWebUI
? this._openIngress
: undefined}
>
${this.supervisor.localize(
"addon.dashboard.open_web_ui"
)}
</ha-button>
`
: nothing}
`
: html`
<ha-progress-button
.disabled=${!this.addon.available}
@@ -722,58 +776,12 @@ class HassioAddonInfo extends LitElement {
</ha-progress-button>
`}
</div>
<div>
${this.addon.version
? html` ${this._computeShowWebUI
? html`
<a
href=${this._pathWebui!}
tabindex="-1"
target="_blank"
rel="noopener"
>
<ha-button>
${this.supervisor.localize(
"addon.dashboard.open_web_ui"
)}
</ha-button>
</a>
`
: nothing}
${this._computeShowIngressUI
? html`
<ha-button @click=${this._openIngress}>
${this.supervisor.localize(
"addon.dashboard.open_web_ui"
)}
</ha-button>
`
: nothing}
<ha-progress-button
class="warning"
@click=${this._uninstallClicked}
.disabled=${systemManaged && !this.controlEnabled}
>
${this.supervisor.localize("addon.dashboard.uninstall")}
</ha-progress-button>
${this.addon.build
? html`
<ha-progress-button
class="warning"
@click=${this._rebuildClicked}
>
${this.supervisor.localize("addon.dashboard.rebuild")}
</ha-progress-button>
`
: nothing}`
: nothing}
</div>
</div>
</ha-card>
${this.addon.long_description
? html`
<ha-card outlined>
<ha-card class="long-description" outlined>
<div class="card-content">
<ha-markdown
.content=${this.addon.long_description}
@@ -1146,15 +1154,17 @@ class HassioAddonInfo extends LitElement {
),
dismissText: this.supervisor.localize("common.cancel"),
});
button.actionError();
button.progress = false;
return;
}
} catch (err: any) {
button.actionError();
button.progress = false;
showAlertDialog(this, {
title: "Failed to validate addon configuration",
text: extractApiErrorMessage(err),
});
button.progress = false;
return;
}
@@ -1168,11 +1178,15 @@ class HassioAddonInfo extends LitElement {
};
fireEvent(this, "hass-api-called", eventdata);
} catch (err: any) {
button.actionError();
button.progress = false;
showAlertDialog(this, {
title: this.supervisor.localize("addon.dashboard.action_error.start"),
text: extractApiErrorMessage(err),
});
return;
}
button.actionSuccess();
button.progress = false;
}
@@ -1228,6 +1242,7 @@ class HassioAddonInfo extends LitElement {
path: "uninstall",
};
fireEvent(this, "hass-api-called", eventdata);
button.actionSuccess();
} catch (err: any) {
showAlertDialog(this, {
title: this.supervisor.localize(
@@ -1235,6 +1250,7 @@ class HassioAddonInfo extends LitElement {
),
text: extractApiErrorMessage(err),
});
button.actionError();
}
button.progress = false;
}
@@ -1317,6 +1333,9 @@ class HassioAddonInfo extends LitElement {
.description a {
color: var(--primary-color);
}
.long-description {
direction: ltr;
}
ha-assist-chip {
--md-sys-color-primary: var(--text-primary-color);
--md-sys-color-on-surface: var(--text-primary-color);
@@ -1,4 +1,3 @@
import "@material/mwc-button";
import type { TemplateResult } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { customElement, property } from "lit/decorators";
+6 -5
View File
@@ -1,4 +1,3 @@
import "@material/mwc-button";
import type { ActionDetail } from "@material/mwc-list";
import { mdiBackupRestore, mdiDelete, mdiDotsVertical, mdiPlus } from "@mdi/js";
@@ -17,6 +16,7 @@ import type {
} from "../../../src/components/data-table/ha-data-table";
import "../../../src/components/ha-button-menu";
import "../../../src/components/ha-fab";
import "../../../src/components/ha-button";
import "../../../src/components/ha-icon-button";
import "../../../src/components/ha-list-item";
import "../../../src/components/ha-svg-icon";
@@ -241,12 +241,13 @@ export class HassioBackups extends LitElement {
<div class="header-btns">
${!this.narrow
? html`
<mwc-button
<ha-button
appearance="plain"
variant="danger"
@click=${this._deleteSelected}
class="warning"
>
${this.supervisor.localize("backup.delete_selected")}
</mwc-button>
</ha-button>
`
: html`
<ha-icon-button
@@ -408,7 +409,7 @@ export class HassioBackups extends LitElement {
margin-inline-end: -12px;
margin-inline-start: initial;
}
.header-btns > mwc-button,
.header-btns > ha-button,
.header-btns > ha-icon-button {
margin: 8px;
}
+4 -6
View File
@@ -1,10 +1,9 @@
import "@material/mwc-button";
import type { CSSResultGroup } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import memoizeOne from "memoize-one";
import "../../../src/components/buttons/ha-progress-button";
import "../../../src/components/ha-card";
import "../../../src/components/ha-button";
import "../../../src/components/ha-settings-row";
import "../../../src/components/ha-svg-icon";
import type { HassioHassOSInfo } from "../../../src/data/hassio/host";
@@ -109,10 +108,9 @@ export class HassioUpdate extends LitElement {
</ha-settings-row>
</div>
<div class="card-actions">
<a href="/hassio/update-available/${key}">
<mwc-button .label=${this.supervisor.localize("common.show")}>
</mwc-button>
</a>
<ha-button appearance="plain" href="/hassio/update-available/${key}">
${this.supervisor.localize("common.show")}
</ha-button>
</div>
</ha-card>
`;
@@ -1,10 +1,10 @@
import "@material/mwc-button/mwc-button";
import type { CSSResultGroup } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../src/common/dom/fire_event";
import "../../../../src/components/ha-dialog";
import "../../../../src/components/ha-button";
import "../../../../src/components/ha-form/ha-form";
import type { SchemaUnion } from "../../../../src/components/ha-form/types";
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
@@ -77,20 +77,21 @@ class HassioBackupLocationDialog extends LitElement {
@value-changed=${this._valueChanged}
dialogInitialFocus
></ha-form>
<mwc-button
<ha-button
appearance="plain"
slot="secondaryAction"
@click=${this.closeDialog}
dialogInitialFocus
>
${this._dialogParams.supervisor.localize("common.cancel")}
</mwc-button>
<mwc-button
</ha-button>
<ha-button
.disabled=${this._waiting || !this._data}
slot="primaryAction"
@click=${this._changeMount}
>
${this._dialogParams.supervisor.localize("common.save")}
</mwc-button>
</ha-button>
</ha-dialog>
`;
}
@@ -8,7 +8,6 @@ import { atLeastVersion } from "../../../../src/common/config/version";
import { fireEvent } from "../../../../src/common/dom/fire_event";
import { stopPropagation } from "../../../../src/common/dom/stop_propagation";
import { slugify } from "../../../../src/common/string/slugify";
import "../../../../src/components/buttons/ha-progress-button";
import "../../../../src/components/ha-alert";
import "../../../../src/components/ha-button";
import "../../../../src/components/ha-button-menu";
@@ -1,10 +1,9 @@
import "@material/mwc-button";
import type { CSSResultGroup } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { fireEvent } from "../../../../src/common/dom/fire_event";
import "../../../../src/components/buttons/ha-progress-button";
import "../../../../src/components/ha-alert";
import "../../../../src/components/ha-button";
import "../../../../src/components/ha-spinner";
import { createCloseHeading } from "../../../../src/components/ha-dialog";
import {
@@ -69,16 +68,20 @@ class HassioCreateBackupDialog extends LitElement {
${this._error
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
: ""}
<mwc-button slot="secondaryAction" @click=${this.closeDialog}>
<ha-button
appearance="plain"
slot="secondaryAction"
@click=${this.closeDialog}
>
${this._dialogParams.supervisor.localize("common.close")}
</mwc-button>
<mwc-button
</ha-button>
<ha-button
.disabled=${this._creatingBackup}
slot="primaryAction"
@click=${this._createBackup}
>
${this._dialogParams.supervisor.localize("backup.create")}
</mwc-button>
</ha-button>
</ha-dialog>
`;
}
@@ -4,6 +4,7 @@ import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../src/common/dom/fire_event";
import "../../../../src/components/ha-dialog";
import "../../../../src/components/ha-button";
import "../../../../src/components/ha-list-item";
import "../../../../src/components/ha-select";
import "../../../../src/components/ha-spinner";
@@ -20,8 +21,8 @@ import type { HomeAssistant } from "../../../../src/types";
import type { HassioDatatiskDialogParams } from "./show-dialog-hassio-datadisk";
const calculateMoveTime = memoizeOne((supervisor: Supervisor): number => {
const speed = supervisor.host.disk_life_time !== "" ? 30 : 10;
const moveTime = (supervisor.host.disk_used * 1000) / 60 / speed;
// Assume a speed of 30 MB/s.
const moveTime = (supervisor.host.disk_used * 1000) / 60 / 30;
const rebootTime = (supervisor.host.startup_time * 4) / 60;
return Math.ceil((moveTime + rebootTime) / 10) * 10;
});
@@ -109,17 +110,18 @@ class HassioDatadiskDialog extends LitElement {
"dialog.datadisk_move.no_devices"
)}
<mwc-button
slot="secondaryAction"
<ha-button
appearance="plain"
slot="primaryAction"
@click=${this.closeDialog}
dialogInitialFocus
>
${this.dialogParams.supervisor.localize(
"dialog.datadisk_move.cancel"
)}
</mwc-button>
</ha-button>
<mwc-button
<ha-button
.disabled=${!this.selectedDevice}
slot="primaryAction"
@click=${this._moveDatadisk}
@@ -127,7 +129,7 @@ class HassioDatadiskDialog extends LitElement {
${this.dialogParams.supervisor.localize(
"dialog.datadisk_move.move"
)}
</mwc-button>`}
</ha-button>`}
</ha-dialog>
`;
}
@@ -1,4 +1,3 @@
import "@material/mwc-button/mwc-button";
import { mdiClose } from "@mdi/js";
import type { CSSResultGroup } from "lit";
import { css, html, LitElement, nothing } from "lit";
@@ -6,6 +5,7 @@ import { customElement, property, state } from "lit/decorators";
import { cache } from "lit/directives/cache";
import { fireEvent } from "../../../../src/common/dom/fire_event";
import "../../../../src/components/ha-alert";
import "../../../../src/components/ha-button";
import "../../../../src/components/ha-dialog";
import "../../../../src/components/ha-expansion-panel";
import "../../../../src/components/ha-formfield";
@@ -15,7 +15,6 @@ import "../../../../src/components/ha-list";
import "../../../../src/components/ha-list-item";
import "../../../../src/components/ha-password-field";
import "../../../../src/components/ha-radio";
import "../../../../src/components/ha-spinner";
import "../../../../src/components/ha-textfield";
import type { HaTextField } from "../../../../src/components/ha-textfield";
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
@@ -154,16 +153,16 @@ export class DialogHassioNetwork
)}
</p>`
: ""}
<mwc-button
<ha-button
appearance="plain"
size="small"
class="scan"
@click=${this._scanForAP}
.disabled=${this._scanning}
.loading=${this._scanning}
>
${this._scanning
? html`<ha-spinner aria-label="Scanning" size="small">
</ha-spinner>`
: this.supervisor.localize("dialog.network.scan_ap")}
</mwc-button>
${this.supervisor.localize("dialog.network.scan_ap")}
</ha-button>
${this._accessPoints &&
this._accessPoints.accesspoints &&
this._accessPoints.accesspoints.length !== 0
@@ -270,16 +269,16 @@ export class DialogHassioNetwork
: ""}
</div>
<div class="buttons">
<mwc-button
.label=${this.supervisor.localize("common.cancel")}
@click=${this.closeDialog}
<ha-button @click=${this.closeDialog} appearance="plain">
${this.supervisor.localize("common.cancel")}
</ha-button>
<ha-button
@click=${this._updateNetwork}
.disabled=${!this._dirty}
.loading=${this._processing}
>
</mwc-button>
<mwc-button @click=${this._updateNetwork} .disabled=${!this._dirty}>
${this._processing
? html`<ha-spinner size="small"> </ha-spinner>`
: this.supervisor.localize("common.save")}
</mwc-button>
${this.supervisor.localize("common.save")}
</ha-button>
</div>`;
}
@@ -584,11 +583,7 @@ export class DialogHassioNetwork
}
}
mwc-button.warning {
--mdc-theme-primary: var(--error-color);
}
mwc-button.scan {
ha-button.scan {
margin-left: 8px;
margin-inline-start: 8px;
margin-inline-end: initial;
@@ -609,8 +604,8 @@ export class DialogHassioNetwork
var(--mdc-dialog-scroll-divider-color, rgba(0, 0, 0, 0.12));
display: flex;
justify-content: space-between;
padding: 8px;
padding-bottom: max(var(--safe-area-inset-bottom), 8px);
padding: 16px;
padding-bottom: max(var(--safe-area-inset-bottom), 16px);
background-color: var(--mdc-theme-surface, #fff);
}
.warning {
@@ -1,13 +1,14 @@
import "@material/mwc-button/mwc-button";
import { mdiDelete } from "@mdi/js";
import { mdiDelete, mdiPlus } from "@mdi/js";
import type { CSSResultGroup, TemplateResult } from "lit";
import { css, html, LitElement } from "lit";
import { customElement, property, state } from "lit/decorators";
import "../../../../src/components/ha-button";
import { createCloseHeading } from "../../../../src/components/ha-dialog";
import "../../../../src/components/ha-form/ha-form";
import type { SchemaUnion } from "../../../../src/components/ha-form/types";
import "../../../../src/components/ha-icon-button";
import "../../../../src/components/ha-settings-row";
import "../../../../src/components/ha-svg-icon";
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
import {
addHassioDockerRegistry,
@@ -84,16 +85,19 @@ class HassioRegistriesDialog extends LitElement {
dialogInitialFocus
></ha-form>
<div class="action">
<mwc-button
<ha-button
?disabled=${Boolean(
!this._input.registry ||
!this._input.username ||
!this._input.password
)}
@click=${this._addNewRegistry}
appearance="filled"
size="small"
>
<ha-svg-icon slot="start" .path=${mdiPlus}></ha-svg-icon>
${this.supervisor.localize("dialog.registries.add_registry")}
</mwc-button>
</ha-button>
</div>
`
: html`${this._registries?.length
@@ -126,11 +130,17 @@ class HassioRegistriesDialog extends LitElement {
</ha-alert>
`}
<div class="action">
<mwc-button @click=${this._addRegistry} dialogInitialFocus>
<ha-button
@click=${this._addRegistry}
dialogInitialFocus
appearance="filled"
size="small"
>
<ha-svg-icon slot="start" .path=${mdiPlus}></ha-svg-icon>
${this.supervisor.localize(
"dialog.registries.add_new_registry"
)}
</mwc-button>
</ha-button>
</div> `}
</ha-dialog>
`;
@@ -1,5 +1,4 @@
import "@material/mwc-button/mwc-button";
import { mdiDelete, mdiDeleteOff } from "@mdi/js";
import { mdiDelete, mdiDeleteOff, mdiPlus } from "@mdi/js";
import type { CSSResultGroup } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
@@ -7,10 +6,15 @@ import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../src/common/dom/fire_event";
import { caseInsensitiveStringCompare } from "../../../../src/common/string/compare";
import "../../../../src/components/ha-alert";
import "../../../../src/components/ha-tooltip";
import "../../../../src/components/ha-spinner";
import "../../../../src/components/ha-button";
import { createCloseHeading } from "../../../../src/components/ha-dialog";
import "../../../../src/components/ha-icon-button";
import "../../../../src/components/ha-md-list";
import "../../../../src/components/ha-md-list-item";
import "../../../../src/components/ha-svg-icon";
import "../../../../src/components/ha-textfield";
import type { HaTextField } from "../../../../src/components/ha-textfield";
import "../../../../src/components/ha-tooltip";
import type {
HassioAddonInfo,
HassioAddonRepository,
@@ -24,10 +28,6 @@ import {
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
import type { HomeAssistant } from "../../../../src/types";
import type { HassioRepositoryDialogParams } from "./show-dialog-repositories";
import type { HaTextField } from "../../../../src/components/ha-textfield";
import "../../../../src/components/ha-textfield";
import "../../../../src/components/ha-md-list";
import "../../../../src/components/ha-md-list-item";
@customElement("dialog-hassio-repositories")
class HassioRepositoriesDialog extends LitElement {
@@ -159,18 +159,22 @@ class HassioRepositoriesDialog extends LitElement {
@keydown=${this._handleKeyAdd}
dialogInitialFocus
></ha-textfield>
<mwc-button @click=${this._addRepository}>
${this._processing
? html`<ha-spinner size="small"></ha-spinner>`
: this._dialogParams!.supervisor.localize(
"dialog.repositories.add"
)}
</mwc-button>
<ha-button
.loading=${this._processing}
@click=${this._addRepository}
appearance="filled"
size="small"
>
<ha-svg-icon slot="start" .path=${mdiPlus}></ha-svg-icon>
${this._dialogParams!.supervisor.localize(
"dialog.repositories.add"
)}
</ha-button>
</div>
</div>
<mwc-button slot="primaryAction" @click=${this.closeDialog}>
<ha-button slot="primaryAction" @click=${this.closeDialog}>
${this._dialogParams?.supervisor.localize("common.close")}
</mwc-button>
</ha-button>
</ha-dialog>
`;
}
@@ -191,16 +195,11 @@ class HassioRepositoriesDialog extends LitElement {
border-radius: 4px;
margin-top: 4px;
}
mwc-button {
ha-button {
margin-left: 8px;
margin-inline-start: 8px;
margin-inline-end: initial;
}
ha-spinner {
display: block;
margin: 32px;
text-align: center;
}
div.delete ha-icon-button {
color: var(--error-color);
}
+8 -14
View File
@@ -1,10 +1,9 @@
import "@material/mwc-button";
import type { CSSResultGroup, TemplateResult } from "lit";
import { css, html, LitElement } from "lit";
import { customElement, property, state } from "lit/decorators";
import { atLeastVersion } from "../../../src/common/config/version";
import "../../../src/components/buttons/ha-progress-button";
import "../../../src/components/ha-button";
import "../../../src/components/ha-button-menu";
import "../../../src/components/ha-card";
import "../../../src/components/ha-settings-row";
@@ -70,12 +69,12 @@ class HassioCoreInfo extends LitElement {
${!atLeastVersion(this.hass.config.version, 2021, 12) &&
this.supervisor.core.update_available
? html`
<a href="/hassio/update-available/core">
<mwc-button
.label=${this.supervisor.localize("common.show")}
>
</mwc-button>
</a>
<ha-button
appearance="plain"
href="/hassio/update-available/core"
>
${this.supervisor.localize("common.show")}
</ha-button>
`
: ""}
</ha-settings-row>
@@ -95,7 +94,7 @@ class HassioCoreInfo extends LitElement {
<div class="card-actions">
<ha-progress-button
slot="primaryAction"
class="warning"
variant="danger"
@click=${this._coreRestart}
.title=${this.supervisor.localize("common.restart_name", {
name: "Core",
@@ -188,11 +187,6 @@ class HassioCoreInfo extends LitElement {
white-space: normal;
color: var(--secondary-text-color);
}
.warning {
--mdc-theme-primary: var(--error-color);
}
ha-button-menu {
color: var(--secondary-text-color);
--mdc-menu-min-width: 200px;
+24 -28
View File
@@ -1,5 +1,3 @@
import "@material/mwc-button";
import { mdiDotsVertical } from "@mdi/js";
import type { CSSResultGroup, TemplateResult } from "lit";
import { css, html, LitElement } from "lit";
@@ -8,10 +6,11 @@ import memoizeOne from "memoize-one";
import { atLeastVersion } from "../../../src/common/config/version";
import { fireEvent } from "../../../src/common/dom/fire_event";
import "../../../src/components/buttons/ha-progress-button";
import "../../../src/components/ha-button";
import "../../../src/components/ha-button-menu";
import "../../../src/components/ha-card";
import "../../../src/components/ha-list-item";
import "../../../src/components/ha-icon-button";
import "../../../src/components/ha-list-item";
import "../../../src/components/ha-settings-row";
import {
extractApiErrorMessage,
@@ -77,24 +76,28 @@ class HassioHostInfo extends LitElement {
<span slot="description">
${this.supervisor.host.hostname}
</span>
<mwc-button
.label=${this.supervisor.localize("system.host.change")}
<ha-button
@click=${this._changeHostnameClicked}
appearance="plain"
size="small"
>
</mwc-button>
${this.supervisor.localize("system.host.change")}
</ha-button>
</ha-settings-row>`
: ""}
${this.supervisor.host.features.includes("network")
? html` <ha-settings-row>
? html`<ha-settings-row>
<span slot="heading">
${this.supervisor.localize("system.host.ip_address")}
</span>
<span slot="description"> ${primaryIpAddress} </span>
<mwc-button
.label=${this.supervisor.localize("system.host.change")}
<ha-button
@click=${this._changeNetworkClicked}
appearance="plain"
size="small"
>
</mwc-button>
${this.supervisor.localize("system.host.change")}
</ha-button>
</ha-settings-row>`
: ""}
@@ -108,12 +111,13 @@ class HassioHostInfo extends LitElement {
${!atLeastVersion(this.hass.config.version, 2021, 12) &&
this.supervisor.os.update_available
? html`
<a href="/hassio/update-available/os">
<mwc-button
.label=${this.supervisor.localize("common.show")}
>
</mwc-button>
</a>
<ha-button
appearance="plain"
size="small"
href="/hassio/update-available/os"
>
${this.supervisor.localize("common.show")}
</ha-button>
`
: ""}
</ha-settings-row>
@@ -139,16 +143,12 @@ class HassioHostInfo extends LitElement {
: ""}
</div>
<div>
${this.supervisor.host.disk_life_time !== "" &&
this.supervisor.host.disk_life_time >= 10
${this.supervisor.host.disk_life_time !== null
? html` <ha-settings-row>
<span slot="heading">
${this.supervisor.localize(
"system.host.emmc_lifetime_used"
)}
${this.supervisor.localize("system.host.lifetime_used")}
</span>
<span slot="description">
${this.supervisor.host.disk_life_time - 10} % -
${this.supervisor.host.disk_life_time} %
</span>
</ha-settings-row>`
@@ -167,7 +167,7 @@ class HassioHostInfo extends LitElement {
<div class="card-actions">
${this.supervisor.host.features.includes("reboot")
? html`
<ha-progress-button class="warning" @click=${this._hostReboot}>
<ha-progress-button variant="danger" @click=${this._hostReboot}>
${this.supervisor.localize("system.host.reboot_host")}
</ha-progress-button>
`
@@ -175,7 +175,7 @@ class HassioHostInfo extends LitElement {
${this.supervisor.host.features.includes("shutdown")
? html`
<ha-progress-button
class="warning"
variant="danger"
@click=${this._hostShutdown}
>
${this.supervisor.localize("system.host.shutdown_host")}
@@ -431,10 +431,6 @@ class HassioHostInfo extends LitElement {
color: var(--secondary-text-color);
}
.warning {
--mdc-theme-primary: var(--error-color);
}
ha-button-menu {
color: var(--secondary-text-color);
--mdc-menu-min-width: 200px;

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