Compare commits

..

6 Commits

Author SHA1 Message Date
Aidan Timson ba22a12a20 Fix 2026-03-03 14:46:42 +00:00
Aidan Timson 098b54f749 Fix 2026-03-03 14:46:15 +00:00
Aidan Timson 4c6a7091a6 Filtering 2026-03-03 14:46:15 +00:00
Aidan Timson 322cb35526 More types 2026-03-03 14:46:15 +00:00
Aidan Timson c34f6bea2b Always show 2026-03-03 14:46:15 +00:00
Aidan Timson 41bf0652b0 Setup log classification 2026-03-03 14:46:15 +00:00
633 changed files with 17830 additions and 29496 deletions
+1 -8
View File
@@ -464,14 +464,7 @@ this.hass.localize("ui.panel.config.updates.update_available", {
### 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
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. 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.
### Text and Copy Guidelines
-3
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
+2 -7
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
+4 -13
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
+3 -8
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@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1
uses: github/codeql-action/init@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4
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@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1
uses: github/codeql-action/autobuild@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4
# ️ 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@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1
uses: github/codeql-action/analyze@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4
+2 -7
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
+1 -6
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
+1 -6
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
+1 -1
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:
-4
View File
@@ -5,10 +5,6 @@ on:
schedule:
- cron: "0 * * * *"
permissions:
issues: write
pull-requests: write
jobs:
lock:
runs-on: ubuntu-latest
+1 -3
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
+9 -23
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) }}
+1 -1
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 }}
+16 -22
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
-4
View File
@@ -5,10 +5,6 @@ on:
schedule:
- cron: "0 * * * *"
permissions:
issues: write
pull-requests: write
jobs:
stale:
runs-on: ubuntu-latest
-5
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: |
+1 -1
View File
@@ -1 +1 @@
24.14.1
24.14.0
@@ -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
View File
File diff suppressed because one or more lines are too long
-940
View File
File diff suppressed because one or more lines are too long
+1 -1
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
+3 -9
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]));
-6
View File
@@ -57,12 +57,6 @@ const runDevServer = async ({
directory: contentBase,
watch: true,
},
client: {
overlay: {
runtimeErrors: (error) =>
!error?.message?.includes("ResizeObserver loop"),
},
},
proxy,
},
compiler
+4 -3
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",
+2 -1
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",
+2 -1
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",
+2 -1
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",
+6 -5
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,
+2 -1
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",
+2 -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;
}
+4 -2
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",
+173 -251
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,
}),
];
+67 -77
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",
}),
];
@@ -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 = [
@@ -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[] = [
@@ -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 = [
+23 -48
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,
},
],
},
];
@@ -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.
-232
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;
}
}
+24 -52
View File
@@ -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
);
};
@@ -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)`)
+1 -1
View File
@@ -19,7 +19,7 @@ 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>.
- 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
+14 -29
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 = [
+33 -68
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
+13 -28
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 = [
+138 -245
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 = [
@@ -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 = [
+63 -106
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 = [
+12 -32
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: `
+51 -78
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 = [
@@ -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 = [
+18 -33
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 = [
+49 -76
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 = [
+5 -8
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 = [
@@ -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 = [
@@ -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 = [
@@ -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 = [
+89 -112
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 = [
+97 -148
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 = [
+8 -15
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 = [
+97 -124
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>
`;
}
+128 -179
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>
`;
}
+10 -13
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>
`;
}
+17 -28
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>
`;
}
+20 -27
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>
`;
}
+6 -9
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>
`;
}
+127 -162
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>
`;
}
+9 -16
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>
`;
}
+1 -1
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>
`;
}
+38 -53
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>
`;
}
+9 -16
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>
`;
}
+6 -9
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>
`;
}
+120 -203
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>
`;
}
+9 -12
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>
`;
}
+29 -36
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>
`;
}
+1 -1
View File
@@ -118,7 +118,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");
+50 -43
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.2",
"@codemirror/language": "6.12.2",
"@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.15",
"@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.2",
"@formatjs/intl-displaynames": "7.2.1",
"@formatjs/intl-durationformat": "0.10.1",
"@formatjs/intl-getcanonicallocales": "3.2.1",
"@formatjs/intl-listformat": "8.2.1",
"@formatjs/intl-locale": "5.2.1",
"@formatjs/intl-numberformat": "9.2.2",
"@formatjs/intl-pluralrules": "6.2.2",
"@formatjs/intl-relativetimeformat": "12.2.2",
"@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.2.1-ha.3",
"@lezer/highlight": "1.2.3",
"@lit-labs/motion": "1.1.0",
"@lit-labs/observers": "2.1.0",
@@ -72,6 +72,7 @@
"@material/mwc-list": "patch:@material/mwc-list@npm%3A0.27.0#~/.yarn/patches/@material-mwc-list-npm-0.27.0-5344fc9de4.patch",
"@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",
@@ -87,13 +88,14 @@
"@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.1.0",
"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",
@@ -108,7 +110,7 @@
"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.2",
"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 +118,7 @@
"lit": "3.3.2",
"lit-html": "3.3.2",
"luxon": "3.7.2",
"marked": "17.0.5",
"marked": "17.0.3",
"memoize-one": "6.0.0",
"node-vibrant": "4.0.4",
"object-hash": "3.0.0",
@@ -129,6 +131,9 @@
"stacktrace-js": "2.0.2",
"superstruct": "2.0.2",
"tinykeys": "3.0.0",
"ua-parser-js": "2.0.9",
"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",
@@ -140,17 +145,17 @@
},
"devDependencies": {
"@babel/core": "7.29.0",
"@babel/helper-define-polyfill-provider": "0.6.8",
"@babel/helper-define-polyfill-provider": "0.6.6",
"@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/preset-env": "7.29.0",
"@bundle-stats/plugin-webpack-filter": "4.21.10",
"@html-eslint/eslint-plugin": "0.57.1",
"@lokalise/node-api": "15.6.1",
"@octokit/auth-oauth-device": "8.0.3",
"@octokit/plugin-retry": "8.1.0",
"@octokit/rest": "22.0.1",
"@rsdoctor/rspack-plugin": "1.5.5",
"@rspack/core": "1.7.10",
"@rsdoctor/rspack-plugin": "1.5.2",
"@rspack/core": "1.7.6",
"@rspack/dev-server": "1.2.1",
"@types/babel__plugin-transform-runtime": "7.9.5",
"@types/chromecast-caf-receiver": "6.0.25",
@@ -168,13 +173,14 @@
"@types/qrcode": "1.5.6",
"@types/sortablejs": "1.15.9",
"@types/tar": "7.0.87",
"@types/ua-parser-js": "0.7.39",
"@types/webspeechapi": "0.0.29",
"@vitest/coverage-v8": "4.1.1",
"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.3",
"eslint-config-airbnb-base": "15.0.0",
"eslint-config-prettier": "10.1.8",
"eslint-import-resolver-webpack": "0.13.10",
@@ -184,7 +190,7 @@
"eslint-plugin-unused-imports": "4.4.1",
"eslint-plugin-wc": "3.1.0",
"fancy-log": "2.0.0",
"fs-extra": "11.3.4",
"fs-extra": "11.3.3",
"glob": "13.0.6",
"gulp": "5.0.1",
"gulp-brotli": "3.0.0",
@@ -192,9 +198,9 @@
"gulp-rename": "2.1.0",
"html-minifier-terser": "7.2.0",
"husky": "9.1.7",
"jsdom": "29.0.1",
"jsdom": "28.1.0",
"jszip": "3.10.1",
"lint-staged": "16.4.0",
"lint-staged": "16.3.0",
"lit-analyzer": "2.0.3",
"lodash.merge": "4.6.2",
"lodash.template": "4.5.0",
@@ -202,32 +208,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.9",
"terser-webpack-plugin": "5.3.16",
"ts-lit-plugin": "2.0.2",
"typescript": "5.9.3",
"typescript-eslint": "8.57.2",
"typescript-eslint": "8.56.1",
"vite-tsconfig-paths": "6.1.1",
"vitest": "4.1.1",
"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.3.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.14.0"
}
}
+1 -1
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"
+5
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",
+61 -4
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,63 @@ export class HaAuthFormString extends HaFormString {
return this;
}
public connectedCallback(): void {
super.connectedCallback();
this.style.position = "relative";
public reportValidity(): boolean {
return this.querySelector("ha-auth-textfield")?.reportValidity() ?? true;
}
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;
--ha-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()}
`;
}
}
+1 -4
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;
}
+264
View File
@@ -0,0 +1,264 @@
/* eslint-disable lit/value-after-constraints */
/* eslint-disable lit/prefer-static-styles */
import { floatingLabel } from "@material/mwc-floating-label/mwc-floating-label-directive";
import type { TemplateResult } from "lit";
import { html } from "lit";
import { customElement } from "lit/decorators";
import { ifDefined } from "lit/directives/if-defined";
import { live } from "lit/directives/live";
import { HaTextField } from "../components/ha-textfield";
@customElement("ha-auth-textfield")
export class HaAuthTextField extends HaTextField {
protected renderLabel(): TemplateResult | string {
return !this.label
? ""
: html`
<span
.floatingLabelFoundation=${floatingLabel(
this.label
) as unknown as any}
.id=${this.name}
>${this.label}</span
>
`;
}
protected renderInput(shouldRenderHelperText: boolean): TemplateResult {
const minOrUndef = this.minLength === -1 ? undefined : this.minLength;
const maxOrUndef = this.maxLength === -1 ? undefined : this.maxLength;
const autocapitalizeOrUndef = this.autocapitalize
? (this.autocapitalize as
| "off"
| "none"
| "on"
| "sentences"
| "words"
| "characters")
: undefined;
const showValidationMessage = this.validationMessage && !this.isUiValid;
const ariaLabelledbyOrUndef = this.label ? this.name : undefined;
const ariaControlsOrUndef = shouldRenderHelperText
? "helper-text"
: undefined;
const ariaDescribedbyOrUndef =
this.focused || this.helperPersistent || showValidationMessage
? "helper-text"
: undefined;
// TODO: live() directive needs casting for lit-analyzer
// https://github.com/runem/lit-analyzer/pull/91/files
// TODO: lit-analyzer labels min/max as (number|string) instead of string
return html`<input
aria-labelledby=${ifDefined(ariaLabelledbyOrUndef)}
aria-controls=${ifDefined(ariaControlsOrUndef)}
aria-describedby=${ifDefined(ariaDescribedbyOrUndef)}
class="mdc-text-field__input"
type=${this.type}
.value=${live(this.value) as unknown as string}
?disabled=${this.disabled}
placeholder=${this.placeholder}
?required=${this.required}
?readonly=${this.readOnly}
minlength=${ifDefined(minOrUndef)}
maxlength=${ifDefined(maxOrUndef)}
pattern=${ifDefined(this.pattern ? this.pattern : undefined)}
min=${ifDefined(this.min === "" ? undefined : (this.min as number))}
max=${ifDefined(this.max === "" ? undefined : (this.max as number))}
step=${ifDefined(this.step === null ? undefined : (this.step as number))}
size=${ifDefined(this.size === null ? undefined : this.size)}
name=${ifDefined(this.name === "" ? undefined : this.name)}
inputmode=${ifDefined(this.inputMode)}
autocapitalize=${ifDefined(autocapitalizeOrUndef)}
?autofocus=${this.autofocus}
@input=${this.handleInputChange}
@focus=${this.onInputFocus}
@blur=${this.onInputBlur}
/>`;
}
public render() {
return html`
<style>
ha-auth-textfield {
display: inline-flex;
flex-direction: column;
outline: none;
}
ha-auth-textfield:not([disabled]):hover
:not(.mdc-text-field--invalid):not(.mdc-text-field--focused)
mwc-notched-outline {
--mdc-notched-outline-border-color: var(
--mdc-text-field-outlined-hover-border-color,
rgba(0, 0, 0, 0.87)
);
}
ha-auth-textfield:not([disabled])
.mdc-text-field:not(.mdc-text-field--outlined) {
background-color: var(--mdc-text-field-fill-color, whitesmoke);
}
ha-auth-textfield:not([disabled])
.mdc-text-field.mdc-text-field--invalid
mwc-notched-outline {
--mdc-notched-outline-border-color: var(
--mdc-text-field-error-color,
var(--mdc-theme-error, #b00020)
);
}
ha-auth-textfield:not([disabled])
.mdc-text-field.mdc-text-field--invalid
+ .mdc-text-field-helper-line
.mdc-text-field-character-counter,
ha-auth-textfield:not([disabled])
.mdc-text-field.mdc-text-field--invalid
.mdc-text-field__icon {
color: var(
--mdc-text-field-error-color,
var(--mdc-theme-error, #b00020)
);
}
ha-auth-textfield:not([disabled])
.mdc-text-field:not(.mdc-text-field--invalid):not(
.mdc-text-field--focused
)
.mdc-floating-label,
ha-auth-textfield:not([disabled])
.mdc-text-field:not(.mdc-text-field--invalid):not(
.mdc-text-field--focused
)
.mdc-floating-label::after {
color: var(--mdc-text-field-label-ink-color, rgba(0, 0, 0, 0.6));
}
ha-auth-textfield:not([disabled])
.mdc-text-field.mdc-text-field--focused
mwc-notched-outline {
--mdc-notched-outline-stroke-width: 2px;
}
ha-auth-textfield:not([disabled])
.mdc-text-field.mdc-text-field--focused:not(.mdc-text-field--invalid)
mwc-notched-outline {
--mdc-notched-outline-border-color: var(
--mdc-text-field-focused-label-color,
var(--mdc-theme-primary, rgba(98, 0, 238, 0.87))
);
}
ha-auth-textfield:not([disabled])
.mdc-text-field.mdc-text-field--focused:not(.mdc-text-field--invalid)
.mdc-floating-label {
color: #6200ee;
color: var(--mdc-theme-primary, #6200ee);
}
ha-auth-textfield:not([disabled])
.mdc-text-field
.mdc-text-field__input {
color: var(--mdc-text-field-ink-color, rgba(0, 0, 0, 0.87));
}
ha-auth-textfield:not([disabled])
.mdc-text-field
.mdc-text-field__input::placeholder {
color: var(--mdc-text-field-label-ink-color, rgba(0, 0, 0, 0.6));
}
ha-auth-textfield:not([disabled])
.mdc-text-field-helper-line
.mdc-text-field-helper-text:not(
.mdc-text-field-helper-text--validation-msg
),
ha-auth-textfield:not([disabled])
.mdc-text-field-helper-line:not(.mdc-text-field--invalid)
.mdc-text-field-character-counter {
color: var(--mdc-text-field-label-ink-color, rgba(0, 0, 0, 0.6));
}
ha-auth-textfield[disabled]
.mdc-text-field:not(.mdc-text-field--outlined) {
background-color: var(--mdc-text-field-disabled-fill-color, #fafafa);
}
ha-auth-textfield[disabled]
.mdc-text-field.mdc-text-field--outlined
mwc-notched-outline {
--mdc-notched-outline-border-color: var(
--mdc-text-field-outlined-disabled-border-color,
rgba(0, 0, 0, 0.06)
);
}
ha-auth-textfield[disabled]
.mdc-text-field:not(.mdc-text-field--invalid):not(
.mdc-text-field--focused
)
.mdc-floating-label,
ha-auth-textfield[disabled]
.mdc-text-field:not(.mdc-text-field--invalid):not(
.mdc-text-field--focused
)
.mdc-floating-label::after {
color: var(--mdc-text-field-disabled-ink-color, rgba(0, 0, 0, 0.38));
}
ha-auth-textfield[disabled] .mdc-text-field .mdc-text-field__input,
ha-auth-textfield[disabled]
.mdc-text-field
.mdc-text-field__input::placeholder {
color: var(--mdc-text-field-disabled-ink-color, rgba(0, 0, 0, 0.38));
}
ha-auth-textfield[disabled]
.mdc-text-field-helper-line
.mdc-text-field-helper-text,
ha-auth-textfield[disabled]
.mdc-text-field-helper-line
.mdc-text-field-character-counter {
color: var(--mdc-text-field-disabled-ink-color, rgba(0, 0, 0, 0.38));
}
ha-auth-textfield:not([disabled])
.mdc-text-field.mdc-text-field--focused:not(.mdc-text-field--invalid)
.mdc-floating-label {
color: var(--mdc-theme-primary, #6200ee);
}
ha-auth-textfield[no-spinner] input::-webkit-outer-spin-button,
ha-auth-textfield[no-spinner] input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
/* Firefox */
ha-auth-textfield[no-spinner] input[type="number"] {
-moz-appearance: textfield;
}
</style>
${super.render()}
`;
}
protected createRenderRoot() {
// add parent style to light dom
const style = document.createElement("style");
style.textContent = HaTextField.elementStyles as unknown as string;
this.append(style);
return this;
}
public firstUpdated() {
super.firstUpdated();
if (this.autofocus) {
this.focus();
}
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-auth-textfield": HaAuthTextField;
}
}
+3 -5
View File
@@ -58,11 +58,9 @@ export class CastManager {
this._eventListeners[event].push(listener);
return () => {
const listeners = this._eventListeners[event];
const index = listeners.indexOf(listener);
if (index !== -1) {
listeners.splice(index, 1);
}
this._eventListeners[event].splice(
this._eventListeners[event].indexOf(listener)
);
};
}
+2 -10
View File
@@ -32,17 +32,9 @@ const YAML_ONLY_THEMES_COLORS = new Set([
"disabled",
]);
export function computeCssVariableName(color: string): string {
if (THEME_COLORS.has(color) || YAML_ONLY_THEMES_COLORS.has(color)) {
return `--${color}-color`;
}
return color;
}
export function computeCssColor(color: string): string {
const cssVarName = computeCssVariableName(color);
if (cssVarName !== color) {
return `var(${cssVarName})`;
if (THEME_COLORS.has(color) || YAML_ONLY_THEMES_COLORS.has(color)) {
return `var(--${color}-color)`;
}
return color;
}
+29 -35
View File
@@ -1,17 +1,17 @@
import {
addDays,
subHours,
endOfDay,
endOfMonth,
endOfQuarter,
endOfWeek,
endOfYear,
startOfDay,
startOfMonth,
startOfQuarter,
startOfWeek,
startOfYear,
startOfQuarter,
endOfQuarter,
subDays,
subHours,
subMonths,
} from "date-fns";
import type { HomeAssistant } from "../../types";
@@ -33,89 +33,83 @@ export type DateRange =
| "now-24h";
export const calcDateRange = (
locale: HomeAssistant["locale"],
hassConfig: HomeAssistant["config"],
hass: HomeAssistant,
range: DateRange
): [Date, Date] => {
const today = new Date();
const weekStartsOn = firstWeekdayIndex(locale);
const weekStartsOn = firstWeekdayIndex(hass.locale);
switch (range) {
case "today":
return [
calcDate(today, startOfDay, locale, hassConfig, {
calcDate(today, startOfDay, hass.locale, hass.config, {
weekStartsOn,
}),
calcDate(today, endOfDay, locale, hassConfig, {
calcDate(today, endOfDay, hass.locale, hass.config, {
weekStartsOn,
}),
];
case "yesterday":
return [
calcDate(addDays(today, -1), startOfDay, locale, hassConfig, {
calcDate(addDays(today, -1), startOfDay, hass.locale, hass.config, {
weekStartsOn,
}),
calcDate(addDays(today, -1), endOfDay, locale, hassConfig, {
calcDate(addDays(today, -1), endOfDay, hass.locale, hass.config, {
weekStartsOn,
}),
];
case "this_week":
return [
calcDate(today, startOfWeek, locale, hassConfig, {
calcDate(today, startOfWeek, hass.locale, hass.config, {
weekStartsOn,
}),
calcDate(today, endOfWeek, locale, hassConfig, {
calcDate(today, endOfWeek, hass.locale, hass.config, {
weekStartsOn,
}),
];
case "this_month":
return [
calcDate(today, startOfMonth, locale, hassConfig),
calcDate(today, endOfMonth, locale, hassConfig),
calcDate(today, startOfMonth, hass.locale, hass.config),
calcDate(today, endOfMonth, hass.locale, hass.config),
];
case "this_quarter":
return [
calcDate(today, startOfQuarter, locale, hassConfig),
calcDate(today, endOfQuarter, locale, hassConfig),
calcDate(today, startOfQuarter, hass.locale, hass.config),
calcDate(today, endOfQuarter, hass.locale, hass.config),
];
case "this_year":
return [
calcDate(today, startOfYear, locale, hassConfig),
calcDate(today, endOfYear, locale, hassConfig),
calcDate(today, startOfYear, hass.locale, hass.config),
calcDate(today, endOfYear, hass.locale, hass.config),
];
case "now-7d":
return [
calcDate(today, subDays, locale, hassConfig, 7),
calcDate(today, subDays, locale, hassConfig, 0),
calcDate(today, subDays, hass.locale, hass.config, 7),
calcDate(today, subDays, hass.locale, hass.config, 0),
];
case "now-30d":
return [
calcDate(today, subDays, locale, hassConfig, 30),
calcDate(today, subDays, locale, hassConfig, 0),
calcDate(today, subDays, hass.locale, hass.config, 30),
calcDate(today, subDays, hass.locale, hass.config, 0),
];
case "now-12m":
return [
calcDate(
today,
(date) => subMonths(startOfMonth(date), 11),
locale,
hassConfig
),
calcDate(today, endOfMonth, locale, hassConfig),
calcDate(today, subMonths, hass.locale, hass.config, 12),
calcDate(today, subMonths, hass.locale, hass.config, 0),
];
case "now-1h":
return [
calcDate(today, subHours, locale, hassConfig, 1),
calcDate(today, subHours, locale, hassConfig, 0),
calcDate(today, subHours, hass.locale, hass.config, 1),
calcDate(today, subHours, hass.locale, hass.config, 0),
];
case "now-12h":
return [
calcDate(today, subHours, locale, hassConfig, 12),
calcDate(today, subHours, locale, hassConfig, 0),
calcDate(today, subHours, hass.locale, hass.config, 12),
calcDate(today, subHours, hass.locale, hass.config, 0),
];
case "now-24h":
return [
calcDate(today, subHours, locale, hassConfig, 24),
calcDate(today, subHours, locale, hassConfig, 0),
calcDate(today, subHours, hass.locale, hass.config, 24),
calcDate(today, subHours, hass.locale, hass.config, 0),
];
}
return [today, today];
-48
View File
@@ -166,21 +166,6 @@ const formatDateMonthMem = memoizeOne(
})
);
// Aug
export const formatDateMonthShort = (
dateObj: Date,
locale: FrontendLocaleData,
config: HassConfig
) => formatDateMonthShortMem(locale, config.time_zone).format(dateObj);
const formatDateMonthShortMem = memoizeOne(
(locale: FrontendLocaleData, serverTimeZone: string) =>
new Intl.DateTimeFormat(locale.language, {
month: "short",
timeZone: resolveTimeZone(locale.time_zone, serverTimeZone),
})
);
// 2021
export const formatDateYear = (
dateObj: Date,
@@ -261,36 +246,3 @@ const formatDateWeekdayShortDateMem = memoizeOne(
timeZone: resolveTimeZone(locale.time_zone, serverTimeZone),
})
);
/**
* Format a date as YYYY-MM-DD. Uses "en-CA" because it's the only
* Intl locale that natively outputs ISO 8601 date format.
* Locale/config are only used to resolve the time zone.
*/
export const formatISODateOnly = (
dateObj: Date,
locale: FrontendLocaleData,
config: HassConfig
) => {
const timeZone = resolveTimeZone(locale.time_zone, config.time_zone);
const formatter = new Intl.DateTimeFormat("en-CA", {
year: "numeric",
month: "2-digit",
day: "2-digit",
timeZone,
});
return formatter.format(dateObj);
};
// 2026-08-10/2026-08-15
export const formatCallyDateRange = (
start: Date,
end: Date,
locale: FrontendLocaleData,
config: HassConfig
) => {
const startDate = formatISODateOnly(start, locale, config);
const endDate = formatISODateOnly(end, locale, config);
return `${startDate}/${endDate}`;
};
+5 -5
View File
@@ -11,11 +11,11 @@ export const canOverrideAlphanumericInput = (composedPath: EventTarget[]) => {
const el = composedPath[0] as Element;
if (
el.tagName === "TEXTAREA" ||
el.parentElement?.tagName === "HA-SELECT" ||
el.parentElement?.tagName === "HA-DROPDOWN"
) {
if (el.tagName === "TEXTAREA") {
return false;
}
if (el.parentElement?.tagName === "HA-SELECT") {
return false;
}
@@ -5,7 +5,6 @@ import { computeAreaName } from "./compute_area_name";
import { computeDeviceName } from "./compute_device_name";
import { computeEntityName, entityUseDeviceName } from "./compute_entity_name";
import { computeFloorName } from "./compute_floor_name";
import { computeStateName } from "./compute_state_name";
import { getEntityContext } from "./context/get_entity_context";
const DEFAULT_SEPARATOR = " ";
@@ -30,23 +29,14 @@ export interface EntityNameOptions {
export const computeEntityNameDisplay = (
stateObj: HassEntity,
name: string | EntityNameItem | EntityNameItem[] | undefined,
name: EntityNameItem | EntityNameItem[] | undefined,
entities: HomeAssistant["entities"],
devices: HomeAssistant["devices"],
areas: HomeAssistant["areas"],
floors: HomeAssistant["floors"],
options?: EntityNameOptions
) => {
if (typeof name === "string") {
return name;
}
// If no name config is provided, fall back to the friendly name
if (!name) {
return computeStateName(stateObj);
}
let items = ensureArray(name);
let items = ensureArray(name || DEFAULT_ENTITY_NAME);
const separator = options?.separator ?? DEFAULT_SEPARATOR;
+1 -4
View File
@@ -142,8 +142,6 @@ const computeStateToPartsFromEntityAttributes = (
group: "value",
decimal: "value",
fraction: "value",
minusSign: "value",
plusSign: "value",
literal: "literal",
currency: "unit",
};
@@ -154,7 +152,7 @@ const computeStateToPartsFromEntityAttributes = (
const type = TYPE_MAP[part.type];
if (!type) continue;
const last = valueParts[valueParts.length - 1];
// Merge consecutive value parts (e.g. "-" + "12" + "." + "00" → "-12.00")
// Merge consecutive numeric parts (e.g. "1" + "," + "234" + "." + "56" → "1,234.56")
if (type === "value" && last?.type === "value") {
last.value += part.value;
} else {
@@ -255,7 +253,6 @@ const computeStateToPartsFromEntityAttributes = (
"conversation",
"event",
"image",
"infrared",
"input_button",
"notify",
"scene",
-3
View File
@@ -29,7 +29,6 @@ export const FIXED_DOMAIN_STATES = {
device_tracker: ["home", "not_home"],
fan: ["on", "off"],
humidifier: ["on", "off"],
infrared: [],
input_boolean: ["on", "off"],
input_button: [],
lawn_mower: ["error", "paused", "mowing", "returning", "docked"],
@@ -271,8 +270,6 @@ export const getStates = (
result.push(...state.attributes.preset_modes);
} else if (attribute === "swing_mode") {
result.push(...state.attributes.swing_modes);
} else if (attribute === "swing_horizontal_mode") {
result.push(...state.attributes.swing_horizontal_modes);
}
break;
case "device_tracker":
+1 -3
View File
@@ -6,9 +6,7 @@ export function stateActive(stateObj: HassEntity, state?: string): boolean {
const domain = computeDomain(stateObj.entity_id);
const compareState = state !== undefined ? state : stateObj?.state;
if (
["button", "event", "infrared", "input_button", "scene"].includes(domain)
) {
if (["button", "event", "input_button", "scene"].includes(domain)) {
return compareState !== UNAVAILABLE;
}
@@ -1,11 +0,0 @@
/**
* Indicates whether the current browser supports the Popover API.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/Popover_API
*/
export const popoverSupported = globalThis?.HTMLElement?.prototype
? Object.prototype.hasOwnProperty.call(
globalThis.HTMLElement.prototype,
"popover"
)
: false;
+7
View File
@@ -71,6 +71,13 @@ export const formatNumberToParts = (
? numberFormatToLocale(localeOptions)
: undefined;
// Polyfill for Number.isNaN, which is more reliable than the global isNaN()
Number.isNaN =
Number.isNaN ||
function isNaN(input) {
return typeof input === "number" && isNaN(input);
};
if (
localeOptions?.number_format !== NumberFormat.none &&
!Number.isNaN(Number(num))
+20 -1
View File
@@ -1,5 +1,24 @@
import { deepActiveElement } from "../dom/deep-active-element";
const getClipboardFallbackRoot = (): HTMLElement => {
const activeElement = deepActiveElement();
if (activeElement instanceof HTMLElement) {
let root: Node = activeElement.getRootNode();
let host: HTMLElement | null = null;
while (root instanceof ShadowRoot && root.host instanceof HTMLElement) {
host = root.host;
root = root.host.getRootNode();
}
if (host) {
return host;
}
}
return document.body;
};
export const copyToClipboard = async (str, rootEl?: HTMLElement) => {
if (navigator.clipboard) {
try {
@@ -10,7 +29,7 @@ export const copyToClipboard = async (str, rootEl?: HTMLElement) => {
}
}
const root = rootEl || deepActiveElement()?.getRootNode() || document.body;
const root = rootEl || getClipboardFallbackRoot();
const el = document.createElement("textarea");
el.value = str;
+10 -24
View File
@@ -1,8 +1,9 @@
// From: https://davidwalsh.name/javascript-debounce-function
// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge. The trailing edge only fires if there were additional calls
// during the wait period.
// leading edge and on the trailing.
export const debounce = <T extends any[]>(
func: (...args: T) => void,
@@ -10,35 +11,20 @@ export const debounce = <T extends any[]>(
immediate = false
) => {
let timeout: number | undefined;
let trailingArgs: T | undefined;
const debouncedFunc = (...args: T): void => {
const isLeading = immediate && !timeout;
if (timeout) {
trailingArgs = args;
}
clearTimeout(timeout);
timeout = window.setTimeout(() => {
const later = () => {
timeout = undefined;
if (trailingArgs) {
func(...trailingArgs);
trailingArgs = undefined;
} else if (!immediate) {
func(...args);
}
}, wait);
if (isLeading) {
func(...args);
};
const callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = window.setTimeout(later, wait);
if (callNow) {
func(...args);
}
};
debouncedFunc.cancel = () => {
clearTimeout(timeout);
trailingArgs = undefined;
};
return debouncedFunc;
};
+21 -71
View File
@@ -44,7 +44,6 @@ export type CustomLegendOption = ECOption["legend"] & {
id?: string;
secondaryIds?: string[]; // Other dataset IDs that should be controlled by this legend item.
name: string;
value?: string; // Current value to display next to the name in the legend.
itemStyle?: Record<string, any>;
}[];
};
@@ -280,23 +279,18 @@ export class HaChartBase extends LitElement {
<div class="chart"></div>
</div>
${this._renderLegend()}
<div class="top-controls ${classMap({ small: this.smallControls })}">
<slot name="search"></slot>
<div
class="chart-controls ${classMap({ small: this.smallControls })}"
>
${this._isZoomed && !this.hideResetButton
? html`<ha-icon-button
class="zoom-reset"
.path=${mdiRestart}
@click=${this._handleZoomReset}
title=${this.hass.localize(
"ui.components.history_charts.zoom_reset"
)}
></ha-icon-button>`
: nothing}
<slot name="button"></slot>
</div>
<div class="chart-controls ${classMap({ small: this.smallControls })}">
${this._isZoomed && !this.hideResetButton
? html`<ha-icon-button
class="zoom-reset"
.path=${mdiRestart}
@click=${this._handleZoomReset}
title=${this.hass.localize(
"ui.components.history_charts.zoom_reset"
)}
></ha-icon-button>`
: nothing}
<slot name="button"></slot>
</div>
</div>
`;
@@ -339,14 +333,12 @@ export class HaChartBase extends LitElement {
let itemStyle: Record<string, any> = {};
let name = "";
let id = "";
let value = "";
if (typeof item === "string") {
name = item;
id = item;
} else {
name = item.name ?? "";
id = item.id ?? name;
value = item.value ?? "";
itemStyle = item.itemStyle ?? {};
}
const dataset =
@@ -373,7 +365,6 @@ export class HaChartBase extends LitElement {
})}
></div>
<div class="label">${name}</div>
${value ? html`<div class="value">${value}</div>` : nothing}
</li>`;
})}
${items.length > overflowLimit
@@ -587,10 +578,7 @@ export class HaChartBase extends LitElement {
id: "dataZoom",
type: "inside",
orient: "horizontal",
// "boundaryFilter" is a custom mode added via axis-proxy-patch.ts.
// It rescales the Y-axis to the visible data while keeping one point
// just outside each boundary to avoid line gaps at the zoom edges.
filterMode: "boundaryFilter" as any,
filterMode: "none",
xAxisIndex: 0,
moveOnMouseMove: !this._isTouchDevice || this._isZoomed,
preventDefaultMouseMove: !this._isTouchDevice || this._isZoomed,
@@ -889,20 +877,10 @@ export class HaChartBase extends LitElement {
};
}
if (s.sampling === "minmax") {
const minX = xAxis?.min
? xAxis.min instanceof Date
? xAxis.min.getTime()
: typeof xAxis.min === "number"
? xAxis.min
: undefined
: undefined;
const maxX = xAxis?.max
? xAxis.max instanceof Date
? xAxis.max.getTime()
: typeof xAxis.max === "number"
? xAxis.max
: undefined
: undefined;
const minX =
xAxis?.min && typeof xAxis.min === "number" ? xAxis.min : undefined;
const maxX =
xAxis?.max && typeof xAxis.max === "number" ? xAxis.max : undefined;
return {
...s,
sampling: undefined,
@@ -1121,35 +1099,16 @@ export class HaChartBase extends LitElement {
height: 100%;
width: 100%;
}
.top-controls {
position: absolute;
top: var(--ha-space-4);
inset-inline-start: var(--ha-space-4);
inset-inline-end: var(--ha-space-1);
display: flex;
align-items: flex-start;
gap: var(--ha-space-2);
z-index: 1;
pointer-events: none;
}
::slotted([slot="search"]) {
flex: 1 1 250px;
min-width: 0;
max-width: 250px;
pointer-events: auto;
}
.chart-controls {
position: absolute;
top: 16px;
right: 4px;
display: flex;
flex-direction: column;
gap: var(--ha-space-1);
margin-inline-start: auto;
flex-shrink: 0;
pointer-events: auto;
}
.top-controls.small {
top: 0;
}
.chart-controls.small {
top: 0;
flex-direction: row;
}
.chart-controls ha-icon-button,
@@ -1197,9 +1156,6 @@ export class HaChartBase extends LitElement {
.chart-legend.multiple-items li {
max-width: 220px;
}
.chart-legend.multiple-items li:has(.value) {
max-width: 300px;
}
.chart-legend .hidden {
color: var(--secondary-text-color);
}
@@ -1208,12 +1164,6 @@ export class HaChartBase extends LitElement {
white-space: nowrap;
overflow: hidden;
}
.chart-legend .value {
color: var(--secondary-text-color);
margin-inline-start: var(--ha-space-1);
flex-shrink: 0;
white-space: nowrap;
}
.chart-legend .bullet {
border-width: 1px;
border-style: solid;
+4 -128
View File
@@ -1,9 +1,7 @@
import type { EChartsType } from "echarts/core";
import type { GraphSeriesOption } from "echarts/charts";
import type { PropertyValues } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state, query } from "lit/decorators";
import type {
CallbackDataParams,
TopLevelFormatterParams,
@@ -78,20 +76,8 @@ export class HaNetworkGraph extends SubscribeMixin(LitElement) {
params: TopLevelFormatterParams
) => string;
/**
* Optional callback that returns additional searchable strings for a node.
* These are matched against the search filter in addition to the node's name and context.
*/
@property({ attribute: false }) public searchableAttributes?: (
nodeId: string
) => string[];
@property({ attribute: false }) public searchFilter = "";
public hass!: HomeAssistant;
@state() private _highlightedNodes?: Set<string>;
@state() private _reducedMotion = false;
@state() private _physicsEnabled = true;
@@ -131,9 +117,6 @@ export class HaNetworkGraph extends SubscribeMixin(LitElement) {
"all and (max-width: 450px), all and (max-height: 500px)"
).matches;
const hasHighlightedNodes =
this._highlightedNodes && this._highlightedNodes.size > 0;
return html`<ha-chart-base
.hass=${this.hass}
.data=${this._getSeries(
@@ -141,14 +124,12 @@ export class HaNetworkGraph extends SubscribeMixin(LitElement) {
this._physicsEnabled,
this._reducedMotion,
this._showLabels,
isMobile,
hasHighlightedNodes
isMobile
)}
.options=${this._createOptions(this.data?.categories)}
height="100%"
.extraComponents=${[GraphChart]}
>
<slot name="search" slot="search"></slot>
<slot name="button" slot="button"></slot>
<ha-icon-button
slot="button"
@@ -184,7 +165,7 @@ export class HaNetworkGraph extends SubscribeMixin(LitElement) {
...category,
icon: category.symbol,
})),
bottom: 8,
top: 8,
},
dataZoom: {
type: "inside",
@@ -194,56 +175,13 @@ export class HaNetworkGraph extends SubscribeMixin(LitElement) {
deepEqual
);
protected updated(changedProperties: PropertyValues): void {
super.updated(changedProperties);
if (changedProperties.has("searchFilter")) {
const filter = this.searchFilter;
if (!filter) {
this._highlightedNodes = undefined;
} else {
const lowerFilter = filter.toLowerCase();
const matchingIds = new Set<string>();
for (const node of this.data.nodes) {
if (this._nodeMatchesFilter(node, lowerFilter)) {
matchingIds.add(node.id);
}
}
this._highlightedNodes = matchingIds;
}
this._applyHighlighting();
this._updateMouseoverHandler();
}
}
private _nodeMatchesFilter(node: NetworkNode, lowerFilter: string): boolean {
if (node.name?.toLowerCase().includes(lowerFilter)) {
return true;
}
if (node.context?.toLowerCase().includes(lowerFilter)) {
return true;
}
if (node.id?.toLowerCase().includes(lowerFilter)) {
return true;
}
if (this.searchableAttributes) {
const extraValues = this.searchableAttributes(node.id);
for (const value of extraValues) {
if (value?.toLowerCase().includes(lowerFilter)) {
return true;
}
}
}
return false;
}
private _getSeries = memoizeOne(
(
data: NetworkData,
physicsEnabled: boolean,
reducedMotion: boolean,
showLabels: boolean,
isMobile: boolean,
hasHighlightedNodes?: boolean
isMobile: boolean
) => ({
id: "network",
type: "graph",
@@ -276,7 +214,7 @@ export class HaNetworkGraph extends SubscribeMixin(LitElement) {
},
},
emphasis: {
focus: hasHighlightedNodes ? "self" : isMobile ? "none" : "adjacency",
focus: isMobile ? "none" : "adjacency",
},
force: {
repulsion: [400, 600],
@@ -424,68 +362,6 @@ export class HaNetworkGraph extends SubscribeMixin(LitElement) {
});
}
private _applyHighlighting() {
const chart = this._baseChart?.chart;
if (!chart) {
return;
}
// Reset all nodes to normal opacity first
chart.dispatchAction({ type: "downplay" });
const highlighted = this._highlightedNodes;
if (!highlighted || highlighted.size === 0) {
return;
}
const dataIndices: number[] = [];
this.data.nodes.forEach((node, index) => {
if (highlighted.has(node.id)) {
dataIndices.push(index);
}
});
if (dataIndices.length > 0) {
chart.dispatchAction({ type: "highlight", dataIndex: dataIndices });
}
}
private _emphasisGuardHandler?: () => void;
private _updateMouseoverHandler() {
const chart = this._baseChart?.chart;
if (!chart) {
return;
}
// When there are highlighted nodes, re-apply highlighting on hover
// and mouseout to prevent hover from overriding the search state
if (this._highlightedNodes && this._highlightedNodes.size > 0) {
if (this._emphasisGuardHandler) {
// Guard already set
return;
}
this._emphasisGuardHandler = () => {
this._applyHighlighting();
};
chart.on("mouseover", this._emphasisGuardHandler);
chart.on("mouseout", this._emphasisGuardHandler);
} else {
if (!this._emphasisGuardHandler) {
return;
}
chart.off("mouseover", this._emphasisGuardHandler);
chart.off("mouseout", this._emphasisGuardHandler);
this._emphasisGuardHandler = undefined;
}
}
public disconnectedCallback(): void {
super.disconnectedCallback();
if (this._emphasisGuardHandler) {
this._baseChart?.chart?.off("mouseover", this._emphasisGuardHandler);
this._baseChart?.chart?.off("mouseout", this._emphasisGuardHandler);
this._emphasisGuardHandler = undefined;
}
}
private _togglePhysics() {
this._saveNodePositions();
this._physicsEnabled = !this._physicsEnabled;
@@ -28,13 +28,6 @@ const safeParseFloat = (value) => {
return isFinite(parsed) ? parsed : null;
};
const CLIMATE_MODE_CONFIGS = [
{ mode: "heat", action: "heating", cssVar: "--state-climate-heat-color" },
{ mode: "cool", action: "cooling", cssVar: "--state-climate-cool-color" },
{ mode: "dry", action: "drying", cssVar: "--state-climate-dry-color" },
{ mode: "fan_only", action: "fan", cssVar: "--state-climate-fan_only-color" },
] as const;
@customElement("state-history-chart-line")
export class StateHistoryChartLine extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@@ -246,9 +239,7 @@ export class StateHistoryChartLine extends LitElement {
changedProps.has("fitYData") ||
changedProps.has("paddingYAxis") ||
changedProps.has("_visualMap") ||
changedProps.has("_yWidth") ||
(changedProps.has("hass") &&
this._hasEntityStatesChanged(changedProps.get("hass")))
changedProps.has("_yWidth")
) {
const rtl = computeRTL(this.hass);
let minYAxis: number | ((values: { min: number }) => number) | undefined =
@@ -305,19 +296,6 @@ export class StateHistoryChartLine extends LitElement {
legend: {
type: "custom",
show: this.showNames,
data: this._chartData
.map((d, i) => ({ dataset: d, entityId: this._entityIds[i] }))
.filter((item) => !(item.dataset as LineSeriesOption).areaStyle)
.map((item) => {
const stateObj = this.hass.states[item.entityId];
return {
id: item.dataset.id as string,
name: item.dataset.name as string,
value: stateObj
? this.hass.formatEntityState(stateObj)
: undefined,
};
}),
},
grid: {
top: 15,
@@ -338,13 +316,6 @@ export class StateHistoryChartLine extends LitElement {
}
}
private _hasEntityStatesChanged(oldHass: HomeAssistant): boolean {
return this._entityIds.some(
(entityId) =>
this.hass.states[entityId]?.state !== oldHass.states[entityId]?.state
);
}
private _generateData() {
let colorIndex = 0;
const computedStyles = getComputedStyle(this);
@@ -436,18 +407,23 @@ export class StateHistoryChartLine extends LitElement {
(entityState) => entityState.attributes?.hvac_action
);
const activeModes = CLIMATE_MODE_CONFIGS.map(
({ mode, action, cssVar }) => {
const isActive =
domain === "climate" && hasHvacAction
? (entityState: LineChartState) =>
CLIMATE_HVAC_ACTION_TO_MODE[
entityState.attributes?.hvac_action
] === mode
: (entityState: LineChartState) => entityState.state === mode;
return { action, cssVar, isActive };
}
).filter(({ isActive }) => states.states.some(isActive));
const isHeating =
domain === "climate" && hasHvacAction
? (entityState: LineChartState) =>
CLIMATE_HVAC_ACTION_TO_MODE[
entityState.attributes?.hvac_action
] === "heat"
: (entityState: LineChartState) => entityState.state === "heat";
const isCooling =
domain === "climate" && hasHvacAction
? (entityState: LineChartState) =>
CLIMATE_HVAC_ACTION_TO_MODE[
entityState.attributes?.hvac_action
] === "cool"
: (entityState: LineChartState) => entityState.state === "cool";
const hasHeat = states.states.some(isHeating);
const hasCool = states.states.some(isCooling);
// We differentiate between thermostats that have a target temperature
// range versus ones that have just a target temperature
@@ -468,19 +444,33 @@ export class StateHistoryChartLine extends LitElement {
"component.climate.entity_component._.state_attributes.current_temperature.name"
)
);
for (const { action, cssVar } of activeModes) {
if (hasHeat) {
addDataSet(
`${states.entity_id}-${action}`,
states.entity_id + "-heating",
this.showNames
? this.hass.localize(`ui.card.climate.${action}`, {
name: name,
})
? this.hass.localize("ui.card.climate.heating", { name: name })
: this.hass.localize(
`component.climate.entity_component._.state_attributes.hvac_action.state.${action}`
"component.climate.entity_component._.state_attributes.hvac_action.state.heating"
),
computedStyles.getPropertyValue(cssVar),
computedStyles.getPropertyValue("--state-climate-heat-color"),
true
);
// The "heating" series uses steppedArea to shade the area below the current
// temperature when the thermostat is calling for heat.
}
if (hasCool) {
addDataSet(
states.entity_id + "-cooling",
this.showNames
? this.hass.localize("ui.card.climate.cooling", { name: name })
: this.hass.localize(
"component.climate.entity_component._.state_attributes.hvac_action.state.cooling"
),
computedStyles.getPropertyValue("--state-climate-cool-color"),
true
);
// The "cooling" series uses steppedArea to shade the area below the current
// temperature when the thermostat is calling for heat.
}
if (hasTargetRange) {
@@ -528,8 +518,11 @@ export class StateHistoryChartLine extends LitElement {
entityState.attributes.current_temperature
);
const series = [curTemp];
for (const { isActive } of activeModes) {
series.push(isActive(entityState) ? curTemp : null);
if (hasHeat) {
series.push(isHeating(entityState) ? curTemp : null);
}
if (hasCool) {
series.push(isCooling(entityState) ? curTemp : null);
}
if (hasTargetRange) {
const targetHigh = safeParseFloat(
+32 -42
View File
@@ -27,7 +27,6 @@ import {
getDisplayUnit,
getStatisticLabel,
getStatisticMetadata,
isExternalStatistic,
statisticsHaveType,
} from "../../data/recorder";
import type { ECOption } from "../../resources/echarts/echarts";
@@ -399,31 +398,7 @@ export class StatisticsChart extends LitElement {
endTime = new Date();
}
// Check if we need to display most recent data. Allow 10m of leeway for "now",
// because stats are 5 minute aggregated.
// Use same now point for all statistics even if processing time means the
// state value is actually from a slightly later time. Otherwise the points
// end up separated slightly and disappear from the tooltips.
const now = new Date();
const displayCurrentState = now.getTime() - endTime.getTime() <= 600000;
// Try to determine chart unit if it has not already been set explicitly
if (!this.unit) {
let unit: string | undefined | null;
statisticsData.forEach(([statistic_id, _stats]) => {
const meta = statisticsMetaData?.[statistic_id];
const statisticUnit = getDisplayUnit(this.hass, statistic_id, meta);
if (unit === undefined) {
unit = statisticUnit;
} else if (unit !== null && unit !== statisticUnit) {
// Clear unit if not all statistics have same unit
unit = null;
}
});
if (unit) {
this.unit = unit;
}
}
let unit: string | undefined | null;
const names = this.names || {};
statisticsData.forEach(([statistic_id, stats]) => {
@@ -433,6 +408,18 @@ export class StatisticsChart extends LitElement {
name = getStatisticLabel(this.hass, statistic_id, meta);
}
if (!this.unit) {
if (unit === undefined) {
unit = getDisplayUnit(this.hass, statistic_id, meta);
} else if (
unit !== null &&
unit !== getDisplayUnit(this.hass, statistic_id, meta)
) {
// Clear unit if not all statistics have same unit
unit = null;
}
}
// array containing [value1, value2, etc]
let prevValues: (number | null)[][] | null = null;
let prevEndTime: Date | undefined;
@@ -520,6 +507,7 @@ export class StatisticsChart extends LitElement {
id: `${statistic_id}-${type}`,
type: this.chartType,
smooth: this.chartType === "line" ? 0.4 : false,
smoothMonotone: "x",
cursor: "default",
data: [],
name: name
@@ -556,7 +544,7 @@ export class StatisticsChart extends LitElement {
(series as LineSeriesOption).areaStyle = undefined;
} else {
series.stackOrder = "seriesAsc";
if (type === bandTop) {
if (drawBands && type === bandTop) {
(series as LineSeriesOption).areaStyle = {
color: color + "3F",
};
@@ -625,10 +613,10 @@ export class StatisticsChart extends LitElement {
}
});
// For line charts, close out the last stat segment at prevEndTime
// Close out the last stat segment at prevEndTime
const lastEndTime = prevEndTime;
const lastValues = prevValues;
if (this.chartType === "line" && lastEndTime && lastValues) {
if (lastEndTime && lastValues) {
statDataSets.forEach((d, i) => {
d.data!.push(
this._transformDataValue([lastEndTime, ...lastValues[i]!])
@@ -636,14 +624,13 @@ export class StatisticsChart extends LitElement {
});
}
// Show current state if required, and units match (or are unknown)
const statisticUnit = getDisplayUnit(this.hass, statistic_id, meta);
if (
displayCurrentState &&
(!this.unit || !statisticUnit || this.unit === statisticUnit)
) {
// Skip external statistics
if (!isExternalStatistic(statistic_id)) {
// Append current state if viewing recent data
const now = new Date();
// allow 10m of leeway for "now", because stats are 5 minute aggregated
const isUpToNow = now.getTime() - endTime.getTime() <= 600000;
if (isUpToNow) {
// Skip external statistics (they have ":" in the ID)
if (!statistic_id.includes(":")) {
const stateObj = this.hass.states[statistic_id];
if (stateObj) {
const currentValue = parseFloat(stateObj.state);
@@ -653,12 +640,11 @@ export class StatisticsChart extends LitElement {
) {
// Then push the current state at now
statTypes.forEach((type, i) => {
if (type === "sum" || type === "change") {
// Skip cumulative types - need special calculation.
return;
}
const val: (number | null)[] = [];
if (
if (type === "sum" || type === "change") {
// Skip cumulative types - need special calculation
val.push(null);
} else if (
type === bandTop &&
this.chartType === "line" &&
drawBands &&
@@ -684,6 +670,10 @@ export class StatisticsChart extends LitElement {
Array.prototype.push.apply(legendData, statLegendData);
});
if (unit) {
this.unit = unit;
}
legendData.forEach(({ id, name, color, borderColor }) => {
// Add an empty series for the legend
totalDataSets.push({
+12 -10
View File
@@ -27,7 +27,7 @@ import type { HomeAssistant } from "../../types";
import "../ha-checkbox";
import type { HaCheckbox } from "../ha-checkbox";
import "../ha-svg-icon";
import "../input/ha-input-search";
import "../search-input";
import { filterData, sortData } from "./sort-filter";
export interface RowClickedEvent {
@@ -391,11 +391,11 @@ export class HaDataTable extends LitElement {
${this._filterable
? html`
<div class="table-header">
<ha-input-search
appearance="outlined"
@input=${this._handleSearchChange}
.placeholder=${this.searchLabel}
></ha-input-search>
<search-input
.hass=${this.hass}
@value-changed=${this._handleSearchChange}
.label=${this.searchLabel}
></search-input>
</div>
`
: ""}
@@ -970,12 +970,12 @@ export class HaDataTable extends LitElement {
});
}
private _handleSearchChange(ev: InputEvent): void {
private _handleSearchChange(ev: CustomEvent): void {
if (this.filter) {
return;
}
this._lastSelectedRowId = null;
this._debounceSearch((ev.target as HTMLInputElement).value);
this._debounceSearch(ev.detail.value);
}
private async _calcTableHeight() {
@@ -1388,9 +1388,11 @@ export class HaDataTable extends LitElement {
.table-header {
border-bottom: 1px solid var(--divider-color);
}
ha-input-search {
search-input {
display: block;
flex: 1;
padding: var(--ha-space-3);
--mdc-text-field-fill-color: var(--sidebar-background-color);
--mdc-text-field-idle-line-color: transparent;
}
slot[name="header"] {
display: block;

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