mirror of
https://github.com/home-assistant/frontend.git
synced 2026-07-26 22:41:29 +00:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 18de8b13d3 | |||
| 39e70ff315 | |||
| 14fd9114d6 | |||
| 406a0b2bad | |||
| 4c86785d21 | |||
| 2aeb522eed | |||
| bef1c0def6 | |||
| f7e5bdb2a0 | |||
| de59237cfb | |||
| f778572b89 | |||
| d2c753b589 | |||
| 8fbb38a25d | |||
| 2f6c1388e8 | |||
| ced5ffdd18 | |||
| 5a6a16ed27 |
@@ -32,7 +32,7 @@ jobs:
|
||||
ACTIONLINT_VERSION: 1.7.12
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Run actionlint
|
||||
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out workflow scripts
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
sparse-checkout: .github/scripts
|
||||
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
url: ${{ steps.deploy.outputs.netlify_url }}
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
ref: dev
|
||||
persist-credentials: false
|
||||
@@ -56,7 +56,7 @@ jobs:
|
||||
url: ${{ steps.deploy.outputs.netlify_url }}
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
ref: master
|
||||
persist-credentials: false
|
||||
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Prepare dependencies
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Setup Node with shared dependencies
|
||||
@@ -82,7 +82,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Setup Node with shared dependencies
|
||||
@@ -104,7 +104,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Setup Node with shared dependencies
|
||||
|
||||
@@ -27,17 +27,17 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0
|
||||
uses: github/codeql-action/init@7188fc363630916deb702c7fdcf4e481b751f97a # v4.37.1
|
||||
with:
|
||||
languages: javascript-typescript
|
||||
build-mode: none
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0
|
||||
uses: github/codeql-action/analyze@7188fc363630916deb702c7fdcf4e481b751f97a # v4.37.1
|
||||
with:
|
||||
category: "/language:javascript-typescript"
|
||||
|
||||
@@ -18,7 +18,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
url: ${{ steps.deploy.outputs.unique_deploy_url }}
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
ref: dev
|
||||
persist-credentials: false
|
||||
@@ -56,7 +56,7 @@ jobs:
|
||||
url: ${{ steps.deploy.outputs.netlify_url }}
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
ref: master
|
||||
persist-credentials: false
|
||||
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
url: ${{ steps.deploy.outputs.netlify_url }}
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
if: github.repository == 'home-assistant/frontend' && contains(github.event.pull_request.labels.*.name, 'needs design preview')
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
shell: bash
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -92,7 +92,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -123,7 +123,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -170,7 +170,7 @@ jobs:
|
||||
- 2
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -224,7 +224,7 @@ jobs:
|
||||
- 4
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -278,7 +278,7 @@ jobs:
|
||||
- 4
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -320,7 +320,7 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
|
||||
@@ -10,6 +10,6 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Apply labels
|
||||
uses: actions/labeler@b8dd2d9be0f68b860e7dae5dae7d772984eacd6d # v6.2.0
|
||||
uses: actions/labeler@bf12e9b00b37c5c0ca2b87b79b2daf7891dbda13 # v7.0.0
|
||||
with:
|
||||
sync-labels: true
|
||||
|
||||
@@ -20,12 +20,12 @@ jobs:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
||||
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
|
||||
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
pull-requests: write # To label and comment on pull requests
|
||||
steps:
|
||||
- name: Check out workflow scripts
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
sparse-checkout: .github/scripts
|
||||
|
||||
@@ -18,6 +18,6 @@ jobs:
|
||||
pull-requests: read
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: release-drafter/release-drafter@4d75298e00d9e34c483e5ff8c68d0ea1c1940c1e # v7.5.1
|
||||
- uses: release-drafter/release-drafter@eada3c96a64734dd381cfbda23511034e328ddb0 # v7.6.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -26,17 +26,17 @@ jobs:
|
||||
if: github.repository_owner == 'home-assistant'
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
||||
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
|
||||
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
|
||||
- name: Verify version
|
||||
uses: home-assistant/actions/helpers/verify-version@f4ca6f671bd429efb108c0f2fa0ae8af0215986c # master
|
||||
uses: home-assistant/actions/helpers/verify-version@e3fb68ebda13d88a0d695082f471ba2c83d025fb # master
|
||||
|
||||
- name: Setup Node and install
|
||||
uses: ./.github/actions/setup
|
||||
@@ -56,7 +56,7 @@ jobs:
|
||||
script/release
|
||||
|
||||
- name: Publish to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
|
||||
uses: pypa/gh-action-pypi-publish@ba38be9e461d3875417946c167d0b5f3d385a247 # v1.14.1
|
||||
with:
|
||||
skip-existing: true
|
||||
|
||||
@@ -111,7 +111,7 @@ jobs:
|
||||
contents: write # Required to upload release assets
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Setup Node and install
|
||||
|
||||
@@ -42,7 +42,7 @@ jobs:
|
||||
if: github.event.issue.type.name == 'Task'
|
||||
steps:
|
||||
- name: Check out workflow scripts
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
sparse-checkout: .github/scripts
|
||||
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
You are helping develop the Home Assistant frontend. This repository is a TypeScript application built from Lit-based Web Components for the Home Assistant web UI.
|
||||
|
||||
For the standalone demo — including how to open a specific demo configuration or page via URL — read `demo/AGENTS.md`.
|
||||
|
||||
## Essential Commands
|
||||
|
||||
```bash
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
# Demo Agent Instructions
|
||||
|
||||
This file applies to all files under `demo/`. Follow the root `AGENTS.md` for repository-wide standards.
|
||||
|
||||
The demo is the full Home Assistant frontend running against a mocked backend (published at https://demo.home-assistant.io). It needs no Home Assistant server, which makes it the easiest way to load the real UI in a browser, for example to take screenshots.
|
||||
|
||||
## Running the demo
|
||||
|
||||
Run from the repository root:
|
||||
|
||||
```bash
|
||||
yarn dev:demo # dev server on http://localhost:8090
|
||||
yarn dev:demo --background # detached; also supports --status/--stop/--logs
|
||||
```
|
||||
|
||||
## Opening a specific demo
|
||||
|
||||
The demo contains multiple demo configurations. Select one directly with the `demo` query parameter, e.g. `http://localhost:8090/?demo=<slug>`. The valid slugs are defined in `demoConfigs` in `src/configs/demo-configs.ts`, so "the second demo" means the slug of the second entry in that list. An unknown slug falls back to the default (the first entry).
|
||||
|
||||
## Opening a specific page
|
||||
|
||||
The demo build uses hash-based routing: the frontend path goes in the URL hash, and the `demo` query parameter goes before the `#`. The URL format is:
|
||||
|
||||
```
|
||||
http://localhost:8090/?demo=<slug>#/<path>
|
||||
```
|
||||
|
||||
Useful paths:
|
||||
|
||||
- `/lovelace/0` — the selected demo's dashboard (also the default when no hash is given)
|
||||
- `/energy` — energy dashboard. Its tabs are views: `/energy/overview` (Summary), `/energy/electricity`, `/energy/gas`, `/energy/water`, `/energy/now`
|
||||
- `/map`, `/history`, `/todo`, `/config` — other sidebar panels
|
||||
|
||||
Example — the water tab of the energy dashboard of the second demo:
|
||||
|
||||
```
|
||||
http://localhost:8090/?demo=<second slug>#/energy/water
|
||||
```
|
||||
|
||||
## Structure
|
||||
|
||||
- `src/ha-demo.ts`: Root element; sets up all backend mocks.
|
||||
- `src/configs/<slug>/`: One directory per demo configuration (entities, dashboard, theme).
|
||||
- `src/configs/demo-configs.ts`: Registry of demo configurations and URL slug handling.
|
||||
- `src/stubs/`: Mocked WebSocket/REST APIs.
|
||||
- `script/develop_demo`, `script/build_demo`: Dev server and static build wrappers.
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
AGENTS.md
|
||||
@@ -1,3 +1,4 @@
|
||||
import { navigate } from "../../../src/common/navigate";
|
||||
import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||
import type { Lovelace } from "../../../src/panels/lovelace/types";
|
||||
import { setDemoAreas } from "../stubs/area_registry";
|
||||
@@ -14,35 +15,53 @@ export const applyDemoTheme = (hass: MockHomeAssistant, theme: DemoTheme) => {
|
||||
hass.mockTheme(null, getDemoTheme(theme));
|
||||
};
|
||||
|
||||
export const demoConfigs: (() => Promise<DemoConfig>)[] = [
|
||||
() => import("./sections").then((mod) => mod.demoSections),
|
||||
() => import("./arsaboo").then((mod) => mod.demoArsaboo),
|
||||
() => import("./teachingbirds").then((mod) => mod.demoTeachingbirds),
|
||||
() => import("./kernehed").then((mod) => mod.demoKernehed),
|
||||
() => import("./jimpower").then((mod) => mod.demoJimpower),
|
||||
];
|
||||
export const demoConfigs: Record<string, () => Promise<DemoConfig>> = {
|
||||
sections: () => import("./sections").then((mod) => mod.demoSections),
|
||||
home: () => import("./home").then((mod) => mod.demoHome),
|
||||
arsaboo: () => import("./arsaboo").then((mod) => mod.demoArsaboo),
|
||||
teachingbirds: () =>
|
||||
import("./teachingbirds").then((mod) => mod.demoTeachingbirds),
|
||||
kernehed: () => import("./kernehed").then((mod) => mod.demoKernehed),
|
||||
jimpower: () => import("./jimpower").then((mod) => mod.demoJimpower),
|
||||
};
|
||||
|
||||
export const demos = Object.keys(demoConfigs);
|
||||
|
||||
const initialDemo = () => {
|
||||
const slug = new URLSearchParams(window.location.search).get("demo");
|
||||
return slug && demos.includes(slug) ? slug : demos[0];
|
||||
};
|
||||
|
||||
// eslint-disable-next-line import-x/no-mutable-exports
|
||||
export let selectedDemoConfigIndex = 0;
|
||||
export let selectedDemo = initialDemo();
|
||||
// eslint-disable-next-line import-x/no-mutable-exports
|
||||
export let selectedDemoConfig: Promise<DemoConfig> =
|
||||
demoConfigs[selectedDemoConfigIndex]();
|
||||
demoConfigs[selectedDemo]();
|
||||
|
||||
export const setDemoConfig = async (
|
||||
hass: MockHomeAssistant,
|
||||
lovelace: Lovelace,
|
||||
index: number
|
||||
demo: string
|
||||
) => {
|
||||
const confProm = demoConfigs[index]();
|
||||
const confProm = demoConfigs[demo]();
|
||||
const config = await confProm;
|
||||
|
||||
selectedDemoConfigIndex = index;
|
||||
selectedDemo = demo;
|
||||
selectedDemoConfig = confProm;
|
||||
|
||||
setDemoFloors(hass, config.floors);
|
||||
setDemoAreas(hass, config.areas);
|
||||
hass.addEntities(config.entities(hass.localize), true);
|
||||
hass.addEntities(energyEntities());
|
||||
lovelace.saveConfig(config.lovelace(hass.localize));
|
||||
|
||||
// Let the new registries and entities reach the dashboard before saving the
|
||||
// config, so dashboard strategies generate against them
|
||||
await new Promise((resolve) => {
|
||||
setTimeout(resolve, 0);
|
||||
});
|
||||
|
||||
await lovelace.saveConfig(config.lovelace(hass.localize));
|
||||
// The view of the previous demo might not exist in the new one
|
||||
navigate(`/${hass.panelUrl}?demo=${demo}`, { replace: true });
|
||||
applyDemoTheme(hass, config.theme);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
import type { DemoArea } from "../../stubs/area_registry";
|
||||
import type { DemoFloor } from "../../stubs/floor_registry";
|
||||
|
||||
export const demoFloorsHome: DemoFloor[] = [
|
||||
{
|
||||
floor_id: "ground",
|
||||
name: "Ground floor",
|
||||
level: 0,
|
||||
},
|
||||
{
|
||||
floor_id: "upstairs",
|
||||
name: "Upstairs",
|
||||
level: 1,
|
||||
},
|
||||
];
|
||||
|
||||
export const demoAreasHome: DemoArea[] = [
|
||||
{
|
||||
area_id: "living_room",
|
||||
name: "Living room",
|
||||
floor_id: "ground",
|
||||
icon: "mdi:sofa",
|
||||
temperature_entity_id: "sensor.living_room_temperature",
|
||||
humidity_entity_id: "sensor.living_room_humidity",
|
||||
},
|
||||
{
|
||||
area_id: "kitchen",
|
||||
name: "Kitchen",
|
||||
floor_id: "ground",
|
||||
icon: "mdi:fridge",
|
||||
temperature_entity_id: "sensor.kitchen_temperature",
|
||||
humidity_entity_id: "sensor.kitchen_humidity",
|
||||
},
|
||||
{
|
||||
area_id: "entrance",
|
||||
name: "Entrance",
|
||||
floor_id: "ground",
|
||||
icon: "mdi:door",
|
||||
},
|
||||
{
|
||||
area_id: "bedroom",
|
||||
name: "Bedroom",
|
||||
floor_id: "upstairs",
|
||||
icon: "mdi:bed",
|
||||
temperature_entity_id: "sensor.bedroom_temperature",
|
||||
humidity_entity_id: "sensor.bedroom_humidity",
|
||||
},
|
||||
{
|
||||
area_id: "office",
|
||||
name: "Office",
|
||||
floor_id: "upstairs",
|
||||
icon: "mdi:desk",
|
||||
temperature_entity_id: "sensor.office_temperature",
|
||||
humidity_entity_id: "sensor.office_humidity",
|
||||
},
|
||||
{
|
||||
area_id: "garden",
|
||||
name: "Garden",
|
||||
icon: "mdi:tree",
|
||||
temperature_entity_id: "sensor.garden_temperature",
|
||||
humidity_entity_id: "sensor.garden_humidity",
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,465 @@
|
||||
import type { DemoConfig } from "../types";
|
||||
|
||||
export const demoEntitiesHome: DemoConfig["entities"] = () => [
|
||||
// The devices tile on the overview uses zone.home, which always exists in
|
||||
// a real installation.
|
||||
{
|
||||
entity_id: "zone.home",
|
||||
state: "1",
|
||||
attributes: {
|
||||
latitude: 52.3731339,
|
||||
longitude: 4.8903147,
|
||||
radius: 100,
|
||||
friendly_name: "Home",
|
||||
icon: "mdi:home",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.living_room_floor_lamp",
|
||||
state: "on",
|
||||
area_id: "living_room",
|
||||
attributes: {
|
||||
min_color_temp_kelvin: 2000,
|
||||
max_color_temp_kelvin: 6535,
|
||||
supported_color_modes: ["color_temp", "xy"],
|
||||
color_mode: "color_temp",
|
||||
brightness: 178,
|
||||
color_temp_kelvin: 2583,
|
||||
icon: "mdi:floor-lamp",
|
||||
friendly_name: "Floor lamp",
|
||||
supported_features: 44,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.living_room_spotlights",
|
||||
state: "on",
|
||||
area_id: "living_room",
|
||||
attributes: {
|
||||
supported_color_modes: ["brightness"],
|
||||
color_mode: "brightness",
|
||||
brightness: 126,
|
||||
icon: "mdi:ceiling-light-multiple",
|
||||
friendly_name: "Spotlights",
|
||||
supported_features: 32,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "climate.living_room",
|
||||
state: "heat",
|
||||
area_id: "living_room",
|
||||
attributes: {
|
||||
hvac_modes: ["auto", "heat", "off"],
|
||||
min_temp: 7,
|
||||
max_temp: 35,
|
||||
current_temperature: 21.5,
|
||||
temperature: 21,
|
||||
friendly_name: "Thermostat",
|
||||
supported_features: 385,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "cover.living_room_blinds",
|
||||
state: "open",
|
||||
area_id: "living_room",
|
||||
attributes: {
|
||||
current_position: 100,
|
||||
device_class: "blind",
|
||||
friendly_name: "Blinds",
|
||||
supported_features: 15,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "binary_sensor.living_room_window",
|
||||
state: "off",
|
||||
area_id: "living_room",
|
||||
attributes: {
|
||||
device_class: "window",
|
||||
friendly_name: "Window",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.living_room_speaker",
|
||||
state: "playing",
|
||||
area_id: "living_room",
|
||||
attributes: {
|
||||
device_class: "speaker",
|
||||
volume_level: 0.28,
|
||||
is_volume_muted: false,
|
||||
media_content_type: "music",
|
||||
media_duration: 300,
|
||||
media_position: 0,
|
||||
media_position_updated_at: new Date(
|
||||
new Date().getTime() - 23000
|
||||
).toISOString(),
|
||||
media_title: "I Wasn't Born To Follow",
|
||||
media_artist: "The Byrds",
|
||||
media_album_name: "The Notorious Byrd Brothers",
|
||||
shuffle: false,
|
||||
friendly_name: "Living room speaker",
|
||||
supported_features: 64063,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "sensor.living_room_temperature",
|
||||
state: "21.5",
|
||||
area_id: "living_room",
|
||||
attributes: {
|
||||
state_class: "measurement",
|
||||
unit_of_measurement: "°C",
|
||||
device_class: "temperature",
|
||||
friendly_name: "Temperature",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "sensor.living_room_humidity",
|
||||
state: "45",
|
||||
area_id: "living_room",
|
||||
attributes: {
|
||||
state_class: "measurement",
|
||||
unit_of_measurement: "%",
|
||||
device_class: "humidity",
|
||||
friendly_name: "Humidity",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.kitchen_spotlights",
|
||||
state: "on",
|
||||
area_id: "kitchen",
|
||||
attributes: {
|
||||
supported_color_modes: ["brightness"],
|
||||
color_mode: "brightness",
|
||||
brightness: 255,
|
||||
icon: "mdi:ceiling-light-multiple",
|
||||
friendly_name: "Spotlights",
|
||||
supported_features: 32,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.kitchen_worktop",
|
||||
state: "off",
|
||||
area_id: "kitchen",
|
||||
attributes: {
|
||||
supported_color_modes: ["brightness"],
|
||||
color_mode: null,
|
||||
brightness: null,
|
||||
icon: "mdi:light-flood-down",
|
||||
friendly_name: "Worktop",
|
||||
supported_features: 32,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "switch.coffee_machine",
|
||||
state: "on",
|
||||
area_id: "kitchen",
|
||||
attributes: {
|
||||
icon: "mdi:coffee-maker",
|
||||
friendly_name: "Coffee machine",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "binary_sensor.kitchen_motion",
|
||||
state: "off",
|
||||
area_id: "kitchen",
|
||||
attributes: {
|
||||
device_class: "motion",
|
||||
friendly_name: "Motion",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.kitchen_speaker",
|
||||
state: "idle",
|
||||
area_id: "kitchen",
|
||||
attributes: {
|
||||
device_class: "speaker",
|
||||
volume_level: 0.18,
|
||||
is_volume_muted: false,
|
||||
friendly_name: "Kitchen speaker",
|
||||
supported_features: 64063,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "sensor.kitchen_temperature",
|
||||
state: "22.1",
|
||||
area_id: "kitchen",
|
||||
attributes: {
|
||||
state_class: "measurement",
|
||||
unit_of_measurement: "°C",
|
||||
device_class: "temperature",
|
||||
friendly_name: "Temperature",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "sensor.kitchen_humidity",
|
||||
state: "48",
|
||||
area_id: "kitchen",
|
||||
attributes: {
|
||||
state_class: "measurement",
|
||||
unit_of_measurement: "%",
|
||||
device_class: "humidity",
|
||||
friendly_name: "Humidity",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "lock.front_door",
|
||||
state: "locked",
|
||||
area_id: "entrance",
|
||||
attributes: {
|
||||
friendly_name: "Front door",
|
||||
supported_features: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "binary_sensor.front_door",
|
||||
state: "off",
|
||||
area_id: "entrance",
|
||||
attributes: {
|
||||
device_class: "door",
|
||||
friendly_name: "Front door",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "alarm_control_panel.home_alarm",
|
||||
state: "disarmed",
|
||||
area_id: "entrance",
|
||||
attributes: {
|
||||
changed_by: null,
|
||||
code_arm_required: false,
|
||||
friendly_name: "Home alarm",
|
||||
supported_features: 3,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.entrance_ceiling",
|
||||
state: "off",
|
||||
area_id: "entrance",
|
||||
attributes: {
|
||||
supported_color_modes: ["brightness"],
|
||||
color_mode: null,
|
||||
brightness: null,
|
||||
friendly_name: "Ceiling light",
|
||||
supported_features: 32,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "sensor.front_door_lock_battery",
|
||||
state: "88",
|
||||
area_id: "entrance",
|
||||
attributes: {
|
||||
state_class: "measurement",
|
||||
unit_of_measurement: "%",
|
||||
device_class: "battery",
|
||||
friendly_name: "Front door lock battery",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.bedroom_ceiling",
|
||||
state: "off",
|
||||
area_id: "bedroom",
|
||||
attributes: {
|
||||
supported_color_modes: ["color_temp"],
|
||||
color_mode: null,
|
||||
brightness: null,
|
||||
friendly_name: "Ceiling light",
|
||||
supported_features: 44,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.bedside_lamp",
|
||||
state: "off",
|
||||
area_id: "bedroom",
|
||||
attributes: {
|
||||
supported_color_modes: ["brightness"],
|
||||
color_mode: null,
|
||||
brightness: null,
|
||||
icon: "mdi:lamp",
|
||||
friendly_name: "Bedside lamp",
|
||||
supported_features: 32,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "cover.bedroom_shutter",
|
||||
state: "open",
|
||||
area_id: "bedroom",
|
||||
attributes: {
|
||||
current_position: 100,
|
||||
device_class: "shutter",
|
||||
friendly_name: "Shutter",
|
||||
supported_features: 15,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "media_player.bedroom_speaker",
|
||||
state: "off",
|
||||
area_id: "bedroom",
|
||||
attributes: {
|
||||
device_class: "speaker",
|
||||
friendly_name: "Bedroom speaker",
|
||||
supported_features: 64063,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "binary_sensor.bedroom_motion",
|
||||
state: "off",
|
||||
area_id: "bedroom",
|
||||
attributes: {
|
||||
device_class: "motion",
|
||||
friendly_name: "Motion",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "sensor.bedroom_motion_battery",
|
||||
state: "15",
|
||||
area_id: "bedroom",
|
||||
attributes: {
|
||||
state_class: "measurement",
|
||||
unit_of_measurement: "%",
|
||||
device_class: "battery",
|
||||
friendly_name: "Motion sensor battery",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "sensor.bedroom_temperature",
|
||||
state: "19.6",
|
||||
area_id: "bedroom",
|
||||
attributes: {
|
||||
state_class: "measurement",
|
||||
unit_of_measurement: "°C",
|
||||
device_class: "temperature",
|
||||
friendly_name: "Temperature",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "sensor.bedroom_humidity",
|
||||
state: "52",
|
||||
area_id: "bedroom",
|
||||
attributes: {
|
||||
state_class: "measurement",
|
||||
unit_of_measurement: "%",
|
||||
device_class: "humidity",
|
||||
friendly_name: "Humidity",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.office_desk_lamp",
|
||||
state: "on",
|
||||
area_id: "office",
|
||||
attributes: {
|
||||
supported_color_modes: ["brightness"],
|
||||
color_mode: "brightness",
|
||||
brightness: 200,
|
||||
icon: "mdi:desk-lamp",
|
||||
friendly_name: "Desk lamp",
|
||||
supported_features: 32,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "fan.office_ceiling_fan",
|
||||
state: "off",
|
||||
area_id: "office",
|
||||
attributes: {
|
||||
percentage: 0,
|
||||
percentage_step: 25,
|
||||
friendly_name: "Ceiling fan",
|
||||
supported_features: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "binary_sensor.office_window",
|
||||
state: "off",
|
||||
area_id: "office",
|
||||
attributes: {
|
||||
device_class: "window",
|
||||
friendly_name: "Window",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "sensor.office_temperature",
|
||||
state: "21.8",
|
||||
area_id: "office",
|
||||
attributes: {
|
||||
state_class: "measurement",
|
||||
unit_of_measurement: "°C",
|
||||
device_class: "temperature",
|
||||
friendly_name: "Temperature",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "sensor.office_humidity",
|
||||
state: "50",
|
||||
area_id: "office",
|
||||
attributes: {
|
||||
state_class: "measurement",
|
||||
unit_of_measurement: "%",
|
||||
device_class: "humidity",
|
||||
friendly_name: "Humidity",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "switch.garden_sprinkler",
|
||||
state: "off",
|
||||
area_id: "garden",
|
||||
attributes: {
|
||||
icon: "mdi:sprinkler-variant",
|
||||
friendly_name: "Sprinkler",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "light.garden_path",
|
||||
state: "off",
|
||||
area_id: "garden",
|
||||
attributes: {
|
||||
supported_color_modes: ["brightness"],
|
||||
color_mode: null,
|
||||
brightness: null,
|
||||
icon: "mdi:outdoor-lamp",
|
||||
friendly_name: "Path lights",
|
||||
supported_features: 32,
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "binary_sensor.garden_gate",
|
||||
state: "off",
|
||||
area_id: "garden",
|
||||
attributes: {
|
||||
device_class: "opening",
|
||||
friendly_name: "Gate",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "sensor.garden_temperature",
|
||||
state: "14.2",
|
||||
area_id: "garden",
|
||||
attributes: {
|
||||
state_class: "measurement",
|
||||
unit_of_measurement: "°C",
|
||||
device_class: "temperature",
|
||||
friendly_name: "Temperature",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "sensor.garden_humidity",
|
||||
state: "68",
|
||||
area_id: "garden",
|
||||
attributes: {
|
||||
state_class: "measurement",
|
||||
unit_of_measurement: "%",
|
||||
device_class: "humidity",
|
||||
friendly_name: "Humidity",
|
||||
},
|
||||
},
|
||||
{
|
||||
entity_id: "weather.home",
|
||||
state: "partlycloudy",
|
||||
area_id: "garden",
|
||||
attributes: {
|
||||
temperature: 14.2,
|
||||
temperature_unit: "°C",
|
||||
humidity: 68,
|
||||
pressure: 1012,
|
||||
pressure_unit: "hPa",
|
||||
wind_speed: 11.2,
|
||||
wind_speed_unit: "km/h",
|
||||
friendly_name: "Home",
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,17 @@
|
||||
import type { DemoConfig } from "../types";
|
||||
import { demoAreasHome, demoFloorsHome } from "./areas";
|
||||
import { demoEntitiesHome } from "./entities";
|
||||
import { demoLovelaceHome } from "./lovelace";
|
||||
|
||||
export const demoHome: DemoConfig = {
|
||||
authorName: "Home Assistant",
|
||||
authorUrl: "https://www.home-assistant.io",
|
||||
name: "Home page",
|
||||
description:
|
||||
"The page you land on when you open Home Assistant, automatically built from the areas in your home and the devices in them.",
|
||||
lovelace: demoLovelaceHome,
|
||||
entities: demoEntitiesHome,
|
||||
floors: demoFloorsHome,
|
||||
areas: demoAreasHome,
|
||||
theme: { theme: "default", dark: false },
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
import "./strategies";
|
||||
import type { DemoConfig } from "../types";
|
||||
|
||||
export const demoLovelaceHome: DemoConfig["lovelace"] = () => ({
|
||||
strategy: {
|
||||
type: "custom:demo-home",
|
||||
favorite_entities: ["lock.front_door", "switch.garden_sprinkler"],
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,109 @@
|
||||
import { ReactiveElement } from "lit";
|
||||
import { isStrategySection } from "../../../../src/data/lovelace/config/section";
|
||||
import type { LovelaceConfig } from "../../../../src/data/lovelace/config/types";
|
||||
import type { LovelaceViewConfig } from "../../../../src/data/lovelace/config/view";
|
||||
import { isStrategyView } from "../../../../src/data/lovelace/config/view";
|
||||
import type { HomeDashboardStrategyConfig } from "../../../../src/panels/lovelace/strategies/home/home-dashboard-strategy";
|
||||
import { HomeDashboardStrategy } from "../../../../src/panels/lovelace/strategies/home/home-dashboard-strategy";
|
||||
import type { HomeOverviewViewStrategyConfig } from "../../../../src/panels/lovelace/strategies/home/home-overview-view-strategy";
|
||||
import { HomeOverviewViewStrategy } from "../../../../src/panels/lovelace/strategies/home/home-overview-view-strategy";
|
||||
import { generateLovelaceSectionStrategy } from "../../../../src/panels/lovelace/strategies/get-strategy";
|
||||
import type { HomeAssistant } from "../../../../src/types";
|
||||
|
||||
export interface DemoHomeDashboardStrategyConfig extends Omit<
|
||||
HomeDashboardStrategyConfig,
|
||||
"type"
|
||||
> {
|
||||
type: "custom:demo-home";
|
||||
}
|
||||
|
||||
interface DemoHomeOverviewViewStrategyConfig extends Omit<
|
||||
HomeOverviewViewStrategyConfig,
|
||||
"type"
|
||||
> {
|
||||
type: "custom:demo-home-overview";
|
||||
}
|
||||
|
||||
class DemoHomeDashboardStrategy extends ReactiveElement {
|
||||
static registryDependencies = HomeDashboardStrategy.registryDependencies;
|
||||
|
||||
static async generate(
|
||||
config: DemoHomeDashboardStrategyConfig,
|
||||
hass: HomeAssistant
|
||||
): Promise<LovelaceConfig> {
|
||||
const generated = await HomeDashboardStrategy.generate(
|
||||
{ ...config, type: "home" },
|
||||
hass
|
||||
);
|
||||
// Swap the overview view for the demo version, which adds the demo card.
|
||||
return {
|
||||
...generated,
|
||||
views: generated.views.map((view) =>
|
||||
isStrategyView(view) && view.strategy.type === "home-overview"
|
||||
? {
|
||||
...view,
|
||||
strategy: { ...view.strategy, type: "custom:demo-home-overview" },
|
||||
}
|
||||
: view
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class DemoHomeOverviewViewStrategy extends ReactiveElement {
|
||||
static registryDependencies = HomeOverviewViewStrategy.registryDependencies;
|
||||
|
||||
static async generate(
|
||||
config: DemoHomeOverviewViewStrategyConfig,
|
||||
hass: HomeAssistant
|
||||
): Promise<LovelaceViewConfig> {
|
||||
const view = await HomeOverviewViewStrategy.generate(
|
||||
{ ...config, type: "home-overview" },
|
||||
hass
|
||||
);
|
||||
// Expand the favorites section so the demo card can be added to it
|
||||
const sections = await Promise.all(
|
||||
(view.sections || []).map(async (section) => {
|
||||
if (
|
||||
!isStrategySection(section) ||
|
||||
section.strategy.type !== "common-controls"
|
||||
) {
|
||||
return section;
|
||||
}
|
||||
// The demo card takes up the space of two tiles
|
||||
const limit = (section.strategy.limit as number | undefined) ?? 8;
|
||||
const favorites = await generateLovelaceSectionStrategy(
|
||||
{ ...section, strategy: { ...section.strategy, limit: limit - 2 } },
|
||||
hass
|
||||
);
|
||||
const [heading, ...cards] = favorites.cards || [];
|
||||
return {
|
||||
...favorites,
|
||||
// Place the demo card first so the tiles fill the rows next to it
|
||||
cards: [heading, { type: "custom:ha-demo-next-card" }, ...cards],
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
return {
|
||||
...view,
|
||||
sections: sections,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define(
|
||||
"ll-strategy-dashboard-demo-home",
|
||||
DemoHomeDashboardStrategy
|
||||
);
|
||||
customElements.define(
|
||||
"ll-strategy-view-demo-home-overview",
|
||||
DemoHomeOverviewViewStrategy
|
||||
);
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ll-strategy-dashboard-demo-home": DemoHomeDashboardStrategy;
|
||||
"ll-strategy-view-demo-home-overview": DemoHomeOverviewViewStrategy;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { TemplateResult } from "lit";
|
||||
import type { LocalizeFunc } from "../../../src/common/translations/localize";
|
||||
import type { LovelaceConfig } from "../../../src/data/lovelace/config/types";
|
||||
import type { LovelaceRawConfig } from "../../../src/data/lovelace/config/types";
|
||||
import type { EntityInput } from "../../../src/fake_data/entities/types";
|
||||
import type { ThemeSettings } from "../../../src/types";
|
||||
import type { DemoArea } from "../stubs/area_registry";
|
||||
@@ -15,7 +15,7 @@ export interface DemoConfig {
|
||||
authorUrl: string;
|
||||
description?:
|
||||
string | ((localize: LocalizeFunc) => string | TemplateResult<1>);
|
||||
lovelace: (localize: LocalizeFunc) => LovelaceConfig;
|
||||
lovelace: (localize: LocalizeFunc) => LovelaceRawConfig;
|
||||
entities: (localize: LocalizeFunc) => EntityInput[];
|
||||
floors?: DemoFloor[];
|
||||
areas?: DemoArea[];
|
||||
|
||||
@@ -13,9 +13,9 @@ import type {
|
||||
LovelaceCard,
|
||||
} from "../../../src/panels/lovelace/types";
|
||||
import {
|
||||
demoConfigs,
|
||||
demos,
|
||||
selectedDemo,
|
||||
selectedDemoConfig,
|
||||
selectedDemoConfigIndex,
|
||||
} from "../configs/demo-configs";
|
||||
|
||||
@customElement("ha-demo-card")
|
||||
@@ -112,16 +112,12 @@ export class HADemoCard extends LitElement implements LovelaceCard {
|
||||
}
|
||||
|
||||
private _nextConfig() {
|
||||
this._updateConfig(
|
||||
selectedDemoConfigIndex < demoConfigs.length - 1
|
||||
? selectedDemoConfigIndex + 1
|
||||
: 0
|
||||
);
|
||||
this._updateConfig(demos[(demos.indexOf(selectedDemo) + 1) % demos.length]);
|
||||
}
|
||||
|
||||
private async _updateConfig(index: number) {
|
||||
private async _updateConfig(demo: string) {
|
||||
this._switching = true;
|
||||
fireEvent(this, "set-demo-config" as any, { index });
|
||||
fireEvent(this, "set-demo-config" as any, { demo });
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { until } from "lit/directives/until";
|
||||
import { fireEvent } from "../../../src/common/dom/fire_event";
|
||||
import "../../../src/components/ha-card";
|
||||
import "../../../src/components/ha-control-button";
|
||||
import "../../../src/components/ha-control-button-group";
|
||||
import "../../../src/components/tile/ha-tile-container";
|
||||
import "../../../src/components/tile/ha-tile-icon";
|
||||
import "../../../src/components/tile/ha-tile-info";
|
||||
import type { LovelaceCardConfig } from "../../../src/data/lovelace/config/card";
|
||||
import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||
import { tileCardStyle } from "../../../src/panels/lovelace/cards/tile/tile-card-style";
|
||||
import type {
|
||||
LovelaceCard,
|
||||
LovelaceGridOptions,
|
||||
} from "../../../src/panels/lovelace/types";
|
||||
import {
|
||||
demos,
|
||||
selectedDemo,
|
||||
selectedDemoConfig,
|
||||
} from "../configs/demo-configs";
|
||||
|
||||
@customElement("ha-demo-next-card")
|
||||
export class HADemoNextCard extends LitElement implements LovelaceCard {
|
||||
@property({ attribute: false }) public hass!: MockHomeAssistant;
|
||||
|
||||
@state() private _switching = false;
|
||||
|
||||
public getCardSize() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
public getGridOptions(): LovelaceGridOptions {
|
||||
return {
|
||||
columns: 6,
|
||||
rows: 2,
|
||||
min_columns: 6,
|
||||
min_rows: 2,
|
||||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
public setConfig(_config: LovelaceCardConfig) {}
|
||||
|
||||
protected render() {
|
||||
return html`
|
||||
<ha-card>
|
||||
<ha-tile-container>
|
||||
<ha-tile-icon slot="icon" icon="mdi:home-assistant"></ha-tile-icon>
|
||||
<ha-tile-info slot="info">
|
||||
<span slot="primary">
|
||||
${until(
|
||||
selectedDemoConfig.then((conf) => conf.name),
|
||||
nothing
|
||||
)}
|
||||
</span>
|
||||
<span slot="secondary">
|
||||
${this.hass.localize(
|
||||
"ui.panel.page-demo.cards.demo.interactive_demo"
|
||||
)}
|
||||
</span>
|
||||
</ha-tile-info>
|
||||
<ha-control-button-group slot="features">
|
||||
<ha-control-button
|
||||
.disabled=${this._switching}
|
||||
@click=${this._nextConfig}
|
||||
>
|
||||
${this.hass.localize("ui.panel.page-demo.cards.demo.next_demo")}
|
||||
</ha-control-button>
|
||||
</ha-control-button-group>
|
||||
</ha-tile-container>
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
private _nextConfig() {
|
||||
this._switching = true;
|
||||
fireEvent(this, "set-demo-config" as any, {
|
||||
demo: demos[(demos.indexOf(selectedDemo) + 1) % demos.length],
|
||||
});
|
||||
}
|
||||
|
||||
static styles = [
|
||||
tileCardStyle,
|
||||
css`
|
||||
:host {
|
||||
--tile-color: var(--primary-color);
|
||||
}
|
||||
ha-control-button-group {
|
||||
--control-button-group-spacing: 0;
|
||||
--control-button-group-thickness: var(--feature-height, 42px);
|
||||
padding: 0 var(--ha-space-3) var(--ha-space-3);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-demo-next-card": HADemoNextCard;
|
||||
}
|
||||
}
|
||||
@@ -12,21 +12,12 @@ export interface DemoArea {
|
||||
|
||||
let areas: AreaRegistryEntry[] = [];
|
||||
|
||||
const setAreas = (hass: MockHomeAssistant, data: AreaRegistryEntry[]) => {
|
||||
areas = data;
|
||||
const areasById: Record<string, AreaRegistryEntry> = {};
|
||||
areas.forEach((area) => {
|
||||
areasById[area.area_id] = area;
|
||||
});
|
||||
hass.updateHass({ areas: areasById });
|
||||
};
|
||||
|
||||
export const mockAreaRegistry = (
|
||||
hass: MockHomeAssistant,
|
||||
data: AreaRegistryEntry[] = []
|
||||
data: DemoArea[] = []
|
||||
) => {
|
||||
hass.mockWS("config/area_registry/list", () => areas);
|
||||
setAreas(hass, data);
|
||||
setDemoAreas(hass, data);
|
||||
};
|
||||
|
||||
/** Set the areas of the currently loaded demo config. */
|
||||
@@ -34,20 +25,22 @@ export const setDemoAreas = (
|
||||
hass: MockHomeAssistant,
|
||||
demoAreas: DemoArea[] = []
|
||||
) => {
|
||||
setAreas(
|
||||
hass,
|
||||
demoAreas.map((area) => ({
|
||||
area_id: area.area_id,
|
||||
name: area.name,
|
||||
floor_id: area.floor_id ?? null,
|
||||
icon: area.icon ?? null,
|
||||
temperature_entity_id: area.temperature_entity_id ?? null,
|
||||
humidity_entity_id: area.humidity_entity_id ?? null,
|
||||
aliases: [],
|
||||
labels: [],
|
||||
picture: null,
|
||||
created_at: 0,
|
||||
modified_at: 0,
|
||||
}))
|
||||
);
|
||||
areas = demoAreas.map((area) => ({
|
||||
area_id: area.area_id,
|
||||
name: area.name,
|
||||
floor_id: area.floor_id ?? null,
|
||||
icon: area.icon ?? null,
|
||||
temperature_entity_id: area.temperature_entity_id ?? null,
|
||||
humidity_entity_id: area.humidity_entity_id ?? null,
|
||||
aliases: [],
|
||||
labels: [],
|
||||
picture: null,
|
||||
created_at: 0,
|
||||
modified_at: 0,
|
||||
}));
|
||||
const areasById: Record<string, AreaRegistryEntry> = {};
|
||||
areas.forEach((area) => {
|
||||
areasById[area.area_id] = area;
|
||||
});
|
||||
hass.updateHass({ areas: areasById });
|
||||
};
|
||||
|
||||
@@ -85,7 +85,7 @@ export const energyEntities = () =>
|
||||
},
|
||||
},
|
||||
"sensor.energy_consumption_tarif_1": {
|
||||
entity_id: "sensor.energy_consumption_tarif_1 ",
|
||||
entity_id: "sensor.energy_consumption_tarif_1",
|
||||
state: "88.6",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
|
||||
@@ -10,21 +10,12 @@ export interface DemoFloor {
|
||||
|
||||
let floors: FloorRegistryEntry[] = [];
|
||||
|
||||
const setFloors = (hass: MockHomeAssistant, data: FloorRegistryEntry[]) => {
|
||||
floors = data;
|
||||
const floorsById: Record<string, FloorRegistryEntry> = {};
|
||||
floors.forEach((floor) => {
|
||||
floorsById[floor.floor_id] = floor;
|
||||
});
|
||||
hass.updateHass({ floors: floorsById });
|
||||
};
|
||||
|
||||
export const mockFloorRegistry = (
|
||||
hass: MockHomeAssistant,
|
||||
data: FloorRegistryEntry[] = []
|
||||
data: DemoFloor[] = []
|
||||
) => {
|
||||
hass.mockWS("config/floor_registry/list", () => floors);
|
||||
setFloors(hass, data);
|
||||
setDemoFloors(hass, data);
|
||||
};
|
||||
|
||||
/** Set the floors of the currently loaded demo config. */
|
||||
@@ -32,16 +23,18 @@ export const setDemoFloors = (
|
||||
hass: MockHomeAssistant,
|
||||
demoFloors: DemoFloor[] = []
|
||||
) => {
|
||||
setFloors(
|
||||
hass,
|
||||
demoFloors.map((floor) => ({
|
||||
floor_id: floor.floor_id,
|
||||
name: floor.name,
|
||||
level: floor.level ?? null,
|
||||
icon: floor.icon ?? null,
|
||||
aliases: [],
|
||||
created_at: 0,
|
||||
modified_at: 0,
|
||||
}))
|
||||
);
|
||||
floors = demoFloors.map((floor) => ({
|
||||
floor_id: floor.floor_id,
|
||||
name: floor.name,
|
||||
level: floor.level ?? null,
|
||||
icon: floor.icon ?? null,
|
||||
aliases: [],
|
||||
created_at: 0,
|
||||
modified_at: 0,
|
||||
}));
|
||||
const floorsById: Record<string, FloorRegistryEntry> = {};
|
||||
floors.forEach((floor) => {
|
||||
floorsById[floor.floor_id] = floor;
|
||||
});
|
||||
hass.updateHass({ floors: floorsById });
|
||||
};
|
||||
|
||||
@@ -2,12 +2,13 @@ import type { LocalizeFunc } from "../../../src/common/translations/localize";
|
||||
import type { LovelaceInfo } from "../../../src/data/lovelace/resource";
|
||||
import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||
import {
|
||||
selectedDemo,
|
||||
selectedDemoConfig,
|
||||
selectedDemoConfigIndex,
|
||||
setDemoConfig,
|
||||
} from "../configs/demo-configs";
|
||||
import "../custom-cards/cast-demo-row";
|
||||
import "../custom-cards/ha-demo-card";
|
||||
import "../custom-cards/ha-demo-next-card";
|
||||
import { mapEntities } from "./entities";
|
||||
|
||||
export const mockLovelace = (
|
||||
@@ -45,11 +46,11 @@ customElements.whenDefined("hui-root").then(() => {
|
||||
HUIRoot.prototype.firstUpdated = function (changedProperties) {
|
||||
oldFirstUpdated.call(this, changedProperties);
|
||||
this.addEventListener("set-demo-config", async (ev) => {
|
||||
const index = (ev as CustomEvent).detail.index;
|
||||
const demo = (ev as CustomEvent).detail.demo;
|
||||
try {
|
||||
await setDemoConfig(this.hass, this.lovelace!, index);
|
||||
await setDemoConfig(this.hass, this.lovelace!, demo);
|
||||
} catch (_err: any) {
|
||||
setDemoConfig(this.hass, this.lovelace!, selectedDemoConfigIndex);
|
||||
setDemoConfig(this.hass, this.lovelace!, selectedDemo);
|
||||
alert("Failed to switch config :-(");
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
import type { TemplateResult } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
import { mockAreaRegistry } from "../../../../demo/src/stubs/area_registry";
|
||||
import {
|
||||
mockAreaRegistry,
|
||||
type DemoArea,
|
||||
} from "../../../../demo/src/stubs/area_registry";
|
||||
import { mockConfigEntries } from "../../../../demo/src/stubs/config_entries";
|
||||
import { mockDeviceRegistry } from "../../../../demo/src/stubs/device_registry";
|
||||
import { mockEntityRegistry } from "../../../../demo/src/stubs/entity_registry";
|
||||
@@ -10,7 +13,6 @@ import { mockHassioSupervisor } from "../../../../demo/src/stubs/hassio_supervis
|
||||
import { computeInitialHaFormData } from "../../../../src/components/ha-form/compute-initial-ha-form-data";
|
||||
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 { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import type { HomeAssistant } from "../../../../src/types";
|
||||
@@ -136,45 +138,20 @@ const DEVICES: DeviceRegistryEntry[] = [
|
||||
},
|
||||
];
|
||||
|
||||
const AREAS: AreaRegistryEntry[] = [
|
||||
const AREAS: DemoArea[] = [
|
||||
{
|
||||
area_id: "backyard",
|
||||
floor_id: null,
|
||||
name: "Backyard",
|
||||
icon: null,
|
||||
picture: null,
|
||||
aliases: [],
|
||||
labels: [],
|
||||
temperature_entity_id: null,
|
||||
humidity_entity_id: null,
|
||||
created_at: 0,
|
||||
modified_at: 0,
|
||||
},
|
||||
{
|
||||
area_id: "bedroom",
|
||||
floor_id: null,
|
||||
name: "Bedroom",
|
||||
icon: "mdi:bed",
|
||||
picture: null,
|
||||
aliases: [],
|
||||
labels: [],
|
||||
temperature_entity_id: null,
|
||||
humidity_entity_id: null,
|
||||
created_at: 0,
|
||||
modified_at: 0,
|
||||
},
|
||||
{
|
||||
area_id: "livingroom",
|
||||
floor_id: null,
|
||||
name: "Livingroom",
|
||||
icon: "mdi:sofa",
|
||||
picture: null,
|
||||
aliases: [],
|
||||
labels: [],
|
||||
temperature_entity_id: null,
|
||||
humidity_entity_id: null,
|
||||
created_at: 0,
|
||||
modified_at: 0,
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -1,21 +1,25 @@
|
||||
import type { TemplateResult } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
import { mockAreaRegistry } from "../../../../demo/src/stubs/area_registry";
|
||||
import {
|
||||
mockAreaRegistry,
|
||||
type DemoArea,
|
||||
} from "../../../../demo/src/stubs/area_registry";
|
||||
import { mockConfigEntries } from "../../../../demo/src/stubs/config_entries";
|
||||
import { mockDeviceRegistry } from "../../../../demo/src/stubs/device_registry";
|
||||
import { mockEntityRegistry } from "../../../../demo/src/stubs/entity_registry";
|
||||
import { mockFloorRegistry } from "../../../../demo/src/stubs/floor_registry";
|
||||
import {
|
||||
mockFloorRegistry,
|
||||
type DemoFloor,
|
||||
} 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";
|
||||
import type { AreaRegistryEntry } from "../../../../src/data/area/area_registry";
|
||||
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,
|
||||
@@ -147,75 +151,43 @@ const DEVICES: DeviceRegistryEntry[] = [
|
||||
},
|
||||
];
|
||||
|
||||
const AREAS: AreaRegistryEntry[] = [
|
||||
const AREAS: DemoArea[] = [
|
||||
{
|
||||
area_id: "backyard",
|
||||
floor_id: "ground",
|
||||
name: "Backyard",
|
||||
icon: null,
|
||||
picture: null,
|
||||
aliases: [],
|
||||
labels: [],
|
||||
temperature_entity_id: null,
|
||||
humidity_entity_id: null,
|
||||
created_at: 0,
|
||||
modified_at: 0,
|
||||
},
|
||||
{
|
||||
area_id: "bedroom",
|
||||
floor_id: "first",
|
||||
name: "Bedroom",
|
||||
icon: "mdi:bed",
|
||||
picture: null,
|
||||
aliases: [],
|
||||
labels: [],
|
||||
temperature_entity_id: null,
|
||||
humidity_entity_id: null,
|
||||
created_at: 0,
|
||||
modified_at: 0,
|
||||
},
|
||||
{
|
||||
area_id: "livingroom",
|
||||
floor_id: "ground",
|
||||
name: "Livingroom",
|
||||
icon: "mdi:sofa",
|
||||
picture: null,
|
||||
aliases: [],
|
||||
labels: [],
|
||||
temperature_entity_id: null,
|
||||
humidity_entity_id: null,
|
||||
created_at: 0,
|
||||
modified_at: 0,
|
||||
},
|
||||
];
|
||||
|
||||
const FLOORS: FloorRegistryEntry[] = [
|
||||
const FLOORS: DemoFloor[] = [
|
||||
{
|
||||
floor_id: "ground",
|
||||
name: "Ground floor",
|
||||
level: 0,
|
||||
icon: null,
|
||||
aliases: [],
|
||||
created_at: 0,
|
||||
modified_at: 0,
|
||||
},
|
||||
{
|
||||
floor_id: "first",
|
||||
name: "First floor",
|
||||
level: 1,
|
||||
icon: "mdi:numeric-1",
|
||||
aliases: [],
|
||||
created_at: 0,
|
||||
modified_at: 0,
|
||||
},
|
||||
{
|
||||
floor_id: "second",
|
||||
name: "Second floor",
|
||||
level: 2,
|
||||
icon: "mdi:numeric-2",
|
||||
aliases: [],
|
||||
created_at: 0,
|
||||
modified_at: 0,
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
+2
-2
@@ -91,7 +91,7 @@
|
||||
"@webcomponents/webcomponentsjs": "2.8.0",
|
||||
"barcode-detector": "3.2.1",
|
||||
"cally": "0.9.2",
|
||||
"color-name": "2.1.0",
|
||||
"color-name": "2.1.1",
|
||||
"comlink": "4.4.2",
|
||||
"core-js": "3.49.0",
|
||||
"cropperjs": "1.6.2",
|
||||
@@ -196,7 +196,7 @@
|
||||
"jsdom": "29.1.1",
|
||||
"jszip": "3.10.1",
|
||||
"license-checker-rseidelsohn": "5.0.1",
|
||||
"lint-staged": "17.1.1",
|
||||
"lint-staged": "17.2.0",
|
||||
"lit-analyzer": "2.0.3",
|
||||
"lodash.merge": "4.6.2",
|
||||
"lodash.template": "4.18.1",
|
||||
|
||||
+11
-18
@@ -64,22 +64,15 @@ export const navigate = async (path: string, options?: NavigateOptions) => {
|
||||
const replace = options?.replace || false;
|
||||
|
||||
if (__DEMO__) {
|
||||
if (path.includes("#")) {
|
||||
if (replace) {
|
||||
mainWindow.history.replaceState(
|
||||
mainWindow.history.state?.root
|
||||
? { root: true }
|
||||
: (options?.data ?? null),
|
||||
"",
|
||||
path
|
||||
);
|
||||
} else {
|
||||
mainWindow.history.pushState(options?.data ?? null, "", path);
|
||||
}
|
||||
fireEvent(mainWindow, "location-changed", {
|
||||
replace,
|
||||
});
|
||||
return true;
|
||||
if (!path.includes("#")) {
|
||||
// The demo routes with the hash instead of the pathname. Resolve the
|
||||
// path like the browser would do for pushState, and keep the query
|
||||
// parameters in the URL query instead of inside the hash.
|
||||
const url = new URL(
|
||||
path,
|
||||
`${mainWindow.location.origin}${mainWindow.location.hash.substring(1)}`
|
||||
);
|
||||
path = `${mainWindow.location.pathname}${url.search}#${url.pathname}`;
|
||||
}
|
||||
if (replace) {
|
||||
mainWindow.history.replaceState(
|
||||
@@ -87,10 +80,10 @@ export const navigate = async (path: string, options?: NavigateOptions) => {
|
||||
? { root: true }
|
||||
: (options?.data ?? null),
|
||||
"",
|
||||
`${mainWindow.location.pathname}#${path}`
|
||||
path
|
||||
);
|
||||
} else {
|
||||
mainWindow.location.hash = path;
|
||||
mainWindow.history.pushState(options?.data ?? null, "", path);
|
||||
}
|
||||
} else if (replace) {
|
||||
mainWindow.history.replaceState(
|
||||
|
||||
@@ -108,7 +108,7 @@ class StepFlowForm extends LitElement {
|
||||
: nothing
|
||||
}
|
||||
${
|
||||
step.data_schema.length
|
||||
step.data_schema.length || this._errors
|
||||
? html`<ha-form
|
||||
${ref(this._formRef)}
|
||||
?autofocus=${this.autoFocus}
|
||||
|
||||
@@ -130,6 +130,8 @@ export class MockBaseEntity {
|
||||
"entity_picture",
|
||||
"assumed_state",
|
||||
"device_class",
|
||||
"state_class",
|
||||
"unit_of_measurement",
|
||||
"supported_features",
|
||||
]) {
|
||||
if (key in attrs) {
|
||||
|
||||
@@ -173,11 +173,7 @@ export class HomeAssistantAppEl extends QuickBarMixin(HassElement) {
|
||||
window.addEventListener("location-changed", () => updateRoute());
|
||||
|
||||
// Handle history changes
|
||||
if (useHash) {
|
||||
window.addEventListener("hashchange", () => updateRoute());
|
||||
} else {
|
||||
window.addEventListener("popstate", () => updateRoute());
|
||||
}
|
||||
window.addEventListener("popstate", () => updateRoute());
|
||||
|
||||
// Handle clicking on links
|
||||
window.addEventListener("click", (ev) => {
|
||||
|
||||
@@ -101,8 +101,7 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
|
||||
const columns = Math.floor(
|
||||
(totalWidth - padding + columnGap) / (minColumnWidth + columnGap)
|
||||
);
|
||||
const maxColumns = this._config?.max_columns ?? DEFAULT_MAX_COLUMNS;
|
||||
return clamp(columns, 1, maxColumns);
|
||||
return Math.max(columns, 1);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -155,7 +154,14 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
|
||||
}
|
||||
|
||||
private _updateMaxColumnCount(): void {
|
||||
const maxColumnCount = this._columnsController.value ?? 1;
|
||||
// The column count is clamped here instead of in the resize callback, so
|
||||
// that it follows the config of the view the element is currently used for
|
||||
const maxColumns = this._config?.max_columns ?? DEFAULT_MAX_COLUMNS;
|
||||
const maxColumnCount = clamp(
|
||||
this._columnsController.value ?? 1,
|
||||
1,
|
||||
maxColumns
|
||||
);
|
||||
|
||||
if (maxColumnCount !== this._maxColumns) {
|
||||
this._maxColumns = maxColumnCount;
|
||||
|
||||
@@ -11252,6 +11252,7 @@
|
||||
"cards": {
|
||||
"demo": {
|
||||
"demo_by": "by {name}",
|
||||
"interactive_demo": "Interactive demo",
|
||||
"next_demo": "Next demo",
|
||||
"introduction": "Welcome home! You've reached the Home Assistant demo where we showcase the best UIs created by our community.",
|
||||
"learn_more": "Learn more about Home Assistant"
|
||||
|
||||
@@ -7671,10 +7671,10 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"color-name@npm:2.1.0":
|
||||
version: 2.1.0
|
||||
resolution: "color-name@npm:2.1.0"
|
||||
checksum: 10/eb014f71d87408e318e95d3f554f188370d354ba8e0ffa4341d0fd19de391bfe2bc96e563d4f6614644d676bc24f475560dffee3fe310c2d6865d007410a9a2b
|
||||
"color-name@npm:2.1.1":
|
||||
version: 2.1.1
|
||||
resolution: "color-name@npm:2.1.1"
|
||||
checksum: 10/17bbb03a1e64299e5f61ccc514c502baf66980b88d013ca0161e8c0009edc3813ab3ffc8558ae99b8633d23c2e94b6551a248015b86cb5f3a84b645b4d0f3bb5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -9986,7 +9986,7 @@ __metadata:
|
||||
barcode-detector: "npm:3.2.1"
|
||||
browserslist-useragent-regexp: "npm:4.1.4"
|
||||
cally: "npm:0.9.2"
|
||||
color-name: "npm:2.1.0"
|
||||
color-name: "npm:2.1.1"
|
||||
comlink: "npm:4.4.2"
|
||||
core-js: "npm:3.49.0"
|
||||
cropperjs: "npm:1.6.2"
|
||||
@@ -10030,7 +10030,7 @@ __metadata:
|
||||
leaflet-draw: "patch:leaflet-draw@npm%3A1.0.4#./.yarn/patches/leaflet-draw-npm-1.0.4-0ca0ebcf65.patch"
|
||||
leaflet.markercluster: "npm:1.5.3"
|
||||
license-checker-rseidelsohn: "npm:5.0.1"
|
||||
lint-staged: "npm:17.1.1"
|
||||
lint-staged: "npm:17.2.0"
|
||||
lit: "npm:3.3.3"
|
||||
lit-analyzer: "npm:2.0.3"
|
||||
lit-html: "npm:3.3.3"
|
||||
@@ -11423,9 +11423,9 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lint-staged@npm:17.1.1":
|
||||
version: 17.1.1
|
||||
resolution: "lint-staged@npm:17.1.1"
|
||||
"lint-staged@npm:17.2.0":
|
||||
version: 17.2.0
|
||||
resolution: "lint-staged@npm:17.2.0"
|
||||
dependencies:
|
||||
picomatch: "npm:^4.0.5"
|
||||
string-argv: "npm:^0.3.2"
|
||||
@@ -11436,7 +11436,7 @@ __metadata:
|
||||
optional: true
|
||||
bin:
|
||||
lint-staged: bin/lint-staged.js
|
||||
checksum: 10/a54e063a579c093cecd4fc210f78c424fa0aa1dbe8196cf57160fad05acbfe4cb60ddd50751c799277094a0b7949de0ea3b61392b90e137f5c5bdf8473826e72
|
||||
checksum: 10/7c26129d6dd27f20d8fc8d70b13252f15baf569124721d7f5e9ed289e054ed43f5cc2a35de4129b65a7c7f1ad4a5900859a7488f8a1f0b96428b995b29ec2ba8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
Reference in New Issue
Block a user