Compare commits

..

1 Commits

Author SHA1 Message Date
Bram Kragten
081a13b3d7 Add back path to protocol config pages 2026-02-03 14:28:12 +01:00
1115 changed files with 36642 additions and 58638 deletions

View File

@@ -1,4 +1,4 @@
FROM mcr.microsoft.com/devcontainers/python:3.14
FROM mcr.microsoft.com/devcontainers/python:1-3.14
ENV \
DEBIAN_FRONTEND=noninteractive \

View File

@@ -21,14 +21,6 @@
-->
## Screenshots
<!--
If your PR includes visual changes, please add screenshots or a short video
showing the before and after. This helps reviewers understand the impact of
your changes.
Note: Remove this section if this PR has no visual changes.
-->
## Type of change
<!--
What type of change does your PR introduce to the Home Assistant frontend?
@@ -43,6 +35,16 @@
- [ ] Breaking change (fix/feature causing existing functionality to break)
- [ ] Code quality improvements to existing code or addition of tests
## Example configuration
<!--
Supplying a configuration snippet, makes it easier for a maintainer to test
your PR.
-->
```yaml
```
## Additional information
<!--
Details are important, and help maintainers processing your PR.
@@ -52,8 +54,6 @@
- This PR fixes or closes issue: fixes #
- This PR is related to issue or discussion:
- Link to documentation pull request:
- Link to developer documentation pull request:
- Link to backend pull request:
## Checklist
<!--
@@ -61,50 +61,18 @@
creating the PR. If you're unsure about any of them, don't hesitate to ask.
We're here to help! This is simply a reminder of what we are going to look
for before merging your code.
AI tools are welcome, but contributors are responsible for *fully*
understanding the code before submitting a PR.
-->
- [ ] I understand the code I am submitting and can explain how it works.
- [ ] The code change is tested and works locally.
- [ ] There is no commented out code in this PR.
- [ ] I have followed the [development checklist][dev-checklist]
- [ ] I have followed the [perfect PR recommendations][perfect-pr]
- [ ] Any generated code has been carefully reviewed for correctness and compliance with project standards.
- [ ] Tests have been added to verify that the new code works.
If user exposed functionality or configuration variables are added/changed:
- [ ] Documentation added/updated for [www.home-assistant.io][docs-repository]
<!--
This project is very active and we have a high turnover of pull requests.
Unfortunately, the number of incoming pull requests is higher than what our
reviewers can review and merge so there is a long backlog of pull requests
waiting for review. You can help here!
By reviewing another pull request, you will help raise the code quality of
that pull request and the final review will be faster. This way the general
pace of pull request reviews will go up and your wait time will go down.
When picking a pull request to review, try to choose one that hasn't yet
been reviewed.
Thanks for helping out!
-->
To help with the load of incoming pull requests:
- [ ] I have reviewed two other [open pull requests][prs] in this repository.
[prs]: https://github.com/home-assistant/frontend/pulls?q=is%3Aopen+is%3Apr+-author%3A%40me+-draft%3Atrue+sort%3Acreated-desc+review%3Anone+-status%3Afailure
<!--
Thank you for contributing <3
Below, some useful links you could explore:
-->
[dev-checklist]: https://developers.home-assistant.io/docs/development_checklist/
[docs-repository]: https://github.com/home-assistant/home-assistant.io
[perfect-pr]: https://developers.home-assistant.io/docs/review-process/#creating-the-perfect-pr

View File

