mirror of
https://github.com/home-assistant/frontend.git
synced 2025-11-26 03:07:21 +00:00
Compare commits
9 Commits
encryption
...
sec_pypi_p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9a45fbc488 | ||
|
|
aef3cb1c36 | ||
|
|
8535ee0694 | ||
|
|
b8110d1a45 | ||
|
|
19e9de39c5 | ||
|
|
f22f01e513 | ||
|
|
3f86f144b5 | ||
|
|
4efef5ed16 | ||
|
|
cac7ae2a40 |
4
.github/workflows/cast_deployment.yaml
vendored
4
.github/workflows/cast_deployment.yaml
vendored
@@ -21,7 +21,7 @@ jobs:
|
|||||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
with:
|
with:
|
||||||
ref: dev
|
ref: dev
|
||||||
|
|
||||||
@@ -56,7 +56,7 @@ jobs:
|
|||||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
with:
|
with:
|
||||||
ref: master
|
ref: master
|
||||||
|
|
||||||
|
|||||||
8
.github/workflows/ci.yaml
vendored
8
.github/workflows/ci.yaml
vendored
@@ -24,7 +24,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
@@ -58,7 +58,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
@@ -76,7 +76,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
@@ -100,7 +100,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
|
|||||||
8
.github/workflows/codeql-analysis.yml
vendored
8
.github/workflows/codeql-analysis.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
with:
|
with:
|
||||||
# We must fetch at least the immediate parents so that if this is
|
# We must fetch at least the immediate parents so that if this is
|
||||||
# a pull request then we can checkout the head.
|
# a pull request then we can checkout the head.
|
||||||
@@ -36,14 +36,14 @@ jobs:
|
|||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@e12f0178983d466f2f6028f5cc7a6d786fd97f4b # v4.31.4
|
uses: github/codeql-action/init@014f16e7ab1402f30e7c3329d33797e7948572db # v4.31.3
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
|
|
||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
# 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)
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@e12f0178983d466f2f6028f5cc7a6d786fd97f4b # v4.31.4
|
uses: github/codeql-action/autobuild@014f16e7ab1402f30e7c3329d33797e7948572db # v4.31.3
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
# 📚 https://git.io/JvXDl
|
# 📚 https://git.io/JvXDl
|
||||||
@@ -57,4 +57,4 @@ jobs:
|
|||||||
# make release
|
# make release
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@e12f0178983d466f2f6028f5cc7a6d786fd97f4b # v4.31.4
|
uses: github/codeql-action/analyze@014f16e7ab1402f30e7c3329d33797e7948572db # v4.31.3
|
||||||
|
|||||||
4
.github/workflows/demo_deployment.yaml
vendored
4
.github/workflows/demo_deployment.yaml
vendored
@@ -22,7 +22,7 @@ jobs:
|
|||||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
with:
|
with:
|
||||||
ref: dev
|
ref: dev
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ jobs:
|
|||||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
with:
|
with:
|
||||||
ref: master
|
ref: master
|
||||||
|
|
||||||
|
|||||||
2
.github/workflows/design_deployment.yaml
vendored
2
.github/workflows/design_deployment.yaml
vendored
@@ -16,7 +16,7 @@ jobs:
|
|||||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
|
|||||||
2
.github/workflows/design_preview.yaml
vendored
2
.github/workflows/design_preview.yaml
vendored
@@ -21,7 +21,7 @@ jobs:
|
|||||||
if: github.repository == 'home-assistant/frontend' && contains(github.event.pull_request.labels.*.name, 'needs design preview')
|
if: github.repository == 'home-assistant/frontend' && contains(github.event.pull_request.labels.*.name, 'needs design preview')
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
|
|||||||
2
.github/workflows/nightly.yaml
vendored
2
.github/workflows/nightly.yaml
vendored
@@ -20,7 +20,7 @@ jobs:
|
|||||||
contents: write
|
contents: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repository
|
- name: Checkout the repository
|
||||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
|
|
||||||
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
||||||
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6
|
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6
|
||||||
|
|||||||
19
.github/workflows/release.yaml
vendored
19
.github/workflows/release.yaml
vendored
@@ -19,11 +19,14 @@ jobs:
|
|||||||
release:
|
release:
|
||||||
name: Release
|
name: Release
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
environment: pypi
|
||||||
permissions:
|
permissions:
|
||||||
contents: write # Required to upload release assets
|
contents: write # Required to upload release assets
|
||||||
|
id-token: write # For "Trusted Publisher" to PyPi
|
||||||
|
if: github.repository_owner == 'home-assistant'
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repository
|
- name: Checkout the repository
|
||||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
|
|
||||||
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
||||||
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
||||||
@@ -46,14 +49,18 @@ jobs:
|
|||||||
run: ./script/translations_download
|
run: ./script/translations_download
|
||||||
env:
|
env:
|
||||||
LOKALISE_TOKEN: ${{ secrets.LOKALISE_TOKEN }}
|
LOKALISE_TOKEN: ${{ secrets.LOKALISE_TOKEN }}
|
||||||
|
|
||||||
- name: Build and release package
|
- name: Build and release package
|
||||||
run: |
|
run: |
|
||||||
python3 -m pip install twine build
|
python3 -m pip install build
|
||||||
export TWINE_USERNAME="__token__"
|
|
||||||
export TWINE_PASSWORD="${{ secrets.TWINE_TOKEN }}"
|
|
||||||
export SKIP_FETCH_NIGHTLY_TRANSLATIONS=1
|
export SKIP_FETCH_NIGHTLY_TRANSLATIONS=1
|
||||||
script/release
|
script/release
|
||||||
|
|
||||||
|
- name: Publish to PyPI
|
||||||
|
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
|
||||||
|
with:
|
||||||
|
skip-existing: true
|
||||||
|
|
||||||
- name: Upload release assets
|
- name: Upload release assets
|
||||||
uses: softprops/action-gh-release@5be0e66d93ac7ed76da52eca8bb058f665c3a5fe # v2.4.2
|
uses: softprops/action-gh-release@5be0e66d93ac7ed76da52eca8bb058f665c3a5fe # v2.4.2
|
||||||
with:
|
with:
|
||||||
@@ -91,7 +98,7 @@ jobs:
|
|||||||
contents: write # Required to upload release assets
|
contents: write # Required to upload release assets
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repository
|
- name: Checkout the repository
|
||||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
@@ -120,7 +127,7 @@ jobs:
|
|||||||
contents: write # Required to upload release assets
|
contents: write # Required to upload release assets
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repository
|
- name: Checkout the repository
|
||||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||||
with:
|
with:
|
||||||
|
|||||||
2
.github/workflows/translations.yaml
vendored
2
.github/workflows/translations.yaml
vendored
@@ -14,7 +14,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repository
|
- name: Checkout the repository
|
||||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
|
|
||||||
- name: Upload Translations
|
- name: Upload Translations
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -112,7 +112,7 @@
|
|||||||
"google-timezones-json": "1.2.0",
|
"google-timezones-json": "1.2.0",
|
||||||
"gulp-zopfli-green": "6.0.2",
|
"gulp-zopfli-green": "6.0.2",
|
||||||
"hls.js": "1.6.14",
|
"hls.js": "1.6.14",
|
||||||
"home-assistant-js-websocket": "9.6.0",
|
"home-assistant-js-websocket": "9.5.0",
|
||||||
"idb-keyval": "6.2.2",
|
"idb-keyval": "6.2.2",
|
||||||
"intl-messageformat": "10.7.18",
|
"intl-messageformat": "10.7.18",
|
||||||
"js-yaml": "4.1.1",
|
"js-yaml": "4.1.1",
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# Pushes a new version to PyPi.
|
|
||||||
|
|
||||||
# Stop on errors
|
# Stop on errors
|
||||||
set -e
|
set -e
|
||||||
@@ -12,5 +11,4 @@ yarn install
|
|||||||
script/build_frontend
|
script/build_frontend
|
||||||
|
|
||||||
rm -rf dist home_assistant_frontend.egg-info
|
rm -rf dist home_assistant_frontend.egg-info
|
||||||
python3 -m build
|
python3 -m build -q
|
||||||
python3 -m twine upload dist/*.whl --skip-existing
|
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
import { genClientId } from "home-assistant-js-websocket";
|
import { genClientId } from "home-assistant-js-websocket";
|
||||||
import type { PropertyValues } from "lit";
|
import type { PropertyValues } from "lit";
|
||||||
import { html, LitElement, nothing } from "lit";
|
import { html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
|
||||||
import { keyed } from "lit/directives/keyed";
|
import { keyed } from "lit/directives/keyed";
|
||||||
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import type { LocalizeFunc } from "../common/translations/localize";
|
import type { LocalizeFunc } from "../common/translations/localize";
|
||||||
import "../components/ha-alert";
|
import "../components/ha-alert";
|
||||||
import "../components/ha-button";
|
import "../components/ha-button";
|
||||||
@@ -118,9 +118,6 @@ export class HaAuthFlow extends LitElement {
|
|||||||
display: block;
|
display: block;
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
}
|
}
|
||||||
.action ha-button {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
<form>${this._renderForm()}</form>
|
<form>${this._renderForm()}</form>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -75,15 +75,11 @@ export class HaDialogHeader extends LitElement {
|
|||||||
font-size: var(--ha-font-size-xl);
|
font-size: var(--ha-font-size-xl);
|
||||||
line-height: var(--ha-line-height-condensed);
|
line-height: var(--ha-line-height-condensed);
|
||||||
font-weight: var(--ha-font-weight-medium);
|
font-weight: var(--ha-font-weight-medium);
|
||||||
color: var(--ha-dialog-header-title-color, var(--primary-text-color));
|
|
||||||
}
|
}
|
||||||
.header-subtitle {
|
.header-subtitle {
|
||||||
font-size: var(--ha-font-size-m);
|
font-size: var(--ha-font-size-m);
|
||||||
line-height: var(--ha-line-height-normal);
|
line-height: var(--ha-line-height-normal);
|
||||||
color: var(
|
color: var(--secondary-text-color);
|
||||||
--ha-dialog-header-subtitle-color,
|
|
||||||
var(--secondary-text-color)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
@media all and (min-width: 450px) and (min-height: 500px) {
|
@media all and (min-width: 450px) and (min-height: 500px) {
|
||||||
.header-bar {
|
.header-bar {
|
||||||
|
|||||||
@@ -209,7 +209,6 @@ export class HaExpansionPanel extends LitElement {
|
|||||||
::slotted([slot="header"]) {
|
::slotted([slot="header"]) {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow-wrap: anywhere;
|
overflow-wrap: anywhere;
|
||||||
color: var(--primary-text-color);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
|
|||||||
@@ -1,178 +0,0 @@
|
|||||||
import { css, html, LitElement, nothing } from "lit";
|
|
||||||
import { customElement, property, state } from "lit/decorators";
|
|
||||||
import type { HomeAssistant } from "../types";
|
|
||||||
import { subscribeLabFeatures } from "../data/labs";
|
|
||||||
import { SubscribeMixin } from "../mixins/subscribe-mixin";
|
|
||||||
|
|
||||||
interface Snowflake {
|
|
||||||
id: number;
|
|
||||||
left: number;
|
|
||||||
size: number;
|
|
||||||
duration: number;
|
|
||||||
delay: number;
|
|
||||||
blur: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
@customElement("ha-snowflakes")
|
|
||||||
export class HaSnowflakes extends SubscribeMixin(LitElement) {
|
|
||||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
|
||||||
|
|
||||||
@property({ type: Boolean }) public narrow = false;
|
|
||||||
|
|
||||||
@state() private _enabled = false;
|
|
||||||
|
|
||||||
@state() private _snowflakes: Snowflake[] = [];
|
|
||||||
|
|
||||||
private _maxSnowflakes = 50;
|
|
||||||
|
|
||||||
public hassSubscribe() {
|
|
||||||
return [
|
|
||||||
subscribeLabFeatures(this.hass!.connection, (features) => {
|
|
||||||
this._enabled =
|
|
||||||
features.find(
|
|
||||||
(f) =>
|
|
||||||
f.domain === "frontend" && f.preview_feature === "winter_mode"
|
|
||||||
)?.enabled ?? false;
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
private _generateSnowflakes() {
|
|
||||||
if (!this._enabled) {
|
|
||||||
this._snowflakes = [];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const snowflakes: Snowflake[] = [];
|
|
||||||
for (let i = 0; i < this._maxSnowflakes; i++) {
|
|
||||||
snowflakes.push({
|
|
||||||
id: i,
|
|
||||||
left: Math.random() * 100, // Random position from 0-100%
|
|
||||||
size: Math.random() * 12 + 8, // Random size between 8-20px
|
|
||||||
duration: Math.random() * 8 + 8, // Random duration between 8-16s
|
|
||||||
delay: Math.random() * 8, // Random delay between 0-8s
|
|
||||||
blur: Math.random() * 1, // Random blur between 0-1px
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this._snowflakes = snowflakes;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected willUpdate(changedProps: Map<string, unknown>) {
|
|
||||||
super.willUpdate(changedProps);
|
|
||||||
if (changedProps.has("_enabled")) {
|
|
||||||
this._generateSnowflakes();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected render() {
|
|
||||||
if (!this._enabled) {
|
|
||||||
return nothing;
|
|
||||||
}
|
|
||||||
|
|
||||||
const isDark = this.hass?.themes.darkMode ?? false;
|
|
||||||
|
|
||||||
return html`
|
|
||||||
<div class="snowflakes ${isDark ? "dark" : "light"}" aria-hidden="true">
|
|
||||||
${this._snowflakes.map(
|
|
||||||
(flake) => html`
|
|
||||||
<div
|
|
||||||
class="snowflake ${this.narrow && flake.id >= 30
|
|
||||||
? "hide-narrow"
|
|
||||||
: ""}"
|
|
||||||
style="
|
|
||||||
left: ${flake.left}%;
|
|
||||||
font-size: ${flake.size}px;
|
|
||||||
animation-duration: ${flake.duration}s;
|
|
||||||
animation-delay: ${flake.delay}s;
|
|
||||||
filter: blur(${flake.blur}px);
|
|
||||||
"
|
|
||||||
>
|
|
||||||
❄
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly styles = css`
|
|
||||||
:host {
|
|
||||||
display: block;
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
pointer-events: none;
|
|
||||||
z-index: 9999;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.snowflakes {
|
|
||||||
position: absolute;
|
|
||||||
top: -10%;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 110%;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.snowflake {
|
|
||||||
position: absolute;
|
|
||||||
top: -10%;
|
|
||||||
opacity: 0.7;
|
|
||||||
user-select: none;
|
|
||||||
pointer-events: none;
|
|
||||||
animation: fall linear infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
.light .snowflake {
|
|
||||||
color: #00bcd4;
|
|
||||||
text-shadow:
|
|
||||||
0 0 5px #00bcd4,
|
|
||||||
0 0 10px #00e5ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark .snowflake {
|
|
||||||
color: #fff;
|
|
||||||
text-shadow:
|
|
||||||
0 0 5px rgba(255, 255, 255, 0.8),
|
|
||||||
0 0 10px rgba(255, 255, 255, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.snowflake.hide-narrow {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes fall {
|
|
||||||
0% {
|
|
||||||
transform: translateY(-10vh) translateX(0);
|
|
||||||
}
|
|
||||||
25% {
|
|
||||||
transform: translateY(30vh) translateX(10px);
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
transform: translateY(60vh) translateX(-10px);
|
|
||||||
}
|
|
||||||
75% {
|
|
||||||
transform: translateY(85vh) translateX(10px);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
transform: translateY(120vh) translateX(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-reduced-motion: reduce) {
|
|
||||||
.snowflake {
|
|
||||||
animation: none;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
"ha-snowflakes": HaSnowflakes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,7 +5,6 @@ export interface AnalyticsPreferences {
|
|||||||
diagnostics?: boolean;
|
diagnostics?: boolean;
|
||||||
usage?: boolean;
|
usage?: boolean;
|
||||||
statistics?: boolean;
|
statistics?: boolean;
|
||||||
snapshots?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Analytics {
|
export interface Analytics {
|
||||||
|
|||||||
@@ -214,8 +214,6 @@ export interface PipelineRun {
|
|||||||
stage: "ready" | "wake_word" | "stt" | "intent" | "tts" | "done" | "error";
|
stage: "ready" | "wake_word" | "stt" | "intent" | "tts" | "done" | "error";
|
||||||
run: PipelineRunStartEvent["data"];
|
run: PipelineRunStartEvent["data"];
|
||||||
error?: PipelineErrorEvent["data"];
|
error?: PipelineErrorEvent["data"];
|
||||||
started: Date;
|
|
||||||
finished?: Date;
|
|
||||||
wake_word?: PipelineWakeWordStartEvent["data"] &
|
wake_word?: PipelineWakeWordStartEvent["data"] &
|
||||||
Partial<PipelineWakeWordEndEvent["data"]> & { done: boolean };
|
Partial<PipelineWakeWordEndEvent["data"]> & { done: boolean };
|
||||||
stt?: PipelineSTTStartEvent["data"] &
|
stt?: PipelineSTTStartEvent["data"] &
|
||||||
@@ -237,7 +235,6 @@ export const processEvent = (
|
|||||||
stage: "ready",
|
stage: "ready",
|
||||||
run: event.data,
|
run: event.data,
|
||||||
events: [event],
|
events: [event],
|
||||||
started: new Date(event.timestamp),
|
|
||||||
};
|
};
|
||||||
return run;
|
return run;
|
||||||
}
|
}
|
||||||
@@ -293,14 +290,9 @@ export const processEvent = (
|
|||||||
tts: { ...run.tts!, ...event.data, done: true },
|
tts: { ...run.tts!, ...event.data, done: true },
|
||||||
};
|
};
|
||||||
} else if (event.type === "run-end") {
|
} else if (event.type === "run-end") {
|
||||||
run = { ...run, finished: new Date(event.timestamp), stage: "done" };
|
run = { ...run, stage: "done" };
|
||||||
} else if (event.type === "error") {
|
} else if (event.type === "error") {
|
||||||
run = {
|
run = { ...run, stage: "error", error: event.data };
|
||||||
...run,
|
|
||||||
finished: new Date(event.timestamp),
|
|
||||||
stage: "error",
|
|
||||||
error: event.data,
|
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
run = { ...run };
|
run = { ...run };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,228 +0,0 @@
|
|||||||
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
|
|
||||||
import type { HomeAssistant } from "../types";
|
|
||||||
|
|
||||||
export const enum ChatLogEventType {
|
|
||||||
INITIAL_STATE = "initial_state",
|
|
||||||
CREATED = "created",
|
|
||||||
UPDATED = "updated",
|
|
||||||
DELETED = "deleted",
|
|
||||||
CONTENT_ADDED = "content_added",
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ChatLogAttachment {
|
|
||||||
media_content_id: string;
|
|
||||||
mime_type: string;
|
|
||||||
path: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ChatLogSystemContent {
|
|
||||||
role: "system";
|
|
||||||
content: string;
|
|
||||||
created: Date;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ChatLogUserContent {
|
|
||||||
role: "user";
|
|
||||||
content: string;
|
|
||||||
created: Date;
|
|
||||||
attachments?: ChatLogAttachment[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ChatLogAssistantContent {
|
|
||||||
role: "assistant";
|
|
||||||
agent_id: string;
|
|
||||||
created: Date;
|
|
||||||
content?: string;
|
|
||||||
thinking_content?: string;
|
|
||||||
tool_calls?: any[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ChatLogToolResultContent {
|
|
||||||
role: "tool_result";
|
|
||||||
agent_id: string;
|
|
||||||
tool_call_id: string;
|
|
||||||
tool_name: string;
|
|
||||||
tool_result: any;
|
|
||||||
created: Date;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ChatLogContent =
|
|
||||||
| ChatLogSystemContent
|
|
||||||
| ChatLogUserContent
|
|
||||||
| ChatLogAssistantContent
|
|
||||||
| ChatLogToolResultContent;
|
|
||||||
|
|
||||||
export interface ChatLog {
|
|
||||||
conversation_id: string;
|
|
||||||
continue_conversation: boolean;
|
|
||||||
content: ChatLogContent[];
|
|
||||||
created: Date;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Internal wire format types (not exported)
|
|
||||||
interface ChatLogSystemContentWire {
|
|
||||||
role: "system";
|
|
||||||
content: string;
|
|
||||||
created: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ChatLogUserContentWire {
|
|
||||||
role: "user";
|
|
||||||
content: string;
|
|
||||||
created: string;
|
|
||||||
attachments?: ChatLogAttachment[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ChatLogAssistantContentWire {
|
|
||||||
role: "assistant";
|
|
||||||
agent_id: string;
|
|
||||||
created: string;
|
|
||||||
content?: string;
|
|
||||||
thinking_content?: string;
|
|
||||||
tool_calls?: {
|
|
||||||
tool_name: string;
|
|
||||||
tool_args: Record<string, any>;
|
|
||||||
id: string;
|
|
||||||
external: boolean;
|
|
||||||
}[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ChatLogToolResultContentWire {
|
|
||||||
role: "tool_result";
|
|
||||||
agent_id: string;
|
|
||||||
tool_call_id: string;
|
|
||||||
tool_name: string;
|
|
||||||
tool_result: any;
|
|
||||||
created: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
type ChatLogContentWire =
|
|
||||||
| ChatLogSystemContentWire
|
|
||||||
| ChatLogUserContentWire
|
|
||||||
| ChatLogAssistantContentWire
|
|
||||||
| ChatLogToolResultContentWire;
|
|
||||||
|
|
||||||
interface ChatLogWire {
|
|
||||||
conversation_id: string;
|
|
||||||
continue_conversation: boolean;
|
|
||||||
content: ChatLogContentWire[];
|
|
||||||
created: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const processContent = (content: ChatLogContentWire): ChatLogContent => ({
|
|
||||||
...content,
|
|
||||||
created: new Date(content.created),
|
|
||||||
});
|
|
||||||
|
|
||||||
const processChatLog = (chatLog: ChatLogWire): ChatLog => ({
|
|
||||||
...chatLog,
|
|
||||||
created: new Date(chatLog.created),
|
|
||||||
content: chatLog.content.map(processContent),
|
|
||||||
});
|
|
||||||
|
|
||||||
interface ChatLogInitialStateEvent {
|
|
||||||
event_type: ChatLogEventType.INITIAL_STATE;
|
|
||||||
data: ChatLogWire;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ChatLogIndexInitialStateEvent {
|
|
||||||
event_type: ChatLogEventType.INITIAL_STATE;
|
|
||||||
data: ChatLogWire[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ChatLogCreatedEvent {
|
|
||||||
conversation_id: string;
|
|
||||||
event_type: ChatLogEventType.CREATED;
|
|
||||||
data: ChatLogWire;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ChatLogUpdatedEvent {
|
|
||||||
conversation_id: string;
|
|
||||||
event_type: ChatLogEventType.UPDATED;
|
|
||||||
data: { chat_log: ChatLogWire };
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ChatLogDeletedEvent {
|
|
||||||
conversation_id: string;
|
|
||||||
event_type: ChatLogEventType.DELETED;
|
|
||||||
data: ChatLogWire;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ChatLogContentAddedEvent {
|
|
||||||
conversation_id: string;
|
|
||||||
event_type: ChatLogEventType.CONTENT_ADDED;
|
|
||||||
data: { content: ChatLogContentWire };
|
|
||||||
}
|
|
||||||
|
|
||||||
type ChatLogSubscriptionEvent =
|
|
||||||
| ChatLogInitialStateEvent
|
|
||||||
| ChatLogUpdatedEvent
|
|
||||||
| ChatLogDeletedEvent
|
|
||||||
| ChatLogContentAddedEvent;
|
|
||||||
|
|
||||||
type ChatLogIndexSubscriptionEvent =
|
|
||||||
| ChatLogIndexInitialStateEvent
|
|
||||||
| ChatLogCreatedEvent
|
|
||||||
| ChatLogDeletedEvent;
|
|
||||||
|
|
||||||
export const subscribeChatLog = (
|
|
||||||
hass: HomeAssistant,
|
|
||||||
conversationId: string,
|
|
||||||
callback: (chatLog: ChatLog | null) => void
|
|
||||||
): Promise<UnsubscribeFunc> => {
|
|
||||||
let chatLog: ChatLog | null = null;
|
|
||||||
|
|
||||||
return hass.connection.subscribeMessage<ChatLogSubscriptionEvent>(
|
|
||||||
(event) => {
|
|
||||||
if (event.event_type === ChatLogEventType.INITIAL_STATE) {
|
|
||||||
chatLog = processChatLog(event.data);
|
|
||||||
callback(chatLog);
|
|
||||||
} else if (event.event_type === ChatLogEventType.CONTENT_ADDED) {
|
|
||||||
if (chatLog) {
|
|
||||||
chatLog = {
|
|
||||||
...chatLog,
|
|
||||||
content: [...chatLog.content, processContent(event.data.content)],
|
|
||||||
};
|
|
||||||
callback(chatLog);
|
|
||||||
}
|
|
||||||
} else if (event.event_type === ChatLogEventType.UPDATED) {
|
|
||||||
chatLog = processChatLog(event.data.chat_log);
|
|
||||||
callback(chatLog);
|
|
||||||
} else if (event.event_type === ChatLogEventType.DELETED) {
|
|
||||||
chatLog = null;
|
|
||||||
callback(null);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "conversation/chat_log/subscribe",
|
|
||||||
conversation_id: conversationId,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const subscribeChatLogIndex = (
|
|
||||||
hass: HomeAssistant,
|
|
||||||
callback: (chatLogs: ChatLog[]) => void
|
|
||||||
): Promise<UnsubscribeFunc> => {
|
|
||||||
let chatLogs: ChatLog[] = [];
|
|
||||||
|
|
||||||
return hass.connection.subscribeMessage<ChatLogIndexSubscriptionEvent>(
|
|
||||||
(event) => {
|
|
||||||
if (event.event_type === ChatLogEventType.INITIAL_STATE) {
|
|
||||||
chatLogs = event.data.map(processChatLog);
|
|
||||||
callback(chatLogs);
|
|
||||||
} else if (event.event_type === ChatLogEventType.CREATED) {
|
|
||||||
chatLogs = [...chatLogs, processChatLog(event.data)];
|
|
||||||
callback(chatLogs);
|
|
||||||
} else if (event.event_type === ChatLogEventType.DELETED) {
|
|
||||||
chatLogs = chatLogs.filter(
|
|
||||||
(chatLog) => chatLog.conversation_id !== event.conversation_id
|
|
||||||
);
|
|
||||||
callback(chatLogs);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "conversation/chat_log/subscribe_index",
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -775,7 +775,6 @@ export const getEnergyDataCollection = (
|
|||||||
hass.locale,
|
hass.locale,
|
||||||
hass.config
|
hass.config
|
||||||
);
|
);
|
||||||
collection.refresh();
|
|
||||||
scheduleUpdatePeriod();
|
scheduleUpdatePeriod();
|
||||||
},
|
},
|
||||||
addHours(
|
addHours(
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
import type { HomeAssistant } from "../types";
|
|
||||||
|
|
||||||
export interface ESPHomeEncryptionKey {
|
|
||||||
encryption_key: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const fetchESPHomeEncryptionKey = (
|
|
||||||
hass: HomeAssistant,
|
|
||||||
entry_id: string
|
|
||||||
): Promise<ESPHomeEncryptionKey> =>
|
|
||||||
hass.callWS({
|
|
||||||
type: "esphome/get_encryption_key",
|
|
||||||
entry_id,
|
|
||||||
});
|
|
||||||
@@ -2,15 +2,14 @@ import { LitElement, css, html, nothing } from "lit";
|
|||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import "../../components/ha-alert";
|
import "../../components/ha-alert";
|
||||||
import "../../components/ha-icon";
|
import "../../components/ha-icon";
|
||||||
import "../../components/ha-md-list-item";
|
import "../../components/ha-list-item";
|
||||||
import "../../components/ha-spinner";
|
import "../../components/ha-spinner";
|
||||||
import type {
|
import type {
|
||||||
ExternalEntityAddToAction,
|
|
||||||
ExternalEntityAddToActions,
|
ExternalEntityAddToActions,
|
||||||
|
ExternalEntityAddToAction,
|
||||||
} from "../../external_app/external_messaging";
|
} from "../../external_app/external_messaging";
|
||||||
import { showToast } from "../../util/toast";
|
import { showToast } from "../../util/toast";
|
||||||
|
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
|
||||||
import type { HomeAssistant } from "../../types";
|
import type { HomeAssistant } from "../../types";
|
||||||
|
|
||||||
@customElement("ha-more-info-add-to")
|
@customElement("ha-more-info-add-to")
|
||||||
@@ -52,7 +51,6 @@ export class HaMoreInfoAddTo extends LitElement {
|
|||||||
app_payload: action.app_payload,
|
app_payload: action.app_payload,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
fireEvent(this, "add-to-action-selected");
|
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
showToast(this, {
|
showToast(this, {
|
||||||
message: this.hass.localize(
|
message: this.hass.localize(
|
||||||
@@ -93,18 +91,19 @@ export class HaMoreInfoAddTo extends LitElement {
|
|||||||
<div class="actions-list">
|
<div class="actions-list">
|
||||||
${this._externalActions.actions.map(
|
${this._externalActions.actions.map(
|
||||||
(action) => html`
|
(action) => html`
|
||||||
<ha-md-list-item
|
<ha-list-item
|
||||||
type="button"
|
graphic="icon"
|
||||||
.disabled=${!action.enabled}
|
.disabled=${!action.enabled}
|
||||||
.action=${action}
|
.action=${action}
|
||||||
|
.twoline=${!!action.details}
|
||||||
@click=${this._actionSelected}
|
@click=${this._actionSelected}
|
||||||
>
|
>
|
||||||
<ha-icon slot="start" .icon=${action.mdi_icon}></ha-icon>
|
|
||||||
<span>${action.name}</span>
|
<span>${action.name}</span>
|
||||||
${action.details
|
${action.details
|
||||||
? html`<span slot="supporting-text">${action.details}</span>`
|
? html`<span slot="secondary">${action.details}</span>`
|
||||||
: nothing}
|
: nothing}
|
||||||
</ha-md-list-item>
|
<ha-icon slot="graphic" .icon=${action.mdi_icon}></ha-icon>
|
||||||
|
</ha-list-item>
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -130,6 +129,15 @@ export class HaMoreInfoAddTo extends LitElement {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ha-list-item {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
ha-list-item[disabled] {
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
ha-icon {
|
ha-icon {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -141,8 +149,4 @@ declare global {
|
|||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"ha-more-info-add-to": HaMoreInfoAddTo;
|
"ha-more-info-add-to": HaMoreInfoAddTo;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface HASSDomEvents {
|
|
||||||
"add-to-action-selected": undefined;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -645,7 +645,6 @@ export class MoreInfoDialog extends LitElement {
|
|||||||
<ha-more-info-add-to
|
<ha-more-info-add-to
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.entityId=${entityId}
|
.entityId=${entityId}
|
||||||
@add-to-action-selected=${this._goBack}
|
|
||||||
></ha-more-info-add-to>
|
></ha-more-info-add-to>
|
||||||
`
|
`
|
||||||
: nothing
|
: nothing
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import { listenMediaQuery } from "../common/dom/media_query";
|
|||||||
import { toggleAttribute } from "../common/dom/toggle_attribute";
|
import { toggleAttribute } from "../common/dom/toggle_attribute";
|
||||||
import { computeRTLDirection } from "../common/util/compute_rtl";
|
import { computeRTLDirection } from "../common/util/compute_rtl";
|
||||||
import "../components/ha-drawer";
|
import "../components/ha-drawer";
|
||||||
import "../components/ha-snowflakes";
|
|
||||||
import { showNotificationDrawer } from "../dialogs/notifications/show-notification-drawer";
|
import { showNotificationDrawer } from "../dialogs/notifications/show-notification-drawer";
|
||||||
import type { HomeAssistant, Route } from "../types";
|
import type { HomeAssistant, Route } from "../types";
|
||||||
import "./partial-panel-resolver";
|
import "./partial-panel-resolver";
|
||||||
@@ -51,7 +50,6 @@ export class HomeAssistantMain extends LitElement {
|
|||||||
this.hass.panels && this.hass.userData && this.hass.systemData;
|
this.hass.panels && this.hass.userData && this.hass.systemData;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-snowflakes .hass=${this.hass} .narrow=${this.narrow}></ha-snowflakes>
|
|
||||||
<ha-drawer
|
<ha-drawer
|
||||||
.type=${sidebarNarrow ? "modal" : ""}
|
.type=${sidebarNarrow ? "modal" : ""}
|
||||||
.open=${sidebarNarrow ? this._drawerOpen : false}
|
.open=${sidebarNarrow ? this._drawerOpen : false}
|
||||||
|
|||||||
@@ -188,7 +188,6 @@ export default class HaAutomationSidebar extends LitElement {
|
|||||||
class="handle ${this._resizing ? "resizing" : ""}"
|
class="handle ${this._resizing ? "resizing" : ""}"
|
||||||
@mousedown=${this._handleMouseDown}
|
@mousedown=${this._handleMouseDown}
|
||||||
@touchstart=${this._handleMouseDown}
|
@touchstart=${this._handleMouseDown}
|
||||||
@dblclick=${this._handleDoubleClick}
|
|
||||||
@focus=${this._startKeyboardResizing}
|
@focus=${this._startKeyboardResizing}
|
||||||
@blur=${this._stopKeyboardResizing}
|
@blur=${this._stopKeyboardResizing}
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
@@ -259,17 +258,6 @@ export default class HaAutomationSidebar extends LitElement {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
private _handleDoubleClick = (ev: MouseEvent) => {
|
|
||||||
ev.preventDefault();
|
|
||||||
ev.stopPropagation();
|
|
||||||
this._unregisterResizeHandlers();
|
|
||||||
this._tinykeysUnsub?.();
|
|
||||||
this._tinykeysUnsub = undefined;
|
|
||||||
this._resizing = false;
|
|
||||||
document.body.style.removeProperty("cursor");
|
|
||||||
fireEvent(this, "sidebar-reset-size");
|
|
||||||
};
|
|
||||||
|
|
||||||
private _startResizing(clientX: number) {
|
private _startResizing(clientX: number) {
|
||||||
// register event listeners for drag handling
|
// register event listeners for drag handling
|
||||||
document.addEventListener("mousemove", this._handleMouseMove);
|
document.addEventListener("mousemove", this._handleMouseMove);
|
||||||
@@ -434,6 +422,5 @@ declare global {
|
|||||||
deltaInPx: number;
|
deltaInPx: number;
|
||||||
};
|
};
|
||||||
"sidebar-resizing-stopped": undefined;
|
"sidebar-resizing-stopped": undefined;
|
||||||
"sidebar-reset-size": undefined;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -317,7 +317,6 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
@value-changed=${this._sidebarConfigChanged}
|
@value-changed=${this._sidebarConfigChanged}
|
||||||
@sidebar-resized=${this._resizeSidebar}
|
@sidebar-resized=${this._resizeSidebar}
|
||||||
@sidebar-resizing-stopped=${this._stopResizeSidebar}
|
@sidebar-resizing-stopped=${this._stopResizeSidebar}
|
||||||
@sidebar-reset-size=${this._resetSidebarWidth}
|
|
||||||
></ha-automation-sidebar>
|
></ha-automation-sidebar>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -701,16 +700,6 @@ export class HaManualAutomationEditor extends LitElement {
|
|||||||
this._prevSidebarWidthPx = undefined;
|
this._prevSidebarWidthPx = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _resetSidebarWidth(ev: Event) {
|
|
||||||
ev.stopPropagation();
|
|
||||||
this._prevSidebarWidthPx = undefined;
|
|
||||||
this._sidebarWidthPx = SIDEBAR_DEFAULT_WIDTH;
|
|
||||||
this.style.setProperty(
|
|
||||||
"--sidebar-dynamic-width",
|
|
||||||
`${this._sidebarWidthPx}px`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return [
|
return [
|
||||||
saveFabStyles,
|
saveFabStyles,
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ import {
|
|||||||
} from "../../../data/blueprint";
|
} from "../../../data/blueprint";
|
||||||
import { showScriptEditor } from "../../../data/script";
|
import { showScriptEditor } from "../../../data/script";
|
||||||
import { findRelated } from "../../../data/search";
|
import { findRelated } from "../../../data/search";
|
||||||
import "../../../components/chips/ha-assist-chip";
|
|
||||||
import {
|
import {
|
||||||
showAlertDialog,
|
showAlertDialog,
|
||||||
showConfirmationDialog,
|
showConfirmationDialog,
|
||||||
@@ -61,7 +60,6 @@ type BlueprintMetaDataPath = BlueprintMetaData & {
|
|||||||
error: boolean;
|
error: boolean;
|
||||||
type: "automation" | "script";
|
type: "automation" | "script";
|
||||||
fullpath: string;
|
fullpath: string;
|
||||||
usageCount?: number;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const createNewFunctions = {
|
const createNewFunctions = {
|
||||||
@@ -130,20 +128,14 @@ class HaBlueprintOverview extends LitElement {
|
|||||||
})
|
})
|
||||||
private _filter = "";
|
private _filter = "";
|
||||||
|
|
||||||
@state() private _usageCounts: Record<string, number> = {};
|
|
||||||
|
|
||||||
private _usageCountRequest = 0;
|
|
||||||
|
|
||||||
private _processedBlueprints = memoizeOne(
|
private _processedBlueprints = memoizeOne(
|
||||||
(
|
(
|
||||||
blueprints: Record<string, Blueprints>,
|
blueprints: Record<string, Blueprints>,
|
||||||
localize: LocalizeFunc,
|
localize: LocalizeFunc
|
||||||
usageCounts: Record<string, number>
|
|
||||||
): BlueprintMetaDataPath[] => {
|
): BlueprintMetaDataPath[] => {
|
||||||
const result: any[] = [];
|
const result: any[] = [];
|
||||||
Object.entries(blueprints).forEach(([type, typeBlueprints]) =>
|
Object.entries(blueprints).forEach(([type, typeBlueprints]) =>
|
||||||
Object.entries(typeBlueprints).forEach(([path, blueprint]) => {
|
Object.entries(typeBlueprints).forEach(([path, blueprint]) => {
|
||||||
const fullpath = `${type}/${path}`;
|
|
||||||
if ("error" in blueprint) {
|
if ("error" in blueprint) {
|
||||||
result.push({
|
result.push({
|
||||||
name: blueprint.error,
|
name: blueprint.error,
|
||||||
@@ -153,8 +145,7 @@ class HaBlueprintOverview extends LitElement {
|
|||||||
),
|
),
|
||||||
error: true,
|
error: true,
|
||||||
path,
|
path,
|
||||||
fullpath,
|
fullpath: `${type}/${path}`,
|
||||||
usageCount: 0,
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
result.push({
|
result.push({
|
||||||
@@ -165,8 +156,7 @@ class HaBlueprintOverview extends LitElement {
|
|||||||
),
|
),
|
||||||
error: false,
|
error: false,
|
||||||
path,
|
path,
|
||||||
fullpath,
|
fullpath: `${type}/${path}`,
|
||||||
usageCount: usageCounts[fullpath] || 0,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -199,34 +189,6 @@ class HaBlueprintOverview extends LitElement {
|
|||||||
filterable: true,
|
filterable: true,
|
||||||
flex: 2,
|
flex: 2,
|
||||||
},
|
},
|
||||||
usage_count: {
|
|
||||||
title: localize(
|
|
||||||
"ui.panel.config.blueprint.overview.headers.usage_count"
|
|
||||||
),
|
|
||||||
sortable: true,
|
|
||||||
valueColumn: "usageCount",
|
|
||||||
type: "numeric",
|
|
||||||
minWidth: "100px",
|
|
||||||
maxWidth: "120px",
|
|
||||||
template: (blueprint) => {
|
|
||||||
const count = blueprint.usageCount ?? 0;
|
|
||||||
return html`
|
|
||||||
<ha-assist-chip
|
|
||||||
filled
|
|
||||||
.active=${count > 0}
|
|
||||||
label=${String(count)}
|
|
||||||
title=${blueprint.error
|
|
||||||
? String(count)
|
|
||||||
: this.hass.localize(
|
|
||||||
`ui.panel.config.blueprint.overview.view_${blueprint.type}`
|
|
||||||
)}
|
|
||||||
?disabled=${blueprint.error}
|
|
||||||
data-fullpath=${blueprint.fullpath}
|
|
||||||
@click=${this._handleUsageClick}
|
|
||||||
></ha-assist-chip>
|
|
||||||
`;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
fullpath: {
|
fullpath: {
|
||||||
title: "fullpath",
|
title: "fullpath",
|
||||||
hidden: true,
|
hidden: true,
|
||||||
@@ -304,7 +266,6 @@ class HaBlueprintOverview extends LitElement {
|
|||||||
|
|
||||||
protected firstUpdated(changedProps: PropertyValues) {
|
protected firstUpdated(changedProps: PropertyValues) {
|
||||||
super.firstUpdated(changedProps);
|
super.firstUpdated(changedProps);
|
||||||
this._loadUsageCounts();
|
|
||||||
if (this.route.path === "/import") {
|
if (this.route.path === "/import") {
|
||||||
const url = extractSearchParam("blueprint_url");
|
const url = extractSearchParam("blueprint_url");
|
||||||
navigate("/config/blueprint/dashboard", { replace: true });
|
navigate("/config/blueprint/dashboard", { replace: true });
|
||||||
@@ -314,13 +275,6 @@ class HaBlueprintOverview extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected updated(changedProps: PropertyValues) {
|
|
||||||
super.updated(changedProps);
|
|
||||||
if (changedProps.has("blueprints")) {
|
|
||||||
this._loadUsageCounts();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<hass-tabs-subpage-data-table
|
<hass-tabs-subpage-data-table
|
||||||
@@ -330,11 +284,7 @@ class HaBlueprintOverview extends LitElement {
|
|||||||
.route=${this.route}
|
.route=${this.route}
|
||||||
.tabs=${configSections.automations}
|
.tabs=${configSections.automations}
|
||||||
.columns=${this._columns(this.hass.localize)}
|
.columns=${this._columns(this.hass.localize)}
|
||||||
.data=${this._processedBlueprints(
|
.data=${this._processedBlueprints(this.blueprints, this.hass.localize)}
|
||||||
this.blueprints,
|
|
||||||
this.hass.localize,
|
|
||||||
this._usageCounts
|
|
||||||
)}
|
|
||||||
id="fullpath"
|
id="fullpath"
|
||||||
.noDataText=${this.hass.localize(
|
.noDataText=${this.hass.localize(
|
||||||
"ui.panel.config.blueprint.overview.no_blueprints"
|
"ui.panel.config.blueprint.overview.no_blueprints"
|
||||||
@@ -430,51 +380,10 @@ class HaBlueprintOverview extends LitElement {
|
|||||||
fireEvent(this, "reload-blueprints");
|
fireEvent(this, "reload-blueprints");
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _loadUsageCounts() {
|
|
||||||
if (!this.blueprints) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const request = ++this._usageCountRequest;
|
|
||||||
const usageCounts: Record<string, number> = {};
|
|
||||||
|
|
||||||
const blueprintList = this._processedBlueprints(
|
|
||||||
this.blueprints,
|
|
||||||
this.hass.localize,
|
|
||||||
{}
|
|
||||||
);
|
|
||||||
|
|
||||||
await Promise.all(
|
|
||||||
blueprintList.map(async (blueprint) => {
|
|
||||||
if (blueprint.error) {
|
|
||||||
usageCounts[blueprint.fullpath] = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const related = await findRelated(
|
|
||||||
this.hass,
|
|
||||||
`${blueprint.domain}_blueprint`,
|
|
||||||
blueprint.path
|
|
||||||
);
|
|
||||||
const count =
|
|
||||||
(related.automation?.length || 0) + (related.script?.length || 0);
|
|
||||||
usageCounts[blueprint.fullpath] = count;
|
|
||||||
} catch (_err) {
|
|
||||||
usageCounts[blueprint.fullpath] = 0;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
if (request === this._usageCountRequest) {
|
|
||||||
this._usageCounts = usageCounts;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _handleRowClicked(ev: HASSDomEvent<RowClickedEvent>) {
|
private _handleRowClicked(ev: HASSDomEvent<RowClickedEvent>) {
|
||||||
const blueprint = this._processedBlueprints(
|
const blueprint = this._processedBlueprints(
|
||||||
this.blueprints,
|
this.blueprints,
|
||||||
this.hass.localize,
|
this.hass.localize
|
||||||
this._usageCounts
|
|
||||||
).find((b) => b.fullpath === ev.detail.id)!;
|
).find((b) => b.fullpath === ev.detail.id)!;
|
||||||
if (blueprint.error) {
|
if (blueprint.error) {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
@@ -488,25 +397,6 @@ class HaBlueprintOverview extends LitElement {
|
|||||||
this._createNew(blueprint);
|
this._createNew(blueprint);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleUsageClick = (ev: Event) => {
|
|
||||||
ev.stopPropagation();
|
|
||||||
ev.preventDefault();
|
|
||||||
const target = ev.currentTarget as HTMLElement | null;
|
|
||||||
const fullpath = target?.dataset.fullpath;
|
|
||||||
if (!fullpath) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const blueprint = this._processedBlueprints(
|
|
||||||
this.blueprints,
|
|
||||||
this.hass.localize,
|
|
||||||
this._usageCounts
|
|
||||||
).find((item) => item.fullpath === fullpath);
|
|
||||||
if (!blueprint || blueprint.error) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._showUsed(blueprint);
|
|
||||||
};
|
|
||||||
|
|
||||||
private _showUsed = (blueprint: BlueprintMetaDataPath) => {
|
private _showUsed = (blueprint: BlueprintMetaDataPath) => {
|
||||||
navigate(
|
navigate(
|
||||||
`/config/${blueprint.domain}/dashboard?blueprint=${encodeURIComponent(
|
`/config/${blueprint.domain}/dashboard?blueprint=${encodeURIComponent(
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
|
import { mdiOpenInNew } from "@mdi/js";
|
||||||
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
|
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
|
||||||
import { css, html, LitElement, nothing } from "lit";
|
import { css, html, LitElement } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||||
import "../../../components/ha-analytics";
|
import "../../../components/ha-analytics";
|
||||||
|
import "../../../components/ha-button";
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
|
import "../../../components/ha-checkbox";
|
||||||
import "../../../components/ha-settings-row";
|
import "../../../components/ha-settings-row";
|
||||||
|
import "../../../components/ha-svg-icon";
|
||||||
import type { Analytics } from "../../../data/analytics";
|
import type { Analytics } from "../../../data/analytics";
|
||||||
import {
|
import {
|
||||||
getAnalyticsDetails,
|
getAnalyticsDetails,
|
||||||
@@ -13,8 +17,6 @@ import {
|
|||||||
import { haStyle } from "../../../resources/styles";
|
import { haStyle } from "../../../resources/styles";
|
||||||
import type { HomeAssistant } from "../../../types";
|
import type { HomeAssistant } from "../../../types";
|
||||||
import { documentationUrl } from "../../../util/documentation-url";
|
import { documentationUrl } from "../../../util/documentation-url";
|
||||||
import type { HaSwitch } from "../../../components/ha-switch";
|
|
||||||
import "../../../components/ha-alert";
|
|
||||||
|
|
||||||
@customElement("ha-config-analytics")
|
@customElement("ha-config-analytics")
|
||||||
class ConfigAnalytics extends LitElement {
|
class ConfigAnalytics extends LitElement {
|
||||||
@@ -32,22 +34,10 @@ class ConfigAnalytics extends LitElement {
|
|||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-card
|
<ha-card outlined>
|
||||||
outlined
|
|
||||||
.header=${this.hass.localize("ui.panel.config.analytics.header") ||
|
|
||||||
"Home Assistant analytics"}
|
|
||||||
>
|
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
${error ? html`<div class="error">${error}</div>` : nothing}
|
${error ? html`<div class="error">${error}</div>` : ""}
|
||||||
<p>
|
<p>${this.hass.localize("ui.panel.config.analytics.intro")}</p>
|
||||||
${this.hass.localize("ui.panel.config.analytics.intro")}
|
|
||||||
<a
|
|
||||||
href=${documentationUrl(this.hass, "/integrations/analytics/")}
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>${this.hass.localize("ui.panel.config.analytics.learn_more")}</a
|
|
||||||
>.
|
|
||||||
</p>
|
|
||||||
<ha-analytics
|
<ha-analytics
|
||||||
translation_key_panel="config"
|
translation_key_panel="config"
|
||||||
@analytics-preferences-changed=${this._preferencesChanged}
|
@analytics-preferences-changed=${this._preferencesChanged}
|
||||||
@@ -55,59 +45,26 @@ class ConfigAnalytics extends LitElement {
|
|||||||
.analytics=${this._analyticsDetails}
|
.analytics=${this._analyticsDetails}
|
||||||
></ha-analytics>
|
></ha-analytics>
|
||||||
</div>
|
</div>
|
||||||
</ha-card>
|
<div class="card-actions">
|
||||||
${this._analyticsDetails &&
|
<ha-button @click=${this._save}>
|
||||||
"snapshots" in this._analyticsDetails.preferences
|
${this.hass.localize(
|
||||||
? html`<ha-card
|
"ui.panel.config.core.section.core.core_config.save_button"
|
||||||
outlined
|
|
||||||
.header=${this.hass.localize(
|
|
||||||
"ui.panel.config.analytics.preferences.snapshots.header"
|
|
||||||
)}
|
)}
|
||||||
>
|
</ha-button>
|
||||||
<div class="card-content">
|
</div>
|
||||||
<p>
|
</ha-card>
|
||||||
${this.hass.localize(
|
<div class="footer">
|
||||||
"ui.panel.config.analytics.preferences.snapshots.info"
|
<ha-button
|
||||||
)}
|
size="small"
|
||||||
<a
|
appearance="plain"
|
||||||
href=${documentationUrl(this.hass, "/device-database/")}
|
href=${documentationUrl(this.hass, "/integrations/analytics/")}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
>${this.hass.localize(
|
>
|
||||||
"ui.panel.config.analytics.preferences.snapshots.learn_more"
|
<ha-svg-icon slot="end" .path=${mdiOpenInNew}></ha-svg-icon>
|
||||||
)}</a
|
${this.hass.localize("ui.panel.config.analytics.learn_more")}
|
||||||
>.
|
</ha-button>
|
||||||
</p>
|
</div>
|
||||||
<ha-alert
|
|
||||||
.title=${this.hass.localize(
|
|
||||||
"ui.panel.config.analytics.preferences.snapshots.alert.title"
|
|
||||||
)}
|
|
||||||
>${this.hass.localize(
|
|
||||||
"ui.panel.config.analytics.preferences.snapshots.alert.content"
|
|
||||||
)}</ha-alert
|
|
||||||
>
|
|
||||||
<ha-settings-row>
|
|
||||||
<span slot="heading" data-for="snapshots">
|
|
||||||
${this.hass.localize(
|
|
||||||
`ui.panel.config.analytics.preferences.snapshots.title`
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
<span slot="description" data-for="snapshots">
|
|
||||||
${this.hass.localize(
|
|
||||||
`ui.panel.config.analytics.preferences.snapshots.description`
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
<ha-switch
|
|
||||||
@change=${this._handleDeviceRowClick}
|
|
||||||
.checked=${!!this._analyticsDetails?.preferences.snapshots}
|
|
||||||
.disabled=${this._analyticsDetails === undefined}
|
|
||||||
name="snapshots"
|
|
||||||
>
|
|
||||||
</ha-switch>
|
|
||||||
</ha-settings-row>
|
|
||||||
</div>
|
|
||||||
</ha-card>`
|
|
||||||
: nothing}
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,25 +96,11 @@ class ConfigAnalytics extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleDeviceRowClick(ev: Event) {
|
|
||||||
const target = ev.target as HaSwitch;
|
|
||||||
|
|
||||||
this._analyticsDetails = {
|
|
||||||
...this._analyticsDetails!,
|
|
||||||
preferences: {
|
|
||||||
...this._analyticsDetails!.preferences,
|
|
||||||
snapshots: target.checked,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
this._save();
|
|
||||||
}
|
|
||||||
|
|
||||||
private _preferencesChanged(event: CustomEvent): void {
|
private _preferencesChanged(event: CustomEvent): void {
|
||||||
this._analyticsDetails = {
|
this._analyticsDetails = {
|
||||||
...this._analyticsDetails!,
|
...this._analyticsDetails!,
|
||||||
preferences: event.detail.preferences,
|
preferences: event.detail.preferences,
|
||||||
};
|
};
|
||||||
this._save();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
@@ -174,10 +117,21 @@ class ConfigAnalytics extends LitElement {
|
|||||||
p {
|
p {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
ha-card:not(:first-of-type) {
|
.card-actions {
|
||||||
margin-top: 24px;
|
display: flex;
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
`,
|
.footer {
|
||||||
|
padding: 32px 0 16px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
ha-button[size="small"] ha-svg-icon {
|
||||||
|
--mdc-icon-size: 16px;
|
||||||
|
}
|
||||||
|
`, // row-reverse so we tab first to "save"
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ import { mdiDotsVertical, mdiDownload } from "@mdi/js";
|
|||||||
import type { TemplateResult } from "lit";
|
import type { TemplateResult } from "lit";
|
||||||
import { css, html, LitElement, nothing } from "lit";
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import "../../../components/ha-button-menu";
|
||||||
import "../../../components/ha-icon-button";
|
import "../../../components/ha-icon-button";
|
||||||
|
import "../../../components/ha-list-item";
|
||||||
import "../../../components/ha-svg-icon";
|
import "../../../components/ha-svg-icon";
|
||||||
import { getSignedPath } from "../../../data/auth";
|
import { getSignedPath } from "../../../data/auth";
|
||||||
import "../../../layouts/hass-subpage";
|
import "../../../layouts/hass-subpage";
|
||||||
@@ -12,8 +14,6 @@ import {
|
|||||||
downloadFileSupported,
|
downloadFileSupported,
|
||||||
fileDownload,
|
fileDownload,
|
||||||
} from "../../../util/file_download";
|
} from "../../../util/file_download";
|
||||||
import "../../../components/ha-dropdown-item";
|
|
||||||
import "../../../components/ha-dropdown";
|
|
||||||
|
|
||||||
@customElement("ha-config-section-analytics")
|
@customElement("ha-config-section-analytics")
|
||||||
class HaConfigSectionAnalytics extends LitElement {
|
class HaConfigSectionAnalytics extends LitElement {
|
||||||
@@ -33,19 +33,22 @@ class HaConfigSectionAnalytics extends LitElement {
|
|||||||
>
|
>
|
||||||
${downloadFileSupported(this.hass)
|
${downloadFileSupported(this.hass)
|
||||||
? html`
|
? html`
|
||||||
<ha-dropdown
|
<ha-button-menu
|
||||||
@wa-select=${this._handleOverflowAction}
|
@action=${this._handleOverflowAction}
|
||||||
slot="toolbar-icon"
|
slot="toolbar-icon"
|
||||||
>
|
>
|
||||||
<ha-icon-button slot="trigger" .path=${mdiDotsVertical}>
|
<ha-icon-button slot="trigger" .path=${mdiDotsVertical}>
|
||||||
</ha-icon-button>
|
</ha-icon-button>
|
||||||
<ha-dropdown-item .value=${"download_device_info"}>
|
<ha-list-item graphic="icon">
|
||||||
<ha-svg-icon slot="icon" .path=${mdiDownload}></ha-svg-icon>
|
<ha-svg-icon
|
||||||
|
slot="graphic"
|
||||||
|
.path=${mdiDownload}
|
||||||
|
></ha-svg-icon>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.analytics.download_device_info"
|
"ui.panel.config.analytics.download_device_info"
|
||||||
)}
|
)}
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
</ha-dropdown>
|
</ha-button-menu>
|
||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
<div class="content">
|
<div class="content">
|
||||||
@@ -55,16 +58,9 @@ class HaConfigSectionAnalytics extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _handleOverflowAction(
|
private async _handleOverflowAction(): Promise<void> {
|
||||||
ev: CustomEvent<{ item: { value: string } }>
|
const signedPath = await getSignedPath(this.hass, "/api/analytics/devices");
|
||||||
): Promise<void> {
|
fileDownload(signedPath.path);
|
||||||
if (ev.detail.item.value === "download_device_info") {
|
|
||||||
const signedPath = await getSignedPath(
|
|
||||||
this.hass,
|
|
||||||
"/api/analytics/devices"
|
|
||||||
);
|
|
||||||
fileDownload(signedPath.path);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static styles = css`
|
static styles = css`
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type { RequestSelectedDetail } from "@material/mwc-list/mwc-list-item";
|
||||||
import { mdiDotsVertical, mdiRefresh } from "@mdi/js";
|
import { mdiDotsVertical, mdiRefresh } from "@mdi/js";
|
||||||
import type { HassEntities } from "home-assistant-js-websocket";
|
import type { HassEntities } from "home-assistant-js-websocket";
|
||||||
import type { TemplateResult } from "lit";
|
import type { TemplateResult } from "lit";
|
||||||
@@ -5,9 +6,13 @@ import { LitElement, css, html } from "lit";
|
|||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||||
|
import { shouldHandleRequestSelectedEvent } from "../../../common/mwc/handle-request-selected-event";
|
||||||
import "../../../components/ha-alert";
|
import "../../../components/ha-alert";
|
||||||
import "../../../components/ha-bar";
|
import "../../../components/ha-bar";
|
||||||
|
import "../../../components/ha-button-menu";
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
|
import "../../../components/ha-check-list-item";
|
||||||
|
import "../../../components/ha-list-item";
|
||||||
import "../../../components/ha-metric";
|
import "../../../components/ha-metric";
|
||||||
import { extractApiErrorMessage } from "../../../data/hassio/common";
|
import { extractApiErrorMessage } from "../../../data/hassio/common";
|
||||||
import type {
|
import type {
|
||||||
@@ -28,9 +33,6 @@ import "../../../layouts/hass-subpage";
|
|||||||
import type { HomeAssistant } from "../../../types";
|
import type { HomeAssistant } from "../../../types";
|
||||||
import "../dashboard/ha-config-updates";
|
import "../dashboard/ha-config-updates";
|
||||||
import { showJoinBetaDialog } from "./updates/show-dialog-join-beta";
|
import { showJoinBetaDialog } from "./updates/show-dialog-join-beta";
|
||||||
import "../../../components/ha-dropdown";
|
|
||||||
import "../../../components/ha-dropdown-item";
|
|
||||||
import "@home-assistant/webawesome/dist/components/divider/divider";
|
|
||||||
|
|
||||||
@customElement("ha-config-section-updates")
|
@customElement("ha-config-section-updates")
|
||||||
class HaConfigSectionUpdates extends LitElement {
|
class HaConfigSectionUpdates extends LitElement {
|
||||||
@@ -71,25 +73,24 @@ class HaConfigSectionUpdates extends LitElement {
|
|||||||
.path=${mdiRefresh}
|
.path=${mdiRefresh}
|
||||||
@click=${this._checkUpdates}
|
@click=${this._checkUpdates}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
<ha-dropdown @wa-select=${this._handleOverflowAction}>
|
<ha-button-menu multi>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass.localize("ui.common.menu")}
|
.label=${this.hass.localize("ui.common.menu")}
|
||||||
.path=${mdiDotsVertical}
|
.path=${mdiDotsVertical}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
|
<ha-check-list-item
|
||||||
<ha-dropdown-item
|
left
|
||||||
type="checkbox"
|
@request-selected=${this._toggleSkipped}
|
||||||
value="show_skipped"
|
.selected=${this._showSkipped}
|
||||||
.checked=${this._showSkipped}
|
|
||||||
>
|
>
|
||||||
${this.hass.localize("ui.panel.config.updates.show_skipped")}
|
${this.hass.localize("ui.panel.config.updates.show_skipped")}
|
||||||
</ha-dropdown-item>
|
</ha-check-list-item>
|
||||||
${this._supervisorInfo
|
${this._supervisorInfo
|
||||||
? html`
|
? html`
|
||||||
<wa-divider></wa-divider>
|
<li divider role="separator"></li>
|
||||||
<ha-dropdown-item
|
<ha-list-item
|
||||||
value="toggle_beta"
|
@request-selected=${this._toggleBeta}
|
||||||
.disabled=${this._supervisorInfo.channel === "dev"}
|
.disabled=${this._supervisorInfo.channel === "dev"}
|
||||||
>
|
>
|
||||||
${this._supervisorInfo.channel === "stable"
|
${this._supervisorInfo.channel === "stable"
|
||||||
@@ -97,10 +98,10 @@ class HaConfigSectionUpdates extends LitElement {
|
|||||||
: this.hass.localize(
|
: this.hass.localize(
|
||||||
"ui.panel.config.updates.leave_beta"
|
"ui.panel.config.updates.leave_beta"
|
||||||
)}
|
)}
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
</ha-dropdown>
|
</ha-button-menu>
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<ha-card outlined>
|
<ha-card outlined>
|
||||||
@@ -132,19 +133,27 @@ class HaConfigSectionUpdates extends LitElement {
|
|||||||
this._supervisorInfo = await fetchHassioSupervisorInfo(this.hass);
|
this._supervisorInfo = await fetchHassioSupervisorInfo(this.hass);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _handleOverflowAction(
|
private _toggleSkipped(ev: CustomEvent<RequestSelectedDetail>): void {
|
||||||
ev: CustomEvent<{ item: { value: string } }>
|
if (ev.detail.source !== "property") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._showSkipped = !this._showSkipped;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _toggleBeta(
|
||||||
|
ev: CustomEvent<RequestSelectedDetail>
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (ev.detail.item.value === "toggle_beta") {
|
if (!shouldHandleRequestSelectedEvent(ev)) {
|
||||||
if (this._supervisorInfo!.channel === "stable") {
|
return;
|
||||||
showJoinBetaDialog(this, {
|
}
|
||||||
join: async () => this._setChannel("beta"),
|
|
||||||
});
|
if (this._supervisorInfo!.channel === "stable") {
|
||||||
} else {
|
showJoinBetaDialog(this, {
|
||||||
this._setChannel("stable");
|
join: async () => this._setChannel("beta"),
|
||||||
}
|
});
|
||||||
} else if (ev.detail.item.value === "show_skipped") {
|
} else {
|
||||||
this._showSkipped = !this._showSkipped;
|
this._setChannel("stable");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
import type { CSSResultGroup, TemplateResult } from "lit";
|
import type { CSSResultGroup, TemplateResult } from "lit";
|
||||||
import { css, html, LitElement, nothing } from "lit";
|
import { css, html, LitElement } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
|
||||||
import { computeDeviceNameDisplay } from "../../../../common/entity/compute_device_name";
|
import { computeDeviceNameDisplay } from "../../../../common/entity/compute_device_name";
|
||||||
import { stringCompare } from "../../../../common/string/compare";
|
|
||||||
import { titleCase } from "../../../../common/string/title-case";
|
import { titleCase } from "../../../../common/string/title-case";
|
||||||
import "../../../../components/ha-card";
|
import "../../../../components/ha-card";
|
||||||
import type { DeviceRegistryEntry } from "../../../../data/device_registry";
|
import type { DeviceRegistryEntry } from "../../../../data/device_registry";
|
||||||
@@ -11,61 +9,16 @@ import { haStyle } from "../../../../resources/styles";
|
|||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
import { createSearchParam } from "../../../../common/url/search-params";
|
import { createSearchParam } from "../../../../common/url/search-params";
|
||||||
import { isComponentLoaded } from "../../../../common/config/is_component_loaded";
|
import { isComponentLoaded } from "../../../../common/config/is_component_loaded";
|
||||||
import "../../../../components/ha-icon";
|
|
||||||
import "../../../../components/ha-label";
|
|
||||||
import type { LabelRegistryEntry } from "../../../../data/label_registry";
|
|
||||||
import { subscribeLabelRegistry } from "../../../../data/label_registry";
|
|
||||||
import { computeCssColor } from "../../../../common/color/compute-color";
|
|
||||||
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
|
|
||||||
|
|
||||||
@customElement("ha-device-info-card")
|
@customElement("ha-device-info-card")
|
||||||
export class HaDeviceCard extends SubscribeMixin(LitElement) {
|
export class HaDeviceCard extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property({ attribute: false }) public device!: DeviceRegistryEntry;
|
@property({ attribute: false }) public device!: DeviceRegistryEntry;
|
||||||
|
|
||||||
@property({ type: Boolean }) public narrow = false;
|
@property({ type: Boolean }) public narrow = false;
|
||||||
|
|
||||||
@state() private _labelRegistry?: LabelRegistryEntry[];
|
|
||||||
|
|
||||||
private _labelsData = memoizeOne(
|
|
||||||
(
|
|
||||||
labels: LabelRegistryEntry[] | undefined,
|
|
||||||
labelIds: string[],
|
|
||||||
language: string
|
|
||||||
): {
|
|
||||||
map: Map<string, LabelRegistryEntry>;
|
|
||||||
ids: string[];
|
|
||||||
} => {
|
|
||||||
const map = labels
|
|
||||||
? new Map(labels.map((label) => [label.label_id, label]))
|
|
||||||
: new Map<string, LabelRegistryEntry>();
|
|
||||||
const ids = [...labelIds].sort((labelA, labelB) =>
|
|
||||||
stringCompare(
|
|
||||||
map.get(labelA)?.name || labelA,
|
|
||||||
map.get(labelB)?.name || labelB,
|
|
||||||
language
|
|
||||||
)
|
|
||||||
);
|
|
||||||
return { map, ids };
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
public hassSubscribe() {
|
|
||||||
return [
|
|
||||||
subscribeLabelRegistry(this.hass.connection, (labels) => {
|
|
||||||
this._labelRegistry = labels;
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
const { map: labelMap, ids: labels } = this._labelsData(
|
|
||||||
this._labelRegistry,
|
|
||||||
this.device.labels,
|
|
||||||
this.hass.locale.language
|
|
||||||
);
|
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-card
|
<ha-card
|
||||||
outlined
|
outlined
|
||||||
@@ -105,7 +58,7 @@ export class HaDeviceCard extends SubscribeMixin(LitElement) {
|
|||||||
<span class="hub"
|
<span class="hub"
|
||||||
><a
|
><a
|
||||||
href="/config/devices/device/${this.device.via_device_id}"
|
href="/config/devices/device/${this.device.via_device_id}"
|
||||||
>${this._computeDeviceNameDisplay(
|
>${this._computeDeviceNameDislay(
|
||||||
this.device.via_device_id
|
this.device.via_device_id
|
||||||
)}</a
|
)}</a
|
||||||
></span
|
></span
|
||||||
@@ -173,34 +126,6 @@ export class HaDeviceCard extends SubscribeMixin(LitElement) {
|
|||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
${labels.length > 0
|
|
||||||
? html`
|
|
||||||
<div class="extra-info labels">
|
|
||||||
${labels.map((labelId) => {
|
|
||||||
const label = labelMap.get(labelId);
|
|
||||||
const color =
|
|
||||||
label?.color && typeof label.color === "string"
|
|
||||||
? computeCssColor(label.color)
|
|
||||||
: undefined;
|
|
||||||
return html`
|
|
||||||
<ha-label
|
|
||||||
style=${color ? `--color: ${color}` : ""}
|
|
||||||
.description=${label?.description}
|
|
||||||
>
|
|
||||||
${label?.icon
|
|
||||||
? html`<ha-icon
|
|
||||||
slot="icon"
|
|
||||||
.icon=${label.icon}
|
|
||||||
></ha-icon>`
|
|
||||||
: nothing}
|
|
||||||
${label?.name || labelId}
|
|
||||||
</ha-label>
|
|
||||||
`;
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
: nothing}
|
|
||||||
|
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
<slot name="actions"></slot>
|
<slot name="actions"></slot>
|
||||||
@@ -214,7 +139,7 @@ export class HaDeviceCard extends SubscribeMixin(LitElement) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _computeDeviceNameDisplay(deviceId: string) {
|
private _computeDeviceNameDislay(deviceId) {
|
||||||
const device = this.hass.devices[deviceId];
|
const device = this.hass.devices[deviceId];
|
||||||
return device
|
return device
|
||||||
? computeDeviceNameDisplay(device, this.hass)
|
? computeDeviceNameDisplay(device, this.hass)
|
||||||
@@ -237,26 +162,8 @@ export class HaDeviceCard extends SubscribeMixin(LitElement) {
|
|||||||
.device {
|
.device {
|
||||||
width: 30%;
|
width: 30%;
|
||||||
}
|
}
|
||||||
.labels {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: var(--ha-space-1);
|
|
||||||
width: 100%;
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
.labels ha-label {
|
|
||||||
min-width: 0;
|
|
||||||
max-width: 100%;
|
|
||||||
flex: 0 1 auto;
|
|
||||||
}
|
|
||||||
ha-label {
|
|
||||||
--ha-label-background-color: var(--color, var(--grey-color));
|
|
||||||
--ha-label-background-opacity: 0.5;
|
|
||||||
--ha-label-text-color: var(--primary-text-color);
|
|
||||||
--ha-label-icon-color: var(--primary-text-color);
|
|
||||||
}
|
|
||||||
.extra-info {
|
.extra-info {
|
||||||
margin-top: var(--ha-space-2);
|
margin-top: 8px;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
}
|
}
|
||||||
.manuf,
|
.manuf,
|
||||||
|
|||||||
@@ -1,52 +0,0 @@
|
|||||||
import { mdiKey } from "@mdi/js";
|
|
||||||
import { getConfigEntries } from "../../../../../../data/config_entries";
|
|
||||||
import type { DeviceRegistryEntry } from "../../../../../../data/device_registry";
|
|
||||||
import { fetchESPHomeEncryptionKey } from "../../../../../../data/esphome";
|
|
||||||
import type { HomeAssistant } from "../../../../../../types";
|
|
||||||
import { showESPHomeEncryptionKeyDialog } from "../../../../integrations/integration-panels/esphome/show-dialog-esphome-encryption-key";
|
|
||||||
import type { DeviceAction } from "../../../ha-config-device-page";
|
|
||||||
|
|
||||||
export const getESPHomeDeviceActions = async (
|
|
||||||
el: HTMLElement,
|
|
||||||
hass: HomeAssistant,
|
|
||||||
device: DeviceRegistryEntry
|
|
||||||
): Promise<DeviceAction[]> => {
|
|
||||||
const actions: DeviceAction[] = [];
|
|
||||||
|
|
||||||
const configEntries = await getConfigEntries(hass, {
|
|
||||||
domain: "esphome",
|
|
||||||
});
|
|
||||||
|
|
||||||
const configEntry = configEntries.find((entry) =>
|
|
||||||
device.config_entries.includes(entry.entry_id)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!configEntry) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const entryId = configEntry.entry_id;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const encryptionKey = await fetchESPHomeEncryptionKey(hass, entryId);
|
|
||||||
|
|
||||||
if (encryptionKey.encryption_key) {
|
|
||||||
actions.push({
|
|
||||||
label: hass.localize(
|
|
||||||
"ui.panel.config.devices.esphome.show_encryption_key"
|
|
||||||
),
|
|
||||||
icon: mdiKey,
|
|
||||||
action: () =>
|
|
||||||
showESPHomeEncryptionKeyDialog(el, {
|
|
||||||
entry_id: entryId,
|
|
||||||
encryption_key: encryptionKey.encryption_key,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.error("Failed to fetch ESPHome encryption key:", err);
|
|
||||||
}
|
|
||||||
|
|
||||||
return actions;
|
|
||||||
};
|
|
||||||
@@ -1162,17 +1162,6 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
);
|
);
|
||||||
deviceActions.push(...actions);
|
deviceActions.push(...actions);
|
||||||
}
|
}
|
||||||
if (domains.includes("esphome")) {
|
|
||||||
const esphome = await import(
|
|
||||||
"./device-detail/integration-elements/esphome/device-actions"
|
|
||||||
);
|
|
||||||
const actions = await esphome.getESPHomeDeviceActions(
|
|
||||||
this,
|
|
||||||
this.hass,
|
|
||||||
device
|
|
||||||
);
|
|
||||||
deviceActions.push(...actions);
|
|
||||||
}
|
|
||||||
if (domains.includes("matter")) {
|
if (domains.includes("matter")) {
|
||||||
const matter = await import(
|
const matter = await import(
|
||||||
"./device-detail/integration-elements/matter/device-actions"
|
"./device-detail/integration-elements/matter/device-actions"
|
||||||
|
|||||||
@@ -1,137 +0,0 @@
|
|||||||
import { mdiClose, mdiContentCopy } from "@mdi/js";
|
|
||||||
import type { CSSResultGroup } from "lit";
|
|
||||||
import { css, html, LitElement, nothing } from "lit";
|
|
||||||
import { customElement, property, state } from "lit/decorators";
|
|
||||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
|
||||||
import { copyToClipboard } from "../../../../../common/util/copy-clipboard";
|
|
||||||
import "../../../../../components/ha-dialog-header";
|
|
||||||
import "../../../../../components/ha-icon-button";
|
|
||||||
import "../../../../../components/ha-wa-dialog";
|
|
||||||
import { haStyleDialog } from "../../../../../resources/styles";
|
|
||||||
import type { HomeAssistant } from "../../../../../types";
|
|
||||||
import { showToast } from "../../../../../util/toast";
|
|
||||||
import type { ESPHomeEncryptionKeyDialogParams } from "./show-dialog-esphome-encryption-key";
|
|
||||||
|
|
||||||
@customElement("dialog-esphome-encryption-key")
|
|
||||||
class DialogESPHomeEncryptionKey extends LitElement {
|
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
|
||||||
|
|
||||||
@state() private _params?: ESPHomeEncryptionKeyDialogParams;
|
|
||||||
|
|
||||||
public async showDialog(
|
|
||||||
params: ESPHomeEncryptionKeyDialogParams
|
|
||||||
): Promise<void> {
|
|
||||||
this._params = params;
|
|
||||||
}
|
|
||||||
|
|
||||||
public closeDialog(): void {
|
|
||||||
this._params = undefined;
|
|
||||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
|
||||||
}
|
|
||||||
|
|
||||||
protected render() {
|
|
||||||
if (!this._params) {
|
|
||||||
return nothing;
|
|
||||||
}
|
|
||||||
|
|
||||||
return html`
|
|
||||||
<ha-wa-dialog
|
|
||||||
open
|
|
||||||
@closed=${this.closeDialog}
|
|
||||||
hideActions
|
|
||||||
header-title=${this.hass.localize(
|
|
||||||
"ui.panel.config.devices.esphome.encryption_key_title"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<ha-dialog-header slot="heading">
|
|
||||||
<ha-icon-button
|
|
||||||
slot="navigationIcon"
|
|
||||||
dialogAction="cancel"
|
|
||||||
.label=${this.hass.localize("ui.common.close")}
|
|
||||||
.path=${mdiClose}
|
|
||||||
></ha-icon-button>
|
|
||||||
<span slot="title">
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.devices.esphome.encryption_key_title"
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
</ha-dialog-header>
|
|
||||||
|
|
||||||
<div class="content">
|
|
||||||
<p>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.devices.esphome.encryption_key_description"
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
<div class="key-row">
|
|
||||||
<div class="key-container">
|
|
||||||
<code>${this._params.encryption_key}</code>
|
|
||||||
</div>
|
|
||||||
<ha-icon-button
|
|
||||||
@click=${this._copyToClipboard}
|
|
||||||
.label=${this.hass.localize("ui.common.copy")}
|
|
||||||
.path=${mdiContentCopy}
|
|
||||||
></ha-icon-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ha-wa-dialog>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _copyToClipboard(): Promise<void> {
|
|
||||||
if (!this._params?.encryption_key) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await copyToClipboard(this._params.encryption_key);
|
|
||||||
showToast(this, {
|
|
||||||
message: this.hass.localize("ui.common.copied_clipboard"),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
|
||||||
return [
|
|
||||||
haStyleDialog,
|
|
||||||
css`
|
|
||||||
ha-dialog {
|
|
||||||
--mdc-dialog-max-width: 500px;
|
|
||||||
}
|
|
||||||
.content {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--ha-space-6);
|
|
||||||
}
|
|
||||||
|
|
||||||
.key-row {
|
|
||||||
display: flex;
|
|
||||||
gap: var(--ha-space-2);
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.key-container {
|
|
||||||
flex: 1;
|
|
||||||
border-radius: var(--ha-space-2);
|
|
||||||
border: 1px solid var(--divider-color);
|
|
||||||
background-color: var(
|
|
||||||
--code-editor-background-color,
|
|
||||||
var(--secondary-background-color)
|
|
||||||
);
|
|
||||||
padding: var(--ha-space-3);
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 0;
|
|
||||||
color: var(--secondary-text-color);
|
|
||||||
line-height: var(--ha-line-height-condensed);
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
"dialog-esphome-encryption-key": DialogESPHomeEncryptionKey;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
|
||||||
|
|
||||||
export interface ESPHomeEncryptionKeyDialogParams {
|
|
||||||
entry_id: string;
|
|
||||||
encryption_key: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const loadESPHomeEncryptionKeyDialog = () =>
|
|
||||||
import("./dialog-esphome-encryption-key");
|
|
||||||
|
|
||||||
export const showESPHomeEncryptionKeyDialog = (
|
|
||||||
element: HTMLElement,
|
|
||||||
dialogParams: ESPHomeEncryptionKeyDialogParams
|
|
||||||
): void => {
|
|
||||||
fireEvent(element, "show-dialog", {
|
|
||||||
dialogTag: "dialog-esphome-encryption-key",
|
|
||||||
dialogImport: loadESPHomeEncryptionKeyDialog,
|
|
||||||
dialogParams,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
@@ -270,7 +270,6 @@ export class HaManualScriptEditor extends LitElement {
|
|||||||
@value-changed=${this._sidebarConfigChanged}
|
@value-changed=${this._sidebarConfigChanged}
|
||||||
@sidebar-resized=${this._resizeSidebar}
|
@sidebar-resized=${this._resizeSidebar}
|
||||||
@sidebar-resizing-stopped=${this._stopResizeSidebar}
|
@sidebar-resizing-stopped=${this._stopResizeSidebar}
|
||||||
@sidebar-reset-size=${this._resetSidebarWidth}
|
|
||||||
></ha-automation-sidebar>
|
></ha-automation-sidebar>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -619,16 +618,6 @@ export class HaManualScriptEditor extends LitElement {
|
|||||||
this._prevSidebarWidthPx = undefined;
|
this._prevSidebarWidthPx = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _resetSidebarWidth(ev: Event) {
|
|
||||||
ev.stopPropagation();
|
|
||||||
this._prevSidebarWidthPx = undefined;
|
|
||||||
this._sidebarWidthPx = SIDEBAR_DEFAULT_WIDTH;
|
|
||||||
this.style.setProperty(
|
|
||||||
"--sidebar-dynamic-width",
|
|
||||||
`${this._sidebarWidthPx}px`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return [
|
return [
|
||||||
saveFabStyles,
|
saveFabStyles,
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import {
|
|||||||
import { LitElement, css, html } from "lit";
|
import { LitElement, css, html } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { repeat } from "lit/directives/repeat";
|
import { repeat } from "lit/directives/repeat";
|
||||||
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
|
|
||||||
import { formatDateTimeWithSeconds } from "../../../../common/datetime/format_date_time";
|
import { formatDateTimeWithSeconds } from "../../../../common/datetime/format_date_time";
|
||||||
import type {
|
import type {
|
||||||
PipelineRunEvent,
|
PipelineRunEvent,
|
||||||
@@ -21,8 +20,6 @@ import "../../../../layouts/hass-subpage";
|
|||||||
import { haStyle } from "../../../../resources/styles";
|
import { haStyle } from "../../../../resources/styles";
|
||||||
import type { HomeAssistant, Route } from "../../../../types";
|
import type { HomeAssistant, Route } from "../../../../types";
|
||||||
import "./assist-render-pipeline-events";
|
import "./assist-render-pipeline-events";
|
||||||
import type { ChatLog } from "../../../../data/chat_log";
|
|
||||||
import { subscribeChatLog } from "../../../../data/chat_log";
|
|
||||||
|
|
||||||
@customElement("assist-pipeline-debug")
|
@customElement("assist-pipeline-debug")
|
||||||
export class AssistPipelineDebug extends LitElement {
|
export class AssistPipelineDebug extends LitElement {
|
||||||
@@ -40,12 +37,8 @@ export class AssistPipelineDebug extends LitElement {
|
|||||||
|
|
||||||
@state() private _events?: PipelineRunEvent[];
|
@state() private _events?: PipelineRunEvent[];
|
||||||
|
|
||||||
@state() private _chatLog?: ChatLog;
|
|
||||||
|
|
||||||
private _unsubRefreshEventsID?: number;
|
private _unsubRefreshEventsID?: number;
|
||||||
|
|
||||||
private _unsubChatLogUpdates?: Promise<UnsubscribeFunc>;
|
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
return html`<hass-subpage
|
return html`<hass-subpage
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
@@ -113,7 +106,6 @@ export class AssistPipelineDebug extends LitElement {
|
|||||||
? html`<assist-render-pipeline-events
|
? html`<assist-render-pipeline-events
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.events=${this._events}
|
.events=${this._events}
|
||||||
.chatLog=${this._chatLog}
|
|
||||||
></assist-render-pipeline-events>`
|
></assist-render-pipeline-events>`
|
||||||
: ""}
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
@@ -128,10 +120,6 @@ export class AssistPipelineDebug extends LitElement {
|
|||||||
clearRefresh = true;
|
clearRefresh = true;
|
||||||
}
|
}
|
||||||
if (changedProperties.has("_runId")) {
|
if (changedProperties.has("_runId")) {
|
||||||
if (this._unsubChatLogUpdates) {
|
|
||||||
this._unsubChatLogUpdates.then((unsub) => unsub());
|
|
||||||
this._unsubChatLogUpdates = undefined;
|
|
||||||
}
|
|
||||||
this._fetchEvents();
|
this._fetchEvents();
|
||||||
clearRefresh = true;
|
clearRefresh = true;
|
||||||
}
|
}
|
||||||
@@ -147,10 +135,6 @@ export class AssistPipelineDebug extends LitElement {
|
|||||||
clearTimeout(this._unsubRefreshEventsID);
|
clearTimeout(this._unsubRefreshEventsID);
|
||||||
this._unsubRefreshEventsID = undefined;
|
this._unsubRefreshEventsID = undefined;
|
||||||
}
|
}
|
||||||
if (this._unsubChatLogUpdates) {
|
|
||||||
this._unsubChatLogUpdates.then((unsub) => unsub());
|
|
||||||
this._unsubChatLogUpdates = undefined;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _fetchRuns() {
|
private async _fetchRuns() {
|
||||||
@@ -201,27 +185,8 @@ export class AssistPipelineDebug extends LitElement {
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!this._events!.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!this._unsubChatLogUpdates && this._events[0].type === "run-start") {
|
|
||||||
this._unsubChatLogUpdates = subscribeChatLog(
|
|
||||||
this.hass,
|
|
||||||
this._events[0].data.conversation_id,
|
|
||||||
(chatLog) => {
|
|
||||||
if (chatLog) {
|
|
||||||
this._chatLog = chatLog;
|
|
||||||
} else {
|
|
||||||
this._unsubChatLogUpdates?.then((unsub) => unsub());
|
|
||||||
this._unsubChatLogUpdates = undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
this._unsubChatLogUpdates.catch(() => {
|
|
||||||
this._unsubChatLogUpdates = undefined;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (
|
if (
|
||||||
|
this._events?.length &&
|
||||||
// If the last event is not a finish run event, the run is still ongoing.
|
// If the last event is not a finish run event, the run is still ongoing.
|
||||||
// Refresh events automatically.
|
// Refresh events automatically.
|
||||||
!["run-end", "error"].includes(this._events[this._events.length - 1].type)
|
!["run-end", "error"].includes(this._events[this._events.length - 1].type)
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import type { TemplateResult } from "lit";
|
import type { TemplateResult } from "lit";
|
||||||
import { css, html, LitElement } from "lit";
|
import { css, html, LitElement } from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
|
|
||||||
import { extractSearchParam } from "../../../../common/url/search-params";
|
import { extractSearchParam } from "../../../../common/url/search-params";
|
||||||
import "../../../../components/ha-assist-pipeline-picker";
|
import "../../../../components/ha-assist-pipeline-picker";
|
||||||
import "../../../../components/ha-button";
|
import "../../../../components/ha-button";
|
||||||
@@ -25,8 +24,6 @@ import type { HomeAssistant } from "../../../../types";
|
|||||||
import { AudioRecorder } from "../../../../util/audio-recorder";
|
import { AudioRecorder } from "../../../../util/audio-recorder";
|
||||||
import { fileDownload } from "../../../../util/file_download";
|
import { fileDownload } from "../../../../util/file_download";
|
||||||
import "./assist-render-pipeline-run";
|
import "./assist-render-pipeline-run";
|
||||||
import type { ChatLog } from "../../../../data/chat_log";
|
|
||||||
import { subscribeChatLog } from "../../../../data/chat_log";
|
|
||||||
|
|
||||||
@customElement("assist-pipeline-run-debug")
|
@customElement("assist-pipeline-run-debug")
|
||||||
export class AssistPipelineRunDebug extends LitElement {
|
export class AssistPipelineRunDebug extends LitElement {
|
||||||
@@ -49,13 +46,6 @@ export class AssistPipelineRunDebug extends LitElement {
|
|||||||
@state() private _pipelineId?: string =
|
@state() private _pipelineId?: string =
|
||||||
extractSearchParam("pipeline") || undefined;
|
extractSearchParam("pipeline") || undefined;
|
||||||
|
|
||||||
@state() private _chatLog?: ChatLog;
|
|
||||||
|
|
||||||
private _chatLogSubscription: {
|
|
||||||
conversationId: string;
|
|
||||||
unsub: Promise<UnsubscribeFunc>;
|
|
||||||
} | null = null;
|
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<hass-subpage
|
<hass-subpage
|
||||||
@@ -188,7 +178,6 @@ export class AssistPipelineRunDebug extends LitElement {
|
|||||||
<assist-render-pipeline-run
|
<assist-render-pipeline-run
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.pipelineRun=${run}
|
.pipelineRun=${run}
|
||||||
.chatLog=${this._chatLog}
|
|
||||||
></assist-render-pipeline-run>
|
></assist-render-pipeline-run>
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
@@ -197,14 +186,6 @@ export class AssistPipelineRunDebug extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
public disconnectedCallback(): void {
|
|
||||||
super.disconnectedCallback();
|
|
||||||
if (this._chatLogSubscription) {
|
|
||||||
this._chatLogSubscription.unsub.then((unsub) => unsub());
|
|
||||||
this._chatLogSubscription = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private get conversationId(): string | null {
|
private get conversationId(): string | null {
|
||||||
return this._pipelineRuns.length === 0
|
return this._pipelineRuns.length === 0
|
||||||
? null
|
? null
|
||||||
@@ -427,32 +408,6 @@ export class AssistPipelineRunDebug extends LitElement {
|
|||||||
added = true;
|
added = true;
|
||||||
}
|
}
|
||||||
callback(updatedRun);
|
callback(updatedRun);
|
||||||
|
|
||||||
const conversationId = this.conversationId;
|
|
||||||
if (
|
|
||||||
!this._chatLog &&
|
|
||||||
conversationId &&
|
|
||||||
(!this._chatLogSubscription ||
|
|
||||||
this._chatLogSubscription.conversationId !== conversationId)
|
|
||||||
) {
|
|
||||||
if (this._chatLogSubscription) {
|
|
||||||
this._chatLogSubscription.unsub.then((unsub) => unsub());
|
|
||||||
}
|
|
||||||
this._chatLogSubscription = {
|
|
||||||
conversationId,
|
|
||||||
unsub: subscribeChatLog(this.hass, conversationId, (chatLog) => {
|
|
||||||
if (chatLog) {
|
|
||||||
this._chatLog = chatLog;
|
|
||||||
} else {
|
|
||||||
this._chatLogSubscription?.unsub.then((unsub) => unsub());
|
|
||||||
this._chatLogSubscription = null;
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
this._chatLogSubscription.unsub.catch(() => {
|
|
||||||
this._chatLogSubscription = null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
...options,
|
...options,
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import type {
|
|||||||
import { processEvent } from "../../../../data/assist_pipeline";
|
import { processEvent } from "../../../../data/assist_pipeline";
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
import "./assist-render-pipeline-run";
|
import "./assist-render-pipeline-run";
|
||||||
import type { ChatLog } from "../../../../data/chat_log";
|
|
||||||
|
|
||||||
@customElement("assist-render-pipeline-events")
|
@customElement("assist-render-pipeline-events")
|
||||||
export class AssistPipelineEvents extends LitElement {
|
export class AssistPipelineEvents extends LitElement {
|
||||||
@@ -17,8 +16,6 @@ export class AssistPipelineEvents extends LitElement {
|
|||||||
|
|
||||||
@property({ attribute: false }) public events!: PipelineRunEvent[];
|
@property({ attribute: false }) public events!: PipelineRunEvent[];
|
||||||
|
|
||||||
@property({ attribute: false }) public chatLog?: ChatLog;
|
|
||||||
|
|
||||||
private _processEvents = memoizeOne(
|
private _processEvents = memoizeOne(
|
||||||
(events: PipelineRunEvent[]): PipelineRun | undefined => {
|
(events: PipelineRunEvent[]): PipelineRun | undefined => {
|
||||||
let run: PipelineRun | undefined;
|
let run: PipelineRun | undefined;
|
||||||
@@ -59,7 +56,6 @@ export class AssistPipelineEvents extends LitElement {
|
|||||||
<assist-render-pipeline-run
|
<assist-render-pipeline-run
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.pipelineRun=${run}
|
.pipelineRun=${run}
|
||||||
.chatLog=${this.chatLog}
|
|
||||||
></assist-render-pipeline-run>
|
></assist-render-pipeline-run>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { TemplateResult } from "lit";
|
import type { TemplateResult } from "lit";
|
||||||
import { css, html, LitElement, nothing } from "lit";
|
import { css, html, LitElement } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import "../../../../components/ha-card";
|
import "../../../../components/ha-card";
|
||||||
import "../../../../components/ha-alert";
|
import "../../../../components/ha-alert";
|
||||||
@@ -12,12 +12,6 @@ import { formatNumber } from "../../../../common/number/format_number";
|
|||||||
import "../../../../components/ha-yaml-editor";
|
import "../../../../components/ha-yaml-editor";
|
||||||
import { showAlertDialog } from "../../../../dialogs/generic/show-dialog-box";
|
import { showAlertDialog } from "../../../../dialogs/generic/show-dialog-box";
|
||||||
import type { LocalizeKeys } from "../../../../common/translations/localize";
|
import type { LocalizeKeys } from "../../../../common/translations/localize";
|
||||||
import type {
|
|
||||||
ChatLogAssistantContent,
|
|
||||||
ChatLog,
|
|
||||||
ChatLogContent,
|
|
||||||
ChatLogUserContent,
|
|
||||||
} from "../../../../data/chat_log";
|
|
||||||
|
|
||||||
const RUN_DATA = ["pipeline", "language"];
|
const RUN_DATA = ["pipeline", "language"];
|
||||||
const WAKE_WORD_DATA = ["engine"];
|
const WAKE_WORD_DATA = ["engine"];
|
||||||
@@ -125,7 +119,7 @@ const dataMinusKeysRender = (
|
|||||||
result[key] = data[key];
|
result[key] = data[key];
|
||||||
}
|
}
|
||||||
return render
|
return render
|
||||||
? html`<ha-expansion-panel class="yaml-expansion">
|
? html`<ha-expansion-panel>
|
||||||
<span slot="header"
|
<span slot="header"
|
||||||
>${hass.localize("ui.panel.config.voice_assistants.debug.raw")}</span
|
>${hass.localize("ui.panel.config.voice_assistants.debug.raw")}</span
|
||||||
>
|
>
|
||||||
@@ -140,8 +134,6 @@ export class AssistPipelineDebug extends LitElement {
|
|||||||
|
|
||||||
@property({ attribute: false }) public pipelineRun!: PipelineRun;
|
@property({ attribute: false }) public pipelineRun!: PipelineRun;
|
||||||
|
|
||||||
@property({ attribute: false }) public chatLog?: ChatLog;
|
|
||||||
|
|
||||||
private _audioElement?: HTMLAudioElement;
|
private _audioElement?: HTMLAudioElement;
|
||||||
|
|
||||||
private get _isPlaying(): boolean {
|
private get _isPlaying(): boolean {
|
||||||
@@ -155,47 +147,31 @@ export class AssistPipelineDebug extends LitElement {
|
|||||||
) || "ready"
|
) || "ready"
|
||||||
: "ready";
|
: "ready";
|
||||||
|
|
||||||
let messages: ChatLogContent[];
|
const messages: { from: string; text: string }[] = [];
|
||||||
|
|
||||||
if (this.chatLog) {
|
const userMessage =
|
||||||
messages = this.chatLog.content.filter(
|
(this.pipelineRun.init_options &&
|
||||||
this.pipelineRun.finished
|
"text" in this.pipelineRun.init_options.input
|
||||||
? (content: ChatLogContent) =>
|
? this.pipelineRun.init_options.input.text
|
||||||
content.role === "system" ||
|
: undefined) ||
|
||||||
(content.created >= this.pipelineRun.started &&
|
this.pipelineRun?.stt?.stt_output?.text ||
|
||||||
content.created <= this.pipelineRun.finished!)
|
this.pipelineRun?.intent?.intent_input;
|
||||||
: (content: ChatLogContent) =>
|
|
||||||
content.role === "system" ||
|
|
||||||
content.created >= this.pipelineRun.started
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
messages = [];
|
|
||||||
|
|
||||||
// We don't have the chat log everywhere yet, just fallback for now.
|
if (userMessage) {
|
||||||
const userMessage =
|
messages.push({
|
||||||
(this.pipelineRun.init_options &&
|
from: "user",
|
||||||
"text" in this.pipelineRun.init_options.input
|
text: userMessage,
|
||||||
? this.pipelineRun.init_options.input.text
|
});
|
||||||
: undefined) ||
|
}
|
||||||
this.pipelineRun?.stt?.stt_output?.text ||
|
|
||||||
this.pipelineRun?.intent?.intent_input;
|
|
||||||
|
|
||||||
if (userMessage) {
|
if (
|
||||||
messages.push({
|
this.pipelineRun?.intent?.intent_output?.response?.speech?.plain?.speech
|
||||||
role: "user",
|
) {
|
||||||
content: userMessage,
|
messages.push({
|
||||||
} as ChatLogUserContent);
|
from: "hass",
|
||||||
}
|
text: this.pipelineRun.intent.intent_output.response.speech.plain
|
||||||
|
.speech,
|
||||||
if (
|
});
|
||||||
this.pipelineRun?.intent?.intent_output?.response?.speech?.plain?.speech
|
|
||||||
) {
|
|
||||||
messages.push({
|
|
||||||
role: "assistant",
|
|
||||||
content:
|
|
||||||
this.pipelineRun.intent.intent_output.response.speech.plain.speech,
|
|
||||||
} as ChatLogAssistantContent);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
@@ -214,58 +190,10 @@ export class AssistPipelineDebug extends LitElement {
|
|||||||
${messages.length > 0
|
${messages.length > 0
|
||||||
? html`
|
? html`
|
||||||
<div class="messages">
|
<div class="messages">
|
||||||
${messages.map((content) =>
|
${messages.map(
|
||||||
content.role === "system" || content.role === "tool_result"
|
({ from, text }) => html`
|
||||||
? html`
|
<div class=${`message ${from}`}>${text}</div>
|
||||||
<ha-expansion-panel
|
`
|
||||||
class="content-expansion ${content.role}"
|
|
||||||
>
|
|
||||||
<div slot="header">
|
|
||||||
${content.role === "system"
|
|
||||||
? "System"
|
|
||||||
: `Result for ${content.tool_name}`}
|
|
||||||
</div>
|
|
||||||
${content.role === "system"
|
|
||||||
? html`<pre>${content.content}</pre>`
|
|
||||||
: html`
|
|
||||||
<ha-yaml-editor
|
|
||||||
read-only
|
|
||||||
auto-update
|
|
||||||
.value=${content}
|
|
||||||
></ha-yaml-editor>
|
|
||||||
`}
|
|
||||||
</ha-expansion-panel>
|
|
||||||
`
|
|
||||||
: html`
|
|
||||||
${content.content
|
|
||||||
? html`
|
|
||||||
<div class=${`message ${content.role}`}>
|
|
||||||
${content.content}
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
: nothing}
|
|
||||||
${content.role === "assistant" &&
|
|
||||||
content.tool_calls?.length
|
|
||||||
? html`
|
|
||||||
<ha-expansion-panel
|
|
||||||
class="content-expansion assistant"
|
|
||||||
>
|
|
||||||
<span slot="header">
|
|
||||||
Call
|
|
||||||
${content.tool_calls.length === 1
|
|
||||||
? content.tool_calls[0].tool_name
|
|
||||||
: `${content.tool_calls.length} tools`}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<ha-yaml-editor
|
|
||||||
read-only
|
|
||||||
auto-update
|
|
||||||
.value=${content.tool_calls}
|
|
||||||
></ha-yaml-editor>
|
|
||||||
</ha-expansion-panel>
|
|
||||||
`
|
|
||||||
: nothing}
|
|
||||||
`
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div style="clear:both"></div>
|
<div style="clear:both"></div>
|
||||||
@@ -514,7 +442,7 @@ export class AssistPipelineDebug extends LitElement {
|
|||||||
: ""}
|
: ""}
|
||||||
${maybeRenderError(this.pipelineRun, "tts", lastRunStage)}
|
${maybeRenderError(this.pipelineRun, "tts", lastRunStage)}
|
||||||
<ha-card>
|
<ha-card>
|
||||||
<ha-expansion-panel class="yaml-expansion">
|
<ha-expansion-panel>
|
||||||
<span slot="header"
|
<span slot="header"
|
||||||
>${this.hass.localize(
|
>${this.hass.localize(
|
||||||
"ui.panel.config.voice_assistants.debug.raw"
|
"ui.panel.config.voice_assistants.debug.raw"
|
||||||
@@ -591,12 +519,12 @@ export class AssistPipelineDebug extends LitElement {
|
|||||||
.row > div:last-child {
|
.row > div:last-child {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
.yaml-expansion {
|
ha-expansion-panel {
|
||||||
padding-left: 8px;
|
padding-left: 8px;
|
||||||
padding-inline-start: 8px;
|
padding-inline-start: 8px;
|
||||||
padding-inline-end: initial;
|
padding-inline-end: initial;
|
||||||
}
|
}
|
||||||
.card-content .yaml-expansion {
|
.card-content ha-expansion-panel {
|
||||||
padding-left: 0px;
|
padding-left: 0px;
|
||||||
padding-inline-start: 0px;
|
padding-inline-start: 0px;
|
||||||
padding-inline-end: initial;
|
padding-inline-end: initial;
|
||||||
@@ -612,59 +540,27 @@ export class AssistPipelineDebug extends LitElement {
|
|||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-expansion {
|
|
||||||
margin: 8px 0;
|
|
||||||
border-radius: var(--ha-border-radius-xl);
|
|
||||||
clear: both;
|
|
||||||
padding: 0 8px;
|
|
||||||
--input-fill-color: none;
|
|
||||||
max-width: calc(100% - 24px);
|
|
||||||
--expansion-panel-summary-padding: 0px;
|
|
||||||
--expansion-panel-content-padding: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-expansion *[slot="header"] {
|
|
||||||
font-weight: var(--ha-font-weight-normal);
|
|
||||||
}
|
|
||||||
|
|
||||||
.system {
|
|
||||||
background-color: var(--success-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.message {
|
.message {
|
||||||
padding: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message,
|
|
||||||
.content-expansion {
|
|
||||||
font-size: var(--ha-font-size-l);
|
font-size: var(--ha-font-size-l);
|
||||||
margin: 8px 0;
|
margin: 8px 0;
|
||||||
|
padding: 8px;
|
||||||
border-radius: var(--ha-border-radius-xl);
|
border-radius: var(--ha-border-radius-xl);
|
||||||
clear: both;
|
clear: both;
|
||||||
}
|
}
|
||||||
|
|
||||||
.messages pre {
|
.message.user {
|
||||||
white-space: pre-wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user,
|
|
||||||
.tool_result {
|
|
||||||
margin-left: 24px;
|
margin-left: 24px;
|
||||||
margin-inline-start: 24px;
|
margin-inline-start: 24px;
|
||||||
margin-inline-end: initial;
|
margin-inline-end: initial;
|
||||||
float: var(--float-end);
|
float: var(--float-end);
|
||||||
|
text-align: right;
|
||||||
border-bottom-right-radius: 0px;
|
border-bottom-right-radius: 0px;
|
||||||
background-color: var(--light-primary-color);
|
background-color: var(--light-primary-color);
|
||||||
color: var(--text-light-primary-color, var(--primary-text-color));
|
color: var(--text-light-primary-color, var(--primary-text-color));
|
||||||
direction: var(--direction);
|
direction: var(--direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
.message.user,
|
.message.hass {
|
||||||
.content-expansion div[slot="header"] {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.assistant {
|
|
||||||
margin-right: 24px;
|
margin-right: 24px;
|
||||||
margin-inline-end: 24px;
|
margin-inline-end: 24px;
|
||||||
margin-inline-start: initial;
|
margin-inline-start: initial;
|
||||||
|
|||||||
@@ -4796,8 +4796,7 @@
|
|||||||
"headers": {
|
"headers": {
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"type": "Type",
|
"type": "Type",
|
||||||
"file_name": "File name",
|
"file_name": "File name"
|
||||||
"usage_count": "In use"
|
|
||||||
},
|
},
|
||||||
"types": {
|
"types": {
|
||||||
"automation": "Automation",
|
"automation": "Automation",
|
||||||
@@ -5412,11 +5411,6 @@
|
|||||||
"partial_failure": "Some devices failed to delete successfully. Check system logs for more information."
|
"partial_failure": "Some devices failed to delete successfully. Check system logs for more information."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"esphome": {
|
|
||||||
"show_encryption_key": "Show encryption key",
|
|
||||||
"encryption_key_title": "ESPHome Encryption Key",
|
|
||||||
"encryption_key_description": "This is the encryption key for your ESPHome device. Keep it in a safe place, as you may need it when transferring devices between Home Assistant instances."
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"entities": {
|
"entities": {
|
||||||
@@ -6785,7 +6779,6 @@
|
|||||||
},
|
},
|
||||||
"analytics": {
|
"analytics": {
|
||||||
"caption": "Analytics",
|
"caption": "Analytics",
|
||||||
"header": "Home Assistant analytics",
|
|
||||||
"description": "Learn how to share data to improve Home Assistant",
|
"description": "Learn how to share data to improve Home Assistant",
|
||||||
"preferences": {
|
"preferences": {
|
||||||
"base": {
|
"base": {
|
||||||
@@ -6803,21 +6796,10 @@
|
|||||||
"diagnostics": {
|
"diagnostics": {
|
||||||
"title": "Diagnostics",
|
"title": "Diagnostics",
|
||||||
"description": "Share crash reports when unexpected errors occur."
|
"description": "Share crash reports when unexpected errors occur."
|
||||||
},
|
|
||||||
"snapshots": {
|
|
||||||
"title": "Devices",
|
|
||||||
"description": "Generic information about your devices.",
|
|
||||||
"header": "Device analytics",
|
|
||||||
"info": "Anonymously share data about your devices to help build the Open Home Foundation’s device database. This free, open source resource helps users find useful information about smart home devices. Only device-specific details (like model or manufacturer) are shared — never personally identifying information (like the names you assign).",
|
|
||||||
"learn_more": "Learn more about the device database and how we process your data",
|
|
||||||
"alert": {
|
|
||||||
"title": "Important",
|
|
||||||
"content": "Only enable this option if you understand that your device information will be shared."
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"need_base_enabled": "You need to enable basic analytics for this option to be available",
|
"need_base_enabled": "You need to enable basic analytics for this option to be available",
|
||||||
"learn_more": "Learn how we process your data",
|
"learn_more": "How we process your data",
|
||||||
"intro": "Share anonymized information from your installation to help make Home Assistant better and help us convince manufacturers to add local control and privacy-focused features.",
|
"intro": "Share anonymized information from your installation to help make Home Assistant better and help us convince manufacturers to add local control and privacy-focused features.",
|
||||||
"download_device_info": "Preview device analytics"
|
"download_device_info": "Preview device analytics"
|
||||||
},
|
},
|
||||||
|
|||||||
10
yarn.lock
10
yarn.lock
@@ -9332,7 +9332,7 @@ __metadata:
|
|||||||
gulp-rename: "npm:2.1.0"
|
gulp-rename: "npm:2.1.0"
|
||||||
gulp-zopfli-green: "npm:6.0.2"
|
gulp-zopfli-green: "npm:6.0.2"
|
||||||
hls.js: "npm:1.6.14"
|
hls.js: "npm:1.6.14"
|
||||||
home-assistant-js-websocket: "npm:9.6.0"
|
home-assistant-js-websocket: "npm:9.5.0"
|
||||||
html-minifier-terser: "npm:7.2.0"
|
html-minifier-terser: "npm:7.2.0"
|
||||||
husky: "npm:9.1.7"
|
husky: "npm:9.1.7"
|
||||||
idb-keyval: "npm:6.2.2"
|
idb-keyval: "npm:6.2.2"
|
||||||
@@ -9393,10 +9393,10 @@ __metadata:
|
|||||||
languageName: unknown
|
languageName: unknown
|
||||||
linkType: soft
|
linkType: soft
|
||||||
|
|
||||||
"home-assistant-js-websocket@npm:9.6.0":
|
"home-assistant-js-websocket@npm:9.5.0":
|
||||||
version: 9.6.0
|
version: 9.5.0
|
||||||
resolution: "home-assistant-js-websocket@npm:9.6.0"
|
resolution: "home-assistant-js-websocket@npm:9.5.0"
|
||||||
checksum: 10/0eded7864632b5e19e92289ffac0e24308b1e8f425e292ae87ed21450852f7705db521e202614b1d5bbdb7948633143dce2524ed548db0c38486b40ed1ffa474
|
checksum: 10/42f991b3b85aa61be28984f099a001ac083fb3da54b2777283d0c97976c564a303d8d4ba467e1b8e29cbc33151cd6eef64c1a7d3392d62bbb9cbb27aa7ca9942
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user