@@ -194,13 +194,13 @@ The View Transitions API creates smooth animations between DOM state changes. Wh
- **Utility wrapper**: `src/common/util/view-transition.ts` - `withViewTransition()` function with graceful fallback
- **Real-world example**: `src/util/launch-screen.ts` - Launch screen fade pattern with browser support detection
- **Animation keyframes**: `src/resources/theme/animations.globals.ts` - Global `fade-in`, `fade-out`, `scale` animations
- **Animation duration**: `src/resources/theme/core.globals.ts` - `--ha-animation-duration-fast` (150ms), `--ha-animation-duration-normal` (250ms), `--ha-animation-duration-slow` (350ms) (all respect `prefers-reduced-motion`)
- **Animation duration**: `src/resources/theme/core.globals.ts` - `--ha-animation-base-duration` (350ms, respects `prefers-reduced-motion`)
**Implementation Guidelines:**
1. Always use `withViewTransition()` wrapper for automatic fallback
2. Keep transitions simple (subtle crossfades and fades work best)
3. Use `--ha-animation-duration-*` CSS variables for consistent timing (`fast`, `normal`, `slow`)
3. Use `--ha-animation-base-duration` CSS variable for consistent timing
4. Assign unique `view-transition-name` to elements (must be unique at any given time)
5. For Lit components: Override `performUpdate()` or use `::part()` for internal elements
@@ -214,6 +214,13 @@ By default, `:root` receives `view-transition-name: root`, creating a full-page
- Only one view transition can run at a time
- **Shadow DOM incompatibility**: View transitions operate at document level and do not work within Shadow DOM due to style isolation ([spec discussion](https://github.com/w3c/csswg-drafts/issues/10303)). For web components, set `view-transition-name` on the `:host` element or use document-level transitions
**Current Usage & Planned Applications:**
- Launch screen fade out (implemented)
- Automation sidebar transitions (planned - #27238)
- More info dialog content changes (planned - #27672)
- Toolbar navigation, ha-spinner transitions (planned)
**Specification & Documentation:**
For browser support, API details, and current specifications, refer to these authoritative sources (note: check publication dates as specs evolve):
@@ -239,7 +246,13 @@ For browser support, API details, and current specifications, refer to these aut
## Component Library
### Dialog Component
### Dialog Components
**Available Dialog Types:**
- `ha-wa-dialog` - Preferred for new dialogs (Web Awesome based)
- `ha-md-dialog` - Material Design 3 dialog component
- `ha-dialog` - Legacy component (still widely used)
**Opening Dialogs (Fire Event Pattern - Recommended):**
@@ -253,7 +266,6 @@ fireEvent(this, "show-dialog", {
**Dialog Implementation Requirements:**
- Use `ha-dialog` component
- Implement `HassDialog<T>` interface
- Use `@state() private _open = false` to control dialog visibility
- Set `_open = true` in `showDialog()`, `_open = false` in `closeDialog()`
@@ -269,6 +281,7 @@ fireEvent(this, "show-dialog", {
- Use `width` attribute with predefined sizes: `"small"` (320px), `"medium"` (560px - default), `"large"` (720px), or `"full"`
- Custom sizing is NOT recommended - use the standard width presets
- Example: `<ha-wa-dialog width="small">` for alert/confirmation dialogs
**Button Appearance Guidelines:**
@@ -278,9 +291,17 @@ fireEvent(this, "show-dialog", {
- **Button sizes**: Use `size="small"` (32px height) or default/medium (40px height)
- Always place primary action in `slot="primaryAction"` and secondary in `slot="secondaryAction"` within `ha-dialog-footer`
**Recent Examples:**
See these files for current patterns:
- `src/panels/config/repairs/dialog-repairs-issue.ts`
- `src/dialogs/restart/dialog-restart.ts`
- `src/panels/config/lovelace/resources/dialog-lovelace-resource-detail.ts`
**Gallery Documentation:**
- `gallery/src/pages/components/ha-dialog.markdown`
- `gallery/src/pages/components/ha-wa-dialog.markdown`
- `gallery/src/pages/components/ha-dialogs.markdown`
### Form Component (ha-form)
@@ -288,6 +309,7 @@ fireEvent(this, "show-dialog", {
- Schema-driven using `HaFormSchema[]`
- Supports entity, device, area, target, number, boolean, time, action, text, object, select, icon, media, location selectors
- Built-in validation with error display
- Use `dialogInitialFocus` in dialogs
- Use `computeLabel`, `computeError`, `computeHelper` for translations
```typescript
@@ -372,6 +394,81 @@ export class HaPanelMyFeature extends SubscribeMixin(LitElement) {
}
```
### Creating a Dialog
```typescript
@customElement("dialog-my-feature")
export class DialogMyFeature
extends LitElement
implements HassDialog<MyDialogParams>
{
@property({ attribute: false })
hass!: HomeAssistant;
@state()
private _params?: MyDialogParams;
@state()
private _open = false;
public async showDialog(params: MyDialogParams): Promise<void> {
this._params = params;
this._open = true;
}
public closeDialog(): void {
this._open = false;
}
private _dialogClosed(): void {
this._params = undefined;
fireEvent(this, "dialog-closed", { dialog: this.localName });
}
protected render() {
if (!this._params) {
return nothing;
}
return html`
<ha-wa-dialog
.hass=${this.hass}
.open=${this._open}
header-title=${this._params.title}
header-subtitle=${this._params.subtitle}
@closed=${this._dialogClosed}
>
<p>Dialog content</p>
<ha-dialog-footer slot="footer">
<ha-button
slot="secondaryAction"
appearance="plain"
@click=${this.closeDialog}
>
${this.hass.localize("ui.common.cancel")}
</ha-button>
<ha-button slot="primaryAction" @click=${this._submit}>
${this.hass.localize("ui.common.save")}
</ha-button>
</ha-dialog-footer>
</ha-wa-dialog>
`;
}
static styles = [haStyleDialog, css``];
}
```
### Dialog Design Guidelines
- Max width: 560px (Alert/confirmation: 320px fixed width)
- Close X-icon on top left (all screen sizes)
- Submit button grouped with cancel at bottom right
- Keep button labels short: "Save", "Delete", "Enable"
- Destructive actions use red warning button
- Always use a title (best practice)
- Strive for minimalism
#### Creating a Lovelace Card
**Purpose**: Cards allow users to tell different stories about their house (based on gallery)
@@ -462,17 +559,6 @@ this.hass.localize("ui.panel.config.updates.update_available", {
- Use HTTPS - All external resources must use HTTPS
- CSP compliance - Ensure code works with Content Security Policy
### Pull Requests
When creating a pull request, you **must** use the PR template located at `.github/PULL_REQUEST_TEMPLATE.md`. Read the template file and use its full content as the PR body, filling in each section appropriately.
- Do not omit, reorder, or rewrite the template sections
- Check the appropriate "Type of change" box based on the changes
- Do not check the checklist items on behalf of the user — those are the user's responsibility to review and check
- If the PR includes UI changes, remind the user to add screenshots or a short video to the PR after creating it
- Be simple and user friendly — explain what the change does, not implementation details
- Use markdown so the user can copy it
### Text and Copy Guidelines
#### Terminology Standards
@@ -630,6 +716,9 @@ this.hass.localize("ui.panel.config.automation.delete_confirm", {
### Component-Specific Checks
- [ ] Dialogs implement HassDialog interface
- [ ] Dialog styling uses haStyleDialog
- [ ] Dialog accessibility includes dialogInitialFocus
- [ ] ha-alert used correctly for messages
- [ ] ha-form uses proper schema structure
- [ ] Components handle all states (loading, error, unavailable)

View File

@@ -5,9 +5,6 @@ updates:
schedule:
interval: weekly
time: "06:00"
cooldown:
default-days-before-reopen: 30
default-days: 7
open-pull-requests-limit: 10
labels:
- Dependencies

View File

@@ -8,9 +8,6 @@ on:
branches:
- master
permissions:
contents: read
env:
NODE_OPTIONS: --max_old_space_size=6144
@@ -27,10 +24,9 @@ jobs:
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: dev
persist-credentials: false
- name: Setup Node
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -63,10 +59,9 @@ jobs:
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: master
persist-credentials: false
- name: Setup Node
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version-file: ".nvmrc"
cache: yarn

View File

@@ -18,9 +18,6 @@ concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
lint:
name: Lint and check format
@@ -28,10 +25,8 @@ jobs:
steps:
- name: Check out files from GitHub
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Setup Node
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -42,7 +37,7 @@ jobs:
- name: Build resources
run: ./node_modules/.bin/gulp gen-icons-json build-translations build-locale-data gather-gallery-pages
- name: Setup lint cache
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
path: |
node_modules/.cache/prettier
@@ -64,10 +59,8 @@ jobs:
steps:
- name: Check out files from GitHub
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Setup Node
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -84,10 +77,8 @@ jobs:
steps:
- name: Check out files from GitHub
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Setup Node
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -98,13 +89,13 @@ jobs:
env:
IS_TEST: "true"
- name: Upload bundle stats
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: frontend-bundle-stats
path: build/stats/*.json
if-no-files-found: error
- name: Upload frontend build
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: frontend-build
path: hass_frontend/

View File

@@ -7,10 +7,6 @@ on:
# The branches below must be a subset of the branches above
branches: [dev]
permissions:
contents: read
security-events: write
jobs:
analyze:
name: Analyze
@@ -32,7 +28,6 @@ jobs:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
fetch-depth: 2
persist-credentials: false
# If this run was triggered by a pull request event, then checkout
# the head of the pull request instead of the merge commit.
@@ -41,14 +36,14 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1
uses: github/codeql-action/init@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4.32.0
with:
languages: ${{ matrix.language }}
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1
uses: github/codeql-action/autobuild@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4.32.0
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@@ -62,4 +57,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1
uses: github/codeql-action/analyze@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4.32.0

View File

@@ -9,9 +9,6 @@ on:
- dev
- master
permissions:
contents: read
env:
NODE_OPTIONS: --max_old_space_size=6144
@@ -28,10 +25,9 @@ jobs:
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: dev
persist-credentials: false
- name: Setup Node
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -64,10 +60,9 @@ jobs:
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: master
persist-credentials: false
- name: Setup Node
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version-file: ".nvmrc"
cache: yarn

View File

@@ -5,9 +5,6 @@ on:
schedule:
- cron: "0 0 * * *"
permissions:
contents: read
env:
NODE_OPTIONS: --max_old_space_size=6144
@@ -20,11 +17,9 @@ jobs:
steps:
- name: Check out files from GitHub
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Setup Node
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version-file: ".nvmrc"
cache: yarn

View File

@@ -10,9 +10,6 @@ on:
branches:
- dev
permissions:
contents: read
env:
NODE_OPTIONS: --max_old_space_size=6144
@@ -25,11 +22,9 @@ jobs:
steps:
- name: Check out files from GitHub
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Setup Node
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version-file: ".nvmrc"
cache: yarn

View File

@@ -1,6 +1,6 @@
name: "Pull Request Labeler"
on: pull_request_target # zizmor: ignore[dangerous-triggers] -- safe: only runs actions/labeler, no PR code checkout
on: pull_request_target
jobs:
triage:

View File

@@ -5,10 +5,6 @@ on:
schedule:
- cron: "0 * * * *"
permissions:
issues: write
pull-requests: write
jobs:
lock:
runs-on: ubuntu-latest

View File

@@ -21,8 +21,6 @@ jobs:
steps:
- name: Checkout the repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
@@ -30,7 +28,7 @@ jobs:
python-version: ${{ env.PYTHON_VERSION }}
- name: Setup Node
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version-file: ".nvmrc"
cache: yarn
@@ -59,14 +57,14 @@ jobs:
run: tar -czvf translations.tar.gz translations
- name: Upload build artifacts
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: wheels
path: dist/home_assistant_frontend*.whl
if-no-files-found: error
- name: Upload translations
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: translations
path: translations.tar.gz

View File

@@ -1,39 +1,25 @@
name: RelativeCI
on:
# zizmor: ignore[dangerous-triggers] -- safe: only downloads artifacts, no PR code checkout
workflow_run:
workflows: [CI]
types:
- completed
permissions:
contents: read
actions: read
jobs:
upload-frontend-modern:
name: Upload stats (frontend/modern)
upload:
name: Upload stats
if: ${{ github.event.workflow_run.conclusion == 'success' }}
strategy:
matrix:
bundle: [frontend]
build: [modern, legacy]
runs-on: ubuntu-latest
steps:
- name: Send bundle stats and build information to RelativeCI
uses: relative-ci/agent-action@3c681926017930047fc03acaa35cd6a44efcbfc3 # v3.2.2
with:
key: ${{ secrets.RELATIVE_CI_KEY_frontend_modern }}
key: ${{ secrets[format('RELATIVE_CI_KEY_{0}_{1}', matrix.bundle, matrix.build)] }}
token: ${{ github.token }}
artifactName: frontend-bundle-stats
webpackStatsFile: frontend-modern.json
upload-frontend-legacy:
name: Upload stats (frontend/legacy)
if: ${{ github.event.workflow_run.conclusion == 'success' }}
runs-on: ubuntu-latest
steps:
- name: Send bundle stats and build information to RelativeCI
uses: relative-ci/agent-action@3c681926017930047fc03acaa35cd6a44efcbfc3 # v3.2.2
with:
key: ${{ secrets.RELATIVE_CI_KEY_frontend_legacy }}
token: ${{ github.token }}
artifactName: frontend-bundle-stats
webpackStatsFile: frontend-legacy.json
artifactName: ${{ format('{0}-bundle-stats', matrix.bundle) }}
webpackStatsFile: ${{ format('{0}-{1}.json', matrix.bundle, matrix.build) }}

View File

@@ -18,6 +18,6 @@ jobs:
pull-requests: read
runs-on: ubuntu-latest
steps:
- uses: release-drafter/release-drafter@139054aeaa9adc52ab36ddf67437541f039b88e2 # v7.1.1
- uses: release-drafter/release-drafter@6db134d15f3909ccc9eefd369f02bd1e9cffdf97 # v6.2.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -27,8 +27,6 @@ jobs:
steps:
- name: Checkout the repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
@@ -36,12 +34,13 @@ jobs:
python-version: ${{ env.PYTHON_VERSION }}
- name: Verify version
uses: home-assistant/actions/helpers/verify-version@d56d093b9ab8d2105bc0cb6ee9bcc0ef4ec8b96d # master
uses: home-assistant/actions/helpers/verify-version@master
- name: Setup Node
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version-file: ".nvmrc"
cache: yarn
- name: Install dependencies
run: yarn install
@@ -63,10 +62,11 @@ jobs:
skip-existing: true
- name: Upload release assets
env:
GH_TOKEN: ${{ github.token }}
TAG_NAME: ${{ github.event.release.tag_name }}
run: gh release upload "$TAG_NAME" dist/*.whl dist/*.tar.gz --clobber
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
with:
files: |
dist/*.whl
dist/*.tar.gz
wheels-init:
name: Init wheels build
@@ -74,17 +74,15 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Generate requirements.txt
env:
GITHUB_REF: ${{ github.ref }}
run: |
# Sleep to give pypi time to populate the new version across mirrors
sleep 240
version=$(echo "$GITHUB_REF" | awk -F"/" '{print $NF}' )
version=$(echo "${{ github.ref }}" | awk -F"/" '{print $NF}' )
echo "home-assistant-frontend==$version" > ./requirements.txt
# home-assistant/wheels doesn't support SHA pinning
- name: Build wheels
uses: home-assistant/wheels@e5742a69d69f0e274e2689c998900c7d19652c21 # 2025.12.0
uses: home-assistant/wheels@2025.12.0
with:
abi: cp314
tag: musllinux_1_2
@@ -101,12 +99,11 @@ jobs:
steps:
- name: Checkout the repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Setup Node
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version-file: ".nvmrc"
cache: yarn
- name: Install dependencies
run: yarn install
- name: Download Translations
@@ -116,11 +113,8 @@ jobs:
- name: Build landing-page
run: landing-page/script/build_landing_page
- name: Tar folder
env:
TAG_NAME: ${{ github.event.release.tag_name }}
run: tar -czf "landing-page/home_assistant_frontend_landingpage-${TAG_NAME}.tar.gz" -C landing-page/dist .
run: tar -czf landing-page/home_assistant_frontend_landingpage-${{ github.event.release.tag_name }}.tar.gz -C landing-page/dist .
- name: Upload release asset
env:
GH_TOKEN: ${{ github.token }}
TAG_NAME: ${{ github.event.release.tag_name }}
run: gh release upload "$TAG_NAME" "landing-page/home_assistant_frontend_landingpage-${TAG_NAME}.tar.gz" --clobber
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
with:
files: landing-page/home_assistant_frontend_landingpage-${{ github.event.release.tag_name }}.tar.gz

View File

@@ -5,38 +5,9 @@ on:
issues:
types: [opened]
permissions: {}
concurrency:
group: ${{ github.workflow }}-${{ github.event.issue.number }}
jobs:
add-no-stale:
name: Add no-stale label
runs-on: ubuntu-latest
permissions:
issues: write # To add labels to issues
if: >-
github.event.issue.type.name == 'Task'
|| github.event.issue.type.name == 'Epic'
|| github.event.issue.type.name == 'Opportunity'
steps:
- name: Add no-stale label
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
script: |
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
labels: ['no-stale']
});
check-authorization:
name: Check authorization
runs-on: ubuntu-latest
permissions:
issues: write # To comment on, label, and close issues
# Only run if this is a Task issue type (from the issue form)
if: github.event.issue.type.name == 'Task'
steps:

View File

@@ -5,16 +5,12 @@ on:
schedule:
- cron: "0 * * * *"
permissions:
issues: write
pull-requests: write
jobs:
stale:
runs-on: ubuntu-latest
steps:
- name: 90 days stale policy
uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0
uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 90

View File

@@ -8,9 +8,6 @@ on:
paths:
- src/translations/en.json
permissions:
contents: read
jobs:
upload:
name: Upload
@@ -18,8 +15,6 @@ jobs:
steps:
- name: Checkout the repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Upload Translations
run: |

2
.nvmrc
View File

@@ -1 +1 @@
24.14.1
24.13.0

View File

@@ -31,7 +31,7 @@ index 8795ddcaa77aea7b0356417e4bc4b19e2b3f860c..fcdc68342d9ac53936c9ed40a9ccfc2f
@@ -129,7 +129,10 @@ export async function injectManifest(
searchString: options.injectionPoint!,
});
- filesToWrite[options.swDest] = source;
+ filesToWrite[options.swDest] = source.replace(
+ url!,

942
.yarn/releases/yarn-4.12.0.cjs vendored Executable file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -8,4 +8,4 @@ enableGlobalCache: false
nodeLinker: node-modules
yarnPath: .yarn/releases/yarn-4.13.0.cjs
yarnPath: .yarn/releases/yarn-4.12.0.cjs

View File

@@ -99,44 +99,6 @@ const lokaliseProjects = {
frontend: "3420425759f6d6d241f598.13594006",
};
const POLL_INTERVAL_MS = 1000;
/* eslint-disable no-await-in-loop */
async function pollProcess(lokaliseApi, projectId, processId) {
while (true) {
const process = await lokaliseApi
.queuedProcesses()
.get(processId, { project_id: projectId });
const project =
projectId === lokaliseProjects.backend ? "backend" : "frontend";
if (process.status === "finished") {
console.log(`Lokalise export process for ${project} finished`);
return process;
}
if (process.status === "failed" || process.status === "cancelled") {
throw new Error(
`Lokalise export process for ${project} ${process.status}: ${process.message}`
);
}
console.log(
`Lokalise export process for ${project} in progress...`,
process.status,
process.details?.items_to_process
? `${Math.round(((process.details.items_processed || 0) / process.details.items_to_process) * 100)}% (${process.details.items_processed}/${process.details.items_to_process})`
: ""
);
await new Promise((resolve) => {
setTimeout(resolve, POLL_INTERVAL_MS);
});
}
}
/* eslint-enable no-await-in-loop */
gulp.task("fetch-lokalise", async function () {
let apiKey;
try {
@@ -156,60 +118,55 @@ gulp.task("fetch-lokalise", async function () {
]);
await Promise.all(
Object.entries(lokaliseProjects).map(async ([project, projectId]) => {
try {
const exportProcess = await lokaliseApi
.files()
.async_download(projectId, {
format: "json",
original_filenames: false,
replace_breaks: false,
json_unescaped_slashes: true,
export_empty_as: "skip",
filter_data: ["verified"],
});
const finishedProcess = await pollProcess(
lokaliseApi,
projectId,
exportProcess.process_id
);
const bundleUrl = finishedProcess.details.download_url;
console.log(`Downloading translations from: ${bundleUrl}`);
const response = await fetch(bundleUrl);
if (response.status !== 200 && response.status !== 0) {
Object.entries(lokaliseProjects).map(([project, projectId]) =>
lokaliseApi
.files()
.download(projectId, {
format: "json",
original_filenames: false,
replace_breaks: false,
json_unescaped_slashes: true,
export_empty_as: "skip",
filter_data: ["verified"],
})
.then((download) => fetch(download.bundle_url))
.then((response) => {
if (response.status === 200 || response.status === 0) {
return response.arrayBuffer();
}
throw new Error(response.statusText);
}
console.log(`Extracting translations...`);
const contents = await JSZip.loadAsync(await response.arrayBuffer());
await mkdirPromise;
await Promise.all(
Object.keys(contents.files).map(async (filename) => {
const file = contents.file(filename);
if (!file) {
// no file, probably a directory
return;
}
const content = await file.async("nodebuffer");
await fs.writeFile(
path.join(inDir, project, filename.split("/").splice(-1)[0]),
content,
{ flag: "w", encoding }
);
})
);
} catch (err) {
console.error(err);
throw err;
}
})
})
.then(JSZip.loadAsync)
.then(async (contents) => {
await mkdirPromise;
return Promise.all(
Object.keys(contents.files).map(async (filename) => {
const file = contents.file(filename);
if (!file) {
// no file, probably a directory
return Promise.resolve();
}
return file
.async("nodebuffer")
.then((content) =>
fs.writeFile(
path.join(
inDir,
project,
filename.split("/").splice(-1)[0]
),
content,
{ flag: "w", encoding }
)
);
})
);
})
.catch((err) => {
console.error(err);
throw err;
})
)
);
});

View File

@@ -40,24 +40,18 @@ const convertToJSON = async (
throw e;
}
// Convert to JSON
const parts = localeData.split("} else {");
const firstBlock = parts[0];
const obj = INTL_POLYFILLS[pkg];
const dataRegex = new RegExp(
`Intl\\.${obj}\\.${addFunc}\\((?<data>.*)\\)`,
"s"
);
localeData = firstBlock.match(dataRegex)?.groups?.data;
localeData = localeData.match(dataRegex)?.groups?.data;
if (!localeData) {
throw Error(`Failed to extract data for language ${lang} from ${pkg}`);
}
// Parse to validate JSON, then stringify to minify
try {
localeData = JSON.stringify(JSON.parse(localeData));
await writeFile(join(outDir, `${pkg}/${lang}.json`), localeData);
} catch (e) {
throw Error(`Failed to parse JSON for language ${lang} from ${pkg}: ${e}`);
}
localeData = JSON.stringify(JSON.parse(localeData));
await writeFile(join(outDir, `${pkg}/${lang}.json`), localeData);
};
gulp.task("clean-locale-data", async () => deleteSync([outDir]));

View File

@@ -57,12 +57,6 @@ const runDevServer = async ({
directory: contentBase,
watch: true,
},
client: {
overlay: {
runtimeErrors: (error) =>
!error?.message?.includes("ResizeObserver loop"),
},
},
proxy,
},
compiler

View File

@@ -26,7 +26,7 @@ import "../../../../src/components/ha-svg-icon";
import "../../../../src/layouts/hass-loading-screen";
import { registerServiceWorker } from "../../../../src/util/register-service-worker";
import "./hc-layout";
import "../../../../src/components/input/ha-input";
import "../../../../src/components/ha-textfield";
import "../../../../src/components/ha-button";
const seeFAQ = (qid) => html`
@@ -123,11 +123,11 @@ export class HcConnect extends LitElement {
To get started, enter your Home Assistant URL and click authorize.
If you want a preview instead, click the show demo button.
</p>
<ha-input
<ha-textfield
label="Home Assistant URL"
placeholder="https://abcdefghijklmnop.ui.nabu.casa"
@keydown=${this._handleInputKeyDown}
></ha-input>
></ha-textfield>
${this.error ? html` <p class="error">${this.error}</p> ` : ""}
</div>
<div class="card-actions">
@@ -204,7 +204,7 @@ export class HcConnect extends LitElement {
}
private async _handleConnect() {
const inputEl = this.shadowRoot!.querySelector("ha-input")!;
const inputEl = this.shadowRoot!.querySelector("ha-textfield")!;
const value = inputEl.value || "";
this.error = undefined;
@@ -319,7 +319,7 @@ export class HcConnect extends LitElement {
flex: 1;
}
ha-input {
ha-textfield {
width: 100%;
}
`;

View File

@@ -1,7 +1,8 @@
import type { EntityInput } from "../../../../src/fake_data/entities/types";
import type { Entity } from "../../../../src/fake_data/entity";
import { convertEntities } from "../../../../src/fake_data/entity";
export const castDemoEntities: () => EntityInput[] = () =>
Object.values({
export const castDemoEntities: () => Entity[] = () =>
convertEntities({
"light.reading_light": {
entity_id: "light.reading_light",
state: "on",

View File

@@ -1,7 +1,8 @@
import { convertEntities } from "../../../../src/fake_data/entity";
import type { DemoConfig } from "../types";
export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
Object.values({
convertEntities({
"todo.shopping_list": {
entity_id: "todo.shopping_list",
state: "2",

View File

@@ -1,7 +1,8 @@
import { convertEntities } from "../../../../src/fake_data/entity";
import type { DemoConfig } from "../types";
export const demoEntitiesJimpower: DemoConfig["entities"] = () =>
Object.values({
convertEntities({
"todo.shopping_list": {
entity_id: "todo.shopping_list",
state: "2",

View File

@@ -1,7 +1,8 @@
import { convertEntities } from "../../../../src/fake_data/entity";
import type { DemoConfig } from "../types";
export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
Object.values({
convertEntities({
"todo.shopping_list": {
entity_id: "todo.shopping_list",
state: "2",

View File

@@ -1,7 +1,8 @@
import { convertEntities } from "../../../../src/fake_data/entity";
import type { DemoConfig } from "../types";
export const demoEntitiesSections: DemoConfig["entities"] = (localize) =>
Object.values({
convertEntities({
"cover.living_room_garden_shutter": {
entity_id: "cover.living_room_garden_shutter",
state: "open",
@@ -141,7 +142,7 @@ export const demoEntitiesSections: DemoConfig["entities"] = (localize) =>
},
},
"device_tracker.car": {
entity_id: "device_tracker.car",
entity_id: "sensor.outdoor_humidity",
state: "not_home",
attributes: {
friendly_name: "Car",
@@ -199,7 +200,7 @@ export const demoEntitiesSections: DemoConfig["entities"] = (localize) =>
},
},
"binary_sensor.kitchen_motion": {
entity_id: "binary_sensor.kitchen_motion",
entity_id: "light.kitchen_motion",
state: "on",
attributes: {
device_class: "motion",
@@ -335,7 +336,7 @@ export const demoEntitiesSections: DemoConfig["entities"] = (localize) =>
},
},
"sensor.rain": {
entity_id: "sensor.rain",
entity_id: "sensor.moon_phase",
state: "7.2",
attributes: {
state_class: "total_increasing",
@@ -565,7 +566,7 @@ export const demoEntitiesSections: DemoConfig["entities"] = (localize) =>
},
},
"update.home_assistant_core_update": {
entity_id: "update.home_assistant_core_update",
entity_id: "update.home_assistant_supervisor_update",
state: "off",
attributes: {
auto_update: false,

View File

@@ -1,7 +1,8 @@
import { convertEntities } from "../../../../src/fake_data/entity";
import type { DemoConfig } from "../types";
export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
Object.values({
convertEntities({
"todo.shopping_list": {
entity_id: "todo.shopping_list",
state: "2",

View File

@@ -1,7 +1,7 @@
import type { TemplateResult } from "lit";
import type { LocalizeFunc } from "../../../src/common/translations/localize";
import type { LovelaceConfig } from "../../../src/data/lovelace/config/types";
import type { EntityInput } from "../../../src/fake_data/entities/types";
import type { Entity } from "../../../src/fake_data/entity";
export interface DemoConfig {
index?: number;
@@ -12,6 +12,6 @@ export interface DemoConfig {
| string
| ((localize: LocalizeFunc) => string | TemplateResult<1>);
lovelace: (localize: LocalizeFunc) => LovelaceConfig;
entities: (localize: LocalizeFunc) => EntityInput[];
entities: (localize: LocalizeFunc) => Entity[];
theme: () => Record<string, string> | null;
}

View File

@@ -1,4 +1,3 @@
/// <reference types="chromecast-caf-sender" />
import { mdiTelevision } from "@mdi/js";
import { css, html, LitElement, nothing } from "lit";
import { customElement, state } from "lit/decorators";

View File

@@ -14,28 +14,40 @@ export const mockEnergy = (hass: MockHomeAssistant) => {
energy_sources: [
{
type: "grid",
stat_energy_from: "sensor.energy_consumption_tarif_1",
stat_energy_to: "sensor.energy_production_tarif_1",
stat_cost: "sensor.energy_consumption_tarif_1_cost",
stat_compensation: "sensor.energy_production_tarif_1_compensation",
entity_energy_price: null,
number_energy_price: null,
entity_energy_price_export: null,
number_energy_price_export: null,
stat_rate: "sensor.power_grid",
cost_adjustment_day: 0,
},
{
type: "grid",
stat_energy_from: "sensor.energy_consumption_tarif_2",
stat_energy_to: "sensor.energy_production_tarif_2",
stat_cost: "sensor.energy_consumption_tarif_2_cost",
stat_compensation: "sensor.energy_production_tarif_2_compensation",
entity_energy_price: null,
number_energy_price: null,
entity_energy_price_export: null,
number_energy_price_export: null,
stat_rate: "sensor.power_grid_return",
flow_from: [
{
stat_energy_from: "sensor.energy_consumption_tarif_1",
stat_cost: "sensor.energy_consumption_tarif_1_cost",
entity_energy_price: null,
number_energy_price: null,
},
{
stat_energy_from: "sensor.energy_consumption_tarif_2",
stat_cost: "sensor.energy_consumption_tarif_2_cost",
entity_energy_price: null,
number_energy_price: null,
},
],
flow_to: [
{
stat_energy_to: "sensor.energy_production_tarif_1",
stat_compensation:
"sensor.energy_production_tarif_1_compensation",
entity_energy_price: null,
number_energy_price: null,
},
{
stat_energy_to: "sensor.energy_production_tarif_2",
stat_compensation:
"sensor.energy_production_tarif_2_compensation",
entity_energy_price: null,
number_energy_price: null,
},
],
power: [
{ stat_rate: "sensor.power_grid" },
{ stat_rate: "sensor.power_grid_return" },
],
cost_adjustment_day: 0,
},
{

View File

@@ -1,5 +1,7 @@
import { convertEntities } from "../../../src/fake_data/entity";
export const mapEntities = () =>
Object.values({
convertEntities({
"zone.home": {
entity_id: "zone.home",
state: "zoning",
@@ -49,7 +51,7 @@ export const mapEntities = () =>
});
export const energyEntities = () =>
Object.values({
convertEntities({
"sensor.grid_fossil_fuel_percentage": {
entity_id: "sensor.grid_fossil_fuel_percentage",
state: "88.6",

View File

@@ -1,12 +1,14 @@
import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
let sidebarChangeCallback;
let changeFunction;
export const mockFrontend = (hass: MockHomeAssistant) => {
hass.mockWS("frontend/get_user_data", () => ({ value: null }));
hass.mockWS("frontend/get_user_data", () => ({
value: null,
}));
hass.mockWS("frontend/set_user_data", ({ key, value }) => {
if (key === "sidebar") {
sidebarChangeCallback?.({
changeFunction?.({
value: {
panelOrder: value.panelOrder || [],
hiddenPanels: value.hiddenPanels || [],
@@ -14,11 +16,14 @@ export const mockFrontend = (hass: MockHomeAssistant) => {
});
}
});
hass.mockWS("frontend/subscribe_user_data", (msg, _hass, onChange) => {
if (msg.key === "sidebar") {
sidebarChangeCallback = onChange;
}
onChange?.({ value: null });
hass.mockWS("frontend/subscribe_user_data", (_msg, _hass, onChange) => {
changeFunction = onChange;
onChange?.({
value: {
panelOrder: [],
hiddenPanels: [],
},
});
// eslint-disable-next-line @typescript-eslint/no-empty-function
return () => {};
});
@@ -43,5 +48,4 @@ export const mockFrontend = (hass: MockHomeAssistant) => {
return () => {};
});
hass.mockWS("repairs/list_issues", () => ({ issues: [] }));
hass.mockWS("frontend/get_themes", (_msg, currentHass) => currentHass.themes);
};

View File

@@ -29,7 +29,6 @@ export const mockLovelace = (
hass.mockWS("lovelace/config/save", () => Promise.resolve());
hass.mockWS("lovelace/resources", () => Promise.resolve([]));
hass.mockWS("lovelace/dashboards/list", () => Promise.resolve([]));
};
customElements.whenDefined("hui-root").then(() => {

View File

@@ -12,7 +12,6 @@ import eslintConfigPrettier from "eslint-config-prettier";
import { configs as litConfigs } from "eslint-plugin-lit";
import { configs as wcConfigs } from "eslint-plugin-wc";
import { configs as a11yConfigs } from "eslint-plugin-lit-a11y";
import html from "@html-eslint/eslint-plugin";
const _filename = fileURLToPath(import.meta.url);
const _dirname = path.dirname(_filename);
@@ -193,13 +192,5 @@ export default tseslint.config(
languageOptions: {
globals: globals.audioWorklet,
},
},
{
plugins: {
html,
},
rules: {
"html/no-invalid-attr-value": "error",
},
}
);

View File

@@ -1,253 +1,175 @@
import { getEntity } from "../../../src/fake_data/entity";
export const createMediaPlayerEntities = () => [
{
entity_id: "media_player.music_paused",
state: "paused",
attributes: {
friendly_name: "Pausing The Music",
media_content_type: "music",
media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)",
media_artist: "Technohead",
// Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media +
// Select Source + Stop + Clear + Play + Shuffle Set
supported_features: 64063,
entity_picture: "/images/album_cover_2.jpg",
media_duration: 300,
media_position: 50,
media_position_updated_at: new Date(
// 23 seconds in
new Date().getTime() - 23000
).toISOString(),
volume_level: 0.5,
source_list: ["AirPlay", "Blu-Ray", "TV", "USB", "iPod (USB)"],
source: "AirPlay",
sound_mode_list: ["Movie", "Music", "Game", "Pure Audio"],
sound_mode: "Music",
},
},
{
entity_id: "media_player.music_playing",
state: "playing",
attributes: {
friendly_name: "Playing The Music",
media_content_type: "music",
media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)",
media_artist: "Technohead",
// Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media +
// Select Source + Stop + Clear + Play + Shuffle Set + Browse Media + Grouping
supported_features: 784959,
entity_picture: "/images/album_cover.jpg",
media_duration: 300,
media_position: 0,
media_position_updated_at: new Date(
// 23 seconds in
new Date().getTime() - 23000
).toISOString(),
volume_level: 0.5,
sound_mode_list: ["Movie", "Music", "Game", "Pure Audio"],
sound_mode: "Music",
group_members: ["media_player.playing", "media_player.stream_playing"],
},
},
{
entity_id: "media_player.stream_playing",
state: "playing",
attributes: {
friendly_name: "Playing the Stream",
media_content_type: "movie",
media_title: "Epic sax guy 10 hours",
app_name: "YouTube",
entity_picture: "/images/frenck.jpg",
// Pause + Next Track + Play + Browse Media
supported_features: 147489,
},
},
{
entity_id: "media_player.stream_paused",
state: "paused",
attributes: {
friendly_name: "Paused the Stream",
media_content_type: "movie",
media_title: "Epic sax guy 10 hours",
app_name: "YouTube",
entity_picture: "/images/frenck.jpg",
// Pause + Next Track + Play
supported_features: 16417,
},
},
{
entity_id: "media_player.stream_playing_previous",
state: "playing",
attributes: {
friendly_name: 'Playing the Stream (with "previous" support)',
media_content_type: "movie",
media_title: "Epic sax guy 10 hours",
app_name: "YouTube",
entity_picture: "/images/frenck.jpg",
// Pause + Previous Track + Play
supported_features: 16401,
},
},
{
entity_id: "media_player.tv_playing",
state: "playing",
attributes: {
friendly_name: "Playing non-skip TV Show",
media_content_type: "tvshow",
media_title: "Chapter 1",
media_series_title: "House of Cards",
app_name: "Netflix",
entity_picture: "/images/netflix.jpg",
// Pause
supported_features: 1,
},
},
{
entity_id: "media_player.sonos_idle",
state: "idle",
attributes: {
friendly_name: "Sonos Idle",
// Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media +
// Select Source + Stop + Clear + Play + Shuffle Set
supported_features: 64063,
volume_level: 0.33,
is_volume_muted: true,
},
},
{
entity_id: "media_player.idle_browse_media",
state: "idle",
attributes: {
friendly_name: "Idle waiting for Browse Media (e.g. Spotify)",
// Pause + Seek + Volume Set + Previous Track + Next Track + Play Media +
// Select Source + Play + Shuffle Set + Browse Media
supported_features: 182839,
volume_level: 0.79,
},
},
{
entity_id: "media_player.theater_off",
state: "off",
attributes: {
friendly_name: "TV Off",
// On + Off + Play + Next + Pause
supported_features: 16801,
},
},
{
entity_id: "media_player.theater_on",
state: "on",
attributes: {
friendly_name: "TV On",
// On + Off + Play + Next + Pause
supported_features: 16801,
},
},
{
entity_id: "media_player.theater_off_static",
state: "off",
attributes: {
friendly_name: "TV Off (cannot be switched on)",
// Off + Next + Pause
supported_features: 289,
},
},
{
entity_id: "media_player.theater_on_static",
state: "on",
attributes: {
friendly_name: "TV On (cannot be switched off)",
// On + Next + Pause
supported_features: 161,
},
},
{
entity_id: "media_player.android_cast",
state: "playing",
attributes: {
friendly_name: "Casting App (no supported features)",
media_title: "Android Screen Casting",
app_name: "Screen Mirroring",
},
},
{
entity_id: "media_player.image_display",
state: "playing",
attributes: {
friendly_name: "Digital Picture Frame",
media_content_type: "image",
media_title: "Famous Painting",
media_artist: "Famous Artist",
entity_picture: "/images/sunflowers.jpg",
// On + Off + Browse Media
supported_features: 131456,
},
},
{
entity_id: "media_player.unavailable",
state: "unavailable",
attributes: {
friendly_name: "Player Unavailable",
// Pause + Volume Set + Volume Mute + Previous Track + Next Track +
// Play Media + Stop + Play
supported_features: 21437,
},
},
{
entity_id: "media_player.unknown",
state: "unknown",
attributes: {
friendly_name: "Player Unknown",
// Pause + Volume Set + Volume Mute + Previous Track + Next Track +
// Play Media + Stop + Play
supported_features: 21437,
},
},
{
entity_id: "media_player.playing",
state: "playing",
attributes: {
friendly_name: "Player Playing (no Pause support)",
// Volume Set + Volume Mute + Previous Track + Next Track +
// Play Media + Stop + Play
supported_features: 21436,
volume_level: 1,
},
},
{
entity_id: "media_player.idle",
state: "idle",
attributes: {
friendly_name: "Player Idle",
// Pause + Volume Set + Volume Mute + Previous Track + Next Track +
// Play Media + Stop + Play
supported_features: 21437,
volume_level: 0,
},
},
{
entity_id: "media_player.receiver_on",
state: "on",
attributes: {
source_list: ["AirPlay", "Blu-Ray", "TV", "USB", "iPod (USB)"],
sound_mode_list: ["Movie", "Music", "Game", "Pure Audio"],
volume_level: 0.63,
is_volume_muted: false,
source: "TV",
sound_mode: "Movie",
friendly_name: "Receiver (selectable sources)",
// Volume Set + Volume Mute + On + Off + Select Source + Play + Sound Mode
supported_features: 84364,
},
},
{
entity_id: "media_player.receiver_off",
state: "off",
attributes: {
source_list: ["AirPlay", "Blu-Ray", "TV", "USB", "iPod (USB)"],
sound_mode_list: ["Movie", "Music", "Game", "Pure Audio"],
friendly_name: "Receiver (selectable sources)",
// Volume Set + Volume Mute + On + Off + Select Source + Play + Sound Mode
supported_features: 84364,
},
},
getEntity("media_player", "music_paused", "paused", {
friendly_name: "Pausing The Music",
media_content_type: "music",
media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)",
media_artist: "Technohead",
// Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media +
// Select Source + Stop + Clear + Play + Shuffle Set
supported_features: 64063,
entity_picture: "/images/album_cover_2.jpg",
media_duration: 300,
media_position: 50,
media_position_updated_at: new Date(
// 23 seconds in
new Date().getTime() - 23000
).toISOString(),
volume_level: 0.5,
source_list: ["AirPlay", "Blu-Ray", "TV", "USB", "iPod (USB)"],
source: "AirPlay",
sound_mode_list: ["Movie", "Music", "Game", "Pure Audio"],
sound_mode: "Music",
}),
getEntity("media_player", "music_playing", "playing", {
friendly_name: "Playing The Music",
media_content_type: "music",
media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)",
media_artist: "Technohead",
// Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media +
// Select Source + Stop + Clear + Play + Shuffle Set + Browse Media + Grouping
supported_features: 784959,
entity_picture: "/images/album_cover.jpg",
media_duration: 300,
media_position: 0,
media_position_updated_at: new Date(
// 23 seconds in
new Date().getTime() - 23000
).toISOString(),
volume_level: 0.5,
sound_mode_list: ["Movie", "Music", "Game", "Pure Audio"],
sound_mode: "Music",
group_members: ["media_player.playing", "media_player.stream_playing"],
}),
getEntity("media_player", "stream_playing", "playing", {
friendly_name: "Playing the Stream",
media_content_type: "movie",
media_title: "Epic sax guy 10 hours",
app_name: "YouTube",
entity_picture: "/images/frenck.jpg",
// Pause + Next Track + Play + Browse Media
supported_features: 147489,
}),
getEntity("media_player", "stream_paused", "paused", {
friendly_name: "Paused the Stream",
media_content_type: "movie",
media_title: "Epic sax guy 10 hours",
app_name: "YouTube",
entity_picture: "/images/frenck.jpg",
// Pause + Next Track + Play
supported_features: 16417,
}),
getEntity("media_player", "stream_playing_previous", "playing", {
friendly_name: 'Playing the Stream (with "previous" support)',
media_content_type: "movie",
media_title: "Epic sax guy 10 hours",
app_name: "YouTube",
entity_picture: "/images/frenck.jpg",
// Pause + Previous Track + Play
supported_features: 16401,
}),
getEntity("media_player", "tv_playing", "playing", {
friendly_name: "Playing non-skip TV Show",
media_content_type: "tvshow",
media_title: "Chapter 1",
media_series_title: "House of Cards",
app_name: "Netflix",
entity_picture: "/images/netflix.jpg",
// Pause
supported_features: 1,
}),
getEntity("media_player", "sonos_idle", "idle", {
friendly_name: "Sonos Idle",
// Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media +
// Select Source + Stop + Clear + Play + Shuffle Set
supported_features: 64063,
volume_level: 0.33,
is_volume_muted: true,
}),
getEntity("media_player", "idle_browse_media", "idle", {
friendly_name: "Idle waiting for Browse Media (e.g. Spotify)",
// Pause + Seek + Volume Set + Previous Track + Next Track + Play Media +
// Select Source + Play + Shuffle Set + Browse Media
supported_features: 182839,
volume_level: 0.79,
}),
getEntity("media_player", "theater_off", "off", {
friendly_name: "TV Off",
// On + Off + Play + Next + Pause
supported_features: 16801,
}),
getEntity("media_player", "theater_on", "on", {
friendly_name: "TV On",
// On + Off + Play + Next + Pause
supported_features: 16801,
}),
getEntity("media_player", "theater_off_static", "off", {
friendly_name: "TV Off (cannot be switched on)",
// Off + Next + Pause
supported_features: 289,
}),
getEntity("media_player", "theater_on_static", "on", {
friendly_name: "TV On (cannot be switched off)",
// On + Next + Pause
supported_features: 161,
}),
getEntity("media_player", "android_cast", "playing", {
friendly_name: "Casting App (no supported features)",
media_title: "Android Screen Casting",
app_name: "Screen Mirroring",
}),
getEntity("media_player", "image_display", "playing", {
friendly_name: "Digital Picture Frame",
media_content_type: "image",
media_title: "Famous Painting",
media_artist: "Famous Artist",
entity_picture: "/images/sunflowers.jpg",
// On + Off + Browse Media
supported_features: 131456,
}),
getEntity("media_player", "unavailable", "unavailable", {
friendly_name: "Player Unavailable",
// Pause + Volume Set + Volume Mute + Previous Track + Next Track +
// Play Media + Stop + Play
supported_features: 21437,
}),
getEntity("media_player", "unknown", "unknown", {
friendly_name: "Player Unknown",
// Pause + Volume Set + Volume Mute + Previous Track + Next Track +
// Play Media + Stop + Play
supported_features: 21437,
}),
getEntity("media_player", "playing", "playing", {
friendly_name: "Player Playing (no Pause support)",
// Volume Set + Volume Mute + Previous Track + Next Track +
// Play Media + Stop + Play
supported_features: 21436,
volume_level: 1,
}),
getEntity("media_player", "idle", "idle", {
friendly_name: "Player Idle",
// Pause + Volume Set + Volume Mute + Previous Track + Next Track +
// Play Media + Stop + Play
supported_features: 21437,
volume_level: 0,
}),
getEntity("media_player", "receiver_on", "on", {
source_list: ["AirPlay", "Blu-Ray", "TV", "USB", "iPod (USB)"],
sound_mode_list: ["Movie", "Music", "Game", "Pure Audio"],
volume_level: 0.63,
is_volume_muted: false,
source: "TV",
sound_mode: "Movie",
friendly_name: "Receiver (selectable sources)",
// Volume Set + Volume Mute + On + Off + Select Source + Play + Sound Mode
supported_features: 84364,
}),
getEntity("media_player", "receiver_off", "off", {
source_list: ["AirPlay", "Blu-Ray", "TV", "USB", "iPod (USB)"],
sound_mode_list: ["Movie", "Music", "Game", "Pure Audio"],
friendly_name: "Receiver (selectable sources)",
// Volume Set + Volume Mute + On + Off + Select Source + Play + Sound Mode
supported_features: 84364,
}),
];

View File

@@ -1,82 +1,72 @@
import { getEntity } from "../../../src/fake_data/entity";
export const createPlantEntities = () => [
{
entity_id: "plant.lemon_tree",
state: "ok",
attributes: {
problem: "none",
sensors: {
moisture: "sensor.lemon_tree_moisture",
battery: "sensor.lemon_tree_battery",
temperature: "sensor.lemon_tree_temperature",
conductivity: "sensor.lemon_tree_conductivity",
brightness: "sensor.lemon_tree_brightness",
},
unit_of_measurement_dict: {
temperature: "°C",
moisture: "%",
brightness: "lx",
battery: "%",
conductivity: "μS/cm",
},
moisture: 54,
battery: 95,
temperature: 15.6,
conductivity: 1,
brightness: 12,
max_brightness: 20,
friendly_name: "Lemon Tree",
getEntity("plant", "lemon_tree", "ok", {
problem: "none",
sensors: {
moisture: "sensor.lemon_tree_moisture",
battery: "sensor.lemon_tree_battery",
temperature: "sensor.lemon_tree_temperature",
conductivity: "sensor.lemon_tree_conductivity",
brightness: "sensor.lemon_tree_brightness",
},
},
{
entity_id: "plant.apple_tree",
state: "ok",
attributes: {
problem: "brightness",
sensors: {
moisture: "sensor.apple_tree_moisture",
battery: "sensor.apple_tree_battery",
temperature: "sensor.apple_tree_temperature",
conductivity: "sensor.apple_tree_conductivity",
brightness: "sensor.apple_tree_brightness",
},
unit_of_measurement_dict: {
temperature: "°C",
moisture: "%",
brightness: "lx",
battery: "%",
conductivity: "μS/cm",
},
moisture: 54,
battery: 2,
temperature: 15.6,
conductivity: 1,
brightness: 25,
max_brightness: 20,
friendly_name: "Apple Tree",
unit_of_measurement_dict: {
temperature: "°C",
moisture: "%",
brightness: "lx",
battery: "%",
conductivity: "μS/cm",
},
},
{
entity_id: "plant.sunflowers",
state: "ok",
attributes: {
problem: "moisture, temperature, conductivity",
sensors: {
moisture: "sensor.sunflowers_moisture",
temperature: "sensor.sunflowers_temperature",
conductivity: "sensor.sunflowers_conductivity",
brightness: "sensor.sunflowers_brightness",
},
unit_of_measurement_dict: {
temperature: "°C",
moisture: "%",
brightness: "lx",
conductivity: "μS/cm",
},
moisture: 54,
temperature: 15.6,
conductivity: 1,
brightness: 25,
entity_picture: "/images/sunflowers.jpg",
moisture: 54,
battery: 95,
temperature: 15.6,
conductivity: 1,
brightness: 12,
max_brightness: 20,
friendly_name: "Lemon Tree",
}),
getEntity("plant", "apple_tree", "ok", {
problem: "brightness",
sensors: {
moisture: "sensor.apple_tree_moisture",
battery: "sensor.apple_tree_battery",
temperature: "sensor.apple_tree_temperature",
conductivity: "sensor.apple_tree_conductivity",
brightness: "sensor.apple_tree_brightness",
},
},
unit_of_measurement_dict: {
temperature: "°C",
moisture: "%",
brightness: "lx",
battery: "%",
conductivity: "μS/cm",
},
moisture: 54,
battery: 2,
temperature: 15.6,
conductivity: 1,
brightness: 25,
max_brightness: 20,
friendly_name: "Apple Tree",
}),
getEntity("plant", "sunflowers", "ok", {
problem: "moisture, temperature, conductivity",
sensors: {
moisture: "sensor.sunflowers_moisture",
temperature: "sensor.sunflowers_temperature",
conductivity: "sensor.sunflowers_conductivity",
brightness: "sensor.sunflowers_brightness",
},
unit_of_measurement_dict: {
temperature: "°C",
moisture: "%",
brightness: "lx",
conductivity: "μS/cm",
},
moisture: 54,
temperature: 15.6,
conductivity: 1,
brightness: 25,
entity_picture: "/images/sunflowers.jpg",
}),
];

View File

@@ -5,24 +5,17 @@ import "../../../../src/components/ha-card";
import "../../../../src/components/ha-yaml-editor";
import type { Action } from "../../../../src/data/script";
import { describeAction } from "../../../../src/data/script_i18n";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import type { HomeAssistant } from "../../../../src/types";
const ENTITIES = [
{
entity_id: "scene.kitchen_morning",
state: "scening",
attributes: {
friendly_name: "Kitchen Morning",
},
},
{
entity_id: "media_player.kitchen",
state: "playing",
attributes: {
friendly_name: "Sonos Kitchen",
},
},
getEntity("scene", "kitchen_morning", "scening", {
friendly_name: "Kitchen Morning",
}),
getEntity("media_player", "kitchen", "playing", {
friendly_name: "Sonos Kitchen",
}),
];
const ACTIONS = [

View File

@@ -5,31 +5,20 @@ import "../../../../src/components/ha-card";
import "../../../../src/components/ha-yaml-editor";
import type { Condition } from "../../../../src/data/automation";
import { describeCondition } from "../../../../src/data/automation_i18n";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import type { HomeAssistant } from "../../../../src/types";
const ENTITIES = [
{
entity_id: "light.kitchen",
state: "on",
attributes: {
friendly_name: "Kitchen Light",
},
},
{
entity_id: "device_tracker.person",
state: "home",
attributes: {
friendly_name: "Person",
},
},
{
entity_id: "zone.home",
state: "",
attributes: {
friendly_name: "Home",
},
},
getEntity("light", "kitchen", "on", {
friendly_name: "Kitchen Light",
}),
getEntity("device_tracker", "person", "home", {
friendly_name: "Person",
}),
getEntity("zone", "home", "", {
friendly_name: "Home",
}),
];
const conditions: Condition[] = [

View File

@@ -5,31 +5,20 @@ import "../../../../src/components/ha-card";
import "../../../../src/components/ha-yaml-editor";
import type { LegacyTrigger } from "../../../../src/data/automation";
import { describeTrigger } from "../../../../src/data/automation_i18n";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import type { HomeAssistant } from "../../../../src/types";
const ENTITIES = [
{
entity_id: "light.kitchen",
state: "on",
attributes: {
friendly_name: "Kitchen Light",
},
},
{
entity_id: "person.person",
state: "",
attributes: {
friendly_name: "Person",
},
},
{
entity_id: "zone.home",
state: "",
attributes: {
friendly_name: "Home",
},
},
getEntity("light", "kitchen", "on", {
friendly_name: "Kitchen Light",
}),
getEntity("person", "person", "", {
friendly_name: "Person",
}),
getEntity("zone", "home", "", {
friendly_name: "Home",
}),
];
const triggers = [

View File

@@ -10,9 +10,7 @@ As a community, we are proud of our logo. Follow these guidelines to ensure it a
![Logo](/images/brand/logo.png)
<ha-alert alert-type="info">
This logo is trademarked and the property of the Open Home Foundation. This means it is not available for commercial use without express written permission from the foundation. We regard commercial use as anything designed to market or promote a product, software or service that is for sale. Please contact <a href="mailto:partner@openhomefoundation.org">partner@openhomefoundation.org</a> for further information
</ha-alert>
Please note that this logo is not released under the CC license. All rights reserved.
# Design

View File

@@ -1 +0,0 @@
import "../../../../src/components/ha-alert";

View File

@@ -21,8 +21,8 @@ type DialogType =
| "basic"
| "basic-subtitle-below"
| "basic-subtitle-above"
| "allow-mode-change"
| "form"
| "form-block-mode"
| "actions"
| "large"
| "small";
@@ -69,8 +69,8 @@ export class DemoHaAdaptiveDialog extends LitElement {
<ha-button @click=${this._handleOpenDialog("form")}
>Adaptive dialog with form</ha-button
>
<ha-button @click=${this._handleOpenDialog("allow-mode-change")}
>Adaptive dialog with allow mode change</ha-button
<ha-button @click=${this._handleOpenDialog("form-block-mode")}
>Adaptive dialog with form (block mode change)</ha-button
>
<ha-button @click=${this._handleOpenDialog("actions")}
>Adaptive dialog with actions</ha-button
@@ -164,15 +164,27 @@ export class DemoHaAdaptiveDialog extends LitElement {
<ha-adaptive-dialog
.hass=${this._hass}
.allowModeChange=${this._openDialog === "allow-mode-change"}
header-title="Adaptive dialog with allow mode change"
header-subtitle="Resize the window while this dialog is open"
.open=${this._openDialog === "form-block-mode"}
header-title="Adaptive dialog with form (block mode change)"
header-subtitle="This form will not reset when the viewport size changes"
block-mode-change
@closed=${this._handleClosed}
>
<div>
This dialog can switch between dialog mode and bottom sheet mode
while open.
</div>
<ha-form autofocus .schema=${SCHEMA}></ha-form>
<ha-dialog-footer slot="footer">
<ha-button
@click=${this._handleClosed}
slot="secondaryAction"
variant="plain"
>Cancel</ha-button
>
<ha-button
@click=${this._handleClosed}
slot="primaryAction"
variant="accent"
>Submit</ha-button
>
</ha-dialog-footer>
</ha-adaptive-dialog>
<ha-adaptive-dialog
@@ -203,7 +215,7 @@ export class DemoHaAdaptiveDialog extends LitElement {
<li>
<strong>Dialog mode:</strong> Used on larger screens (width &gt;
870px and height &gt; 500px). Renders as a centered dialog using
<code>ha-dialog</code>.
<code>ha-wa-dialog</code>.
</li>
<li>
<strong>Bottom sheet mode:</strong> Used on mobile devices and
@@ -213,9 +225,10 @@ export class DemoHaAdaptiveDialog extends LitElement {
</ul>
<p>
By default, the mode is determined at mount time and then stays fixed
while the dialog is open. To allow switching modes while the viewport
changes, use the <code>allow-mode-change</code> attribute.
The mode is determined automatically and updates when the window is
resized. To prevent mode changes after the initial mount (useful for
preventing form resets), use the <code>block-mode-change</code>
attribute.
</p>
<h3>Width</h3>
@@ -381,15 +394,15 @@ export class DemoHaAdaptiveDialog extends LitElement {
<p>
If you don't need responsive behavior, use
<code>ha-dialog</code> directly for desktop-only dialogs or
<code>ha-wa-dialog</code> directly for desktop-only dialogs or
<code>ha-bottom-sheet</code> for mobile-only sheets.
</p>
<p>
Use the <code>allow-mode-change</code> attribute when you want the
dialog to switch between modes as the viewport changes after opening.
For forms, you can keep the default behavior to avoid resetting fields
on resize.
Use the <code>block-mode-change</code> attribute when you want to
prevent the dialog from switching modes after it's opened. This is
especially useful for forms, as it prevents form data from being lost
when users resize their browser window.
</p>
<h3>Example usage</h3>
@@ -397,6 +410,7 @@ export class DemoHaAdaptiveDialog extends LitElement {
<pre><code>&lt;ha-adaptive-dialog
.hass=\${this.hass}
open
width="medium"
header-title="Dialog title"
header-subtitle="Dialog subtitle"
&gt;
@@ -413,10 +427,27 @@ export class DemoHaAdaptiveDialog extends LitElement {
&lt;/ha-dialog-footer&gt;
&lt;/ha-adaptive-dialog&gt;</code></pre>
<p>Example with <code>block-mode-change</code> for forms:</p>
<pre><code>&lt;ha-adaptive-dialog
.hass=\${this.hass}
open
header-title="Edit configuration"
block-mode-change
&gt;
&lt;ha-form .schema=\${schema} .data=\${data}&gt;&lt;/ha-form&gt;
&lt;ha-dialog-footer slot="footer"&gt;
&lt;ha-button slot="secondaryAction" variant="plain"
&gt;Cancel&lt;/ha-button
&gt;
&lt;ha-button slot="primaryAction" variant="accent"&gt;Save&lt;/ha-button&gt;
&lt;/ha-dialog-footer&gt;
&lt;/ha-adaptive-dialog&gt;</code></pre>
<h3>API</h3>
<p>
This component combines <code>ha-dialog</code> and
This component combines <code>ha-wa-dialog</code> and
<code>ha-bottom-sheet</code> with automatic mode switching based on
screen size.
</p>
@@ -490,10 +521,12 @@ export class DemoHaAdaptiveDialog extends LitElement {
<td></td>
</tr>
<tr>
<td><code>allow-mode-change</code></td>
<td><code>block-mode-change</code></td>
<td>
When set, the dialog can switch between modes as the viewport
size changes while it is open.
When set, the mode is determined at mount time based on the
current screen size, but subsequent mode changes are blocked.
Useful for preventing forms from resetting when the viewport
size changes.
</td>
<td><code>false</code></td>
<td><code>false</code>, <code>true</code></td>
@@ -515,14 +548,6 @@ export class DemoHaAdaptiveDialog extends LitElement {
<td><code>--ha-dialog-surface-background</code></td>
<td>Dialog/sheet background color.</td>
</tr>
<tr>
<td><code>--ha-dialog-surface-backdrop-filter</code></td>
<td>Dialog/sheet surface backdrop filter.</td>
</tr>
<tr>
<td><code>--dialog-box-shadow</code></td>
<td>Dialog surface box shadow (dialog mode only).</td>
</tr>
<tr>
<td><code>--ha-dialog-border-radius</code></td>
<td>Border radius of the dialog surface (dialog mode only).</td>
@@ -535,34 +560,6 @@ export class DemoHaAdaptiveDialog extends LitElement {
<td><code>--ha-dialog-hide-duration</code></td>
<td>Hide animation duration (dialog mode only).</td>
</tr>
<tr>
<td><code>--ha-dialog-scrim-backdrop-filter</code></td>
<td>Dialog/sheet scrim backdrop filter.</td>
</tr>
<tr>
<td><code>--dialog-backdrop-filter</code></td>
<td>Dialog/sheet scrim backdrop filter (legacy fallback).</td>
</tr>
<tr>
<td><code>--mdc-dialog-scrim-color</code></td>
<td>Dialog/sheet scrim color (legacy compatibility).</td>
</tr>
<tr>
<td><code>--ha-bottom-sheet-surface-background</code></td>
<td>Bottom sheet background color (sheet mode only).</td>
</tr>
<tr>
<td><code>--ha-bottom-sheet-surface-backdrop-filter</code></td>
<td>Bottom sheet surface backdrop filter (sheet mode only).</td>
</tr>
<tr>
<td><code>--ha-bottom-sheet-scrim-backdrop-filter</code></td>
<td>Bottom sheet scrim backdrop filter (sheet mode only).</td>
</tr>
<tr>
<td><code>--ha-bottom-sheet-scrim-color</code></td>
<td>Bottom sheet scrim color (sheet mode only).</td>
</tr>
</tbody>
</table>

View File

@@ -1,3 +0,0 @@
---
title: Dialog (ha-dialog)
---

View File

@@ -12,53 +12,34 @@ import "../../../../src/components/ha-form/ha-form";
import type { HaFormSchema } from "../../../../src/components/ha-form/types";
import type { AreaRegistryEntry } from "../../../../src/data/area/area_registry";
import type { DeviceRegistryEntry } from "../../../../src/data/device/device_registry";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import type { HomeAssistant } from "../../../../src/types";
import "../../components/demo-black-white-row";
const ENTITIES = [
{
entity_id: "alarm_control_panel.alarm",
state: "disarmed",
attributes: {
friendly_name: "Alarm",
},
},
{
entity_id: "media_player.livingroom",
state: "playing",
attributes: {
friendly_name: "Livingroom",
media_content_type: "music",
device_class: "tv",
},
},
{
entity_id: "media_player.lounge",
state: "idle",
attributes: {
friendly_name: "Lounge",
supported_features: 444983,
device_class: "speaker",
},
},
{
entity_id: "light.bedroom",
state: "on",
attributes: {
friendly_name: "Bedroom",
effect: "colorloop",
effect_list: ["colorloop", "random"],
},
},
{
entity_id: "switch.coffee",
state: "off",
attributes: {
friendly_name: "Coffee",
device_class: "switch",
},
},
getEntity("alarm_control_panel", "alarm", "disarmed", {
friendly_name: "Alarm",
}),
getEntity("media_player", "livingroom", "playing", {
friendly_name: "Livingroom",
media_content_type: "music",
device_class: "tv",
}),
getEntity("media_player", "lounge", "idle", {
friendly_name: "Lounge",
supported_features: 444983,
device_class: "speaker",
}),
getEntity("light", "bedroom", "on", {
friendly_name: "Bedroom",
effect: "colorloop",
effect_list: ["colorloop", "random"],
}),
getEntity("switch", "coffee", "off", {
friendly_name: "Coffee",
device_class: "switch",
}),
];
const DEVICES: DeviceRegistryEntry[] = [
@@ -480,12 +461,6 @@ const SCHEMAS: {
},
{ type: "string", name: "path", default: "/" },
{ type: "boolean", name: "ssl", default: false },
{
type: "string",
name: "comments",
default: "disabled field",
disabled: true,
},
],
},
];

View File

@@ -1,82 +0,0 @@
---
title: Input
---
# Input `<ha-input>`
A text input component supporting Home Assistant theming and validation, based on webawesome input.
Supports multiple input types including text, number, password, email, search, and more.
## Implementation
### Example usage
```html
<ha-input label="Name" value="Hello"></ha-input>
<ha-input label="Email" type="email" placeholder="you@example.com"></ha-input>
<ha-input label="Password" type="password" password-toggle></ha-input>
<ha-input label="Required" required></ha-input>
<ha-input label="Disabled" disabled value="Can't touch this"></ha-input>
```
### API
This component is based on the webawesome input component.
**Slots**
- `start`: Content placed before the input (usually for icons or prefixes).
- `end`: Content placed after the input (usually for icons or suffixes).
- `label`: Custom label content. Overrides the `label` property.
- `hint`: Custom hint content. Overrides the `hint` property.
- `clear-icon`: Custom clear icon.
- `show-password-icon`: Custom show password icon.
- `hide-password-icon`: Custom hide password icon.
**Properties/Attributes**
| Name | Type | Default | Description |
| -------------------- | ---------------------------------------------------------------------------------------------- | ---------- | ---------------------------------------------------------------------------------------------------------- |
| appearance | "material"/"outlined" | "material" | Sets the input appearance style. "material" is the default filled style, "outlined" uses a bordered style. |
| type | "text"/"number"/"password"/"email"/"search"/"tel"/"url"/"date"/"datetime-local"/"time"/"color" | "text" | Sets the input type. |
| value | String | - | The current value of the input. |
| label | String | "" | The input's label text. |
| hint | String | "" | The input's hint/helper text. |
| placeholder | String | "" | Placeholder text shown when the input is empty. |
| with-clear | Boolean | false | Adds a clear button when the input is not empty. |
| readonly | Boolean | false | Makes the input readonly. |
| disabled | Boolean | false | Disables the input and prevents user interaction. |
| required | Boolean | false | Makes the input a required field. |
| password-toggle | Boolean | false | Adds a button to toggle the password visibility. |
| without-spin-buttons | Boolean | false | Hides the browser's built-in spin buttons for number inputs. |
| auto-validate | Boolean | false | Validates the input on blur instead of on form submit. |
| invalid | Boolean | false | Marks the input as invalid. |
| inset-label | Boolean | false | Uses an inset label style where the label stays inside the input. |
| validation-message | String | "" | Custom validation message shown when the input is invalid. |
| pattern | String | - | A regular expression pattern to validate input against. |
| minlength | Number | - | The minimum length of input that will be considered valid. |
| maxlength | Number | - | The maximum length of input that will be considered valid. |
| min | Number/String | - | The input's minimum value. Only applies to date and number input types. |
| max | Number/String | - | The input's maximum value. Only applies to date and number input types. |
| step | Number/"any" | - | Specifies the granularity that the value must adhere to. |
**CSS Custom Properties**
- `--ha-input-padding-top` - Padding above the input.
- `--ha-input-padding-bottom` - Padding below the input. Defaults to `var(--ha-space-2)`.
- `--ha-input-text-align` - Text alignment of the input. Defaults to `start`.
- `--ha-input-required-marker` - The marker shown after the label for required fields. Defaults to `"*"`.
---
## Derivatives
The following components extend or wrap `ha-input` for specific use cases:
- **`<ha-input-search>`** — A pre-configured search input with a magnify icon, clear button, and localized "Search" placeholder. Extends `ha-input`.
- **`<ha-input-copy>`** — A read-only input with a copy-to-clipboard button. Supports optional value masking with a reveal toggle.
- **`<ha-input-multi>`** — A dynamic list of text inputs for managing arrays of strings. Supports adding, removing, and drag-and-drop reordering.

View File

@@ -1,232 +0,0 @@
import { ContextProvider } from "@lit/context";
import { mdiMagnify } 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 "../../../../src/components/ha-card";
import "../../../../src/components/ha-svg-icon";
import "../../../../src/components/input/ha-input";
import "../../../../src/components/input/ha-input-copy";
import "../../../../src/components/input/ha-input-multi";
import "../../../../src/components/input/ha-input-search";
import { localizeContext } from "../../../../src/data/context";
const LOCALIZE_KEYS: Record<string, string> = {
"ui.common.copy": "Copy",
"ui.common.show": "Show",
"ui.common.hide": "Hide",
"ui.common.add": "Add",
"ui.common.remove": "Remove",
"ui.common.search": "Search",
"ui.common.copied_clipboard": "Copied to clipboard",
};
@customElement("demo-components-ha-input")
export class DemoHaInput extends LitElement {
constructor() {
super();
// Provides localizeContext for ha-input-copy, ha-input-multi and ha-input-search
// eslint-disable-next-line no-new
new ContextProvider(this, {
context: localizeContext,
initialValue: ((key: string) => LOCALIZE_KEYS[key] ?? key) as any,
});
}
protected render(): TemplateResult {
return html`
${["light", "dark"].map(
(mode) => html`
<div class=${mode}>
<ha-card header="ha-input in ${mode}">
<div class="card-content">
<h3>Basic</h3>
<div class="row">
<ha-input label="Default"></ha-input>
<ha-input label="With value" value="Hello"></ha-input>
<ha-input
label="With placeholder"
placeholder="Type here..."
></ha-input>
</div>
<h3>Input types</h3>
<div class="row">
<ha-input label="Text" type="text" value="Text"></ha-input>
<ha-input label="Number" type="number" value="42"></ha-input>
<ha-input
label="Email"
type="email"
placeholder="you@example.com"
></ha-input>
</div>
<div class="row">
<ha-input
label="Password"
type="password"
value="secret"
password-toggle
></ha-input>
<ha-input label="URL" type="url" placeholder="https://...">
</ha-input>
<ha-input label="Date" type="date"></ha-input>
</div>
<h3>States</h3>
<div class="row">
<ha-input
label="Disabled"
disabled
value="Disabled"
></ha-input>
<ha-input
label="Readonly"
readonly
value="Readonly"
></ha-input>
<ha-input label="Required" required></ha-input>
</div>
<div class="row">
<ha-input
label="Invalid"
invalid
validation-message="This field is required"
value=""
></ha-input>
<ha-input label="With hint" hint="This is a hint"></ha-input>
<ha-input
label="With clear"
with-clear
value="Clear me"
></ha-input>
</div>
<h3>With slots</h3>
<div class="row">
<ha-input label="With prefix">
<span slot="start">$</span>
</ha-input>
<ha-input label="With suffix">
<span slot="end">kg</span>
</ha-input>
<ha-input label="With icon">
<ha-svg-icon .path=${mdiMagnify} slot="start"></ha-svg-icon>
</ha-input>
</div>
<h3>Appearance: outlined</h3>
<div class="row">
<ha-input
appearance="outlined"
label="Outlined"
value="Hello"
></ha-input>
<ha-input
appearance="outlined"
label="Outlined disabled"
disabled
value="Disabled"
></ha-input>
<ha-input
appearance="outlined"
label="Outlined invalid"
invalid
validation-message="Required"
></ha-input>
</div>
<div class="row">
<ha-input
appearance="outlined"
placeholder="Placeholder only"
></ha-input>
</div>
</div>
</ha-card>
<ha-card header="Derivatives in ${mode}">
<div class="card-content">
<h3>ha-input-search</h3>
<ha-input-search label="Search label"></ha-input-search>
<ha-input-search appearance="outlined"></ha-input-search>
<h3>ha-input-copy</h3>
<ha-input-copy
value="my-api-token-123"
masked-value="••••••••••••••••••"
masked-toggle
></ha-input-copy>
<h3>ha-input-multi</h3>
<ha-input-multi
label="URL"
add-label="Add URL"
.value=${["https://example.com"]}
></ha-input-multi>
</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;
}
ha-card {
margin: 24px auto;
}
.card-content {
display: flex;
flex-direction: column;
gap: var(--ha-space-2);
}
h3 {
margin: var(--ha-space-4) 0 var(--ha-space-1) 0;
font-size: var(--ha-font-size-l);
font-weight: var(--ha-font-weight-medium);
}
h3:first-child {
margin-top: 0;
}
.row {
display: flex;
gap: var(--ha-space-4);
}
.row > * {
flex: 1;
}
`;
}
declare global {
interface HTMLElementTagNameMap {
"demo-components-ha-input": DemoHaInput;
}
}

View File

@@ -1,5 +1,5 @@
import type { TemplateResult } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { css, html, LitElement } from "lit";
import { customElement, state } from "lit/decorators";
import { mockAreaRegistry } from "../../../../demo/src/stubs/area_registry";
import { mockConfigEntries } from "../../../../demo/src/stubs/config_entries";
@@ -8,7 +8,6 @@ import { mockEntityRegistry } from "../../../../demo/src/stubs/entity_registry";
import { mockFloorRegistry } from "../../../../demo/src/stubs/floor_registry";
import { mockHassioSupervisor } from "../../../../demo/src/stubs/hassio_supervisor";
import { mockLabelRegistry } from "../../../../demo/src/stubs/label_registry";
import type { HASSDomEvent } from "../../../../src/common/dom/fire_event";
import "../../../../src/components/ha-formfield";
import "../../../../src/components/ha-selector/ha-selector";
import "../../../../src/components/ha-settings-row";
@@ -17,59 +16,33 @@ import type { BlueprintInput } from "../../../../src/data/blueprint";
import type { DeviceRegistryEntry } from "../../../../src/data/device/device_registry";
import type { FloorRegistryEntry } from "../../../../src/data/floor_registry";
import type { LabelRegistryEntry } from "../../../../src/data/label/label_registry";
import {
showDialog,
type ShowDialogParams,
} from "../../../../src/dialogs/make-dialog-manager";
import { showDialog } from "../../../../src/dialogs/make-dialog-manager";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import type { ProvideHassElement } from "../../../../src/mixins/provide-hass-lit-mixin";
import type { HomeAssistant } from "../../../../src/types";
import "../../components/demo-black-white-row";
const ENTITIES = [
{
entity_id: "alarm_control_panel.alarm",
state: "disarmed",
attributes: {
friendly_name: "Alarm",
},
},
{
entity_id: "media_player.livingroom",
state: "playing",
attributes: {
friendly_name: "Livingroom",
},
},
{
entity_id: "media_player.lounge",
state: "idle",
attributes: {
friendly_name: "Lounge",
supported_features: 444983,
},
},
{
entity_id: "light.bedroom",
state: "on",
attributes: {
friendly_name: "Bedroom",
},
},
{
entity_id: "switch.coffee",
state: "off",
attributes: {
friendly_name: "Coffee",
},
},
{
entity_id: "number.number",
state: "5",
attributes: {
friendly_name: "Number",
},
},
getEntity("alarm_control_panel", "alarm", "disarmed", {
friendly_name: "Alarm",
}),
getEntity("media_player", "livingroom", "playing", {
friendly_name: "Livingroom",
}),
getEntity("media_player", "lounge", "idle", {
friendly_name: "Lounge",
supported_features: 444983,
}),
getEntity("light", "bedroom", "on", {
friendly_name: "Bedroom",
}),
getEntity("switch", "coffee", "off", {
friendly_name: "Coffee",
}),
getEntity("number", "number", 5, {
friendly_name: "Number",
}),
];
const DEVICES: DeviceRegistryEntry[] = [
@@ -638,15 +611,14 @@ class DemoHaSelector extends LitElement implements ProvideHassElement {
};
};
private _dialogManager = (e: HASSDomEvent<ShowDialogParams<unknown>>) => {
const { dialogTag, dialogImport, dialogParams, addHistory, parentElement } =
e.detail;
private _dialogManager = (e) => {
const { dialogTag, dialogImport, dialogParams, addHistory } = e.detail;
showDialog(
this,
this.shadowRoot!,
dialogTag,
dialogParams,
dialogImport,
parentElement,
addHistory
);
};
@@ -692,11 +664,7 @@ class DemoHaSelector extends LitElement implements ProvideHassElement {
([key, value]) => html`
<ha-settings-row narrow slot=${slot}>
<span slot="heading">${value?.name || key}</span>
${value?.description
? html`<span slot="description"
>${value?.description}</span
>`
: nothing}
<span slot="description">${value?.description}</span>
<ha-selector
.hass=${this.hass}
.selector=${value!.selector}

View File

@@ -28,12 +28,9 @@ This element is based on webawesome `wa-tooltip` it only sets some css tokens an
In your theme settings use this without the prefixed `--`.
- `--ha-tooltip-background-color` (Default: `var(--secondary-background-color)`)
- `--ha-tooltip-text-color` (Default: `var(--primary-text-color)`)
- `--ha-tooltip-font-family` (Default: `var(--ha-font-family-body)`)
- `--ha-tooltip-font-size` (Default: `var(--ha-font-size-s)`)
- `--ha-tooltip-font-weight` (Default: `var(--ha-font-weight-normal)`)
- `--ha-tooltip-line-height` (Default: `var(--ha-line-height-condensed)`)
- `--ha-tooltip-padding` (Default: 8px)
- `--ha-tooltip-border-radius` (Default: `var(--ha-border-radius-sm)`)
- `--ha-tooltip-border-radius` (Default: 4px)
- `--ha-tooltip-arrow-size` (Default: 8px)
- `--wa-tooltip-font-family` (Default: `var(--ha-font-family-body)`)
- `--ha-tooltip-font-size` (Default: `var(--ha-font-size-s)`)
- `--wa-tooltip-font-weight` (Default: `var(--ha-font-weight-normal)`)
- `--wa-tooltip-line-height` (Default: `var(--ha-line-height-condensed)`)

View File

@@ -0,0 +1,3 @@
---
title: Dialog (ha-wa-dialog)
---

View File

@@ -6,7 +6,7 @@ import "../../../../src/components/ha-card";
import "../../../../src/components/ha-dialog-footer";
import "../../../../src/components/ha-form/ha-form";
import "../../../../src/components/ha-icon-button";
import "../../../../src/components/ha-dialog";
import "../../../../src/components/ha-wa-dialog";
import type { HaFormSchema } from "../../../../src/components/ha-form/types";
const SCHEMA: HaFormSchema[] = [
@@ -22,14 +22,14 @@ type DialogType =
| "form"
| "actions";
@customElement("demo-components-ha-dialog")
export class DemoHaDialog extends LitElement {
@customElement("demo-components-ha-wa-dialog")
export class DemoHaWaDialog extends LitElement {
@state() private _openDialog: DialogType = false;
protected render() {
return html`
<div class="content">
<h1>Dialog <code>&lt;ha-dialog&gt;</code></h1>
<h1>Dialog <code>&lt;ha-wa-dialog&gt;</code></h1>
<p class="subtitle">Dialog component built with WebAwesome.</p>
@@ -53,24 +53,24 @@ export class DemoHaDialog extends LitElement {
>
</div>
<ha-dialog
<ha-wa-dialog
.open=${this._openDialog === "basic"}
header-title="Basic dialog"
@closed=${this._handleClosed}
>
<div>Dialog content</div>
</ha-dialog>
</ha-wa-dialog>
<ha-dialog
<ha-wa-dialog
.open=${this._openDialog === "basic-subtitle-below"}
header-title="Basic dialog with subtitle"
header-subtitle="This is a basic dialog with a subtitle below"
@closed=${this._handleClosed}
>
<div>Dialog content</div>
</ha-dialog>
</ha-wa-dialog>
<ha-dialog
<ha-wa-dialog
.open=${this._openDialog === "basic-subtitle-above"}
header-title="Dialog with subtitle above"
header-subtitle="This is a basic dialog with a subtitle above"
@@ -78,9 +78,9 @@ export class DemoHaDialog extends LitElement {
@closed=${this._handleClosed}
>
<div>Dialog content</div>
</ha-dialog>
</ha-wa-dialog>
<ha-dialog
<ha-wa-dialog
.open=${this._openDialog === "form"}
header-title="Dialog with form"
header-subtitle="This is a dialog with a form and a footer"
@@ -91,18 +91,17 @@ export class DemoHaDialog extends LitElement {
<ha-dialog-footer slot="footer">
<ha-button
data-dialog="close"
appearance="plain"
slot="secondaryAction"
variant="plain"
>Cancel</ha-button
>
<ha-button data-dialog="close" slot="primaryAction" variant="accent"
>Submit</ha-button
>
Cancel
</ha-button>
<ha-button data-dialog="close" slot="primaryAction">
Submit
</ha-button>
</ha-dialog-footer>
</ha-dialog>
</ha-wa-dialog>
<ha-dialog
<ha-wa-dialog
.open=${this._openDialog === "actions"}
header-title="Dialog with actions"
header-subtitle="This is a dialog with header actions"
@@ -114,7 +113,7 @@ export class DemoHaDialog extends LitElement {
</div>
<div>Dialog content</div>
</ha-dialog>
</ha-wa-dialog>
<h2>Design</h2>
@@ -229,19 +228,19 @@ export class DemoHaDialog extends LitElement {
<tr>
<th>Slot</th>
<th>Description</th>
<th>Appearance to use</th>
<th>Variant to use</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>secondaryAction</code></td>
<td>The secondary action button(s).</td>
<td><code>appearance="plain"</code></td>
<td><code>plain</code></td>
</tr>
<tr>
<td><code>primaryAction</code></td>
<td>The primary action button(s).</td>
<td>Default (no appearance attribute)</td>
<td><code>accent</code></td>
</tr>
</tbody>
</table>
@@ -250,7 +249,7 @@ export class DemoHaDialog extends LitElement {
<h3>Example Usage</h3>
<pre><code>&lt;ha-dialog
<pre><code>&lt;ha-wa-dialog
open
header-title="Dialog title"
header-subtitle="Dialog subtitle"
@@ -262,18 +261,12 @@ export class DemoHaDialog extends LitElement {
&lt;/div&gt;
&lt;div&gt;Dialog content&lt;/div&gt;
&lt;ha-dialog-footer slot="footer"&gt;
&lt;ha-button
data-dialog="close"
appearance="plain"
slot="secondaryAction"
&lt;ha-button data-dialog="close" slot="secondaryAction" variant="plain"
&gt;Cancel&lt;/ha-button
&gt;
Cancel
&lt;/ha-button&gt;
&lt;ha-button data-dialog="close" slot="primaryAction"&gt;
Submit
&lt;/ha-button&gt;
&lt;ha-button slot="primaryAction" variant="accent"&gt;Submit&lt;/ha-button&gt;
&lt;/ha-dialog-footer&gt;
&lt;/ha-dialog&gt;</code></pre>
&lt;/ha-wa-dialog&gt;</code></pre>
<h3>API</h3>
@@ -380,29 +373,13 @@ export class DemoHaDialog extends LitElement {
<td><code>--ha-dialog-surface-background</code></td>
<td>Dialog background color.</td>
</tr>
<tr>
<td><code>--ha-dialog-surface-backdrop-filter</code></td>
<td>Backdrop filter applied to the dialog surface.</td>
</tr>
<tr>
<td><code>--dialog-box-shadow</code></td>
<td>Dialog surface box shadow.</td>
</tr>
<tr>
<td><code>--ha-dialog-border-radius</code></td>
<td>Border radius of the dialog surface.</td>
</tr>
<tr>
<td><code>--ha-dialog-scrim-backdrop-filter</code></td>
<td>Backdrop filter applied to the dialog scrim.</td>
</tr>
<tr>
<td><code>--dialog-backdrop-filter</code></td>
<td>Legacy fallback for the dialog scrim backdrop filter.</td>
</tr>
<tr>
<td><code>--mdc-dialog-scrim-color</code></td>
<td>Dialog scrim color (legacy compatibility).</td>
<td><code>--dialog-z-index</code></td>
<td>Z-index for the dialog.</td>
</tr>
<tr>
<td><code>--dialog-surface-margin-top</code></td>
@@ -537,6 +514,6 @@ export class DemoHaDialog extends LitElement {
declare global {
interface HTMLElementTagNameMap {
"demo-components-ha-dialog": DemoHaDialog;
"demo-components-ha-wa-dialog": DemoHaWaDialog;
}
}

View File

@@ -18,8 +18,8 @@ The Home Assistant interface is based on Material Design. It's a design system c
We want to make it as easy for designers to contribute as it is for developers. Theres a lot a designer can contribute to:
- Meet us at <a href="https://www.home-assistant.io/join-chat-design" rel="noopener noreferrer" target="_blank">Discord #designers channel</a>. If you can't see the channel, make sure you set the correct role in Channels & Roles.
- Start designing with our <a href="https://www.figma.com/design/2WGI8IDGyxINjSV6NRvPur/Home-Assistant-Design-Kit" rel="noopener noreferrer" target="_blank">Figma DesignKit</a>.
- Meet us at <a href="https://www.home-assistant.io/join-chat" rel="noopener noreferrer" target="_blank">devs_ux Discord</a>. Feel free to share your designs, user test or strategic ideas.
- Start designing with our <a href="https://www.figma.com/community/file/967153512097289521/Home-Assistant-DesignKit" rel="noopener noreferrer" target="_blank">Figma DesignKit</a>.
- Find the latest UX <a href="https://github.com/home-assistant/frontend/discussions?discussions_q=label%3Aux" rel="noopener noreferrer" target="_blank">discussions</a> and <a href="https://github.com/home-assistant/frontend/labels/ux" rel="noopener noreferrer" target="_blank">issues</a> on GitHub. Everyone can start a new issue or discussion!
## Developers

View File

@@ -1,40 +1,25 @@
import type { PropertyValues, TemplateResult } from "lit";
import { html, LitElement } from "lit";
import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
import { mockIcons } from "../../../../demo/src/stubs/icons";
const ENTITIES = [
{
entity_id: "alarm_control_panel.alarm",
state: "disarmed",
attributes: {
friendly_name: "Alarm",
},
},
{
entity_id: "alarm_control_panel.alarm_armed",
state: "armed_home",
attributes: {
friendly_name: "Alarm",
},
},
{
entity_id: "alarm_control_panel.unavailable",
state: "unavailable",
attributes: {
friendly_name: "Alarm",
},
},
{
entity_id: "alarm_control_panel.alarm_code",
state: "disarmed",
attributes: {
friendly_name: "Alarm",
code_format: "number",
},
},
getEntity("alarm_control_panel", "alarm", "disarmed", {
friendly_name: "Alarm",
}),
getEntity("alarm_control_panel", "alarm_armed", "armed_home", {
friendly_name: "Alarm",
}),
getEntity("alarm_control_panel", "unavailable", "unavailable", {
friendly_name: "Alarm",
}),
getEntity("alarm_control_panel", "alarm_code", "disarmed", {
friendly_name: "Alarm",
code_format: "number",
}),
];
const CONFIGS = [

View File

@@ -1,79 +1,44 @@
import type { PropertyValues, TemplateResult } from "lit";
import { html, LitElement } from "lit";
import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
import { mockIcons } from "../../../../demo/src/stubs/icons";
const ENTITIES = [
{
entity_id: "light.bed_light",
state: "on",
attributes: {
friendly_name: "Bed Light",
},
},
{
entity_id: "switch.bed_ac",
state: "on",
attributes: {
friendly_name: "Ecobee",
},
},
{
entity_id: "sensor.bed_temp",
state: "72",
attributes: {
friendly_name: "Bedroom Temp",
device_class: "temperature",
unit_of_measurement: "°F",
},
},
{
entity_id: "light.living_room_light",
state: "off",
attributes: {
friendly_name: "Living Room Light",
},
},
{
entity_id: "fan.living_room",
state: "on",
attributes: {
friendly_name: "Living Room Fan",
},
},
{
entity_id: "sensor.office_humidity",
state: "73",
attributes: {
friendly_name: "Office Humidity",
device_class: "humidity",
unit_of_measurement: "%",
},
},
{
entity_id: "light.office",
state: "on",
attributes: {
friendly_name: "Office Light",
},
},
{
entity_id: "fan.kitchen",
state: "on",
attributes: {
friendly_name: "Kitchen Fan",
},
},
{
entity_id: "binary_sensor.kitchen_door",
state: "on",
attributes: {
friendly_name: "Office Door",
device_class: "door",
},
},
getEntity("light", "bed_light", "on", {
friendly_name: "Bed Light",
}),
getEntity("switch", "bed_ac", "on", {
friendly_name: "Ecobee",
}),
getEntity("sensor", "bed_temp", "72", {
friendly_name: "Bedroom Temp",
device_class: "temperature",
unit_of_measurement: "°F",
}),
getEntity("light", "living_room_light", "off", {
friendly_name: "Living Room Light",
}),
getEntity("fan", "living_room", "on", {
friendly_name: "Living Room Fan",
}),
getEntity("sensor", "office_humidity", "73", {
friendly_name: "Office Humidity",
device_class: "humidity",
unit_of_measurement: "%",
}),
getEntity("light", "office", "on", {
friendly_name: "Office Light",
}),
getEntity("fan", "kitchen", "on", {
friendly_name: "Kitchen Fan",
}),
getEntity("binary_sensor", "kitchen_door", "on", {
friendly_name: "Office Door",
device_class: "door",
}),
];
// TODO: Update image here

View File

@@ -1,39 +1,24 @@
import type { PropertyValues, TemplateResult } from "lit";
import { html, LitElement } from "lit";
import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
import { mockIcons } from "../../../../demo/src/stubs/icons";
const ENTITIES = [
{
entity_id: "light.controller_1",
state: "on",
attributes: {
friendly_name: "Controller 1",
},
},
{
entity_id: "light.controller_2",
state: "on",
attributes: {
friendly_name: "Controller 2",
},
},
{
entity_id: "light.floor",
state: "off",
attributes: {
friendly_name: "Floor light",
},
},
{
entity_id: "light.kitchen",
state: "on",
attributes: {
friendly_name: "Kitchen light",
},
},
getEntity("light", "controller_1", "on", {
friendly_name: "Controller 1",
}),
getEntity("light", "controller_2", "on", {
friendly_name: "Controller 2",
}),
getEntity("light", "floor", "off", {
friendly_name: "Floor light",
}),
getEntity("light", "kitchen", "on", {
friendly_name: "Kitchen light",
}),
];
const CONFIGS = [

View File

@@ -1,257 +1,150 @@
import type { PropertyValues, TemplateResult } from "lit";
import { html, LitElement } from "lit";
import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
import { mockIcons } from "../../../../demo/src/stubs/icons";
const ENTITIES = [
{
entity_id: "light.bed_light",
state: "on",
attributes: {
friendly_name: "Bed Light",
},
},
{
entity_id: "group.kitchen",
state: "on",
attributes: {
entity_id: ["light.bed_light"],
order: 8,
friendly_name: "Kitchen Group",
},
},
{
entity_id: "lock.kitchen_door",
state: "locked",
attributes: {
friendly_name: "Kitchen Lock",
},
},
{
entity_id: "cover.kitchen_window",
state: "open",
attributes: {
friendly_name: "Kitchen Window",
supported_features: 11,
},
},
{
entity_id: "scene.romantic_lights",
state: "scening",
attributes: {
entity_id: ["light.bed_light", "light.ceiling_lights"],
friendly_name: "Romantic Scene",
},
},
{
entity_id: "device_tracker.demo_paulus",
state: "home",
attributes: {
source_type: "gps",
latitude: 32.877105,
longitude: 117.232185,
gps_accuracy: 91,
battery: 71,
friendly_name: "Paulus",
},
},
{
entity_id: "climate.ecobee",
state: "auto",
attributes: {
current_temperature: 73,
min_temp: 45,
max_temp: 95,
temperature: null,
target_temp_high: 75,
target_temp_low: 70,
fan_mode: "Auto Low",
fan_list: ["On Low", "On High", "Auto Low", "Auto High", "Off"],
operation_mode: "auto",
operation_list: ["heat", "cool", "auto", "off"],
hold_mode: "home",
swing_mode: "Auto",
swing_list: ["Auto", "1", "2", "3", "Off"],
unit_of_measurement: "°F",
friendly_name: "Ecobee",
supported_features: 1014,
},
},
{
entity_id: "input_number.number",
state: "5",
attributes: {
min: 0,
max: 10,
step: 1,
mode: "slider",
unit_of_measurement: "dB",
friendly_name: "Number",
icon: "mdi:bell-ring",
},
},
{
entity_id: "input_boolean.toggle",
state: "on",
attributes: {
friendly_name: "Toggle",
},
},
{
entity_id: "input_datetime.date_and_time",
state: "2022-01-10 00:00:00",
attributes: {
has_date: true,
has_time: true,
editable: true,
year: 2022,
month: 1,
day: 10,
hour: 0,
minute: 0,
second: 0,
timestamp: 1641801600,
friendly_name: "Date and Time",
},
},
{
entity_id: "sensor.humidity",
state: "23.2",
attributes: {
friendly_name: "Humidity",
unit_of_measurement: "%",
},
},
{
entity_id: "input_select.dropdown",
state: "Soda",
attributes: {
friendly_name: "Dropdown",
options: ["Soda", "Beer", "Wine"],
},
},
{
entity_id: "input_text.text",
state: "Inspiration",
attributes: {
friendly_name: "Text",
mode: "text",
},
},
{
entity_id: "timer.timer",
state: "idle",
attributes: {
friendly_name: "Timer",
duration: "0:05:00",
},
},
{
entity_id: "counter.counter",
state: "3",
attributes: {
friendly_name: "Counter",
initial: 0,
step: 1,
minimum: 0,
maximum: 10,
},
},
{
entity_id: "text.message",
state: "Hello!",
attributes: {
friendly_name: "Message",
},
},
getEntity("light", "bed_light", "on", {
friendly_name: "Bed Light",
}),
getEntity("group", "kitchen", "on", {
entity_id: ["light.bed_light"],
order: 8,
friendly_name: "Kitchen Group",
}),
getEntity("lock", "kitchen_door", "locked", {
friendly_name: "Kitchen Lock",
}),
getEntity("cover", "kitchen_window", "open", {
friendly_name: "Kitchen Window",
supported_features: 11,
}),
getEntity("scene", "romantic_lights", "scening", {
entity_id: ["light.bed_light", "light.ceiling_lights"],
friendly_name: "Romantic Scene",
}),
getEntity("device_tracker", "demo_paulus", "home", {
source_type: "gps",
latitude: 32.877105,
longitude: 117.232185,
gps_accuracy: 91,
battery: 71,
friendly_name: "Paulus",
}),
getEntity("climate", "ecobee", "auto", {
current_temperature: 73,
min_temp: 45,
max_temp: 95,
temperature: null,
target_temp_high: 75,
target_temp_low: 70,
fan_mode: "Auto Low",
fan_list: ["On Low", "On High", "Auto Low", "Auto High", "Off"],
operation_mode: "auto",
operation_list: ["heat", "cool", "auto", "off"],
hold_mode: "home",
swing_mode: "Auto",
swing_list: ["Auto", "1", "2", "3", "Off"],
unit_of_measurement: "°F",
friendly_name: "Ecobee",
supported_features: 1014,
}),
getEntity("input_number", "number", 5, {
min: 0,
max: 10,
step: 1,
mode: "slider",
unit_of_measurement: "dB",
friendly_name: "Number",
icon: "mdi:bell-ring",
}),
getEntity("input_boolean", "toggle", "on", {
friendly_name: "Toggle",
}),
getEntity("input_datetime", "date_and_time", "2022-01-10 00:00:00", {
has_date: true,
has_time: true,
editable: true,
year: 2022,
month: 1,
day: 10,
hour: 0,
minute: 0,
second: 0,
timestamp: 1641801600,
friendly_name: "Date and Time",
}),
getEntity("sensor", "humidity", "23.2", {
friendly_name: "Humidity",
unit_of_measurement: "%",
}),
getEntity("input_select", "dropdown", "Soda", {
friendly_name: "Dropdown",
options: ["Soda", "Beer", "Wine"],
}),
getEntity("input_text", "text", "Inspiration", {
friendly_name: "Text",
mode: "text",
}),
getEntity("timer", "timer", "idle", {
friendly_name: "Timer",
duration: "0:05:00",
}),
getEntity("counter", "counter", "3", {
friendly_name: "Counter",
initial: 0,
step: 1,
minimum: 0,
maximum: 10,
}),
getEntity("text", "message", "Hello!", {
friendly_name: "Message",
}),
{
entity_id: "light.unavailable",
state: "unavailable",
attributes: {
friendly_name: "Bed Light",
},
},
{
entity_id: "lock.unavailable",
state: "unavailable",
attributes: {
friendly_name: "Kitchen Door",
},
},
{
entity_id: "cover.unavailable",
state: "unavailable",
attributes: {
friendly_name: "Kitchen Window",
supported_features: 11,
},
},
{
entity_id: "scene.unavailable",
state: "unavailable",
attributes: {
friendly_name: "Romantic Scene",
},
},
{
entity_id: "device_tracker.unavailable",
state: "unavailable",
attributes: {
friendly_name: "Paulus",
},
},
{
entity_id: "climate.unavailable",
state: "unavailable",
attributes: {
unit_of_measurement: "°F",
friendly_name: "Ecobee",
supported_features: 1014,
},
},
{
entity_id: "input_number.unavailable",
state: "unavailable",
attributes: {
friendly_name: "Allowed Noise",
icon: "mdi:bell-ring",
},
},
{
entity_id: "input_select.unavailable",
state: "unavailable",
attributes: {
unit_of_measurement: "dB",
friendly_name: "Who cooks",
icon: "mdi:cheff",
},
},
{
entity_id: "text.unavailable",
state: "unavailable",
attributes: {
friendly_name: "Message",
},
},
{
entity_id: "event.unavailable",
state: "unavailable",
attributes: {
friendly_name: "Empty remote",
},
},
{
entity_id: "event.doorbell",
state: "2023-07-17T21:26:11.615+00:00",
attributes: {
friendly_name: "Doorbell",
device_class: "doorbell",
event_type: "Ding-Dong",
},
},
getEntity("light", "unavailable", "unavailable", {
friendly_name: "Bed Light",
}),
getEntity("lock", "unavailable", "unavailable", {
friendly_name: "Kitchen Door",
}),
getEntity("cover", "unavailable", "unavailable", {
friendly_name: "Kitchen Window",
supported_features: 11,
}),
getEntity("scene", "unavailable", "unavailable", {
friendly_name: "Romantic Scene",
}),
getEntity("device_tracker", "unavailable", "unavailable", {
friendly_name: "Paulus",
}),
getEntity("climate", "unavailable", "unavailable", {
unit_of_measurement: "°F",
friendly_name: "Ecobee",
supported_features: 1014,
}),
getEntity("input_number", "unavailable", "unavailable", {
friendly_name: "Allowed Noise",
icon: "mdi:bell-ring",
}),
getEntity("input_select", "unavailable", "unavailable", {
unit_of_measurement: "dB",
friendly_name: "Who cooks",
icon: "mdi:cheff",
}),
getEntity("text", "unavailable", "unavailable", {
friendly_name: "Message",
}),
getEntity("event", "unavailable", "unavailable", {
friendly_name: "Empty remote",
}),
getEntity("event", "doorbell", "2023-07-17T21:26:11.615+00:00", {
friendly_name: "Doorbell",
device_class: "doorbell",
event_type: "Ding-Dong",
}),
];
const CONFIGS = [

View File

@@ -1,18 +1,15 @@
import type { PropertyValues, TemplateResult } from "lit";
import { html, LitElement } from "lit";
import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
import { mockIcons } from "../../../../demo/src/stubs/icons";
const ENTITIES = [
{
entity_id: "light.bed_light",
state: "on",
attributes: {
friendly_name: "Bed Light",
},
},
getEntity("light", "bed_light", "on", {
friendly_name: "Bed Light",
}),
];
const CONFIGS = [

View File

@@ -1,117 +1,74 @@
import type { PropertyValues, TemplateResult } from "lit";
import { html, LitElement } from "lit";
import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
import { mockIcons } from "../../../../demo/src/stubs/icons";
const ENTITIES = [
{
entity_id: "device_tracker.demo_paulus",
state: "work",
attributes: {
source_type: "gps",
latitude: 32.877105,
longitude: 117.232185,
gps_accuracy: 91,
battery: 25,
friendly_name: "Paulus",
},
},
{
entity_id: "device_tracker.demo_anne_therese",
state: "school",
attributes: {
source_type: "gps",
latitude: 32.877105,
longitude: 117.232185,
gps_accuracy: 91,
battery: 50,
friendly_name: "Anne Therese",
},
},
{
entity_id: "device_tracker.demo_home_boy",
state: "home",
attributes: {
source_type: "gps",
latitude: 32.877105,
longitude: 117.232185,
gps_accuracy: 91,
battery: 75,
friendly_name: "Home Boy",
},
},
{
entity_id: "light.bed_light",
state: "on",
attributes: {
friendly_name: "Bed Light",
},
},
{
entity_id: "light.kitchen_lights",
state: "on",
attributes: {
friendly_name: "Kitchen Lights",
},
},
{
entity_id: "light.ceiling_lights",
state: "off",
attributes: {
friendly_name: "Ceiling Lights",
},
},
{
entity_id: "sensor.battery_1",
state: "20",
attributes: {
device_class: "battery",
friendly_name: "Battery 1",
unit_of_measurement: "%",
},
},
{
entity_id: "sensor.battery_2",
state: "35",
attributes: {
device_class: "battery",
friendly_name: "Battery 2",
unit_of_measurement: "%",
},
},
{
entity_id: "sensor.battery_3",
state: "40",
attributes: {
device_class: "battery",
friendly_name: "Battery 3",
unit_of_measurement: "%",
},
},
{
entity_id: "sensor.battery_4",
state: "80",
attributes: {
device_class: "battery",
friendly_name: "Battery 4",
unit_of_measurement: "%",
},
},
{
entity_id: "input_number.min_battery_level",
state: "30",
attributes: {
mode: "slider",
step: 10,
min: 0,
max: 100,
icon: "mdi:battery-alert-variant",
friendly_name: "Minimum Battery Level",
unit_of_measurement: "%",
},
},
getEntity("device_tracker", "demo_paulus", "work", {
source_type: "gps",
latitude: 32.877105,
longitude: 117.232185,
gps_accuracy: 91,
battery: 25,
friendly_name: "Paulus",
}),
getEntity("device_tracker", "demo_anne_therese", "school", {
source_type: "gps",
latitude: 32.877105,
longitude: 117.232185,
gps_accuracy: 91,
battery: 50,
friendly_name: "Anne Therese",
}),
getEntity("device_tracker", "demo_home_boy", "home", {
source_type: "gps",
latitude: 32.877105,
longitude: 117.232185,
gps_accuracy: 91,
battery: 75,
friendly_name: "Home Boy",
}),
getEntity("light", "bed_light", "on", {
friendly_name: "Bed Light",
}),
getEntity("light", "kitchen_lights", "on", {
friendly_name: "Kitchen Lights",
}),
getEntity("light", "ceiling_lights", "off", {
friendly_name: "Ceiling Lights",
}),
getEntity("sensor", "battery_1", 20, {
device_class: "battery",
friendly_name: "Battery 1",
unit_of_measurement: "%",
}),
getEntity("sensor", "battery_2", 35, {
device_class: "battery",
friendly_name: "Battery 2",
unit_of_measurement: "%",
}),
getEntity("sensor", "battery_3", 40, {
device_class: "battery",
friendly_name: "Battery 3",
unit_of_measurement: "%",
}),
getEntity("sensor", "battery_4", 80, {
device_class: "battery",
friendly_name: "Battery 4",
unit_of_measurement: "%",
}),
getEntity("input_number", "min_battery_level", 30, {
mode: "slider",
step: 10,
min: 0,
max: 100,
icon: "mdi:battery-alert-variant",
friendly_name: "Minimum Battery Level",
unit_of_measurement: "%",
}),
];
const CONFIGS = [

View File

@@ -1,30 +1,23 @@
import type { PropertyValues, TemplateResult } from "lit";
import { html, LitElement } from "lit";
import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
import { mockIcons } from "../../../../demo/src/stubs/icons";
const ENTITIES = [
{ entity_id: "sensor.brightness", state: "12", attributes: {} },
{ entity_id: "sensor.brightness_medium", state: "53", attributes: {} },
{ entity_id: "sensor.brightness_high", state: "87", attributes: {} },
{ entity_id: "plant.bonsai", state: "ok", attributes: {} },
{ entity_id: "sensor.not_working", state: "unavailable", attributes: {} },
{
entity_id: "sensor.outside_humidity",
state: "54",
attributes: {
unit_of_measurement: "%",
},
},
{
entity_id: "sensor.outside_temperature",
state: "15.6",
attributes: {
unit_of_measurement: "°C",
},
},
getEntity("sensor", "brightness", "12", {}),
getEntity("sensor", "brightness_medium", "53", {}),
getEntity("sensor", "brightness_high", "87", {}),
getEntity("plant", "bonsai", "ok", {}),
getEntity("sensor", "not_working", "unavailable", {}),
getEntity("sensor", "outside_humidity", "54", {
unit_of_measurement: "%",
}),
getEntity("sensor", "outside_temperature", "15.6", {
unit_of_measurement: "°C",
}),
];
const CONFIGS = [
@@ -54,19 +47,6 @@ const CONFIGS = [
needle: true
`,
},
{
heading: "Rendering needle and severity levels",
config: `
- type: gauge
entity: sensor.brightness_high
name: Brightness High
needle: true
severity:
red: 75
green: 0
yellow: 50
`,
},
{
heading: "Setting severity levels",
config: `
@@ -134,21 +114,6 @@ const CONFIGS = [
entity: sensor.not_working
`,
},
{
heading: "Lower minimum",
config: `
- type: gauge
entity: sensor.brightness_high
needle: true
severity:
green: 0
yellow: 0.45
red: 0.9
min: -0.05
name: " "
max: 1.9
unit: GBP/h`,
},
];
@customElement("demo-lovelace-gauge-card")

View File

@@ -1,89 +1,62 @@
import type { PropertyValues, TemplateResult } from "lit";
import { html, LitElement } from "lit";
import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
import { mockIcons } from "../../../../demo/src/stubs/icons";
const ENTITIES = [
{
entity_id: "device_tracker.demo_paulus",
state: "home",
attributes: {
source_type: "gps",
latitude: 32.877105,
longitude: 117.232185,
gps_accuracy: 91,
battery: 71,
friendly_name: "Paulus",
},
},
{
entity_id: "media_player.living_room",
state: "playing",
attributes: {
volume_level: 1,
is_volume_muted: false,
media_content_id: "eyU3bRy2x44",
media_content_type: "movie",
media_duration: 300,
media_position: 45.017773,
media_position_updated_at: "2018-07-19T10:44:45.919514+00:00",
media_title: "♥♥ The Best Fireplace Video (3 hours)",
app_name: "YouTube",
sound_mode: "Dummy Music",
sound_mode_list: ["Dummy Music", "Dummy Movie"],
shuffle: false,
friendly_name: "Living Room",
entity_picture:
"/api/media_player_proxy/media_player.living_room?token=e925f8db7f7bd1f317e4524dcb8333d60f6019219a3799a22604b5787f243567&cache=bc2ffb49c4f67034",
supported_features: 115597,
},
},
{
entity_id: "sun.sun",
state: "below_horizon",
attributes: {
next_dawn: "2018-07-19T20:48:47+00:00",
next_dusk: "2018-07-20T11:46:06+00:00",
next_midnight: "2018-07-19T16:17:28+00:00",
next_noon: "2018-07-20T04:17:26+00:00",
next_rising: "2018-07-19T21:16:31+00:00",
next_setting: "2018-07-20T11:18:22+00:00",
elevation: 67.69,
azimuth: 338.55,
friendly_name: "Sun",
},
},
{
entity_id: "cover.kitchen_window",
state: "open",
attributes: {
friendly_name: "Kitchen Window",
supported_features: 11,
},
},
{
entity_id: "light.kitchen_lights",
state: "on",
attributes: {
friendly_name: "Kitchen Lights",
},
},
{
entity_id: "light.ceiling_lights",
state: "off",
attributes: {
friendly_name: "Ceiling Lights",
},
},
{
entity_id: "lock.kitchen_door",
state: "locked",
attributes: {
friendly_name: "Kitchen Door",
},
},
getEntity("device_tracker", "demo_paulus", "home", {
source_type: "gps",
latitude: 32.877105,
longitude: 117.232185,
gps_accuracy: 91,
battery: 71,
friendly_name: "Paulus",
}),
getEntity("media_player", "living_room", "playing", {
volume_level: 1,
is_volume_muted: false,
media_content_id: "eyU3bRy2x44",
media_content_type: "movie",
media_duration: 300,
media_position: 45.017773,
media_position_updated_at: "2018-07-19T10:44:45.919514+00:00",
media_title: "♥♥ The Best Fireplace Video (3 hours)",
app_name: "YouTube",
sound_mode: "Dummy Music",
sound_mode_list: ["Dummy Music", "Dummy Movie"],
shuffle: false,
friendly_name: "Living Room",
entity_picture:
"/api/media_player_proxy/media_player.living_room?token=e925f8db7f7bd1f317e4524dcb8333d60f6019219a3799a22604b5787f243567&cache=bc2ffb49c4f67034",
supported_features: 115597,
}),
getEntity("sun", "sun", "below_horizon", {
next_dawn: "2018-07-19T20:48:47+00:00",
next_dusk: "2018-07-20T11:46:06+00:00",
next_midnight: "2018-07-19T16:17:28+00:00",
next_noon: "2018-07-20T04:17:26+00:00",
next_rising: "2018-07-19T21:16:31+00:00",
next_setting: "2018-07-20T11:18:22+00:00",
elevation: 67.69,
azimuth: 338.55,
friendly_name: "Sun",
}),
getEntity("cover", "kitchen_window", "open", {
friendly_name: "Kitchen Window",
supported_features: 11,
}),
getEntity("light", "kitchen_lights", "on", {
friendly_name: "Kitchen Lights",
}),
getEntity("light", "ceiling_lights", "off", {
friendly_name: "Ceiling Lights",
}),
getEntity("lock", "kitchen_door", "locked", {
friendly_name: "Kitchen Door",
}),
];
const CONFIGS = [

View File

@@ -2,69 +2,46 @@ import type { PropertyValues, TemplateResult } from "lit";
import { html, LitElement } from "lit";
import { customElement, query } from "lit/decorators";
import { mockHistory } from "../../../../demo/src/stubs/history";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
import { mockIcons } from "../../../../demo/src/stubs/icons";
const ENTITIES = [
{
entity_id: "light.kitchen_lights",
state: "on",
attributes: {
friendly_name: "Kitchen Lights",
},
},
{
entity_id: "light.bed_light",
state: "on",
attributes: {
friendly_name: "Bed Lights",
},
},
{
entity_id: "device_tracker.demo_paulus",
state: "work",
attributes: {
source_type: "gps",
latitude: 32.877105,
longitude: 117.232185,
gps_accuracy: 91,
battery: 71,
friendly_name: "Paulus",
},
},
{
entity_id: "device_tracker.demo_anne_therese",
state: "school",
attributes: {
source_type: "gps",
latitude: 32.877105,
longitude: 117.232185,
gps_accuracy: 91,
battery: 71,
friendly_name: "Anne Therese",
},
},
{
entity_id: "device_tracker.demo_home_boy",
state: "home",
attributes: {
source_type: "gps",
latitude: 32.877105,
longitude: 117.232185,
gps_accuracy: 91,
battery: 71,
friendly_name: "Home Boy",
},
},
{
entity_id: "sensor.illumination",
state: "23",
attributes: {
friendly_name: "Illumination",
unit_of_measurement: "lx",
},
},
getEntity("light", "kitchen_lights", "on", {
friendly_name: "Kitchen Lights",
}),
getEntity("light", "bed_light", "on", {
friendly_name: "Bed Lights",
}),
getEntity("device_tracker", "demo_paulus", "work", {
source_type: "gps",
latitude: 32.877105,
longitude: 117.232185,
gps_accuracy: 91,
battery: 71,
friendly_name: "Paulus",
}),
getEntity("device_tracker", "demo_anne_therese", "school", {
source_type: "gps",
latitude: 32.877105,
longitude: 117.232185,
gps_accuracy: 91,
battery: 71,
friendly_name: "Anne Therese",
}),
getEntity("device_tracker", "demo_home_boy", "home", {
source_type: "gps",
latitude: 32.877105,
longitude: 117.232185,
gps_accuracy: 91,
battery: 71,
friendly_name: "Home Boy",
}),
getEntity("sensor", "illumination", "23", {
friendly_name: "Illumination",
unit_of_measurement: "lx",
}),
];
const CONFIGS = [

View File

@@ -1,44 +1,29 @@
import type { PropertyValues, TemplateResult } from "lit";
import { html, LitElement } from "lit";
import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
import { mockIcons } from "../../../../demo/src/stubs/icons";
const ENTITIES = [
{
entity_id: "light.bed_light",
state: "on",
attributes: {
friendly_name: "Bed Light",
brightness: 255,
},
},
{
entity_id: "light.dim_on",
state: "on",
attributes: {
friendly_name: "Dining Room",
supported_features: 1,
brightness: 100,
},
},
{
entity_id: "light.dim_off",
state: "off",
attributes: {
friendly_name: "Dining Room",
supported_features: 1,
},
},
{
entity_id: "light.unavailable",
state: "unavailable",
attributes: {
friendly_name: "Lost Light",
supported_features: 1,
},
},
getEntity("light", "bed_light", "on", {
friendly_name: "Bed Light",
brightness: 255,
}),
getEntity("light", "dim_on", "on", {
friendly_name: "Dining Room",
supported_features: 1,
brightness: 100,
}),
getEntity("light", "dim_off", "off", {
friendly_name: "Dining Room",
supported_features: 1,
}),
getEntity("light", "unavailable", "unavailable", {
friendly_name: "Lost Light",
supported_features: 1,
}),
];
const CONFIGS = [

View File

@@ -1,86 +1,59 @@
import type { PropertyValues, TemplateResult } from "lit";
import { html, LitElement } from "lit";
import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
const ENTITIES = [
{
entity_id: "device_tracker.demo_paulus",
state: "not_home",
attributes: {
source_type: "gps",
latitude: 32.877105,
longitude: 117.232185,
gps_accuracy: 91,
battery: 71,
friendly_name: "Paulus",
},
},
{
entity_id: "device_tracker.demo_home_boy",
state: "home",
attributes: {
source_type: "gps",
latitude: 32.87334,
longitude: 117.22745,
gps_accuracy: 20,
battery: 53,
friendly_name: "Home Boy",
},
},
{
entity_id: "zone.home",
state: "zoning",
attributes: {
latitude: 32.87354,
longitude: 117.22765,
radius: 100,
friendly_name: "Home",
icon: "mdi:home",
},
},
{
entity_id: "zone.bushfire",
state: "zoning",
attributes: {
latitude: -33.8611,
longitude: 151.203,
radius: 35000,
friendly_name: "Bushfire Zone",
icon: "mdi:home",
},
},
{
entity_id: "geo_location.nelsons_creek",
state: "15",
attributes: {
source: "bushfire_demo",
latitude: -34.07792,
longitude: 151.03219,
friendly_name: "Nelsons Creek",
},
},
{
entity_id: "geo_location.forest_rd_nowra_hill",
state: "8",
attributes: {
source: "bushfire_demo",
latitude: -33.69452,
longitude: 151.19577,
friendly_name: "Forest Rd, Nowra Hill",
},
},
{
entity_id: "geo_location.stoney_ridge_rd_kremnos",
state: "20",
attributes: {
source: "bushfire_demo",
latitude: -33.66584,
longitude: 150.97209,
friendly_name: "Stoney Ridge Rd, Kremnos",
},
},
getEntity("device_tracker", "demo_paulus", "not_home", {
source_type: "gps",
latitude: 32.877105,
longitude: 117.232185,
gps_accuracy: 91,
battery: 71,
friendly_name: "Paulus",
}),
getEntity("device_tracker", "demo_home_boy", "home", {
source_type: "gps",
latitude: 32.87334,
longitude: 117.22745,
gps_accuracy: 20,
battery: 53,
friendly_name: "Home Boy",
}),
getEntity("zone", "home", "zoning", {
latitude: 32.87354,
longitude: 117.22765,
radius: 100,
friendly_name: "Home",
icon: "mdi:home",
}),
getEntity("zone", "bushfire", "zoning", {
latitude: -33.8611,
longitude: 151.203,
radius: 35000,
friendly_name: "Bushfire Zone",
icon: "mdi:home",
}),
getEntity("geo_location", "nelsons_creek", "15", {
source: "bushfire_demo",
latitude: -34.07792,
longitude: 151.03219,
friendly_name: "Nelsons Creek",
}),
getEntity("geo_location", "forest_rd_nowra_hill", "8", {
source: "bushfire_demo",
latitude: -33.69452,
longitude: 151.19577,
friendly_name: "Forest Rd, Nowra Hill",
}),
getEntity("geo_location", "stoney_ridge_rd_kremnos", "20", {
source: "bushfire_demo",
latitude: -33.66584,
longitude: 150.97209,
friendly_name: "Stoney Ridge Rd, Kremnos",
}),
];
const CONFIGS = [

View File

@@ -1,19 +1,16 @@
import type { PropertyValues, TemplateResult } from "lit";
import { html, LitElement } from "lit";
import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
import { mockIcons } from "../../../../demo/src/stubs/icons";
const ENTITIES = [
{
entity_id: "person.paulus",
state: "home",
attributes: {
friendly_name: "Paulus",
entity_picture: "/images/paulus.jpg",
},
},
getEntity("person", "paulus", "home", {
friendly_name: "Paulus",
entity_picture: "/images/paulus.jpg",
}),
];
const CONFIGS = [

View File

@@ -1,63 +1,40 @@
import type { PropertyValues, TemplateResult } from "lit";
import { html, LitElement } from "lit";
import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
import { mockIcons } from "../../../../demo/src/stubs/icons";
const ENTITIES = [
{
entity_id: "light.bed_light",
state: "on",
attributes: {
friendly_name: "Bed Light",
},
},
{
entity_id: "group.all_lights",
state: "on",
attributes: {
entity_id: ["light.bed_light"],
order: 8,
friendly_name: "All Lights",
},
},
{
entity_id: "camera.demo_camera",
state: "idle",
attributes: {
access_token:
"2f5bb163fb91cd8770a9494fa5e7eab172d8d34f4aba806eb6b59411b8c720b8",
friendly_name: "Demo camera",
entity_picture:
"/api/camera_proxy/camera.demo_camera?token=2f5bb163fb91cd8770a9494fa5e7eab172d8d34f4aba806eb6b59411b8c720b8",
},
},
{
entity_id: "binary_sensor.movement_backyard",
state: "on",
attributes: {
friendly_name: "Movement Backyard",
device_class: "motion",
},
},
{
entity_id: "person.paulus",
state: "home",
attributes: {
friendly_name: "Paulus",
entity_picture: "/images/paulus.jpg",
},
},
{
entity_id: "sensor.battery",
state: "35",
attributes: {
device_class: "battery",
friendly_name: "Battery",
unit_of_measurement: "%",
},
},
getEntity("light", "bed_light", "on", {
friendly_name: "Bed Light",
}),
getEntity("group", "all_lights", "on", {
entity_id: ["light.bed_light"],
order: 8,
friendly_name: "All Lights",
}),
getEntity("camera", "demo_camera", "idle", {
access_token:
"2f5bb163fb91cd8770a9494fa5e7eab172d8d34f4aba806eb6b59411b8c720b8",
friendly_name: "Demo camera",
entity_picture:
"/api/camera_proxy/camera.demo_camera?token=2f5bb163fb91cd8770a9494fa5e7eab172d8d34f4aba806eb6b59411b8c720b8",
}),
getEntity("binary_sensor", "movement_backyard", "on", {
friendly_name: "Movement Backyard",
device_class: "motion",
}),
getEntity("person", "paulus", "home", {
friendly_name: "Paulus",
entity_picture: "/images/paulus.jpg",
}),
getEntity("sensor", "battery", 35, {
device_class: "battery",
friendly_name: "Battery",
unit_of_measurement: "%",
}),
];
const CONFIGS = [

View File

@@ -1,33 +1,22 @@
import type { PropertyValues, TemplateResult } from "lit";
import { html, LitElement } from "lit";
import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
import { mockIcons } from "../../../../demo/src/stubs/icons";
const ENTITIES = [
{
entity_id: "light.kitchen_lights",
state: "on",
attributes: {
friendly_name: "Kitchen Lights",
},
},
{
entity_id: "light.bed_light",
state: "off",
attributes: {
friendly_name: "Bed Light",
},
},
{
entity_id: "person.paulus",
state: "home",
attributes: {
friendly_name: "Paulus",
entity_picture: "/images/paulus.jpg",
},
},
getEntity("light", "kitchen_lights", "on", {
friendly_name: "Kitchen Lights",
}),
getEntity("light", "bed_light", "off", {
friendly_name: "Bed Light",
}),
getEntity("person", "paulus", "home", {
friendly_name: "Paulus",
entity_picture: "/images/paulus.jpg",
}),
];
const CONFIGS = [

View File

@@ -1,58 +1,35 @@
import type { PropertyValues, TemplateResult } from "lit";
import { html, LitElement } from "lit";
import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
import { mockIcons } from "../../../../demo/src/stubs/icons";
const ENTITIES = [
{
entity_id: "switch.decorative_lights",
state: "on",
attributes: {
friendly_name: "Decorative Lights",
},
},
{
entity_id: "light.ceiling_lights",
state: "on",
attributes: {
friendly_name: "Ceiling Lights",
},
},
{
entity_id: "binary_sensor.movement_backyard",
state: "on",
attributes: {
friendly_name: "Movement Backyard",
device_class: "moving",
},
},
{
entity_id: "binary_sensor.basement_floor_wet",
state: "off",
attributes: {
friendly_name: "Basement Floor Wet",
device_class: "moisture",
},
},
{
entity_id: "person.paulus",
state: "home",
attributes: {
friendly_name: "Paulus",
entity_picture: "/images/paulus.jpg",
},
},
{
entity_id: "sensor.battery",
state: "35",
attributes: {
device_class: "battery",
friendly_name: "Battery",
unit_of_measurement: "%",
},
},
getEntity("switch", "decorative_lights", "on", {
friendly_name: "Decorative Lights",
}),
getEntity("light", "ceiling_lights", "on", {
friendly_name: "Ceiling Lights",
}),
getEntity("binary_sensor", "movement_backyard", "on", {
friendly_name: "Movement Backyard",
device_class: "moving",
}),
getEntity("binary_sensor", "basement_floor_wet", "off", {
friendly_name: "Basement Floor Wet",
device_class: "moisture",
}),
getEntity("person", "paulus", "home", {
friendly_name: "Paulus",
entity_picture: "/images/paulus.jpg",
}),
getEntity("sensor", "battery", 35, {
device_class: "battery",
friendly_name: "Battery",
unit_of_measurement: "%",
}),
];
const CONFIGS = [

View File

@@ -1,123 +1,100 @@
import type { PropertyValues, TemplateResult } from "lit";
import { html, LitElement } from "lit";
import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
import { mockIcons } from "../../../../demo/src/stubs/icons";
const ENTITIES = [
{
entity_id: "climate.ecobee",
state: "auto",
attributes: {
current_temperature: 73,
min_temp: 45,
max_temp: 95,
temperature: null,
target_temp_high: 75,
target_temp_low: 70,
fan_mode: "Auto Low",
fan_modes: ["On Low", "On High", "Auto Low", "Auto High", "Off"],
hvac_modes: ["heat", "cool", "auto", "off"],
swing_mode: "Auto",
swing_modes: ["Auto", "1", "2", "3", "Off"],
friendly_name: "Ecobee",
supported_features: 59,
preset_mode: "eco",
preset_modes: ["away", "eco"],
},
},
{
entity_id: "climate.nest",
state: "heat",
attributes: {
current_temperature: 17,
min_temp: 15,
max_temp: 25,
temperature: 19,
fan_mode: "Auto Low",
fan_modes: ["On Low", "On High", "Auto Low", "Auto High", "Off"],
hvac_modes: ["heat", "cool", "auto", "off"],
swing_mode: "Auto",
swing_modes: ["Auto", "1", "2", "3", "Off"],
friendly_name: "Nest",
supported_features: 43,
},
},
{
entity_id: "climate.overkiz_radiator",
state: "heat",
attributes: {
current_temperature: 18,
min_temp: 7,
max_temp: 35,
temperature: 20,
hvac_modes: ["heat", "auto", "off"],
friendly_name: "Overkiz radiator",
supported_features: 17,
preset_mode: "comfort",
preset_modes: [
"none",
"frost_protection",
"eco",
"comfort",
"comfort-1",
"comfort-2",
"auto",
"boost",
"external",
"prog",
],
},
},
{
entity_id: "climate.overkiz_towel_dryer",
state: "heat",
attributes: {
current_temperature: null,
min_temp: 7,
max_temp: 35,
hvac_modes: ["heat", "off"],
friendly_name: "Overkiz towel dryer",
supported_features: 16,
preset_mode: "eco",
preset_modes: [
"none",
"frost_protection",
"eco",
"comfort",
"comfort-1",
"comfort-2",
],
},
},
{
entity_id: "climate.sensibo",
state: "fan_only",
attributes: {
current_temperature: null,
temperature: null,
min_temp: 0,
max_temp: 1,
target_temp_step: 1,
hvac_modes: ["fan_only", "off"],
friendly_name: "Sensibo purifier",
fan_modes: ["low", "high"],
fan_mode: "low",
swing_modes: ["both", "rangefull", "off"],
swing_mode: "rangefull",
swing_horizontal_modes: ["both", "rangefull", "off"],
swing_horizontal_mode: "both",
supported_features: 553,
},
},
{
entity_id: "climate.unavailable",
state: "unavailable",
attributes: {
supported_features: 43,
},
},
getEntity("climate", "ecobee", "auto", {
current_temperature: 73,
min_temp: 45,
max_temp: 95,
temperature: null,
target_temp_high: 75,
target_temp_low: 70,
fan_mode: "Auto Low",
fan_modes: ["On Low", "On High", "Auto Low", "Auto High", "Off"],
hvac_modes: ["heat", "cool", "auto", "off"],
swing_mode: "Auto",
swing_modes: ["Auto", "1", "2", "3", "Off"],
friendly_name: "Ecobee",
supported_features: 59,
preset_mode: "eco",
preset_modes: ["away", "eco"],
}),
getEntity("climate", "nest", "heat", {
current_temperature: 17,
min_temp: 15,
max_temp: 25,
temperature: 19,
fan_mode: "Auto Low",
fan_modes: ["On Low", "On High", "Auto Low", "Auto High", "Off"],
hvac_modes: ["heat", "cool", "auto", "off"],
swing_mode: "Auto",
swing_modes: ["Auto", "1", "2", "3", "Off"],
friendly_name: "Nest",
supported_features: 43,
}),
getEntity("climate", "overkiz_radiator", "heat", {
current_temperature: 18,
min_temp: 7,
max_temp: 35,
temperature: 20,
hvac_modes: ["heat", "auto", "off"],
friendly_name: "Overkiz radiator",
supported_features: 17,
preset_mode: "comfort",
preset_modes: [
"none",
"frost_protection",
"eco",
"comfort",
"comfort-1",
"comfort-2",
"auto",
"boost",
"external",
"prog",
],
}),
getEntity("climate", "overkiz_towel_dryer", "heat", {
current_temperature: null,
min_temp: 7,
max_temp: 35,
hvac_modes: ["heat", "off"],
friendly_name: "Overkiz towel dryer",
supported_features: 16,
preset_mode: "eco",
preset_modes: [
"none",
"frost_protection",
"eco",
"comfort",
"comfort-1",
"comfort-2",
],
}),
getEntity("climate", "sensibo", "fan_only", {
current_temperature: null,
temperature: null,
min_temp: 0,
max_temp: 1,
target_temp_step: 1,
hvac_modes: ["fan_only", "off"],
friendly_name: "Sensibo purifier",
fan_modes: ["low", "high"],
fan_mode: "low",
swing_modes: ["both", "rangefull", "off"],
swing_mode: "rangefull",
swing_horizontal_modes: ["both", "rangefull", "off"],
swing_horizontal_mode: "both",
supported_features: 553,
}),
getEntity("climate", "unavailable", "unavailable", {
supported_features: 43,
}),
];
const CONFIGS = [

View File

@@ -6,6 +6,7 @@ import { LightColorMode } from "../../../../src/data/light";
import { LockEntityFeature } from "../../../../src/data/lock";
import { MediaPlayerEntityFeature } from "../../../../src/data/media-player";
import { VacuumEntityFeature } from "../../../../src/data/vacuum";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
import { mockIcons } from "../../../../demo/src/stubs/icons";
@@ -13,154 +14,102 @@ import { ClimateEntityFeature } from "../../../../src/data/climate";
import { FanEntityFeature } from "../../../../src/data/fan";
const ENTITIES = [
{
entity_id: "switch.tv_outlet",
state: "on",
attributes: {
friendly_name: "TV outlet",
device_class: "outlet",
},
},
{
entity_id: "light.bed_light",
state: "on",
attributes: {
friendly_name: "Bed Light",
supported_color_modes: [LightColorMode.HS, LightColorMode.COLOR_TEMP],
},
},
{
entity_id: "light.unavailable",
state: "unavailable",
attributes: {
friendly_name: "Unavailable entity",
},
},
{
entity_id: "lock.front_door",
state: "locked",
attributes: {
friendly_name: "Front Door Lock",
device_class: "lock",
supported_features: LockEntityFeature.OPEN,
},
},
{
entity_id: "media_player.living_room",
state: "playing",
attributes: {
friendly_name: "Living room speaker",
supported_features: MediaPlayerEntityFeature.VOLUME_SET,
},
},
{
entity_id: "climate.thermostat",
state: "heat",
attributes: {
current_temperature: 73,
min_temp: 45,
max_temp: 95,
temperature: 80,
hvac_modes: ["heat", "cool", "auto", "off"],
friendly_name: "Thermostat",
hvac_action: "heating",
},
},
{
entity_id: "person.paulus",
state: "home",
attributes: {
friendly_name: "Paulus",
},
},
{
entity_id: "vacuum.first_floor_vacuum",
state: "docked",
attributes: {
friendly_name: "First floor vacuum",
supported_features:
VacuumEntityFeature.START +
VacuumEntityFeature.STOP +
VacuumEntityFeature.RETURN_HOME,
},
},
{
entity_id: "cover.kitchen_shutter",
state: "open",
attributes: {
friendly_name: "Kitchen shutter",
device_class: "shutter",
supported_features:
CoverEntityFeature.CLOSE +
CoverEntityFeature.OPEN +
CoverEntityFeature.STOP,
},
},
{
entity_id: "cover.pergola_roof",
state: "open",
attributes: {
friendly_name: "Pergola Roof",
supported_features:
CoverEntityFeature.CLOSE_TILT +
CoverEntityFeature.OPEN_TILT +
CoverEntityFeature.STOP_TILT,
},
},
{
entity_id: "input_number.counter",
state: "1.0",
attributes: {
friendly_name: "Counter",
initial: 0,
min: 0,
max: 100,
step: 1,
mode: "slider",
},
},
{
entity_id: "climate.dual_thermostat",
state: "heat/cool",
attributes: {
friendly_name: "Dual thermostat",
hvac_modes: ["off", "cool", "heat_cool", "auto", "dry", "fan_only"],
min_temp: 7,
max_temp: 35,
fan_modes: ["on_low", "on_high", "auto_low", "auto_high", "off"],
preset_modes: ["home", "eco", "away"],
swing_modes: ["auto", "1", "2", "3", "off"],
switch_horizontal_modes: ["auto", "4", "5", "6", "off"],
current_temperature: 23,
target_temp_high: 24,
target_temp_low: 21,
fan_mode: "auto_low",
preset_mode: "home",
swing_mode: "auto",
swing_horizontal_mode: "off",
supported_features:
ClimateEntityFeature.TURN_ON +
ClimateEntityFeature.TURN_OFF +
ClimateEntityFeature.SWING_MODE +
ClimateEntityFeature.SWING_HORIZONTAL_MODE +
ClimateEntityFeature.PRESET_MODE +
ClimateEntityFeature.FAN_MODE +
ClimateEntityFeature.TARGET_TEMPERATURE_RANGE,
},
},
{
entity_id: "fan.fan_demo",
state: "on",
attributes: {
friendly_name: "Ceiling fan",
device_class: "fan",
direction: "reverse",
supported_features:
FanEntityFeature.DIRECTION +
FanEntityFeature.SET_SPEED +
FanEntityFeature.OSCILLATE,
},
},
getEntity("switch", "tv_outlet", "on", {
friendly_name: "TV outlet",
device_class: "outlet",
}),
getEntity("light", "bed_light", "on", {
friendly_name: "Bed Light",
supported_color_modes: [LightColorMode.HS, LightColorMode.COLOR_TEMP],
}),
getEntity("light", "unavailable", "unavailable", {
friendly_name: "Unavailable entity",
}),
getEntity("lock", "front_door", "locked", {
friendly_name: "Front Door Lock",
device_class: "lock",
supported_features: LockEntityFeature.OPEN,
}),
getEntity("media_player", "living_room", "playing", {
friendly_name: "Living room speaker",
supported_features: MediaPlayerEntityFeature.VOLUME_SET,
}),
getEntity("climate", "thermostat", "heat", {
current_temperature: 73,
min_temp: 45,
max_temp: 95,
temperature: 80,
hvac_modes: ["heat", "cool", "auto", "off"],
friendly_name: "Thermostat",
hvac_action: "heating",
}),
getEntity("person", "paulus", "home", {
friendly_name: "Paulus",
}),
getEntity("vacuum", "first_floor_vacuum", "docked", {
friendly_name: "First floor vacuum",
supported_features:
VacuumEntityFeature.START +
VacuumEntityFeature.STOP +
VacuumEntityFeature.RETURN_HOME,
}),
getEntity("cover", "kitchen_shutter", "open", {
friendly_name: "Kitchen shutter",
device_class: "shutter",
supported_features:
CoverEntityFeature.CLOSE +
CoverEntityFeature.OPEN +
CoverEntityFeature.STOP,
}),
getEntity("cover", "pergola_roof", "open", {
friendly_name: "Pergola Roof",
supported_features:
CoverEntityFeature.CLOSE_TILT +
CoverEntityFeature.OPEN_TILT +
CoverEntityFeature.STOP_TILT,
}),
getEntity("input_number", "counter", "1.0", {
friendly_name: "Counter",
initial: 0,
min: 0,
max: 100,
step: 1,
mode: "slider",
}),
getEntity("climate", "dual_thermostat", "heat/cool", {
friendly_name: "Dual thermostat",
hvac_modes: ["off", "cool", "heat_cool", "auto", "dry", "fan_only"],
min_temp: 7,
max_temp: 35,
fan_modes: ["on_low", "on_high", "auto_low", "auto_high", "off"],
preset_modes: ["home", "eco", "away"],
swing_modes: ["auto", "1", "2", "3", "off"],
switch_horizontal_modes: ["auto", "4", "5", "6", "off"],
current_temperature: 23,
target_temp_high: 24,
target_temp_low: 21,
fan_mode: "auto_low",
preset_mode: "home",
swing_mode: "auto",
swing_horizontal_mode: "off",
supported_features:
ClimateEntityFeature.TURN_ON +
ClimateEntityFeature.TURN_OFF +
ClimateEntityFeature.SWING_MODE +
ClimateEntityFeature.SWING_HORIZONTAL_MODE +
ClimateEntityFeature.PRESET_MODE +
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 = [

View File

@@ -3,25 +3,18 @@ import { html, LitElement } from "lit";
import { customElement, query } from "lit/decorators";
import { mockIcons } from "../../../../demo/src/stubs/icons";
import { mockTodo } from "../../../../demo/src/stubs/todo";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
const ENTITIES = [
{
entity_id: "todo.shopping_list",
state: "2",
attributes: {
friendly_name: "Shopping List",
supported_features: 15,
},
},
{
entity_id: "todo.read_only",
state: "2",
attributes: {
friendly_name: "Read only",
},
},
getEntity("todo", "shopping_list", "2", {
friendly_name: "Shopping List",
supported_features: 15,
}),
getEntity("todo", "read_only", "2", {
friendly_name: "Read only",
}),
];
const CONFIGS = [

View File

@@ -422,6 +422,7 @@ export class DemoEntityState extends LitElement {
return html`
<ha-data-table
.hass=${this.hass}
.columns=${this._columns(this.hass)}
.data=${this._rows()}
auto-height

View File

@@ -3,135 +3,108 @@ 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 { ClimateEntityFeature } from "../../../../src/data/climate";
const ENTITIES = [
{
entity_id: "climate.radiator",
state: "heat",
attributes: {
friendly_name: "Basic heater",
hvac_modes: ["heat", "off"],
hvac_mode: "heat",
current_temperature: 18,
temperature: 20,
min_temp: 10,
max_temp: 30,
supported_features: ClimateEntityFeature.TARGET_TEMPERATURE,
},
},
{
entity_id: "climate.ac",
state: "cool",
attributes: {
friendly_name: "Basic air conditioning",
hvac_modes: ["cool", "off"],
hvac_mode: "cool",
current_temperature: 18,
temperature: 20,
min_temp: 10,
max_temp: 30,
supported_features: ClimateEntityFeature.TARGET_TEMPERATURE,
},
},
{
entity_id: "climate.fan",
state: "fan_only",
attributes: {
friendly_name: "Basic fan",
hvac_modes: ["fan_only", "off"],
hvac_mode: "fan_only",
fan_modes: ["low", "high"],
fan_mode: "low",
current_temperature: null,
temperature: null,
min_temp: 0,
max_temp: 1,
target_temp_step: 1,
supported_features:
// eslint-disable-next-line no-bitwise
ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.FAN_MODE,
},
},
{
entity_id: "climate.hvac",
state: "auto",
attributes: {
friendly_name: "Basic hvac",
hvac_modes: ["auto", "off"],
hvac_mode: "auto",
current_temperature: 18,
min_temp: 10,
max_temp: 30,
target_temp_step: 1,
supported_features: ClimateEntityFeature.TARGET_TEMPERATURE_RANGE,
target_temp_low: 20,
target_temp_high: 25,
},
},
{
entity_id: "climate.advanced",
state: "auto",
attributes: {
friendly_name: "Advanced hvac",
supported_features:
// eslint-disable-next-line no-bitwise
ClimateEntityFeature.TARGET_TEMPERATURE_RANGE |
ClimateEntityFeature.TARGET_HUMIDITY |
ClimateEntityFeature.PRESET_MODE,
hvac_modes: ["auto", "off"],
hvac_mode: "auto",
preset_modes: ["eco", "comfort", "boost"],
preset_mode: "eco",
current_temperature: 18,
min_temp: 10,
max_temp: 30,
target_temp_step: 1,
target_temp_low: 20,
target_temp_high: 25,
current_humidity: 40,
min_humidity: 0,
max_humidity: 100,
humidity: 50,
},
},
{
entity_id: "climate.towel_dryer",
state: "heat",
attributes: {
friendly_name: "Preset only heater",
hvac_modes: ["heat", "off"],
hvac_mode: "heat",
preset_modes: [
"none",
"frost_protection",
"eco",
"comfort",
"comfort-1",
"comfort-2",
],
preset_mode: "eco",
current_temperature: null,
min_temp: 7,
max_temp: 35,
supported_features: ClimateEntityFeature.PRESET_MODE,
},
},
{
entity_id: "climate.unavailable",
state: "unavailable",
attributes: {
friendly_name: "Unavailable heater",
hvac_modes: ["heat", "off"],
hvac_mode: "heat",
min_temp: 10,
max_temp: 30,
supported_features: ClimateEntityFeature.TARGET_TEMPERATURE,
},
},
getEntity("climate", "radiator", "heat", {
friendly_name: "Basic heater",
hvac_modes: ["heat", "off"],
hvac_mode: "heat",
current_temperature: 18,
temperature: 20,
min_temp: 10,
max_temp: 30,
supported_features: ClimateEntityFeature.TARGET_TEMPERATURE,
}),
getEntity("climate", "ac", "cool", {
friendly_name: "Basic air conditioning",
hvac_modes: ["cool", "off"],
hvac_mode: "cool",
current_temperature: 18,
temperature: 20,
min_temp: 10,
max_temp: 30,
supported_features: ClimateEntityFeature.TARGET_TEMPERATURE,
}),
getEntity("climate", "fan", "fan_only", {
friendly_name: "Basic fan",
hvac_modes: ["fan_only", "off"],
hvac_mode: "fan_only",
fan_modes: ["low", "high"],
fan_mode: "low",
current_temperature: null,
temperature: null,
min_temp: 0,
max_temp: 1,
target_temp_step: 1,
supported_features:
// eslint-disable-next-line no-bitwise
ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.FAN_MODE,
}),
getEntity("climate", "hvac", "auto", {
friendly_name: "Basic hvac",
hvac_modes: ["auto", "off"],
hvac_mode: "auto",
current_temperature: 18,
min_temp: 10,
max_temp: 30,
target_temp_step: 1,
supported_features: ClimateEntityFeature.TARGET_TEMPERATURE_RANGE,
target_temp_low: 20,
target_temp_high: 25,
}),
getEntity("climate", "advanced", "auto", {
friendly_name: "Advanced hvac",
supported_features:
// eslint-disable-next-line no-bitwise
ClimateEntityFeature.TARGET_TEMPERATURE_RANGE |
ClimateEntityFeature.TARGET_HUMIDITY |
ClimateEntityFeature.PRESET_MODE,
hvac_modes: ["auto", "off"],
hvac_mode: "auto",
preset_modes: ["eco", "comfort", "boost"],
preset_mode: "eco",
current_temperature: 18,
min_temp: 10,
max_temp: 30,
target_temp_step: 1,
target_temp_low: 20,
target_temp_high: 25,
current_humidity: 40,
min_humidity: 0,
max_humidity: 100,
humidity: 50,
}),
getEntity("climate", "towel_dryer", "heat", {
friendly_name: "Preset only heater",
hvac_modes: ["heat", "off"],
hvac_mode: "heat",
preset_modes: [
"none",
"frost_protection",
"eco",
"comfort",
"comfort-1",
"comfort-2",
],
preset_mode: "eco",
current_temperature: null,
min_temp: 7,
max_temp: 35,
supported_features: ClimateEntityFeature.PRESET_MODE,
}),
getEntity("climate", "unavailable", "unavailable", {
friendly_name: "Unavailable heater",
hvac_modes: ["heat", "off"],
hvac_mode: "heat",
min_temp: 10,
max_temp: 30,
supported_features: ClimateEntityFeature.TARGET_TEMPERATURE,
}),
];
@customElement("demo-more-info-climate")
@@ -144,7 +117,7 @@ class DemoMoreInfoClimate extends LitElement {
return html`
<demo-more-infos
.hass=${this.hass}
.entities=${ENTITIES.map((ent) => ent.entity_id)}
.entities=${ENTITIES.map((ent) => ent.entityId)}
></demo-more-infos>
`;
}

View File

@@ -4,189 +4,138 @@ import { customElement, property, query } from "lit/decorators";
import "../../../../src/components/ha-card";
import { CoverEntityFeature } from "../../../../src/data/cover";
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";
const ENTITIES = [
{
entity_id: "cover.position_buttons",
state: "on",
attributes: {
friendly_name: "Position Buttons",
supported_features:
CoverEntityFeature.OPEN +
CoverEntityFeature.STOP +
CoverEntityFeature.CLOSE,
},
},
{
entity_id: "cover.position_slider_half",
state: "on",
attributes: {
friendly_name: "Position Half-Open",
supported_features:
CoverEntityFeature.OPEN +
CoverEntityFeature.STOP +
CoverEntityFeature.CLOSE +
CoverEntityFeature.SET_POSITION,
current_position: 50,
},
},
{
entity_id: "cover.position_slider_open",
state: "on",
attributes: {
friendly_name: "Position Open",
supported_features:
CoverEntityFeature.OPEN +
CoverEntityFeature.STOP +
CoverEntityFeature.CLOSE +
CoverEntityFeature.SET_POSITION,
current_position: 100,
},
},
{
entity_id: "cover.position_slider_closed",
state: "on",
attributes: {
friendly_name: "Position Closed",
supported_features:
CoverEntityFeature.OPEN +
CoverEntityFeature.STOP +
CoverEntityFeature.CLOSE +
CoverEntityFeature.SET_POSITION,
current_position: 0,
},
},
{
entity_id: "cover.tilt_buttons",
state: "on",
attributes: {
friendly_name: "Tilt Buttons",
supported_features:
CoverEntityFeature.OPEN_TILT +
CoverEntityFeature.STOP_TILT +
CoverEntityFeature.CLOSE_TILT,
},
},
{
entity_id: "cover.tilt_slider_half",
state: "on",
attributes: {
friendly_name: "Tilt Half-Open",
supported_features:
CoverEntityFeature.OPEN_TILT +
CoverEntityFeature.STOP_TILT +
CoverEntityFeature.CLOSE_TILT +
CoverEntityFeature.SET_TILT_POSITION,
current_tilt_position: 50,
},
},
{
entity_id: "cover.tilt_slider_open",
state: "on",
attributes: {
friendly_name: "Tilt Open",
supported_features:
CoverEntityFeature.OPEN_TILT +
CoverEntityFeature.STOP_TILT +
CoverEntityFeature.CLOSE_TILT +
CoverEntityFeature.SET_TILT_POSITION,
current_tilt_position: 100,
},
},
{
entity_id: "cover.tilt_slider_closed",
state: "on",
attributes: {
friendly_name: "Tilt Closed",
supported_features:
CoverEntityFeature.OPEN_TILT +
CoverEntityFeature.STOP_TILT +
CoverEntityFeature.CLOSE_TILT +
CoverEntityFeature.SET_TILT_POSITION,
current_tilt_position: 0,
},
},
{
entity_id: "cover.position_slider_tilt_slider",
state: "on",
attributes: {
friendly_name: "Both Sliders",
supported_features:
CoverEntityFeature.OPEN +
CoverEntityFeature.STOP +
CoverEntityFeature.CLOSE +
CoverEntityFeature.SET_POSITION +
CoverEntityFeature.OPEN_TILT +
CoverEntityFeature.STOP_TILT +
CoverEntityFeature.CLOSE_TILT +
CoverEntityFeature.SET_TILT_POSITION,
current_position: 30,
current_tilt_position: 70,
},
},
{
entity_id: "cover.position_tilt_slider",
state: "on",
attributes: {
friendly_name: "Position & Tilt Slider",
supported_features:
CoverEntityFeature.OPEN +
CoverEntityFeature.STOP +
CoverEntityFeature.CLOSE +
CoverEntityFeature.OPEN_TILT +
CoverEntityFeature.STOP_TILT +
CoverEntityFeature.CLOSE_TILT +
CoverEntityFeature.SET_TILT_POSITION,
current_tilt_position: 70,
},
},
{
entity_id: "cover.position_slider_tilt",
state: "on",
attributes: {
friendly_name: "Position Slider & Tilt",
supported_features:
CoverEntityFeature.OPEN +
CoverEntityFeature.STOP +
CoverEntityFeature.CLOSE +
CoverEntityFeature.SET_POSITION +
CoverEntityFeature.OPEN_TILT +
CoverEntityFeature.STOP_TILT +
CoverEntityFeature.CLOSE_TILT,
current_position: 30,
},
},
{
entity_id: "cover.position_slider_only_tilt_slider",
state: "on",
attributes: {
friendly_name: "Position Slider Only & Tilt Buttons",
supported_features:
CoverEntityFeature.SET_POSITION +
CoverEntityFeature.OPEN_TILT +
CoverEntityFeature.STOP_TILT +
CoverEntityFeature.CLOSE_TILT,
current_position: 30,
},
},
{
entity_id: "cover.position_slider_only_tilt",
state: "on",
attributes: {
friendly_name: "Position Slider Only & Tilt",
supported_features:
CoverEntityFeature.SET_POSITION +
CoverEntityFeature.OPEN_TILT +
CoverEntityFeature.STOP_TILT +
CoverEntityFeature.CLOSE_TILT +
CoverEntityFeature.SET_TILT_POSITION,
current_position: 30,
current_tilt_position: 70,
},
},
getEntity("cover", "position_buttons", "on", {
friendly_name: "Position Buttons",
supported_features:
CoverEntityFeature.OPEN +
CoverEntityFeature.STOP +
CoverEntityFeature.CLOSE,
}),
getEntity("cover", "position_slider_half", "on", {
friendly_name: "Position Half-Open",
supported_features:
CoverEntityFeature.OPEN +
CoverEntityFeature.STOP +
CoverEntityFeature.CLOSE +
CoverEntityFeature.SET_POSITION,
current_position: 50,
}),
getEntity("cover", "position_slider_open", "on", {
friendly_name: "Position Open",
supported_features:
CoverEntityFeature.OPEN +
CoverEntityFeature.STOP +
CoverEntityFeature.CLOSE +
CoverEntityFeature.SET_POSITION,
current_position: 100,
}),
getEntity("cover", "position_slider_closed", "on", {
friendly_name: "Position Closed",
supported_features:
CoverEntityFeature.OPEN +
CoverEntityFeature.STOP +
CoverEntityFeature.CLOSE +
CoverEntityFeature.SET_POSITION,
current_position: 0,
}),
getEntity("cover", "tilt_buttons", "on", {
friendly_name: "Tilt Buttons",
supported_features:
CoverEntityFeature.OPEN_TILT +
CoverEntityFeature.STOP_TILT +
CoverEntityFeature.CLOSE_TILT,
}),
getEntity("cover", "tilt_slider_half", "on", {
friendly_name: "Tilt Half-Open",
supported_features:
CoverEntityFeature.OPEN_TILT +
CoverEntityFeature.STOP_TILT +
CoverEntityFeature.CLOSE_TILT +
CoverEntityFeature.SET_TILT_POSITION,
current_tilt_position: 50,
}),
getEntity("cover", "tilt_slider_open", "on", {
friendly_name: "Tilt Open",
supported_features:
CoverEntityFeature.OPEN_TILT +
CoverEntityFeature.STOP_TILT +
CoverEntityFeature.CLOSE_TILT +
CoverEntityFeature.SET_TILT_POSITION,
current_tilt_position: 100,
}),
getEntity("cover", "tilt_slider_closed", "on", {
friendly_name: "Tilt Closed",
supported_features:
CoverEntityFeature.OPEN_TILT +
CoverEntityFeature.STOP_TILT +
CoverEntityFeature.CLOSE_TILT +
CoverEntityFeature.SET_TILT_POSITION,
current_tilt_position: 0,
}),
getEntity("cover", "position_slider_tilt_slider", "on", {
friendly_name: "Both Sliders",
supported_features:
CoverEntityFeature.OPEN +
CoverEntityFeature.STOP +
CoverEntityFeature.CLOSE +
CoverEntityFeature.SET_POSITION +
CoverEntityFeature.OPEN_TILT +
CoverEntityFeature.STOP_TILT +
CoverEntityFeature.CLOSE_TILT +
CoverEntityFeature.SET_TILT_POSITION,
current_position: 30,
current_tilt_position: 70,
}),
getEntity("cover", "position_tilt_slider", "on", {
friendly_name: "Position & Tilt Slider",
supported_features:
CoverEntityFeature.OPEN +
CoverEntityFeature.STOP +
CoverEntityFeature.CLOSE +
CoverEntityFeature.OPEN_TILT +
CoverEntityFeature.STOP_TILT +
CoverEntityFeature.CLOSE_TILT +
CoverEntityFeature.SET_TILT_POSITION,
current_tilt_position: 70,
}),
getEntity("cover", "position_slider_tilt", "on", {
friendly_name: "Position Slider & Tilt",
supported_features:
CoverEntityFeature.OPEN +
CoverEntityFeature.STOP +
CoverEntityFeature.CLOSE +
CoverEntityFeature.SET_POSITION +
CoverEntityFeature.OPEN_TILT +
CoverEntityFeature.STOP_TILT +
CoverEntityFeature.CLOSE_TILT,
current_position: 30,
}),
getEntity("cover", "position_slider_only_tilt_slider", "on", {
friendly_name: "Position Slider Only & Tilt Buttons",
supported_features:
CoverEntityFeature.SET_POSITION +
CoverEntityFeature.OPEN_TILT +
CoverEntityFeature.STOP_TILT +
CoverEntityFeature.CLOSE_TILT,
current_position: 30,
}),
getEntity("cover", "position_slider_only_tilt", "on", {
friendly_name: "Position Slider Only & Tilt",
supported_features:
CoverEntityFeature.SET_POSITION +
CoverEntityFeature.OPEN_TILT +
CoverEntityFeature.STOP_TILT +
CoverEntityFeature.CLOSE_TILT +
CoverEntityFeature.SET_TILT_POSITION,
current_position: 30,
current_tilt_position: 70,
}),
];
@customElement("demo-more-info-cover")
@@ -199,7 +148,7 @@ class DemoMoreInfoCover extends LitElement {
return html`
<demo-more-infos
.hass=${this.hass}
.entities=${ENTITIES.map((ent) => ent.entity_id)}
.entities=${ENTITIES.map((ent) => ent.entityId)}
></demo-more-infos>
`;
}

View File

@@ -3,24 +3,21 @@ 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 = [
{
entity_id: "fan.fan",
state: "on",
attributes: {
friendly_name: "Fan",
device_class: "fan",
supported_features:
FanEntityFeature.OSCILLATE +
FanEntityFeature.DIRECTION +
FanEntityFeature.SET_SPEED,
},
},
getEntity("fan", "fan", "on", {
friendly_name: "Fan",
device_class: "fan",
supported_features:
FanEntityFeature.OSCILLATE +
FanEntityFeature.DIRECTION +
FanEntityFeature.SET_SPEED,
}),
];
@customElement("demo-more-info-fan")
@@ -33,7 +30,7 @@ class DemoMoreInfoFan extends LitElement {
return html`
<demo-more-infos
.hass=${this.hass}
.entities=${ENTITIES.map((ent) => ent.entity_id)}
.entities=${ENTITIES.map((ent) => ent.entityId)}
></demo-more-infos>
`;
}

View File

@@ -3,38 +3,27 @@ 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";
const ENTITIES = [
{
entity_id: "humidifier.humidifier",
state: "on",
attributes: {
friendly_name: "Humidifier",
device_class: "humidifier",
current_humidity: 50,
humidity: 30,
},
},
{
entity_id: "humidifier.dehumidifier",
state: "on",
attributes: {
friendly_name: "Dehumidifier",
device_class: "dehumidifier",
current_humidity: 50,
humidity: 30,
},
},
{
entity_id: "humidifier.unavailable",
state: "unavailable",
attributes: {
friendly_name: "Unavailable humidifier",
},
},
getEntity("humidifier", "humidifier", "on", {
friendly_name: "Humidifier",
device_class: "humidifier",
current_humidity: 50,
humidity: 30,
}),
getEntity("humidifier", "dehumidifier", "on", {
friendly_name: "Dehumidifier",
device_class: "dehumidifier",
current_humidity: 50,
humidity: 30,
}),
getEntity("humidifier", "unavailable", "unavailable", {
friendly_name: "Unavailable humidifier",
}),
];
@customElement("demo-more-info-humidifier")
@@ -47,7 +36,7 @@ class DemoMoreInfoHumidifier extends LitElement {
return html`
<demo-more-infos
.hass=${this.hass}
.entities=${ENTITIES.map((ent) => ent.entity_id)}
.entities=${ENTITIES.map((ent) => ent.entityId)}
></demo-more-infos>
`;
}

View File

@@ -3,37 +3,30 @@ 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";
const ENTITIES = [
{
entity_id: "input_number.box1",
state: "0",
attributes: {
friendly_name: "Box1",
min: 0,
max: 100,
step: 1,
initial: 0,
mode: "box",
unit_of_measurement: "items",
},
},
{
entity_id: "input_number.slider1",
state: "0",
attributes: {
friendly_name: "Slider1",
min: 0,
max: 100,
step: 1,
initial: 0,
mode: "slider",
unit_of_measurement: "items",
},
},
getEntity("input_number", "box1", 0, {
friendly_name: "Box1",
min: 0,
max: 100,
step: 1,
initial: 0,
mode: "box",
unit_of_measurement: "items",
}),
getEntity("input_number", "slider1", 0, {
friendly_name: "Slider1",
min: 0,
max: 100,
step: 1,
initial: 0,
mode: "slider",
unit_of_measurement: "items",
}),
];
@customElement("demo-more-info-input-number")
@@ -46,7 +39,7 @@ class DemoMoreInfoInputNumber extends LitElement {
return html`
<demo-more-infos
.hass=${this.hass}
.entities=${ENTITIES.map((ent) => ent.entity_id)}
.entities=${ENTITIES.map((ent) => ent.entityId)}
></demo-more-infos>
`;
}

View File

@@ -3,19 +3,16 @@ 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";
const ENTITIES = [
{
entity_id: "input_text.text",
state: "Inspiration",
attributes: {
friendly_name: "Text",
mode: "text",
},
},
getEntity("input_text", "text", "Inspiration", {
friendly_name: "Text",
mode: "text",
}),
];
@customElement("demo-more-info-input-text")
@@ -28,7 +25,7 @@ class DemoMoreInfoInputText extends LitElement {
return html`
<demo-more-infos
.hass=${this.hass}
.entities=${ENTITIES.map((ent) => ent.entity_id)}
.entities=${ENTITIES.map((ent) => ent.entityId)}
></demo-more-infos>
`;
}

View File

@@ -4,172 +4,137 @@ import { customElement, property, query } from "lit/decorators";
import "../../../../src/components/ha-card";
import { LightColorMode, LightEntityFeature } from "../../../../src/data/light";
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";
const ENTITIES = [
{
entity_id: "light.bed_light",
state: "on",
attributes: {
friendly_name: "Basic Light",
},
},
{
entity_id: "light.kitchen_light",
state: "on",
attributes: {
friendly_name: "Brightness Light",
brightness: 200,
supported_color_modes: [LightColorMode.BRIGHTNESS],
color_mode: LightColorMode.BRIGHTNESS,
},
},
{
entity_id: "light.color_temperature_light",
state: "on",
attributes: {
friendly_name: "White Color Temperature Light",
brightness: 128,
color_temp: 75,
min_mireds: 30,
max_mireds: 150,
supported_color_modes: [
LightColorMode.BRIGHTNESS,
LightColorMode.COLOR_TEMP,
],
color_mode: LightColorMode.COLOR_TEMP,
},
},
{
entity_id: "light.color_hs_light",
state: "on",
attributes: {
friendly_name: "Color HS Light",
brightness: 255,
hs_color: [30, 100],
rgb_color: [30, 100, 255],
min_mireds: 30,
max_mireds: 150,
supported_features:
LightEntityFeature.EFFECT +
LightEntityFeature.FLASH +
LightEntityFeature.TRANSITION,
supported_color_modes: [
LightColorMode.BRIGHTNESS,
LightColorMode.COLOR_TEMP,
LightColorMode.HS,
],
color_mode: LightColorMode.HS,
effect_list: ["random", "colorloop"],
},
},
{
entity_id: "light.color_rgb_ct_light",
state: "on",
attributes: {
friendly_name: "Color RGB + CT Light",
brightness: 255,
color_temp: 75,
min_mireds: 30,
max_mireds: 150,
supported_features:
LightEntityFeature.EFFECT +
LightEntityFeature.FLASH +
LightEntityFeature.TRANSITION,
supported_color_modes: [
LightColorMode.BRIGHTNESS,
LightColorMode.COLOR_TEMP,
LightColorMode.RGB,
],
color_mode: LightColorMode.COLOR_TEMP,
effect_list: ["random", "colorloop"],
},
},
{
entity_id: "light.color_RGB_light",
state: "on",
attributes: {
friendly_name: "Color Effects Light",
brightness: 255,
rgb_color: [30, 100, 255],
supported_features:
LightEntityFeature.EFFECT +
LightEntityFeature.FLASH +
LightEntityFeature.TRANSITION,
supported_color_modes: [LightColorMode.BRIGHTNESS, LightColorMode.RGB],
color_mode: LightColorMode.RGB,
effect_list: ["random", "colorloop"],
},
},
{
entity_id: "light.color_rgbw_light",
state: "on",
attributes: {
friendly_name: "Color RGBW Light",
brightness: 255,
rgbw_color: [30, 100, 255, 125],
min_mireds: 30,
max_mireds: 150,
supported_features:
LightEntityFeature.EFFECT +
LightEntityFeature.FLASH +
LightEntityFeature.TRANSITION,
supported_color_modes: [
LightColorMode.BRIGHTNESS,
LightColorMode.COLOR_TEMP,
LightColorMode.RGBW,
],
color_mode: LightColorMode.RGBW,
effect_list: ["random", "colorloop"],
},
},
{
entity_id: "light.color_rgbww_light",
state: "on",
attributes: {
friendly_name: "Color RGBWW Light",
brightness: 255,
rgbww_color: [30, 100, 255, 125, 10],
min_mireds: 30,
max_mireds: 150,
supported_features:
LightEntityFeature.EFFECT +
LightEntityFeature.FLASH +
LightEntityFeature.TRANSITION,
supported_color_modes: [
LightColorMode.BRIGHTNESS,
LightColorMode.COLOR_TEMP,
LightColorMode.RGBWW,
],
color_mode: LightColorMode.RGBWW,
effect_list: ["random", "colorloop"],
},
},
{
entity_id: "light.color_xy_light",
state: "on",
attributes: {
friendly_name: "Color XY Light",
brightness: 255,
xy_color: [30, 100],
rgb_color: [30, 100, 255],
min_mireds: 30,
max_mireds: 150,
supported_features:
LightEntityFeature.EFFECT +
LightEntityFeature.FLASH +
LightEntityFeature.TRANSITION,
supported_color_modes: [
LightColorMode.BRIGHTNESS,
LightColorMode.COLOR_TEMP,
LightColorMode.XY,
],
color_mode: LightColorMode.XY,
effect_list: ["random", "colorloop"],
},
},
getEntity("light", "bed_light", "on", {
friendly_name: "Basic Light",
}),
getEntity("light", "kitchen_light", "on", {
friendly_name: "Brightness Light",
brightness: 200,
supported_color_modes: [LightColorMode.BRIGHTNESS],
color_mode: LightColorMode.BRIGHTNESS,
}),
getEntity("light", "color_temperature_light", "on", {
friendly_name: "White Color Temperature Light",
brightness: 128,
color_temp: 75,
min_mireds: 30,
max_mireds: 150,
supported_color_modes: [
LightColorMode.BRIGHTNESS,
LightColorMode.COLOR_TEMP,
],
color_mode: LightColorMode.COLOR_TEMP,
}),
getEntity("light", "color_hs_light", "on", {
friendly_name: "Color HS Light",
brightness: 255,
hs_color: [30, 100],
rgb_color: [30, 100, 255],
min_mireds: 30,
max_mireds: 150,
supported_features:
LightEntityFeature.EFFECT +
LightEntityFeature.FLASH +
LightEntityFeature.TRANSITION,
supported_color_modes: [
LightColorMode.BRIGHTNESS,
LightColorMode.COLOR_TEMP,
LightColorMode.HS,
],
color_mode: LightColorMode.HS,
effect_list: ["random", "colorloop"],
}),
getEntity("light", "color_rgb_ct_light", "on", {
friendly_name: "Color RGB + CT Light",
brightness: 255,
color_temp: 75,
min_mireds: 30,
max_mireds: 150,
supported_features:
LightEntityFeature.EFFECT +
LightEntityFeature.FLASH +
LightEntityFeature.TRANSITION,
supported_color_modes: [
LightColorMode.BRIGHTNESS,
LightColorMode.COLOR_TEMP,
LightColorMode.RGB,
],
color_mode: LightColorMode.COLOR_TEMP,
effect_list: ["random", "colorloop"],
}),
getEntity("light", "color_RGB_light", "on", {
friendly_name: "Color Effects Light",
brightness: 255,
rgb_color: [30, 100, 255],
supported_features:
LightEntityFeature.EFFECT +
LightEntityFeature.FLASH +
LightEntityFeature.TRANSITION,
supported_color_modes: [LightColorMode.BRIGHTNESS, LightColorMode.RGB],
color_mode: LightColorMode.RGB,
effect_list: ["random", "colorloop"],
}),
getEntity("light", "color_rgbw_light", "on", {
friendly_name: "Color RGBW Light",
brightness: 255,
rgbw_color: [30, 100, 255, 125],
min_mireds: 30,
max_mireds: 150,
supported_features:
LightEntityFeature.EFFECT +
LightEntityFeature.FLASH +
LightEntityFeature.TRANSITION,
supported_color_modes: [
LightColorMode.BRIGHTNESS,
LightColorMode.COLOR_TEMP,
LightColorMode.RGBW,
],
color_mode: LightColorMode.RGBW,
effect_list: ["random", "colorloop"],
}),
getEntity("light", "color_rgbww_light", "on", {
friendly_name: "Color RGBWW Light",
brightness: 255,
rgbww_color: [30, 100, 255, 125, 10],
min_mireds: 30,
max_mireds: 150,
supported_features:
LightEntityFeature.EFFECT +
LightEntityFeature.FLASH +
LightEntityFeature.TRANSITION,
supported_color_modes: [
LightColorMode.BRIGHTNESS,
LightColorMode.COLOR_TEMP,
LightColorMode.RGBWW,
],
color_mode: LightColorMode.RGBWW,
effect_list: ["random", "colorloop"],
}),
getEntity("light", "color_xy_light", "on", {
friendly_name: "Color XY Light",
brightness: 255,
xy_color: [30, 100],
rgb_color: [30, 100, 255],
min_mireds: 30,
max_mireds: 150,
supported_features:
LightEntityFeature.EFFECT +
LightEntityFeature.FLASH +
LightEntityFeature.TRANSITION,
supported_color_modes: [
LightColorMode.BRIGHTNESS,
LightColorMode.COLOR_TEMP,
LightColorMode.XY,
],
color_mode: LightColorMode.XY,
effect_list: ["random", "colorloop"],
}),
];
@customElement("demo-more-info-light")
@@ -182,7 +147,7 @@ class DemoMoreInfoLight extends LitElement {
return html`
<demo-more-infos
.hass=${this.hass}
.entities=${ENTITIES.map((ent) => ent.entity_id)}
.entities=${ENTITIES.map((ent) => ent.entityId)}
></demo-more-infos>
`;
}

View File

@@ -3,26 +3,19 @@ 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";
const ENTITIES = [
{
entity_id: "lock.lock",
state: "locked",
attributes: {
friendly_name: "Lock",
device_class: "lock",
},
},
{
entity_id: "lock.unavailable",
state: "unavailable",
attributes: {
friendly_name: "Unavailable lock",
},
},
getEntity("lock", "lock", "locked", {
friendly_name: "Lock",
device_class: "lock",
}),
getEntity("lock", "unavailable", "unavailable", {
friendly_name: "Unavailable lock",
}),
];
@customElement("demo-more-info-lock")
@@ -35,7 +28,7 @@ class DemoMoreInfoLock extends LitElement {
return html`
<demo-more-infos
.hass=${this.hass}
.entities=${ENTITIES.map((ent) => ent.entity_id)}
.entities=${ENTITIES.map((ent) => ent.entityId)}
></demo-more-infos>
`;
}

View File

@@ -20,7 +20,7 @@ class DemoMoreInfoMediaPlayer extends LitElement {
return html`
<demo-more-infos
.hass=${this.hass}
.entities=${ENTITIES.map((ent) => ent.entity_id)}
.entities=${ENTITIES.map((ent) => ent.entityId)}
></demo-more-infos>
`;
}

View File

@@ -3,63 +3,48 @@ 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";
const ENTITIES = [
{
entity_id: "number.box1",
state: "0",
attributes: {
friendly_name: "Box1",
min: 0,
max: 100,
step: 1,
initial: 0,
mode: "box",
unit_of_measurement: "items",
},
},
{
entity_id: "number.slider1",
state: "0",
attributes: {
friendly_name: "Slider1",
min: 0,
max: 100,
step: 1,
initial: 0,
mode: "slider",
unit_of_measurement: "items",
},
},
{
entity_id: "number.auto1",
state: "0",
attributes: {
friendly_name: "Auto1",
min: 0,
max: 1000,
step: 1,
initial: 0,
mode: "auto",
unit_of_measurement: "items",
},
},
{
entity_id: "number.auto2",
state: "0",
attributes: {
friendly_name: "Auto2",
min: 0,
max: 100,
step: 1,
initial: 0,
mode: "auto",
unit_of_measurement: "items",
},
},
getEntity("number", "box1", 0, {
friendly_name: "Box1",
min: 0,
max: 100,
step: 1,
initial: 0,
mode: "box",
unit_of_measurement: "items",
}),
getEntity("number", "slider1", 0, {
friendly_name: "Slider1",
min: 0,
max: 100,
step: 1,
initial: 0,
mode: "slider",
unit_of_measurement: "items",
}),
getEntity("number", "auto1", 0, {
friendly_name: "Auto1",
min: 0,
max: 1000,
step: 1,
initial: 0,
mode: "auto",
unit_of_measurement: "items",
}),
getEntity("number", "auto2", 0, {
friendly_name: "Auto2",
min: 0,
max: 100,
step: 1,
initial: 0,
mode: "auto",
unit_of_measurement: "items",
}),
];
@customElement("demo-more-info-number")
@@ -72,7 +57,7 @@ class DemoMoreInfoNumber extends LitElement {
return html`
<demo-more-infos
.hass=${this.hass}
.entities=${ENTITIES.map((ent) => ent.entity_id)}
.entities=${ENTITIES.map((ent) => ent.entityId)}
></demo-more-infos>
`;
}

View File

@@ -3,26 +3,19 @@ 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";
const ENTITIES = [
{
entity_id: "scene.romantic_lights",
state: "scening",
attributes: {
entity_id: ["light.bed_light", "light.ceiling_lights"],
friendly_name: "Romantic Scene",
},
},
{
entity_id: "scene.unavailable",
state: "unavailable",
attributes: {
friendly_name: "Romantic Scene",
},
},
getEntity("scene", "romantic_lights", "scening", {
entity_id: ["light.bed_light", "light.ceiling_lights"],
friendly_name: "Romantic Scene",
}),
getEntity("scene", "unavailable", "unavailable", {
friendly_name: "Romantic Scene",
}),
];
@customElement("demo-more-info-scene")
@@ -35,7 +28,7 @@ class DemoMoreInfoScene extends LitElement {
return html`
<demo-more-infos
.hass=${this.hass}
.entities=${ENTITIES.map((ent) => ent.entity_id)}
.entities=${ENTITIES.map((ent) => ent.entityId)}
></demo-more-infos>
`;
}

View File

@@ -3,19 +3,16 @@ 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";
const ENTITIES = [
{
entity_id: "timer.timer",
state: "idle",
attributes: {
friendly_name: "Timer",
duration: "0:05:00",
},
},
getEntity("timer", "timer", "idle", {
friendly_name: "Timer",
duration: "0:05:00",
}),
];
@customElement("demo-more-info-timer")
@@ -28,7 +25,7 @@ class DemoMoreInfoTimer extends LitElement {
return html`
<demo-more-infos
.hass=${this.hass}
.entities=${ENTITIES.map((ent) => ent.entity_id)}
.entities=${ENTITIES.map((ent) => ent.entityId)}
></demo-more-infos>
`;
}

View File

@@ -3,6 +3,7 @@ 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";
@@ -22,208 +23,124 @@ const base_attributes = {
};
const ENTITIES = [
{
entity_id: "update.update1",
state: "on",
attributes: {
...base_attributes,
friendly_name: "Update",
},
},
{
entity_id: "update.update2",
state: "on",
attributes: {
...base_attributes,
title: null,
friendly_name: "Update without title",
},
},
{
entity_id: "update.update3",
state: "on",
attributes: {
...base_attributes,
release_url: null,
friendly_name: "Update without release_url",
},
},
{
entity_id: "update.update4",
state: "on",
attributes: {
...base_attributes,
release_summary: null,
friendly_name: "Update without release_summary",
},
},
{
entity_id: "update.update5",
state: "off",
attributes: {
...base_attributes,
installed_version: "1.2.3",
friendly_name: "No update",
},
},
{
entity_id: "update.update6",
state: "off",
attributes: {
...base_attributes,
skipped_version: "1.2.3",
friendly_name: "Skipped version",
},
},
{
entity_id: "update.update7",
state: "on",
attributes: {
...base_attributes,
supported_features:
base_attributes.supported_features + UpdateEntityFeature.BACKUP,
friendly_name: "With backup support",
},
},
{
entity_id: "update.update8",
state: "on",
attributes: {
...base_attributes,
in_progress: true,
friendly_name: "With true in_progress",
},
},
{
entity_id: "update.update9",
state: "on",
attributes: {
...base_attributes,
in_progress: 25,
supported_features:
base_attributes.supported_features + UpdateEntityFeature.PROGRESS,
friendly_name: "With 25 in_progress",
},
},
{
entity_id: "update.update10",
state: "on",
attributes: {
...base_attributes,
in_progress: 50,
supported_features:
base_attributes.supported_features + UpdateEntityFeature.PROGRESS,
friendly_name: "With 50 in_progress",
},
},
{
entity_id: "update.update11",
state: "on",
attributes: {
...base_attributes,
in_progress: 75,
supported_features:
base_attributes.supported_features + UpdateEntityFeature.PROGRESS,
friendly_name: "With 75 in_progress",
},
},
{
entity_id: "update.update12",
state: "unavailable",
attributes: {
...base_attributes,
in_progress: 50,
friendly_name: "Unavailable",
},
},
{
entity_id: "update.update13",
state: "on",
attributes: {
...base_attributes,
supported_features: 0,
friendly_name: "No install support",
},
},
{
entity_id: "update.update14",
state: "off",
attributes: {
...base_attributes,
installed_version: null,
friendly_name: "Update without installed_version",
},
},
{
entity_id: "update.update15",
state: "off",
attributes: {
...base_attributes,
latest_version: null,
friendly_name: "Update without latest_version",
},
},
{
entity_id: "update.update16",
state: "off",
attributes: {
...base_attributes,
friendly_name: "Update with release notes",
supported_features:
base_attributes.supported_features + UpdateEntityFeature.RELEASE_NOTES,
},
},
{
entity_id: "update.update17",
state: "off",
attributes: {
...base_attributes,
friendly_name: "Update with release notes error",
supported_features:
base_attributes.supported_features + UpdateEntityFeature.RELEASE_NOTES,
},
},
{
entity_id: "update.update18",
state: "off",
attributes: {
...base_attributes,
friendly_name: "Update with release notes loading",
supported_features:
base_attributes.supported_features + UpdateEntityFeature.RELEASE_NOTES,
},
},
{
entity_id: "update.update19",
state: "on",
attributes: {
...base_attributes,
friendly_name: "Update with auto update",
auto_update: true,
},
},
{
entity_id: "update.update20",
state: "on",
attributes: {
...base_attributes,
in_progress: true,
title: undefined,
friendly_name: "Installing without title",
},
},
{
entity_id: "update.update21",
state: "on",
attributes: {
...base_attributes,
in_progress: true,
friendly_name:
"Update with in_progress true and UpdateEntityFeature.PROGRESS",
supported_features:
base_attributes.supported_features + UpdateEntityFeature.PROGRESS,
},
},
getEntity("update", "update1", "on", {
...base_attributes,
friendly_name: "Update",
}),
getEntity("update", "update2", "on", {
...base_attributes,
title: null,
friendly_name: "Update without title",
}),
getEntity("update", "update3", "on", {
...base_attributes,
release_url: null,
friendly_name: "Update without release_url",
}),
getEntity("update", "update4", "on", {
...base_attributes,
release_summary: null,
friendly_name: "Update without release_summary",
}),
getEntity("update", "update5", "off", {
...base_attributes,
installed_version: "1.2.3",
friendly_name: "No update",
}),
getEntity("update", "update6", "off", {
...base_attributes,
skipped_version: "1.2.3",
friendly_name: "Skipped version",
}),
getEntity("update", "update7", "on", {
...base_attributes,
supported_features:
base_attributes.supported_features + UpdateEntityFeature.BACKUP,
friendly_name: "With backup support",
}),
getEntity("update", "update8", "on", {
...base_attributes,
in_progress: true,
friendly_name: "With true in_progress",
}),
getEntity("update", "update9", "on", {
...base_attributes,
in_progress: 25,
supported_features:
base_attributes.supported_features + UpdateEntityFeature.PROGRESS,
friendly_name: "With 25 in_progress",
}),
getEntity("update", "update10", "on", {
...base_attributes,
in_progress: 50,
supported_features:
base_attributes.supported_features + UpdateEntityFeature.PROGRESS,
friendly_name: "With 50 in_progress",
}),
getEntity("update", "update11", "on", {
...base_attributes,
in_progress: 75,
supported_features:
base_attributes.supported_features + UpdateEntityFeature.PROGRESS,
friendly_name: "With 75 in_progress",
}),
getEntity("update", "update12", "unavailable", {
...base_attributes,
in_progress: 50,
friendly_name: "Unavailable",
}),
getEntity("update", "update13", "on", {
...base_attributes,
supported_features: 0,
friendly_name: "No install support",
}),
getEntity("update", "update14", "off", {
...base_attributes,
installed_version: null,
friendly_name: "Update without installed_version",
}),
getEntity("update", "update15", "off", {
...base_attributes,
latest_version: null,
friendly_name: "Update without latest_version",
}),
getEntity("update", "update16", "off", {
...base_attributes,
friendly_name: "Update with release notes",
supported_features:
base_attributes.supported_features + UpdateEntityFeature.RELEASE_NOTES,
}),
getEntity("update", "update17", "off", {
...base_attributes,
friendly_name: "Update with release notes error",
supported_features:
base_attributes.supported_features + UpdateEntityFeature.RELEASE_NOTES,
}),
getEntity("update", "update18", "off", {
...base_attributes,
friendly_name: "Update with release notes loading",
supported_features:
base_attributes.supported_features + UpdateEntityFeature.RELEASE_NOTES,
}),
getEntity("update", "update19", "on", {
...base_attributes,
friendly_name: "Update with auto update",
auto_update: true,
}),
getEntity("update", "update20", "on", {
...base_attributes,
in_progress: true,
title: undefined,
friendly_name: "Installing without title",
}),
getEntity("update", "update21", "on", {
...base_attributes,
in_progress: true,
friendly_name:
"Update with in_progress true and UpdateEntityFeature.PROGRESS",
supported_features:
base_attributes.supported_features + UpdateEntityFeature.PROGRESS,
}),
];
@customElement("demo-more-info-update")
@@ -236,7 +153,7 @@ class DemoMoreInfoUpdate extends LitElement {
return html`
<demo-more-infos
.hass=${this.hass}
.entities=${ENTITIES.map((ent) => ent.entity_id)}
.entities=${ENTITIES.map((ent) => ent.entityId)}
></demo-more-infos>
`;
}

View File

@@ -3,23 +3,20 @@ 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 { VacuumEntityFeature } from "../../../../src/data/vacuum";
const ENTITIES = [
{
entity_id: "vacuum.first_floor_vacuum",
state: "docked",
attributes: {
friendly_name: "First floor vacuum",
supported_features:
VacuumEntityFeature.START +
VacuumEntityFeature.STOP +
VacuumEntityFeature.RETURN_HOME,
},
},
getEntity("vacuum", "first_floor_vacuum", "docked", {
friendly_name: "First floor vacuum",
supported_features:
VacuumEntityFeature.START +
VacuumEntityFeature.STOP +
VacuumEntityFeature.RETURN_HOME,
}),
];
@customElement("demo-more-info-vacuum")
@@ -32,7 +29,7 @@ class DemoMoreInfoVacuum extends LitElement {
return html`
<demo-more-infos
.hass=${this.hass}
.entities=${ENTITIES.map((ent) => ent.entity_id)}
.entities=${ENTITIES.map((ent) => ent.entityId)}
></demo-more-infos>
`;
}

View File

@@ -4,46 +4,39 @@ import { customElement, property, query } from "lit/decorators";
import "../../../../src/components/ha-card";
import { WaterHeaterEntityFeature } from "../../../../src/data/water_heater";
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";
const ENTITIES = [
{
entity_id: "water_heater.basic",
state: "eco",
attributes: {
friendly_name: "Basic heater",
operation_list: ["heat_pump", "eco", "performance", "off"],
operation_mode: "eco",
away_mode: "off",
target_temp_step: 1,
current_temperature: 55,
temperature: 60,
min_temp: 20,
max_temp: 70,
supported_features:
// eslint-disable-next-line no-bitwise
WaterHeaterEntityFeature.TARGET_TEMPERATURE |
WaterHeaterEntityFeature.OPERATION_MODE |
WaterHeaterEntityFeature.AWAY_MODE,
},
},
{
entity_id: "water_heater.unavailable",
state: "unavailable",
attributes: {
friendly_name: "Unavailable heater",
operation_list: ["heat_pump", "eco", "performance", "off"],
operation_mode: "off",
min_temp: 20,
max_temp: 70,
supported_features:
// eslint-disable-next-line no-bitwise
WaterHeaterEntityFeature.TARGET_TEMPERATURE |
WaterHeaterEntityFeature.OPERATION_MODE,
},
},
getEntity("water_heater", "basic", "eco", {
friendly_name: "Basic heater",
operation_list: ["heat_pump", "eco", "performance", "off"],
operation_mode: "eco",
away_mode: "off",
target_temp_step: 1,
current_temperature: 55,
temperature: 60,
min_temp: 20,
max_temp: 70,
supported_features:
// eslint-disable-next-line no-bitwise
WaterHeaterEntityFeature.TARGET_TEMPERATURE |
WaterHeaterEntityFeature.OPERATION_MODE |
WaterHeaterEntityFeature.AWAY_MODE,
}),
getEntity("water_heater", "unavailable", "unavailable", {
friendly_name: "Unavailable heater",
operation_list: ["heat_pump", "eco", "performance", "off"],
operation_mode: "off",
min_temp: 20,
max_temp: 70,
supported_features:
// eslint-disable-next-line no-bitwise
WaterHeaterEntityFeature.TARGET_TEMPERATURE |
WaterHeaterEntityFeature.OPERATION_MODE,
}),
];
@customElement("demo-more-info-water-heater")
@@ -56,7 +49,7 @@ class DemoMoreInfoWaterHeater extends LitElement {
return html`
<demo-more-infos
.hass=${this.hass}
.entities=${ENTITIES.map((ent) => ent.entity_id)}
.entities=${ENTITIES.map((ent) => ent.entityId)}
></demo-more-infos>
`;
}

View File

@@ -100,6 +100,7 @@ class HaLandingPage extends LandingPageBaseElement {
button-style
native-name
@value-changed=${this._languageChanged}
inline-arrow
></ha-language-picker>
<ha-button
appearance="plain"
@@ -118,7 +119,7 @@ class HaLandingPage extends LandingPageBaseElement {
protected firstUpdated(changedProps: PropertyValues) {
super.firstUpdated(changedProps);
makeDialogManager(this);
makeDialogManager(this, this.shadowRoot!);
if (window.innerWidth > 450) {
import("../../src/resources/particles");
@@ -222,9 +223,6 @@ class HaLandingPage extends LandingPageBaseElement {
flex-direction: column;
gap: var(--ha-space-4);
}
ha-language-picker {
min-width: 200px;
}
ha-alert p {
text-align: unset;
}

View File

@@ -26,33 +26,33 @@
"license": "Apache-2.0",
"type": "module",
"dependencies": {
"@babel/runtime": "7.29.2",
"@babel/runtime": "7.28.6",
"@braintree/sanitize-url": "7.1.2",
"@codemirror/autocomplete": "6.20.1",
"@codemirror/commands": "6.10.3",
"@codemirror/language": "6.12.3",
"@codemirror/autocomplete": "6.20.0",
"@codemirror/commands": "6.10.1",
"@codemirror/language": "6.12.1",
"@codemirror/legacy-modes": "6.5.2",
"@codemirror/search": "6.6.0",
"@codemirror/state": "6.6.0",
"@codemirror/view": "6.40.0",
"@codemirror/state": "6.5.4",
"@codemirror/view": "6.39.12",
"@date-fns/tz": "1.4.1",
"@egjs/hammerjs": "2.0.17",
"@formatjs/intl-datetimeformat": "7.3.1",
"@formatjs/intl-displaynames": "7.3.1",
"@formatjs/intl-durationformat": "0.10.3",
"@formatjs/intl-getcanonicallocales": "3.2.2",
"@formatjs/intl-listformat": "8.3.1",
"@formatjs/intl-locale": "5.3.1",
"@formatjs/intl-numberformat": "9.3.1",
"@formatjs/intl-pluralrules": "6.3.1",
"@formatjs/intl-relativetimeformat": "12.3.1",
"@formatjs/intl-datetimeformat": "7.2.0",
"@formatjs/intl-displaynames": "7.2.0",
"@formatjs/intl-durationformat": "0.10.0",
"@formatjs/intl-getcanonicallocales": "3.2.0",
"@formatjs/intl-listformat": "8.2.0",
"@formatjs/intl-locale": "5.2.0",
"@formatjs/intl-numberformat": "9.2.1",
"@formatjs/intl-pluralrules": "6.2.1",
"@formatjs/intl-relativetimeformat": "12.2.1",
"@fullcalendar/core": "6.1.20",
"@fullcalendar/daygrid": "6.1.20",
"@fullcalendar/interaction": "6.1.20",
"@fullcalendar/list": "6.1.20",
"@fullcalendar/luxon3": "6.1.20",
"@fullcalendar/timegrid": "6.1.20",
"@home-assistant/webawesome": "3.3.1-ha.0",
"@home-assistant/webawesome": "3.0.0-ha.2",
"@lezer/highlight": "1.2.3",
"@lit-labs/motion": "1.1.0",
"@lit-labs/observers": "2.1.0",
@@ -68,10 +68,13 @@
"@material/mwc-fab": "0.27.0",
"@material/mwc-floating-label": "0.27.0",
"@material/mwc-formfield": "patch:@material/mwc-formfield@npm%3A0.27.0#~/.yarn/patches/@material-mwc-formfield-npm-0.27.0-9528cb60f6.patch",
"@material/mwc-icon-button": "0.27.0",
"@material/mwc-linear-progress": "0.27.0",
"@material/mwc-list": "patch:@material/mwc-list@npm%3A0.27.0#~/.yarn/patches/@material-mwc-list-npm-0.27.0-5344fc9de4.patch",
"@material/mwc-menu": "0.27.0",
"@material/mwc-radio": "0.27.0",
"@material/mwc-select": "0.27.0",
"@material/mwc-snackbar": "0.27.0",
"@material/mwc-switch": "0.27.0",
"@material/mwc-textarea": "0.27.0",
"@material/mwc-textfield": "0.27.0",
@@ -82,18 +85,19 @@
"@mdi/js": "7.4.47",
"@mdi/svg": "7.4.47",
"@replit/codemirror-indentation-markers": "6.5.3",
"@swc/helpers": "0.5.20",
"@swc/helpers": "0.5.18",
"@thomasloven/round-slider": "0.6.0",
"@tsparticles/engine": "3.9.1",
"@tsparticles/preset-links": "3.2.0",
"@vibrant/color": "4.0.4",
"@vue/web-component-wrapper": "1.3.0",
"@webcomponents/scoped-custom-element-registry": "0.0.10",
"@webcomponents/webcomponentsjs": "2.8.0",
"barcode-detector": "3.1.1",
"cally": "0.9.2",
"app-datepicker": "5.1.1",
"barcode-detector": "3.0.8",
"color-name": "2.1.0",
"comlink": "4.4.2",
"core-js": "3.49.0",
"core-js": "3.48.0",
"cropperjs": "1.6.2",
"culori": "4.0.2",
"date-fns": "4.1.0",
@@ -104,11 +108,11 @@
"element-internals-polyfill": "3.0.2",
"fuse.js": "7.1.0",
"google-timezones-json": "1.2.0",
"gulp-zopfli-green": "7.0.0",
"gulp-zopfli-green": "6.0.2",
"hls.js": "1.6.15",
"home-assistant-js-websocket": "9.6.0",
"idb-keyval": "6.2.2",
"intl-messageformat": "11.2.0",
"intl-messageformat": "11.1.1",
"js-yaml": "4.1.1",
"leaflet": "1.9.4",
"leaflet-draw": "patch:leaflet-draw@npm%3A1.0.4#./.yarn/patches/leaflet-draw-npm-1.0.4-0ca0ebcf65.patch",
@@ -116,7 +120,7 @@
"lit": "3.3.2",
"lit-html": "3.3.2",
"luxon": "3.7.2",
"marked": "17.0.5",
"marked": "17.0.1",
"memoize-one": "6.0.0",
"node-vibrant": "4.0.4",
"object-hash": "3.0.0",
@@ -129,6 +133,9 @@
"stacktrace-js": "2.0.2",
"superstruct": "2.0.2",
"tinykeys": "3.0.0",
"ua-parser-js": "2.0.8",
"vue": "2.7.16",
"vue2-daterange-picker": "0.6.8",
"weekstart": "2.0.0",
"workbox-cacheable-response": "7.4.0",
"workbox-core": "7.4.0",
@@ -139,18 +146,17 @@
"xss": "1.0.15"
},
"devDependencies": {
"@babel/core": "7.29.0",
"@babel/helper-define-polyfill-provider": "0.6.8",
"@babel/plugin-transform-runtime": "7.29.0",
"@babel/preset-env": "7.29.2",
"@bundle-stats/plugin-webpack-filter": "4.22.0",
"@html-eslint/eslint-plugin": "0.58.1",
"@babel/core": "7.28.6",
"@babel/helper-define-polyfill-provider": "0.6.6",
"@babel/plugin-transform-runtime": "7.28.5",
"@babel/preset-env": "7.28.6",
"@bundle-stats/plugin-webpack-filter": "4.21.9",
"@lokalise/node-api": "15.6.1",
"@octokit/auth-oauth-device": "8.0.3",
"@octokit/plugin-retry": "8.1.0",
"@octokit/plugin-retry": "8.0.3",
"@octokit/rest": "22.0.1",
"@rsdoctor/rspack-plugin": "1.5.5",
"@rspack/core": "1.7.10",
"@rsdoctor/rspack-plugin": "1.5.1",
"@rspack/core": "1.7.4",
"@rspack/dev-server": "1.2.1",
"@types/babel__plugin-transform-runtime": "7.9.5",
"@types/chromecast-caf-receiver": "6.0.25",
@@ -167,34 +173,35 @@
"@types/mocha": "10.0.10",
"@types/qrcode": "1.5.6",
"@types/sortablejs": "1.15.9",
"@types/tar": "7.0.87",
"@types/tar": "6.1.13",
"@types/ua-parser-js": "0.7.39",
"@types/webspeechapi": "0.0.29",
"@vitest/coverage-v8": "4.1.2",
"babel-loader": "10.1.1",
"@vitest/coverage-v8": "4.0.18",
"babel-loader": "10.0.0",
"babel-plugin-template-html-minifier": "4.1.0",
"browserslist-useragent-regexp": "4.1.3",
"del": "8.0.1",
"eslint": "9.39.4",
"eslint": "9.39.2",
"eslint-config-airbnb-base": "15.0.0",
"eslint-config-prettier": "10.1.8",
"eslint-import-resolver-webpack": "0.13.10",
"eslint-plugin-import": "2.32.0",
"eslint-plugin-lit": "2.2.1",
"eslint-plugin-lit": "2.1.1",
"eslint-plugin-lit-a11y": "5.1.1",
"eslint-plugin-unused-imports": "4.4.1",
"eslint-plugin-wc": "3.1.0",
"eslint-plugin-unused-imports": "4.3.0",
"eslint-plugin-wc": "3.0.2",
"fancy-log": "2.0.0",
"fs-extra": "11.3.4",
"glob": "13.0.6",
"fs-extra": "11.3.3",
"glob": "13.0.0",
"gulp": "5.0.1",
"gulp-brotli": "3.0.0",
"gulp-json-transform": "0.5.0",
"gulp-rename": "2.1.0",
"html-minifier-terser": "7.2.0",
"husky": "9.1.7",
"jsdom": "29.0.1",
"jsdom": "27.4.0",
"jszip": "3.10.1",
"lint-staged": "16.4.0",
"lint-staged": "16.2.7",
"lit-analyzer": "2.0.3",
"lodash.merge": "4.6.2",
"lodash.template": "4.5.0",
@@ -202,32 +209,33 @@
"pinst": "3.0.0",
"prettier": "3.8.1",
"rspack-manifest-plugin": "5.2.1",
"serve": "14.2.6",
"sinon": "21.0.3",
"tar": "7.5.13",
"terser-webpack-plugin": "5.4.0",
"serve": "14.2.5",
"sinon": "21.0.1",
"tar": "7.5.7",
"terser-webpack-plugin": "5.3.16",
"ts-lit-plugin": "2.0.2",
"typescript": "6.0.2",
"typescript-eslint": "8.57.2",
"vite-tsconfig-paths": "6.1.1",
"vitest": "4.1.2",
"typescript": "5.9.3",
"typescript-eslint": "8.54.0",
"vite-tsconfig-paths": "6.0.5",
"vitest": "4.0.18",
"webpack-stats-plugin": "1.1.3",
"webpackbar": "7.0.0",
"workbox-build": "patch:workbox-build@npm%3A7.4.0#~/.yarn/patches/workbox-build-npm-7.4.0-c84561662c.patch"
"workbox-build": "patch:workbox-build@npm%3A7.1.1#~/.yarn/patches/workbox-build-npm-7.1.1-a854f3faae.patch"
},
"resolutions": {
"@material/mwc-button@^0.25.3": "^0.27.0",
"lit": "3.3.2",
"lit-html": "3.3.2",
"clean-css": "5.3.3",
"@lit/reactive-element": "2.1.2",
"@fullcalendar/daygrid": "6.1.20",
"globals": "17.4.0",
"globals": "17.2.0",
"tslib": "2.8.1",
"@material/mwc-list@^0.27.0": "patch:@material/mwc-list@npm%3A0.27.0#~/.yarn/patches/@material-mwc-list-npm-0.27.0-5344fc9de4.patch",
"glob@^10.2.2": "^10.5.0"
},
"packageManager": "yarn@4.13.0",
"packageManager": "yarn@4.12.0",
"volta": {
"node": "24.14.1"
"node": "24.13.0"
}
}

View File

@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "home-assistant-frontend"
version = "20260325.0"
version = "20260128.0"
license = "Apache-2.0"
license-files = ["LICENSE*"]
description = "The Home Assistant frontend"

View File

@@ -24,6 +24,11 @@
"extends": ["monorepo:material-components-web"],
"enabled": false
},
{
"description": "Vue is only used by date range which is only v2",
"matchPackageNames": ["vue"],
"allowedVersions": "< 3"
},
{
"description": "Group MDI packages",
"groupName": "Material Design Icons",

View File

@@ -16,12 +16,6 @@ if [[ -n "$DEVCONTAINER" ]]; then
nvm install --reinstall-packages-from="$nodeCurrent" --default
nvm uninstall "$nodeCurrent"
fi
# install yarn if not already available
if ! command -v yarn &> /dev/null; then
npm install -g corepack
yes | yarn
fi
fi
if ! command -v yarn &> /dev/null; then

View File

@@ -2,7 +2,7 @@
import { genClientId } from "home-assistant-js-websocket";
import type { PropertyValues } from "lit";
import { html, LitElement, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { customElement, property, state } from "lit/decorators";
import { keyed } from "lit/directives/keyed";
import type { LocalizeFunc } from "../common/translations/localize";
import "../components/ha-alert";
@@ -23,7 +23,6 @@ import type {
DataEntryFlowStepForm,
} from "../data/data_entry_flow";
import "./ha-auth-form";
import type { HaAuthForm } from "./ha-auth-form";
type State = "loading" | "error" | "step";
@@ -53,8 +52,6 @@ export class HaAuthFlow extends LitElement {
@state() private _submitting = false;
@query("ha-auth-form") private _form?: HaAuthForm;
createRenderRoot() {
return this;
}
@@ -182,7 +179,7 @@ export class HaAuthFlow extends LitElement {
<div class="action">
<ha-button
@click=${this._handleSubmit}
.loading=${this._submitting}
.disabled=${this._submitting}
>
${this.step.type === "form"
? this.localize("ui.panel.page-authorize.form.next")
@@ -373,11 +370,6 @@ export class HaAuthFlow extends LitElement {
this._providerChanged(this.authProvider);
return;
}
if (!this._form?.reportValidity()) {
return;
}
this._submitting = true;
const postData = { ...this._stepData, client_id: this.clientId };

View File

@@ -1,7 +1,10 @@
/* eslint-disable lit/prefer-static-styles */
import type { TemplateResult } from "lit";
import { html } from "lit";
import { customElement } from "lit/decorators";
import { HaFormString } from "../components/ha-form/ha-form-string";
import "../components/ha-icon-button";
import "../components/input/ha-input";
import "./ha-auth-textfield";
@customElement("ha-auth-form-string")
export class HaAuthFormString extends HaFormString {
@@ -9,9 +12,59 @@ export class HaAuthFormString extends HaFormString {
return this;
}
public connectedCallback(): void {
super.connectedCallback();
this.style.position = "relative";
protected render(): TemplateResult {
return html`
<style>
ha-auth-form-string {
display: block;
position: relative;
}
ha-auth-form-string[own-margin] {
margin-bottom: 5px;
}
ha-auth-form-string ha-auth-textfield {
display: block !important;
}
ha-auth-form-string ha-icon-button {
position: absolute;
top: 8px;
right: 8px;
inset-inline-start: initial;
inset-inline-end: 8px;
--mdc-icon-button-size: 40px;
--mdc-icon-size: 20px;
color: var(--secondary-text-color);
direction: var(--direction);
}
</style>
<ha-auth-textfield
.type=${!this.isPassword
? this.stringType
: this.unmaskedPassword
? "text"
: "password"}
.label=${this.label}
.value=${this.data || ""}
.helper=${this.helper}
helperPersistent
.disabled=${this.disabled}
.required=${this.schema.required}
.autoValidate=${this.schema.required}
.name=${this.schema.name}
.autocomplete=${this.schema.autocomplete}
?autofocus=${this.schema.autofocus}
.suffix=${this.isPassword
? // reserve some space for the icon.
html`<div style="width: 24px"></div>`
: this.schema.description?.suffix}
.validationMessage=${this.schema.required
? this.localize?.("ui.panel.page-authorize.form.error_required")
: undefined}
@input=${this._valueChanged}
@change=${this._valueChanged}
></ha-auth-textfield>
${this.renderIcon()}
`;
}
}

View File

@@ -1,9 +1,9 @@
/* eslint-disable lit/prefer-static-styles */
import { html } from "lit";
import { customElement, property } from "lit/decorators";
import type { LocalizeFunc } from "../common/translations/localize";
import { HaForm } from "../components/ha-form/ha-form";
import "./ha-auth-form-string";
import type { LocalizeFunc } from "../common/translations/localize";
const localizeBaseKey = "ui.panel.page-authorize.form";
@@ -34,9 +34,6 @@ export class HaAuthForm extends HaForm {
protected render() {
return html`
<style>
ha-auth-form {
--ha-input-required-marker: "";
}
ha-auth-form .root > * {
display: block;
}

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