mirror of
https://github.com/home-assistant/frontend.git
synced 2026-07-03 13:42:17 +00:00
Compare commits
156 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f75d3f887e | |||
| c05824c641 | |||
| 3abdffda9c | |||
| 67da851efc | |||
| 5463a27255 | |||
| ec0434c9b0 | |||
| 7d8cb5c863 | |||
| 4f01348ffb | |||
| 2af3400464 | |||
| b6e220a4c5 | |||
| d5d45f100e | |||
| 6b9ca60c47 | |||
| bc445a1e27 | |||
| a087b4c43e | |||
| 8f67ddf968 | |||
| 9ef07484dd | |||
| 5287061699 | |||
| 3ef1110109 | |||
| 2efe2589d2 | |||
| 4fb596357d | |||
| dd98ec771d | |||
| 94f74308d8 | |||
| b982884933 | |||
| c47c6e358b | |||
| 46394d0bf9 | |||
| 4dc154201a | |||
| ebdbab81d3 | |||
| 155098bc41 | |||
| 291638a9dd | |||
| 763c672e36 | |||
| c945534640 | |||
| 5b3074d939 | |||
| 3b89b72568 | |||
| 1d9fa1522c | |||
| 4db743db00 | |||
| d5f8231f97 | |||
| 32c403d069 | |||
| 220da51606 | |||
| 9ae234a02f | |||
| 401bbed67b | |||
| 83190c21db | |||
| ccdd906e2f | |||
| f4c932ef9c | |||
| 0892ed18e5 | |||
| 3afc218adc | |||
| 841b9c0917 | |||
| a479c6e786 | |||
| babb723521 | |||
| 29954e530e | |||
| fb3c94f403 | |||
| dd8c1d359c | |||
| 45e09a262b | |||
| d6d61a4137 | |||
| 8fe7711634 | |||
| a5ec7fc251 | |||
| b9935717dc | |||
| bb25817bae | |||
| bf8a33e086 | |||
| bf56f50e0a | |||
| 3a8e2c429f | |||
| e8fca5d93c | |||
| a39cf99024 | |||
| 0ff27154e6 | |||
| 766fd4cbf5 | |||
| 1869260868 | |||
| 93046d78f6 | |||
| 3e51f9a505 | |||
| d95bf64edf | |||
| 47f7cf5419 | |||
| 267fc3743d | |||
| a6d73f7615 | |||
| b360c854a8 | |||
| a088b20987 | |||
| af6dd545dc | |||
| a26df88022 | |||
| 86ec272581 | |||
| 2a803e09a4 | |||
| 50cf6d2af9 | |||
| 86626b1855 | |||
| 63603a281e | |||
| faf05f5339 | |||
| 4de3db52cb | |||
| 9a9fbda08b | |||
| ea642515c1 | |||
| add2dedc7f | |||
| 4ba4a28aa0 | |||
| 7050453783 | |||
| 8f984517bb | |||
| 2524c96db6 | |||
| 26600e3d78 | |||
| 316756d06a | |||
| 7357b914d0 | |||
| 8548c9767b | |||
| a30a35f82f | |||
| 84938ccc94 | |||
| 8136cc8008 | |||
| 2dc9d268ec | |||
| 226dad309c | |||
| 33cdd51f00 | |||
| a3a099126e | |||
| 4e22fea6e2 | |||
| fd06f28253 | |||
| 553230ca23 | |||
| 208bfebc12 | |||
| 802b0949ac | |||
| b65dc47f72 | |||
| c5a3670838 | |||
| cd167ac645 | |||
| eb3e756637 | |||
| 5049210524 | |||
| eeaad86c4b | |||
| 71483e0bc7 | |||
| 10650e8937 | |||
| 99d72ba817 | |||
| e21ad742b1 | |||
| 1bcb1e7768 | |||
| 618fee98ce | |||
| 83da89437f | |||
| 35ebfc15c9 | |||
| f0a9185e4a | |||
| b3766cbc62 | |||
| fac82fa185 | |||
| 3469013f1a | |||
| 03486e4125 | |||
| 20560fb847 | |||
| bad18da658 | |||
| e26c7c491a | |||
| b0c8ae0c94 | |||
| cc1658cbab | |||
| 4b768f0635 | |||
| 989057d947 | |||
| 6671d24fa6 | |||
| 297c721229 | |||
| 17bd7f9476 | |||
| 045ff7a45e | |||
| 8624853ec4 | |||
| 336376d2a5 | |||
| 189793bff4 | |||
| 1e35f973d6 | |||
| 6033f8b31a | |||
| c3b2ebf380 | |||
| 23cbecb2c4 | |||
| b1e1b44c75 | |||
| abb014745a | |||
| e51c98e1a7 | |||
| 9513699332 | |||
| b57bc8cd06 | |||
| 70c502bb45 | |||
| add0b55657 | |||
| d61fc9ec6c | |||
| d1592bf262 | |||
| 3c744c09f1 | |||
| 3ef61aaf02 | |||
| c738127c09 | |||
| 6e62f568fc | |||
| 2ba3a991a9 |
@@ -2,12 +2,13 @@
|
||||
"name": "Home Assistant Frontend",
|
||||
"build": {
|
||||
"dockerfile": "Dockerfile",
|
||||
"context": "..",
|
||||
"context": ".."
|
||||
},
|
||||
"appPort": "8124:8123",
|
||||
"postCreateCommand": "sudo apt update && sudo apt upgrade -y && sudo apt install -y libpcap-dev",
|
||||
"postStartCommand": "script/bootstrap",
|
||||
"containerEnv": {
|
||||
"WORKSPACE_DIRECTORY": "${containerWorkspaceFolder}",
|
||||
"WORKSPACE_DIRECTORY": "${containerWorkspaceFolder}"
|
||||
},
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
@@ -16,7 +17,7 @@
|
||||
"esbenp.prettier-vscode",
|
||||
"runem.lit-plugin",
|
||||
"github.vscode-pull-request-github",
|
||||
"eamodio.gitlens",
|
||||
"eamodio.gitlens"
|
||||
],
|
||||
"settings": {
|
||||
"files.eol": "\n",
|
||||
@@ -27,17 +28,17 @@
|
||||
"editor.renderWhitespace": "boundary",
|
||||
"editor.rulers": [80],
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"files.trimTrailingWhitespace": true,
|
||||
"terminal.integrated.shell.linux": "/usr/bin/zsh",
|
||||
"gitlens.showWelcomeOnInstall": false,
|
||||
"gitlens.showWhatsNewAfterUpgrades": false,
|
||||
"workbench.startupEditor": "none",
|
||||
},
|
||||
},
|
||||
},
|
||||
"workbench.startupEditor": "none"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
ref: dev
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4.0.1
|
||||
uses: actions/setup-node@v4.0.2
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
@@ -62,7 +62,7 @@ jobs:
|
||||
ref: master
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4.0.1
|
||||
uses: actions/setup-node@v4.0.2
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v4.1.1
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4.0.1
|
||||
uses: actions/setup-node@v4.0.2
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
@@ -60,7 +60,7 @@ jobs:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v4.1.1
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4.0.1
|
||||
uses: actions/setup-node@v4.0.2
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
@@ -78,7 +78,7 @@ jobs:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v4.1.1
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4.0.1
|
||||
uses: actions/setup-node@v4.0.2
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
@@ -89,7 +89,7 @@ jobs:
|
||||
env:
|
||||
IS_TEST: "true"
|
||||
- name: Upload bundle stats
|
||||
uses: actions/upload-artifact@v4.3.0
|
||||
uses: actions/upload-artifact@v4.3.1
|
||||
with:
|
||||
name: frontend-bundle-stats
|
||||
path: build/stats/*.json
|
||||
@@ -102,7 +102,7 @@ jobs:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v4.1.1
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4.0.1
|
||||
uses: actions/setup-node@v4.0.2
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
@@ -113,7 +113,7 @@ jobs:
|
||||
env:
|
||||
IS_TEST: "true"
|
||||
- name: Upload bundle stats
|
||||
uses: actions/upload-artifact@v4.3.0
|
||||
uses: actions/upload-artifact@v4.3.1
|
||||
with:
|
||||
name: supervisor-bundle-stats
|
||||
path: build/stats/*.json
|
||||
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
ref: dev
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4.0.1
|
||||
uses: actions/setup-node@v4.0.2
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
@@ -63,7 +63,7 @@ jobs:
|
||||
ref: master
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4.0.1
|
||||
uses: actions/setup-node@v4.0.2
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
uses: actions/checkout@v4.1.1
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4.0.1
|
||||
uses: actions/setup-node@v4.0.2
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
uses: actions/checkout@v4.1.1
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4.0.1
|
||||
uses: actions/setup-node@v4.0.2
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4.0.1
|
||||
uses: actions/setup-node@v4.0.2
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
@@ -57,14 +57,14 @@ jobs:
|
||||
run: tar -czvf translations.tar.gz translations
|
||||
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v4.3.0
|
||||
uses: actions/upload-artifact@v4.3.1
|
||||
with:
|
||||
name: wheels
|
||||
path: dist/home_assistant_frontend*.whl
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload translations
|
||||
uses: actions/upload-artifact@v4.3.0
|
||||
uses: actions/upload-artifact@v4.3.1
|
||||
with:
|
||||
name: translations
|
||||
path: translations.tar.gz
|
||||
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4.0.1
|
||||
uses: actions/setup-node@v4.0.2
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
cache: yarn
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/simple-tooltip.js b/simple-tooltip.js
|
||||
index 78a87f6a223925f0e29fbedb268c85a142ec6985..3d686dd6a3d5a93342b4b01408089fc316b408ca 100644
|
||||
--- a/simple-tooltip.js
|
||||
+++ b/simple-tooltip.js
|
||||
@@ -195,6 +195,8 @@ class SimpleTooltip extends LitElement {
|
||||
.hidden {
|
||||
position: absolute;
|
||||
left: -10000px;
|
||||
+ inset-inline-start: -10000px;
|
||||
+ inset-inline-end: initial;
|
||||
top: auto;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
@@ -115,7 +115,9 @@ gulp.task("webpack-prod-app", () =>
|
||||
|
||||
gulp.task("webpack-dev-server-demo", () =>
|
||||
runDevServer({
|
||||
compiler: webpack(bothBuilds(createDemoConfig, { isProdBuild: false })),
|
||||
compiler: webpack(
|
||||
createDemoConfig({ isProdBuild: false, latestBuild: true })
|
||||
),
|
||||
contentBase: paths.demo_output_root,
|
||||
port: 8090,
|
||||
})
|
||||
@@ -131,7 +133,9 @@ gulp.task("webpack-prod-demo", () =>
|
||||
|
||||
gulp.task("webpack-dev-server-cast", () =>
|
||||
runDevServer({
|
||||
compiler: webpack(bothBuilds(createCastConfig, { isProdBuild: false })),
|
||||
compiler: webpack(
|
||||
createCastConfig({ isProdBuild: false, latestBuild: true })
|
||||
),
|
||||
contentBase: paths.cast_output_root,
|
||||
port: 8080,
|
||||
// Accessible from the network, because that's how Cast hits it.
|
||||
@@ -174,8 +178,9 @@ gulp.task("webpack-prod-hassio", () =>
|
||||
|
||||
gulp.task("webpack-dev-server-gallery", () =>
|
||||
runDevServer({
|
||||
// We don't use the es5 build, but the dev server will fuck up the publicPath if we don't
|
||||
compiler: webpack(bothBuilds(createGalleryConfig, { isProdBuild: false })),
|
||||
compiler: webpack(
|
||||
createGalleryConfig({ isProdBuild: false, latestBuild: true })
|
||||
),
|
||||
contentBase: paths.gallery_output_root,
|
||||
port: 8100,
|
||||
listenHost: "0.0.0.0",
|
||||
|
||||
@@ -28,7 +28,6 @@ class HcLaunchScreen extends LitElement {
|
||||
:host {
|
||||
display: block;
|
||||
height: 100vh;
|
||||
padding-top: 64px;
|
||||
background-color: white;
|
||||
font-size: 24px;
|
||||
}
|
||||
@@ -36,17 +35,13 @@ class HcLaunchScreen extends LitElement {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
img {
|
||||
width: 717px;
|
||||
height: 376px;
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.status {
|
||||
padding-right: 54px;
|
||||
padding-inline-end: 54px;
|
||||
padding-inline-start: initial;
|
||||
max-width: 80%;
|
||||
object-fit: cover;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ class HcLovelace extends LitElement {
|
||||
@property({ attribute: false })
|
||||
public lovelaceConfig!: LovelaceConfig;
|
||||
|
||||
@property() public viewPath?: string | number;
|
||||
@property() public viewPath?: string | number | null;
|
||||
|
||||
@property() public urlPath: string | null = null;
|
||||
|
||||
@@ -93,6 +93,9 @@ class HcLovelace extends LitElement {
|
||||
}
|
||||
|
||||
private get _viewIndex() {
|
||||
if (this.viewPath === null) {
|
||||
return 0;
|
||||
}
|
||||
const selectedView = this.viewPath;
|
||||
const selectedViewInt = parseInt(selectedView as string, 10);
|
||||
for (let i = 0; i < this.lovelaceConfig.views.length; i++) {
|
||||
|
||||
@@ -51,10 +51,10 @@ export class HcMain extends HassElement {
|
||||
|
||||
@state() private _lovelacePath: string | number | null = null;
|
||||
|
||||
@state() private _error?: string;
|
||||
|
||||
@state() private _urlPath?: string | null;
|
||||
|
||||
@state() private _error?: string;
|
||||
|
||||
private _hassUUID?: string;
|
||||
|
||||
private _unsubLovelace?: UnsubscribeFunc;
|
||||
@@ -81,7 +81,7 @@ export class HcMain extends HassElement {
|
||||
|
||||
if (
|
||||
!this._lovelaceConfig ||
|
||||
this._lovelacePath === null ||
|
||||
this._urlPath === undefined ||
|
||||
// Guard against part of HA not being loaded yet.
|
||||
!this.hass ||
|
||||
!this.hass.states ||
|
||||
@@ -99,8 +99,8 @@ export class HcMain extends HassElement {
|
||||
<hc-lovelace
|
||||
.hass=${this.hass}
|
||||
.lovelaceConfig=${this._lovelaceConfig}
|
||||
.viewPath=${this._lovelacePath}
|
||||
.urlPath=${this._urlPath}
|
||||
.viewPath=${this._lovelacePath}
|
||||
@config-refresh=${this._generateDefaultLovelaceConfig}
|
||||
></hc-lovelace>
|
||||
`;
|
||||
@@ -226,9 +226,9 @@ export class HcMain extends HassElement {
|
||||
this.initializeHass(auth, connection);
|
||||
if (this._hassUUID !== msg.hassUUID) {
|
||||
this._hassUUID = msg.hassUUID;
|
||||
this._lovelacePath = null;
|
||||
this._urlPath = undefined;
|
||||
this._lovelaceConfig = undefined;
|
||||
this._urlPath = undefined;
|
||||
this._lovelacePath = null;
|
||||
if (this._unsubLovelace) {
|
||||
this._unsubLovelace();
|
||||
this._unsubLovelace = undefined;
|
||||
@@ -270,7 +270,7 @@ export class HcMain extends HassElement {
|
||||
}
|
||||
|
||||
this._error = undefined;
|
||||
if (msg.urlPath === "lovelace") {
|
||||
if (msg.urlPath === "lovelace" || msg.urlPath === undefined) {
|
||||
msg.urlPath = null;
|
||||
}
|
||||
this._lovelacePath = msg.viewPath;
|
||||
@@ -285,7 +285,7 @@ export class HcMain extends HassElement {
|
||||
],
|
||||
};
|
||||
this._urlPath = "energy";
|
||||
this._lovelacePath = 0;
|
||||
this._lovelacePath = null;
|
||||
this._sendStatus();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -17,12 +17,14 @@ import { energyEntities } from "./stubs/entities";
|
||||
import { mockEntityRegistry } from "./stubs/entity_registry";
|
||||
import { mockEvents } from "./stubs/events";
|
||||
import { mockFrontend } from "./stubs/frontend";
|
||||
import { mockIcons } from "./stubs/icons";
|
||||
import { mockHistory } from "./stubs/history";
|
||||
import { mockLovelace } from "./stubs/lovelace";
|
||||
import { mockMediaPlayer } from "./stubs/media_player";
|
||||
import { mockPersistentNotification } from "./stubs/persistent_notification";
|
||||
import { mockRecorder } from "./stubs/recorder";
|
||||
import { mockTodo } from "./stubs/todo";
|
||||
import { mockSensor } from "./stubs/sensor";
|
||||
import { mockSystemLog } from "./stubs/system_log";
|
||||
import { mockTemplate } from "./stubs/template";
|
||||
import { mockTranslations } from "./stubs/translations";
|
||||
@@ -50,11 +52,13 @@ export class HaDemo extends HomeAssistantAppEl {
|
||||
mockHistory(hass);
|
||||
mockRecorder(hass);
|
||||
mockTodo(hass);
|
||||
mockSensor(hass);
|
||||
mockSystemLog(hass);
|
||||
mockTemplate(hass);
|
||||
mockEvents(hass);
|
||||
mockMediaPlayer(hass);
|
||||
mockFrontend(hass);
|
||||
mockIcons(hass);
|
||||
mockEnergy(hass);
|
||||
mockPersistentNotification(hass);
|
||||
mockConfigEntries(hass);
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import { IconCategory } from "../../../src/data/icons";
|
||||
import { ENTITY_COMPONENT_ICONS } from "../../../src/fake_data/entity_component_icons";
|
||||
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||
|
||||
export const mockIcons = (hass: MockHomeAssistant) => {
|
||||
hass.mockWS(
|
||||
"frontend/get_icons",
|
||||
async ({
|
||||
category,
|
||||
integration,
|
||||
}: {
|
||||
category: IconCategory;
|
||||
integration?: string;
|
||||
}) => {
|
||||
if (integration) {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`https://raw.githubusercontent.com/home-assistant/core/dev/homeassistant/components/${integration}/icons.json`
|
||||
).then((resp) => resp.json());
|
||||
return { resources: { [integration]: response[category] || {} } };
|
||||
} catch {
|
||||
return { resources: {} };
|
||||
}
|
||||
}
|
||||
if (category === "entity_component") {
|
||||
return {
|
||||
resources: ENTITY_COMPONENT_ICONS,
|
||||
};
|
||||
}
|
||||
return { resources: {} };
|
||||
}
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,58 @@
|
||||
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||
|
||||
export const mockSensor = (hass: MockHomeAssistant) => {
|
||||
hass.mockWS("sensor/numeric_device_classes", () => [
|
||||
{
|
||||
numeric_device_classes: [
|
||||
"volume_storage",
|
||||
"gas",
|
||||
"data_size",
|
||||
"irradiance",
|
||||
"wind_speed",
|
||||
"volatile_organic_compounds",
|
||||
"volatile_organic_compounds_parts",
|
||||
"voltage",
|
||||
"frequency",
|
||||
"precipitation_intensity",
|
||||
"volume",
|
||||
"precipitation",
|
||||
"battery",
|
||||
"nitrogen_dioxide",
|
||||
"speed",
|
||||
"signal_strength",
|
||||
"pm1",
|
||||
"nitrous_oxide",
|
||||
"atmospheric_pressure",
|
||||
"data_rate",
|
||||
"temperature",
|
||||
"power_factor",
|
||||
"aqi",
|
||||
"current",
|
||||
"volume_flow_rate",
|
||||
"humidity",
|
||||
"duration",
|
||||
"ozone",
|
||||
"distance",
|
||||
"pressure",
|
||||
"pm25",
|
||||
"weight",
|
||||
"energy",
|
||||
"carbon_monoxide",
|
||||
"apparent_power",
|
||||
"illuminance",
|
||||
"energy_storage",
|
||||
"moisture",
|
||||
"power",
|
||||
"water",
|
||||
"carbon_dioxide",
|
||||
"ph",
|
||||
"reactive_power",
|
||||
"monetary",
|
||||
"nitrogen_monoxide",
|
||||
"pm10",
|
||||
"sound_pressure",
|
||||
"sulphur_dioxide",
|
||||
],
|
||||
},
|
||||
]);
|
||||
};
|
||||
@@ -21,4 +21,5 @@ export const mockTodo = (hass: MockHomeAssistant) => {
|
||||
},
|
||||
] as TodoItem[],
|
||||
}));
|
||||
hass.mockWS("todo/item/subscribe", (_msg, _hass) => () => {});
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Button } from "@material/mwc-button";
|
||||
import { html, LitElement, css, TemplateResult } from "lit";
|
||||
import { html, LitElement, css, TemplateResult, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { applyThemesOnElement } from "../../../src/common/dom/apply_themes_on_element";
|
||||
import { fireEvent } from "../../../src/common/dom/fire_event";
|
||||
@@ -9,7 +9,7 @@ import "../../../src/components/ha-card";
|
||||
class DemoBlackWhiteRow extends LitElement {
|
||||
@property() title!: string;
|
||||
|
||||
@property() value!: any;
|
||||
@property() value?: any;
|
||||
|
||||
@property({ type: Boolean }) public disabled = false;
|
||||
|
||||
@@ -45,7 +45,9 @@ class DemoBlackWhiteRow extends LitElement {
|
||||
</mwc-button>
|
||||
</div>
|
||||
</ha-card>
|
||||
<pre>${JSON.stringify(this.value, undefined, 2)}</pre>
|
||||
${this.value
|
||||
? html`<pre>${JSON.stringify(this.value, undefined, 2)}</pre>`
|
||||
: nothing}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -17,6 +17,7 @@ export const basicTrace: DemoTrace = {
|
||||
{
|
||||
path: "trigger/0",
|
||||
timestamp: "2021-03-25T04:36:51.223693+00:00",
|
||||
changed_variables: {},
|
||||
},
|
||||
],
|
||||
"condition/0": [
|
||||
|
||||
@@ -17,6 +17,7 @@ export const motionLightTrace: DemoTrace = {
|
||||
{
|
||||
path: "trigger/0",
|
||||
timestamp: "2021-03-25T04:36:51.223693+00:00",
|
||||
changed_variables: {},
|
||||
},
|
||||
],
|
||||
"action/0": [
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/components/trace/hat-script-graph";
|
||||
import "../../../../src/components/trace/hat-trace-timeline";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import { HomeAssistant } from "../../../../src/types";
|
||||
@@ -56,6 +55,7 @@ export class DemoAutomationTraceTimeline extends LitElement {
|
||||
super.firstUpdated(changedProps);
|
||||
const hass = provideHass(this);
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("config", "en");
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
|
||||
@@ -60,6 +60,7 @@ export class DemoAutomationTrace extends LitElement {
|
||||
super.firstUpdated(changedProps);
|
||||
const hass = provideHass(this);
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("config", "en");
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
|
||||
@@ -275,6 +275,14 @@ const SCHEMAS: {
|
||||
selector: { color_temp: {} },
|
||||
},
|
||||
color_rgb: { name: "Color", selector: { color_rgb: {} } },
|
||||
qr_code: {
|
||||
name: "QR Code",
|
||||
selector: { qr_code: { data: "https://home-assistant.io" } },
|
||||
},
|
||||
constant: {
|
||||
name: "Constant",
|
||||
selector: { constant: { value: true, label: "Yes!" } },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -501,7 +509,7 @@ class DemoHaSelector extends LitElement implements ProvideHassElement {
|
||||
this.requestUpdate();
|
||||
};
|
||||
return html`
|
||||
<demo-black-white-row .title=${info.name} .value=${this.data[idx]}>
|
||||
<demo-black-white-row .title=${info.name}>
|
||||
${["light", "dark"].map((slot) =>
|
||||
Object.entries(info.input).map(
|
||||
([key, value]) => html`
|
||||
@@ -534,8 +542,8 @@ class DemoHaSelector extends LitElement implements ProvideHassElement {
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
ha-selector {
|
||||
width: 60;
|
||||
ha-settings-row {
|
||||
--paper-item-body-two-line-min-height: 0;
|
||||
}
|
||||
.options {
|
||||
max-width: 800px;
|
||||
|
||||
@@ -3,6 +3,7 @@ import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("alarm_control_panel", "alarm", "disarmed", {
|
||||
@@ -84,6 +85,7 @@ class DemoAlarmPanelEntity extends LitElement {
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("lovelace", "en");
|
||||
hass.addEntities(ENTITIES);
|
||||
mockIcons(hass);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("light", "bed_light", "on", {
|
||||
@@ -146,6 +147,7 @@ class DemoArea extends LitElement {
|
||||
entity_id: "binary_sensor.kitchen_door",
|
||||
},
|
||||
]);
|
||||
mockIcons(hass);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("light", "controller_1", "on", {
|
||||
@@ -66,6 +67,7 @@ class DemoConditional extends LitElement {
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("lovelace", "en");
|
||||
hass.addEntities(ENTITIES);
|
||||
mockIcons(hass);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("light", "bed_light", "on", {
|
||||
@@ -323,6 +324,7 @@ class DemoEntities extends LitElement {
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("lovelace", "en");
|
||||
hass.addEntities(ENTITIES);
|
||||
mockIcons(hass);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("light", "bed_light", "on", {
|
||||
@@ -82,6 +83,7 @@ class DemoButtonEntity extends LitElement {
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("lovelace", "en");
|
||||
hass.addEntities(ENTITIES);
|
||||
mockIcons(hass);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("device_tracker", "demo_paulus", "work", {
|
||||
@@ -123,6 +124,7 @@ class DemoEntityFilter extends LitElement {
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("lovelace", "en");
|
||||
hass.addEntities(ENTITIES);
|
||||
mockIcons(hass);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("sensor", "brightness", "12", {}),
|
||||
@@ -128,6 +129,7 @@ class DemoGaugeEntity extends LitElement {
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("lovelace", "en");
|
||||
hass.addEntities(ENTITIES);
|
||||
mockIcons(hass);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("device_tracker", "demo_paulus", "home", {
|
||||
@@ -238,6 +239,7 @@ class DemoGlanceEntity extends LitElement {
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("lovelace", "en");
|
||||
hass.addEntities(ENTITIES);
|
||||
mockIcons(hass);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import { mockHistory } from "../../../../demo/src/stubs/history";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("light", "kitchen_lights", "on", {
|
||||
@@ -214,6 +215,7 @@ class DemoStack extends LitElement {
|
||||
hass.updateTranslations("lovelace", "en");
|
||||
hass.addEntities(ENTITIES);
|
||||
mockHistory(hass);
|
||||
mockIcons(hass);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("light", "bed_light", "on", {
|
||||
@@ -76,6 +77,7 @@ class DemoLightEntity extends LitElement {
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("lovelace", "en");
|
||||
hass.addEntities(ENTITIES);
|
||||
mockIcons(hass);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("light", "bed_light", "on", {
|
||||
@@ -138,6 +139,7 @@ class DemoPictureElements extends LitElement {
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("lovelace", "en");
|
||||
hass.addEntities(ENTITIES);
|
||||
mockIcons(hass);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("light", "kitchen_lights", "on", {
|
||||
@@ -93,6 +94,7 @@ class DemoPictureEntity extends LitElement {
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("lovelace", "en");
|
||||
hass.addEntities(ENTITIES);
|
||||
mockIcons(hass);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("switch", "decorative_lights", "on", {
|
||||
@@ -134,6 +135,7 @@ class DemoPictureGlance extends LitElement {
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("lovelace", "en");
|
||||
hass.addEntities(ENTITIES);
|
||||
mockIcons(hass);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { customElement, query } from "lit/decorators";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { createPlantEntities } from "../../data/plants";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const CONFIGS = [
|
||||
{
|
||||
@@ -43,6 +44,7 @@ export class DemoPlantEntity extends LitElement {
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("lovelace", "en");
|
||||
hass.addEntities(createPlantEntities());
|
||||
mockIcons(hass);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { customElement, query } from "lit/decorators";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("climate", "ecobee", "auto", {
|
||||
@@ -116,6 +117,7 @@ class DemoThermostatEntity extends LitElement {
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("lovelace", "en");
|
||||
hass.addEntities(ENTITIES);
|
||||
mockIcons(hass);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import { VacuumEntityFeature } from "../../../../src/data/vacuum";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("switch", "tv_outlet", "on", {
|
||||
@@ -184,6 +185,7 @@ class DemoTile extends LitElement {
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("lovelace", "en");
|
||||
hass.addEntities(ENTITIES);
|
||||
mockIcons(hass);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import "../../components/demo-cards";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { mockTodo } from "../../../../demo/src/stubs/todo";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
|
||||
const ENTITIES = [
|
||||
getEntity("todo", "shopping_list", "2", {
|
||||
@@ -47,6 +48,7 @@ class DemoTodoListEntity extends LitElement {
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("lovelace", "en");
|
||||
hass.addEntities(ENTITIES);
|
||||
mockIcons(hass);
|
||||
|
||||
mockTodo(hass);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import "../../../../src/components/data-table/ha-data-table";
|
||||
import type { DataTableColumnContainer } from "../../../../src/components/data-table/ha-data-table";
|
||||
import "../../../../src/components/entity/state-badge";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||
import { HomeAssistant } from "../../../../src/types";
|
||||
|
||||
const SENSOR_DEVICE_CLASSES = [
|
||||
@@ -291,6 +292,7 @@ const ENTITIES: HassEntity[] = [
|
||||
createEntity("water_heater.high_demand", "high_demand"),
|
||||
createEntity("water_heater.heat_pump", "heat_pump"),
|
||||
createEntity("water_heater.gas", "gas"),
|
||||
createEntity("select.speed", "ridiculous_speed"),
|
||||
];
|
||||
|
||||
function createEntity(
|
||||
@@ -397,6 +399,16 @@ export class DemoEntityState extends LitElement {
|
||||
protected firstUpdated(changedProps) {
|
||||
super.firstUpdated(changedProps);
|
||||
const hass = provideHass(this);
|
||||
mockIcons(hass);
|
||||
hass.updateHass({
|
||||
entities: {
|
||||
"select.speed": {
|
||||
entity_id: "select.speed",
|
||||
translation_key: "speed",
|
||||
platform: "demo",
|
||||
},
|
||||
},
|
||||
});
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("config", "en");
|
||||
}
|
||||
|
||||
@@ -1263,6 +1263,7 @@ class HassioAddonInfo extends LitElement {
|
||||
.card-actions {
|
||||
justify-content: space-between;
|
||||
display: flex;
|
||||
direction: var(--direction);
|
||||
}
|
||||
.changelog {
|
||||
display: contents;
|
||||
|
||||
@@ -154,12 +154,16 @@ class HassioHardwareDialog extends LitElement {
|
||||
ha-icon-button {
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
inset-inline-end: 16px;
|
||||
inset-inline-start: initial;
|
||||
top: 10px;
|
||||
text-decoration: none;
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
h2 {
|
||||
margin: 18px 42px 0 18px;
|
||||
margin-inline-start: 18px;
|
||||
margin-inline-end: 42px;
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import "@material/mwc-button/mwc-button";
|
||||
import { mdiDelete, mdiDeleteOff } from "@mdi/js";
|
||||
import "@polymer/paper-item/paper-item";
|
||||
import "@polymer/paper-item/paper-item-body";
|
||||
import "@lrnwebcomponents/simple-tooltip/simple-tooltip";
|
||||
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
@@ -27,6 +25,8 @@ import type { HomeAssistant } from "../../../../src/types";
|
||||
import { HassioRepositoryDialogParams } from "./show-dialog-repositories";
|
||||
import type { HaTextField } from "../../../../src/components/ha-textfield";
|
||||
import "../../../../src/components/ha-textfield";
|
||||
import "../../../../src/components/ha-list-new";
|
||||
import "../../../../src/components/ha-list-item-new";
|
||||
|
||||
@customElement("dialog-hassio-repositories")
|
||||
class HassioRepositoriesDialog extends LitElement {
|
||||
@@ -106,44 +106,46 @@ class HassioRepositoriesDialog extends LitElement {
|
||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||
: ""}
|
||||
<div class="form">
|
||||
${repositories.length
|
||||
? repositories.map(
|
||||
(repo) => html`
|
||||
<paper-item class="option">
|
||||
<paper-item-body three-line>
|
||||
<div>${repo.name}</div>
|
||||
<div secondary>${repo.maintainer}</div>
|
||||
<div secondary>${repo.url}</div>
|
||||
</paper-item-body>
|
||||
<div class="delete">
|
||||
<ha-icon-button
|
||||
.label=${this._dialogParams!.supervisor.localize(
|
||||
"dialog.repositories.remove"
|
||||
)}
|
||||
.disabled=${usedRepositories.includes(repo.slug)}
|
||||
.slug=${repo.slug}
|
||||
.path=${usedRepositories.includes(repo.slug)
|
||||
? mdiDeleteOff
|
||||
: mdiDelete}
|
||||
@click=${this._removeRepository}
|
||||
>
|
||||
</ha-icon-button>
|
||||
<simple-tooltip
|
||||
animation-delay="0"
|
||||
position="bottom"
|
||||
offset="1"
|
||||
>
|
||||
${this._dialogParams!.supervisor.localize(
|
||||
usedRepositories.includes(repo.slug)
|
||||
? "dialog.repositories.used"
|
||||
: "dialog.repositories.remove"
|
||||
)}
|
||||
</simple-tooltip>
|
||||
</div>
|
||||
</paper-item>
|
||||
`
|
||||
)
|
||||
: html`<paper-item> No repositories </paper-item>`}
|
||||
<ha-list-new>
|
||||
${repositories.length
|
||||
? repositories.map(
|
||||
(repo) => html`
|
||||
<ha-list-item-new class="option">
|
||||
${repo.name}
|
||||
<div slot="supporting-text">
|
||||
<div>${repo.maintainer}</div>
|
||||
<div>${repo.url}</div>
|
||||
</div>
|
||||
<div class="delete" slot="end">
|
||||
<ha-icon-button
|
||||
.label=${this._dialogParams!.supervisor.localize(
|
||||
"dialog.repositories.remove"
|
||||
)}
|
||||
.disabled=${usedRepositories.includes(repo.slug)}
|
||||
.slug=${repo.slug}
|
||||
.path=${usedRepositories.includes(repo.slug)
|
||||
? mdiDeleteOff
|
||||
: mdiDelete}
|
||||
@click=${this._removeRepository}
|
||||
>
|
||||
</ha-icon-button>
|
||||
<simple-tooltip
|
||||
animation-delay="0"
|
||||
position="bottom"
|
||||
offset="1"
|
||||
>
|
||||
${this._dialogParams!.supervisor.localize(
|
||||
usedRepositories.includes(repo.slug)
|
||||
? "dialog.repositories.used"
|
||||
: "dialog.repositories.remove"
|
||||
)}
|
||||
</simple-tooltip>
|
||||
</div>
|
||||
</ha-list-item-new>
|
||||
`
|
||||
)
|
||||
: html`<ha-list-item-new> No repositories </ha-list-item-new>`}
|
||||
</ha-list-new>
|
||||
<div class="layout horizontal bottom">
|
||||
<ha-textfield
|
||||
class="flex-auto"
|
||||
@@ -206,6 +208,9 @@ class HassioRepositoriesDialog extends LitElement {
|
||||
div.delete ha-icon-button {
|
||||
color: var(--error-color);
|
||||
}
|
||||
ha-list-item-new {
|
||||
position: relative;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
+38
-36
@@ -31,9 +31,9 @@
|
||||
"@codemirror/commands": "6.3.3",
|
||||
"@codemirror/language": "6.10.1",
|
||||
"@codemirror/legacy-modes": "6.3.3",
|
||||
"@codemirror/search": "6.5.5",
|
||||
"@codemirror/state": "6.4.0",
|
||||
"@codemirror/view": "6.23.1",
|
||||
"@codemirror/search": "6.5.6",
|
||||
"@codemirror/state": "6.4.1",
|
||||
"@codemirror/view": "6.24.1",
|
||||
"@egjs/hammerjs": "2.0.17",
|
||||
"@formatjs/intl-datetimeformat": "6.12.2",
|
||||
"@formatjs/intl-displaynames": "6.6.6",
|
||||
@@ -43,18 +43,18 @@
|
||||
"@formatjs/intl-numberformat": "8.10.0",
|
||||
"@formatjs/intl-pluralrules": "5.2.12",
|
||||
"@formatjs/intl-relativetimeformat": "11.2.12",
|
||||
"@fullcalendar/core": "6.1.10",
|
||||
"@fullcalendar/daygrid": "6.1.10",
|
||||
"@fullcalendar/interaction": "6.1.10",
|
||||
"@fullcalendar/list": "6.1.10",
|
||||
"@fullcalendar/luxon3": "6.1.10",
|
||||
"@fullcalendar/timegrid": "6.1.10",
|
||||
"@fullcalendar/core": "6.1.11",
|
||||
"@fullcalendar/daygrid": "6.1.11",
|
||||
"@fullcalendar/interaction": "6.1.11",
|
||||
"@fullcalendar/list": "6.1.11",
|
||||
"@fullcalendar/luxon3": "6.1.11",
|
||||
"@fullcalendar/timegrid": "6.1.11",
|
||||
"@lezer/highlight": "1.2.0",
|
||||
"@lit-labs/context": "0.4.1",
|
||||
"@lit-labs/motion": "1.0.7",
|
||||
"@lit-labs/observers": "2.0.2",
|
||||
"@lit-labs/virtualizer": "2.0.12",
|
||||
"@lrnwebcomponents/simple-tooltip": "8.0.0",
|
||||
"@lrnwebcomponents/simple-tooltip": "patch:@lrnwebcomponents/simple-tooltip@npm%3A8.0.0#~/.yarn/patches/@lrnwebcomponents-simple-tooltip-npm-8.0.0-77591f2e0c.patch",
|
||||
"@material/chips": "=14.0.0-canary.53b3cad2f.0",
|
||||
"@material/data-table": "=14.0.0-canary.53b3cad2f.0",
|
||||
"@material/mwc-base": "0.27.0",
|
||||
@@ -72,6 +72,7 @@
|
||||
"@material/mwc-radio": "0.27.0",
|
||||
"@material/mwc-ripple": "0.27.0",
|
||||
"@material/mwc-select": "0.27.0",
|
||||
"@material/mwc-snackbar": "0.27.0",
|
||||
"@material/mwc-switch": "0.27.0",
|
||||
"@material/mwc-tab": "0.27.0",
|
||||
"@material/mwc-tab-bar": "0.27.0",
|
||||
@@ -80,17 +81,16 @@
|
||||
"@material/mwc-top-app-bar": "0.27.0",
|
||||
"@material/mwc-top-app-bar-fixed": "0.27.0",
|
||||
"@material/top-app-bar": "=14.0.0-canary.53b3cad2f.0",
|
||||
"@material/web": "=1.2.0",
|
||||
"@material/web": "=1.3.0",
|
||||
"@mdi/js": "7.4.47",
|
||||
"@mdi/svg": "7.4.47",
|
||||
"@polymer/paper-item": "3.0.1",
|
||||
"@polymer/paper-listbox": "3.0.1",
|
||||
"@polymer/paper-tabs": "3.1.0",
|
||||
"@polymer/paper-toast": "3.0.1",
|
||||
"@polymer/polymer": "3.5.1",
|
||||
"@thomasloven/round-slider": "0.6.0",
|
||||
"@vaadin/combo-box": "24.3.5",
|
||||
"@vaadin/vaadin-themable-mixin": "24.3.5",
|
||||
"@vaadin/combo-box": "24.3.7",
|
||||
"@vaadin/vaadin-themable-mixin": "24.3.7",
|
||||
"@vibrant/color": "3.2.1-alpha.1",
|
||||
"@vibrant/core": "3.2.1-alpha.1",
|
||||
"@vibrant/quantizer-mmcq": "3.2.1-alpha.1",
|
||||
@@ -99,8 +99,9 @@
|
||||
"@webcomponents/webcomponentsjs": "2.8.0",
|
||||
"app-datepicker": "5.1.1",
|
||||
"chart.js": "4.4.1",
|
||||
"color-name": "2.0.0",
|
||||
"comlink": "4.4.1",
|
||||
"core-js": "3.35.1",
|
||||
"core-js": "3.36.0",
|
||||
"cropperjs": "1.6.1",
|
||||
"date-fns": "2.30.0",
|
||||
"date-fns-tz": "2.0.0",
|
||||
@@ -109,7 +110,7 @@
|
||||
"element-internals-polyfill": "1.3.10",
|
||||
"fuse.js": "7.0.0",
|
||||
"google-timezones-json": "1.2.0",
|
||||
"hls.js": "1.5.3",
|
||||
"hls.js": "1.5.7",
|
||||
"home-assistant-js-websocket": "9.1.0",
|
||||
"idb-keyval": "6.2.1",
|
||||
"intl-messageformat": "10.5.11",
|
||||
@@ -118,7 +119,7 @@
|
||||
"leaflet-draw": "1.0.4",
|
||||
"lit": "2.8.0",
|
||||
"luxon": "3.4.4",
|
||||
"marked": "11.2.0",
|
||||
"marked": "12.0.0",
|
||||
"memoize-one": "6.0.0",
|
||||
"node-vibrant": "3.2.1-alpha.1",
|
||||
"proxy-polyfill": "0.3.2",
|
||||
@@ -155,11 +156,11 @@
|
||||
"@babel/plugin-transform-runtime": "7.23.9",
|
||||
"@babel/preset-env": "7.23.9",
|
||||
"@babel/preset-typescript": "7.23.3",
|
||||
"@bundle-stats/plugin-webpack-filter": "4.9.2",
|
||||
"@bundle-stats/plugin-webpack-filter": "4.10.1",
|
||||
"@koa/cors": "5.0.0",
|
||||
"@lokalise/node-api": "12.1.0",
|
||||
"@octokit/auth-oauth-device": "6.0.1",
|
||||
"@octokit/plugin-retry": "6.0.1",
|
||||
"@octokit/auth-oauth-device": "7.0.0",
|
||||
"@octokit/plugin-retry": "7.0.1",
|
||||
"@octokit/rest": "20.0.2",
|
||||
"@open-wc/dev-server-hmr": "0.1.4",
|
||||
"@rollup/plugin-babel": "6.0.4",
|
||||
@@ -170,6 +171,7 @@
|
||||
"@types/babel__plugin-transform-runtime": "7.9.5",
|
||||
"@types/chromecast-caf-receiver": "6.0.13",
|
||||
"@types/chromecast-caf-sender": "1.0.8",
|
||||
"@types/color-name": "1.1.3",
|
||||
"@types/glob": "8.1.0",
|
||||
"@types/html-minifier-terser": "7.0.2",
|
||||
"@types/js-yaml": "4.0.9",
|
||||
@@ -179,19 +181,19 @@
|
||||
"@types/mocha": "10.0.6",
|
||||
"@types/qrcode": "1.5.5",
|
||||
"@types/serve-handler": "6.1.4",
|
||||
"@types/sortablejs": "1.15.7",
|
||||
"@types/sortablejs": "1.15.8",
|
||||
"@types/tar": "6.1.11",
|
||||
"@types/ua-parser-js": "0.7.39",
|
||||
"@types/webspeechapi": "0.0.29",
|
||||
"@typescript-eslint/eslint-plugin": "6.20.0",
|
||||
"@typescript-eslint/parser": "6.20.0",
|
||||
"@typescript-eslint/eslint-plugin": "7.1.0",
|
||||
"@typescript-eslint/parser": "7.1.0",
|
||||
"@web/dev-server": "0.1.38",
|
||||
"@web/dev-server-rollup": "0.4.1",
|
||||
"babel-loader": "9.1.3",
|
||||
"babel-plugin-template-html-minifier": "4.1.0",
|
||||
"chai": "5.0.3",
|
||||
"chai": "5.1.0",
|
||||
"del": "7.1.0",
|
||||
"eslint": "8.56.0",
|
||||
"eslint": "8.57.0",
|
||||
"eslint-config-airbnb-base": "15.0.0",
|
||||
"eslint-config-airbnb-typescript": "17.1.0",
|
||||
"eslint-config-prettier": "9.1.0",
|
||||
@@ -200,31 +202,31 @@
|
||||
"eslint-plugin-import": "2.29.1",
|
||||
"eslint-plugin-lit": "1.11.0",
|
||||
"eslint-plugin-lit-a11y": "4.1.2",
|
||||
"eslint-plugin-unused-imports": "3.0.0",
|
||||
"eslint-plugin-unused-imports": "3.1.0",
|
||||
"eslint-plugin-wc": "2.0.4",
|
||||
"fancy-log": "2.0.0",
|
||||
"fs-extra": "11.2.0",
|
||||
"glob": "10.3.10",
|
||||
"gulp": "4.0.2",
|
||||
"gulp-flatmap": "1.0.2",
|
||||
"gulp-json-transform": "0.4.8",
|
||||
"gulp-json-transform": "0.5.0",
|
||||
"gulp-merge-json": "2.1.2",
|
||||
"gulp-rename": "2.0.0",
|
||||
"gulp-zopfli-green": "6.0.1",
|
||||
"html-minifier-terser": "7.2.0",
|
||||
"husky": "9.0.10",
|
||||
"husky": "9.0.11",
|
||||
"instant-mocha": "1.5.2",
|
||||
"jszip": "3.10.1",
|
||||
"lint-staged": "15.2.1",
|
||||
"lint-staged": "15.2.2",
|
||||
"lit-analyzer": "2.0.3",
|
||||
"lodash.template": "4.5.0",
|
||||
"magic-string": "0.30.6",
|
||||
"magic-string": "0.30.7",
|
||||
"map-stream": "0.0.7",
|
||||
"mocha": "10.2.0",
|
||||
"mocha": "10.3.0",
|
||||
"object-hash": "3.0.0",
|
||||
"open": "10.0.3",
|
||||
"open": "10.0.4",
|
||||
"pinst": "3.0.0",
|
||||
"prettier": "3.2.4",
|
||||
"prettier": "3.2.5",
|
||||
"rollup": "2.79.1",
|
||||
"rollup-plugin-string": "3.0.0",
|
||||
"rollup-plugin-terser": "7.0.2",
|
||||
@@ -240,12 +242,12 @@
|
||||
"typescript": "5.3.3",
|
||||
"vinyl-buffer": "1.0.1",
|
||||
"vinyl-source-stream": "2.0.0",
|
||||
"webpack": "5.90.1",
|
||||
"webpack": "5.90.3",
|
||||
"webpack-cli": "5.1.4",
|
||||
"webpack-dev-server": "4.15.1",
|
||||
"webpack-dev-server": "5.0.2",
|
||||
"webpack-manifest-plugin": "5.0.0",
|
||||
"webpack-stats-plugin": "1.1.3",
|
||||
"webpackbar": "6.0.0",
|
||||
"webpackbar": "6.0.1",
|
||||
"workbox-build": "7.0.0"
|
||||
},
|
||||
"_comment": "Polymer 3.2 contained a bug, fixed in https://github.com/Polymer/polymer/pull/5569, add as patch",
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "home-assistant-frontend"
|
||||
version = "20240205.0"
|
||||
version = "20240228.0"
|
||||
license = {text = "Apache-2.0"}
|
||||
description = "The Home Assistant frontend"
|
||||
readme = "README.md"
|
||||
|
||||
@@ -40,6 +40,7 @@ if [ -n "$ref" ]; then
|
||||
echo "Installing Home Assistant core at ${ref}..."
|
||||
python3 -m pip install --user --upgrade --src "$HOME/src" \
|
||||
--editable "git+${coreURL}@${ref}#egg=homeassistant"
|
||||
(cd ~/src/homeassistant && exec python3 -m script.translations develop --all)
|
||||
fi
|
||||
|
||||
if [ ! -d "${WD}/config" ]; then
|
||||
|
||||
@@ -1,19 +1,25 @@
|
||||
import { PageNavigation } from "../../layouts/hass-tabs-subpage";
|
||||
import { HomeAssistant } from "../../types";
|
||||
import { ensureArray } from "../array/ensure-array";
|
||||
import { isComponentLoaded } from "./is_component_loaded";
|
||||
|
||||
export const canShowPage = (hass: HomeAssistant, page: PageNavigation) =>
|
||||
(isCore(page) || isLoadedIntegration(hass, page)) &&
|
||||
!hideAdvancedPage(hass, page);
|
||||
!hideAdvancedPage(hass, page) &&
|
||||
isNotLoadedIntegration(hass, page);
|
||||
|
||||
const isLoadedIntegration = (hass: HomeAssistant, page: PageNavigation) =>
|
||||
page.component
|
||||
? isComponentLoaded(hass, page.component)
|
||||
: page.components
|
||||
? page.components.some((integration) =>
|
||||
isComponentLoaded(hass, integration)
|
||||
)
|
||||
: true;
|
||||
!page.component ||
|
||||
ensureArray(page.component).some((integration) =>
|
||||
isComponentLoaded(hass, integration)
|
||||
);
|
||||
|
||||
const isNotLoadedIntegration = (hass: HomeAssistant, page: PageNavigation) =>
|
||||
!page.not_component ||
|
||||
!ensureArray(page.not_component).some((integration) =>
|
||||
isComponentLoaded(hass, integration)
|
||||
);
|
||||
|
||||
const isCore = (page: PageNavigation) => page.core;
|
||||
const isAdvancedPage = (page: PageNavigation) => page.advancedOnly;
|
||||
const userWantsAdvanced = (hass: HomeAssistant) => hass.userData?.showAdvanced;
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
import { MAIN_WINDOW_NAME } from "../../data/main_window";
|
||||
|
||||
export const mainWindow =
|
||||
window.name === MAIN_WINDOW_NAME
|
||||
? window
|
||||
: parent.name === MAIN_WINDOW_NAME
|
||||
? parent
|
||||
: top!;
|
||||
export const mainWindow = (() => {
|
||||
try {
|
||||
return window.name === MAIN_WINDOW_NAME
|
||||
? window
|
||||
: parent.name === MAIN_WINDOW_NAME
|
||||
? parent
|
||||
: top!;
|
||||
} catch {
|
||||
return window;
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -53,9 +53,7 @@ export const computeAttributeValueDisplay = (
|
||||
|
||||
if (domain === "weather") {
|
||||
unit = getWeatherUnit(config, stateObj as WeatherEntity, attribute);
|
||||
}
|
||||
|
||||
if (TEMPERATURE_ATTRIBUTES.has(attribute)) {
|
||||
} else if (TEMPERATURE_ATTRIBUTES.has(attribute)) {
|
||||
unit = config.unit_system.temperature;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,14 +20,14 @@ function findNestedItem(
|
||||
}, obj);
|
||||
}
|
||||
|
||||
export function nestedArrayMove<T>(
|
||||
obj: T | T[],
|
||||
export function nestedArrayMove<A>(
|
||||
obj: A,
|
||||
oldIndex: number,
|
||||
newIndex: number,
|
||||
oldPath?: ItemPath,
|
||||
newPath?: ItemPath
|
||||
): T | T[] {
|
||||
const newObj = Array.isArray(obj) ? [...obj] : { ...obj };
|
||||
): A {
|
||||
const newObj = (Array.isArray(obj) ? [...obj] : { ...obj }) as A;
|
||||
const from = oldPath ? findNestedItem(newObj, oldPath) : newObj;
|
||||
const to = newPath ? findNestedItem(newObj, newPath, true) : newObj;
|
||||
|
||||
|
||||
@@ -5,10 +5,18 @@ import type {
|
||||
ChartOptions,
|
||||
TooltipModel,
|
||||
} from "chart.js";
|
||||
import { css, CSSResultGroup, html, LitElement, PropertyValues } from "lit";
|
||||
import {
|
||||
css,
|
||||
CSSResultGroup,
|
||||
html,
|
||||
nothing,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
} from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { clamp } from "../../common/number/clamp";
|
||||
import { HomeAssistant } from "../../types";
|
||||
import { debounce } from "../../common/util/debounce";
|
||||
@@ -27,6 +35,11 @@ interface Tooltip
|
||||
left: string;
|
||||
}
|
||||
|
||||
export interface ChartDatasetExtra {
|
||||
show_legend?: boolean;
|
||||
legend_label?: string;
|
||||
}
|
||||
|
||||
@customElement("ha-chart-base")
|
||||
export class HaChartBase extends LitElement {
|
||||
public chart?: Chart;
|
||||
@@ -38,6 +51,8 @@ export class HaChartBase extends LitElement {
|
||||
|
||||
@property({ attribute: false }) public data: ChartData = { datasets: [] };
|
||||
|
||||
@property({ attribute: false }) public extraData?: ChartDatasetExtra[];
|
||||
|
||||
@property({ attribute: false }) public options?: ChartOptions;
|
||||
|
||||
@property({ attribute: false }) public plugins?: any[];
|
||||
@@ -46,6 +61,8 @@ export class HaChartBase extends LitElement {
|
||||
|
||||
@property({ type: Number }) public paddingYAxis = 0;
|
||||
|
||||
@property({ type: Boolean }) public externalHidden = false;
|
||||
|
||||
@state() private _chartHeight?: number;
|
||||
|
||||
@state() private _tooltip?: Tooltip;
|
||||
@@ -58,6 +75,8 @@ export class HaChartBase extends LitElement {
|
||||
|
||||
private _paddingYAxisInternal = 0;
|
||||
|
||||
private _datasetOrder: number[] = [];
|
||||
|
||||
public disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this._releaseCanvas();
|
||||
@@ -148,6 +167,29 @@ export class HaChartBase extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
// put the legend labels in sorted order if provided
|
||||
if (changedProps.has("data")) {
|
||||
this._datasetOrder = this.data.datasets.map((_, index) => index);
|
||||
if (this.data?.datasets.some((dataset) => dataset.order)) {
|
||||
this._datasetOrder.sort(
|
||||
(a, b) =>
|
||||
(this.data.datasets[a].order || 0) -
|
||||
(this.data.datasets[b].order || 0)
|
||||
);
|
||||
}
|
||||
|
||||
if (this.externalHidden) {
|
||||
this._hiddenDatasets = new Set();
|
||||
if (this.data?.datasets) {
|
||||
this.data.datasets.forEach((dataset, index) => {
|
||||
if (dataset.hidden) {
|
||||
this._hiddenDatasets.add(index);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.hasUpdated || !this.chart) {
|
||||
return;
|
||||
}
|
||||
@@ -157,7 +199,7 @@ export class HaChartBase extends LitElement {
|
||||
return;
|
||||
}
|
||||
if (changedProps.has("data")) {
|
||||
if (this._hiddenDatasets.size) {
|
||||
if (this._hiddenDatasets.size && !this.externalHidden) {
|
||||
this.data.datasets.forEach((dataset, index) => {
|
||||
dataset.hidden = this._hiddenDatasets.has(index);
|
||||
});
|
||||
@@ -175,26 +217,32 @@ export class HaChartBase extends LitElement {
|
||||
${this.options?.plugins?.legend?.display === true
|
||||
? html`<div class="chartLegend">
|
||||
<ul>
|
||||
${this.data.datasets.map(
|
||||
(dataset, index) =>
|
||||
html`<li
|
||||
.datasetIndex=${index}
|
||||
@click=${this._legendClick}
|
||||
class=${classMap({
|
||||
hidden: this._hiddenDatasets.has(index),
|
||||
})}
|
||||
.title=${dataset.label}
|
||||
>
|
||||
<div
|
||||
class="bullet"
|
||||
style=${styleMap({
|
||||
backgroundColor: dataset.backgroundColor as string,
|
||||
borderColor: dataset.borderColor as string,
|
||||
${this._datasetOrder.map((index) => {
|
||||
const dataset = this.data.datasets[index];
|
||||
return this.extraData?.[index]?.show_legend === false
|
||||
? nothing
|
||||
: html`<li
|
||||
.datasetIndex=${index}
|
||||
@click=${this._legendClick}
|
||||
class=${classMap({
|
||||
hidden: this._hiddenDatasets.has(index),
|
||||
})}
|
||||
></div>
|
||||
<div class="label">${dataset.label}</div>
|
||||
</li>`
|
||||
)}
|
||||
.title=${this.extraData?.[index]?.legend_label ??
|
||||
dataset.label}
|
||||
>
|
||||
<div
|
||||
class="bullet"
|
||||
style=${styleMap({
|
||||
backgroundColor: dataset.backgroundColor as string,
|
||||
borderColor: dataset.borderColor as string,
|
||||
})}
|
||||
></div>
|
||||
<div class="label">
|
||||
${this.extraData?.[index]?.legend_label ??
|
||||
dataset.label}
|
||||
</div>
|
||||
</li>`;
|
||||
})}
|
||||
</ul>
|
||||
</div>`
|
||||
: ""}
|
||||
@@ -211,9 +259,9 @@ export class HaChartBase extends LitElement {
|
||||
height: `${
|
||||
this.height ?? this._chartHeight ?? this.clientWidth / 2
|
||||
}px`,
|
||||
"padding-left": `${this._paddingYAxisInternal}`,
|
||||
"padding-left": `${this._paddingYAxisInternal}px`,
|
||||
"padding-right": 0,
|
||||
"padding-inline-start": `${this._paddingYAxisInternal}`,
|
||||
"padding-inline-start": `${this._paddingYAxisInternal}px`,
|
||||
"padding-inline-end": 0,
|
||||
})}
|
||||
>
|
||||
@@ -339,9 +387,19 @@ export class HaChartBase extends LitElement {
|
||||
if (this.chart.isDatasetVisible(index)) {
|
||||
this.chart.setDatasetVisibility(index, false);
|
||||
this._hiddenDatasets.add(index);
|
||||
if (this.externalHidden) {
|
||||
fireEvent(this, "dataset-hidden", {
|
||||
index,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.chart.setDatasetVisibility(index, true);
|
||||
this._hiddenDatasets.delete(index);
|
||||
if (this.externalHidden) {
|
||||
fireEvent(this, "dataset-unhidden", {
|
||||
index,
|
||||
});
|
||||
}
|
||||
}
|
||||
this.chart.update("none");
|
||||
this.requestUpdate("_hiddenDatasets");
|
||||
@@ -486,4 +544,8 @@ declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-chart-base": HaChartBase;
|
||||
}
|
||||
interface HASSDomEvents {
|
||||
"dataset-hidden": { index: number };
|
||||
"dataset-unhidden": { index: number };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ export class StateHistoryChartLine extends LitElement {
|
||||
config: this.hass.config,
|
||||
},
|
||||
},
|
||||
suggestedMin: this.startTime,
|
||||
min: this.startTime,
|
||||
suggestedMax: this.endTime,
|
||||
ticks: {
|
||||
maxRotation: 0,
|
||||
|
||||
@@ -114,7 +114,7 @@ export class StateHistoryChartTimeline extends LitElement {
|
||||
config: this.hass.config,
|
||||
},
|
||||
},
|
||||
suggestedMin: this.startTime,
|
||||
min: this.startTime,
|
||||
suggestedMax: this.endTime,
|
||||
ticks: {
|
||||
autoSkip: true,
|
||||
|
||||
@@ -233,16 +233,32 @@ export class StateHistoryCharts extends LitElement {
|
||||
new Date().getTime() - 60 * 60 * this.hoursToShow * 1000
|
||||
);
|
||||
} else {
|
||||
this._computedStartTime = new Date(
|
||||
(this.historyData?.timeline ?? []).reduce(
|
||||
(minTime, stateInfo) =>
|
||||
Math.min(
|
||||
minTime,
|
||||
new Date(stateInfo.data[0].last_changed).getTime()
|
||||
),
|
||||
new Date().getTime()
|
||||
)
|
||||
let minTimeAll = (this.historyData?.timeline ?? []).reduce(
|
||||
(minTime, stateInfo) =>
|
||||
Math.min(
|
||||
minTime,
|
||||
new Date(stateInfo.data[0].last_changed).getTime()
|
||||
),
|
||||
new Date().getTime()
|
||||
);
|
||||
|
||||
minTimeAll = (this.historyData?.line ?? []).reduce(
|
||||
(minTimeLine, line) =>
|
||||
Math.min(
|
||||
minTimeLine,
|
||||
line.data.reduce(
|
||||
(minTimeData, data) =>
|
||||
Math.min(
|
||||
minTimeData,
|
||||
new Date(data.states[0].last_changed).getTime()
|
||||
),
|
||||
minTimeLine
|
||||
)
|
||||
),
|
||||
minTimeAll
|
||||
);
|
||||
|
||||
this._computedStartTime = new Date(minTimeAll);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,11 @@ import {
|
||||
} from "../../data/recorder";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import "./ha-chart-base";
|
||||
import type { ChartResizeOptions, HaChartBase } from "./ha-chart-base";
|
||||
import type {
|
||||
ChartResizeOptions,
|
||||
ChartDatasetExtra,
|
||||
HaChartBase,
|
||||
} from "./ha-chart-base";
|
||||
|
||||
export const supportedStatTypeMap: Record<StatisticType, StatisticType> = {
|
||||
mean: "mean",
|
||||
@@ -79,10 +83,14 @@ export class StatisticsChart extends LitElement {
|
||||
|
||||
@state() private _chartData: ChartData = { datasets: [] };
|
||||
|
||||
@state() private _chartDatasetExtra: ChartDatasetExtra[] = [];
|
||||
|
||||
@state() private _statisticIds: string[] = [];
|
||||
|
||||
@state() private _chartOptions?: ChartOptions;
|
||||
|
||||
@state() private _hiddenStats = new Set<string>();
|
||||
|
||||
@query("ha-chart-base") private _chart?: HaChartBase;
|
||||
|
||||
private _computedStyle?: CSSStyleDeclaration;
|
||||
@@ -96,6 +104,9 @@ export class StatisticsChart extends LitElement {
|
||||
}
|
||||
|
||||
public willUpdate(changedProps: PropertyValues) {
|
||||
if (changedProps.has("legendMode")) {
|
||||
this._hiddenStats.clear();
|
||||
}
|
||||
if (
|
||||
!this.hasUpdated ||
|
||||
changedProps.has("unit") ||
|
||||
@@ -110,7 +121,8 @@ export class StatisticsChart extends LitElement {
|
||||
changedProps.has("statisticsData") ||
|
||||
changedProps.has("statTypes") ||
|
||||
changedProps.has("chartType") ||
|
||||
changedProps.has("hideLegend")
|
||||
changedProps.has("hideLegend") ||
|
||||
changedProps.has("_hiddenStats")
|
||||
) {
|
||||
this._generateData();
|
||||
}
|
||||
@@ -145,14 +157,30 @@ export class StatisticsChart extends LitElement {
|
||||
|
||||
return html`
|
||||
<ha-chart-base
|
||||
externalHidden
|
||||
.hass=${this.hass}
|
||||
.data=${this._chartData}
|
||||
.extraData=${this._chartDatasetExtra}
|
||||
.options=${this._chartOptions}
|
||||
.chartType=${this.chartType}
|
||||
@dataset-hidden=${this._datasetHidden}
|
||||
@dataset-unhidden=${this._datasetUnhidden}
|
||||
></ha-chart-base>
|
||||
`;
|
||||
}
|
||||
|
||||
private _datasetHidden(ev) {
|
||||
ev.stopPropagation();
|
||||
this._hiddenStats.add(this._statisticIds[ev.detail.index]);
|
||||
this.requestUpdate("_hiddenStats");
|
||||
}
|
||||
|
||||
private _datasetUnhidden(ev) {
|
||||
ev.stopPropagation();
|
||||
this._hiddenStats.delete(this._statisticIds[ev.detail.index]);
|
||||
this.requestUpdate("_hiddenStats");
|
||||
}
|
||||
|
||||
private _createOptions(unit?: string) {
|
||||
this._chartOptions = {
|
||||
parsing: false,
|
||||
@@ -274,6 +302,7 @@ export class StatisticsChart extends LitElement {
|
||||
let colorIndex = 0;
|
||||
const statisticsData = Object.entries(this.statisticsData);
|
||||
const totalDataSets: ChartDataset<"line">[] = [];
|
||||
const totalDatasetExtras: ChartDatasetExtra[] = [];
|
||||
const statisticIds: string[] = [];
|
||||
let endTime: Date;
|
||||
|
||||
@@ -324,6 +353,7 @@ export class StatisticsChart extends LitElement {
|
||||
|
||||
// The datasets for the current statistic
|
||||
const statDataSets: ChartDataset<"line">[] = [];
|
||||
const statDatasetExtras: ChartDatasetExtra[] = [];
|
||||
|
||||
const pushData = (
|
||||
start: Date,
|
||||
@@ -384,9 +414,20 @@ export class StatisticsChart extends LitElement {
|
||||
})
|
||||
: this.statTypes;
|
||||
|
||||
let displayed_legend = false;
|
||||
sortedTypes.forEach((type) => {
|
||||
if (statisticsHaveType(stats, type)) {
|
||||
const band = drawBands && (type === "min" || type === "max");
|
||||
if (!this.hideLegend) {
|
||||
const show_legend = hasMean
|
||||
? type === "mean"
|
||||
: displayed_legend === false;
|
||||
statDatasetExtras.push({
|
||||
legend_label: name,
|
||||
show_legend,
|
||||
});
|
||||
displayed_legend = displayed_legend || show_legend;
|
||||
}
|
||||
statTypes.push(type);
|
||||
statDataSets.push({
|
||||
label: name
|
||||
@@ -408,6 +449,9 @@ export class StatisticsChart extends LitElement {
|
||||
band && hasMean ? color + (this.hideLegend ? "00" : "7F") : color,
|
||||
backgroundColor: band ? color + "3F" : color + "7F",
|
||||
pointRadius: 0,
|
||||
hidden: !this.hideLegend
|
||||
? this._hiddenStats.has(statistic_id)
|
||||
: false,
|
||||
data: [],
|
||||
// @ts-ignore
|
||||
unit: meta?.unit_of_measurement,
|
||||
@@ -446,6 +490,7 @@ export class StatisticsChart extends LitElement {
|
||||
|
||||
// Concat two arrays
|
||||
Array.prototype.push.apply(totalDataSets, statDataSets);
|
||||
Array.prototype.push.apply(totalDatasetExtras, statDatasetExtras);
|
||||
});
|
||||
|
||||
if (unit) {
|
||||
@@ -455,6 +500,7 @@ export class StatisticsChart extends LitElement {
|
||||
this._chartData = {
|
||||
datasets: totalDataSets,
|
||||
};
|
||||
this._chartDatasetExtra = totalDatasetExtras;
|
||||
this._statisticIds = statisticIds;
|
||||
}
|
||||
|
||||
|
||||
@@ -205,7 +205,9 @@ export class TimelineController extends BarController {
|
||||
|
||||
const y = vScale.getPixelForValue(this.index);
|
||||
|
||||
const xStart = iScale.getPixelForValue(data.start.getTime());
|
||||
const xStart = iScale.getPixelForValue(
|
||||
Math.max(iScale.min, data.start.getTime())
|
||||
);
|
||||
const xEnd = iScale.getPixelForValue(data.end.getTime());
|
||||
const width = xEnd - xStart;
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ export class TimeLineScale extends TimeScale {
|
||||
max = isFinite(max) && !isNaN(max) ? max : +adapter.endOf(Date.now(), unit);
|
||||
|
||||
// Make sure that max is strictly higher than min (required by the lookup table)
|
||||
this.min = Math.min(min, max - 1);
|
||||
this.max = Math.max(min + 1, max);
|
||||
this.min = adapter.parse(options.min, this) ?? Math.min(min, max - 1);
|
||||
this.max = adapter.parse(options.max, this) ?? Math.max(min + 1, max);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,8 @@ class HaDataTableIcon extends LitElement {
|
||||
div {
|
||||
position: absolute;
|
||||
right: 28px;
|
||||
inset-inline-end: 28px;
|
||||
inset-inline-start: initial;
|
||||
z-index: 1002;
|
||||
outline: none;
|
||||
font-size: 10px;
|
||||
|
||||
@@ -133,9 +133,9 @@ export class HaStateLabelBadge extends LitElement {
|
||||
entityState,
|
||||
this._timerTimeRemaining
|
||||
)}
|
||||
.description=${this.showName === false
|
||||
? undefined
|
||||
: this.name ?? computeStateName(entityState)}
|
||||
.description=${this.showName
|
||||
? this.name ?? computeStateName(entityState)
|
||||
: undefined}
|
||||
>
|
||||
${!image && showIcon
|
||||
? html`<ha-state-icon
|
||||
|
||||
@@ -32,7 +32,9 @@ export class StateBadge extends LitElement {
|
||||
|
||||
@property() public overrideImage?: string;
|
||||
|
||||
@property({ type: Boolean }) public stateColor = false;
|
||||
// Cannot be a boolean attribute because undefined is treated different than
|
||||
// false. When it is undefined, state is still colored for light entities.
|
||||
@property({ attribute: false }) public stateColor?: boolean;
|
||||
|
||||
@property() public color?: string;
|
||||
|
||||
@@ -70,7 +72,7 @@ export class StateBadge extends LitElement {
|
||||
const domain = this.stateObj
|
||||
? computeStateDomain(this.stateObj)
|
||||
: undefined;
|
||||
return this.stateColor || (domain === "light" && this.stateColor !== false);
|
||||
return this.stateColor ?? domain === "light";
|
||||
}
|
||||
|
||||
protected render() {
|
||||
|
||||
@@ -156,6 +156,7 @@ class HaClimateState extends LitElement {
|
||||
|
||||
.current {
|
||||
color: var(--secondary-text-color);
|
||||
direction: var(--direction);
|
||||
}
|
||||
|
||||
.state-label {
|
||||
|
||||
@@ -127,9 +127,11 @@ export class HaControlButton extends LitElement {
|
||||
opacity 180ms ease-in-out;
|
||||
opacity: var(--control-button-background-opacity);
|
||||
}
|
||||
.button ::slotted(*) {
|
||||
.button {
|
||||
transition: color 180ms ease-in-out;
|
||||
color: var(--control-button-icon-color);
|
||||
}
|
||||
.button ::slotted(*) {
|
||||
pointer-events: none;
|
||||
}
|
||||
.button:disabled {
|
||||
|
||||
@@ -273,9 +273,13 @@ export class HaControlNumberButton extends LitElement {
|
||||
}
|
||||
.button.minus {
|
||||
left: 0;
|
||||
inset-inline-start: 0;
|
||||
inset-inline-end: initial;
|
||||
}
|
||||
.button.plus {
|
||||
right: 0;
|
||||
inset-inline-start: initial;
|
||||
inset-inline-end: 0;
|
||||
}
|
||||
.unit {
|
||||
white-space: pre;
|
||||
|
||||
@@ -3,7 +3,6 @@ import { styles } from "@material/mwc-drawer/mwc-drawer.css";
|
||||
import { css, PropertyValues } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { mainWindow } from "../common/dom/get_main_window";
|
||||
|
||||
const blockingElements = (document as any).$blockingElements;
|
||||
|
||||
@@ -34,7 +33,8 @@ export class HaDrawer extends DrawerBase {
|
||||
protected updated(changedProps: PropertyValues) {
|
||||
super.updated(changedProps);
|
||||
if (changedProps.has("direction")) {
|
||||
if (mainWindow.document.dir === "rtl") {
|
||||
this.mdcRoot.dir = this.direction;
|
||||
if (this.direction === "rtl") {
|
||||
this._rtlStyle = document.createElement("style");
|
||||
this._rtlStyle.innerHTML = `
|
||||
.mdc-drawer--animate {
|
||||
|
||||
@@ -54,7 +54,8 @@ export const computeInitialHaFormData = (
|
||||
"icon" in selector ||
|
||||
"template" in selector ||
|
||||
"text" in selector ||
|
||||
"theme" in selector
|
||||
"theme" in selector ||
|
||||
"object" in selector
|
||||
) {
|
||||
data[field.name] = "";
|
||||
} else if ("number" in selector) {
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { FormfieldBase } from "@material/mwc-formfield/mwc-formfield-base";
|
||||
import { styles } from "@material/mwc-formfield/mwc-formfield.css";
|
||||
import { css } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
|
||||
@customElement("ha-formfield")
|
||||
export class HaFormfield extends FormfieldBase {
|
||||
@property({ type: Boolean, reflect: true }) public disabled = false;
|
||||
|
||||
protected _labelClick() {
|
||||
const input = this.input as HTMLInputElement | undefined;
|
||||
if (!input) return;
|
||||
@@ -44,6 +46,9 @@ export class HaFormfield extends FormfieldBase {
|
||||
padding-inline-start: 4px;
|
||||
padding-inline-end: 0;
|
||||
}
|
||||
:host([disabled]) label {
|
||||
color: var(--disabled-text-color);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -136,6 +136,8 @@ class HaMenuButton extends LitElement {
|
||||
height: 12px;
|
||||
top: 9px;
|
||||
right: 7px;
|
||||
inset-inline-end: 7px;
|
||||
inset-inline-start: initial;
|
||||
border-radius: 50%;
|
||||
border: 2px solid var(--app-header-background-color);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,9 @@ class HaMetric extends LitElement {
|
||||
<ha-settings-row>
|
||||
<span slot="heading"> ${this.heading} </span>
|
||||
<div slot="description" .title=${this.tooltip ?? ""}>
|
||||
<span class="value"> ${roundedValue} % </span>
|
||||
<span class="value">
|
||||
<div>${roundedValue} %</div>
|
||||
</span>
|
||||
<ha-bar
|
||||
class=${classMap({
|
||||
"target-warning": roundedValue > 50,
|
||||
@@ -70,6 +72,10 @@ class HaMetric extends LitElement {
|
||||
padding-inline-start: initial;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.value > div {
|
||||
direction: ltr;
|
||||
text-align: var(--float-start);
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { LitElement, PropertyValues, css, html, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import QRCode from "qrcode";
|
||||
import "./ha-alert";
|
||||
import { rgb2hex } from "../common/color/convert-color";
|
||||
|
||||
@customElement("ha-qr-code")
|
||||
export class HaQrCode extends LitElement {
|
||||
@@ -65,6 +66,26 @@ export class HaQrCode extends LitElement {
|
||||
changedProperties.has("centerImage"))
|
||||
) {
|
||||
const computedStyles = getComputedStyle(this);
|
||||
const textRgb = computedStyles.getPropertyValue(
|
||||
"--rgb-primary-text-color"
|
||||
);
|
||||
const backgroundRgb = computedStyles.getPropertyValue(
|
||||
"--rgb-card-background-color"
|
||||
);
|
||||
const textHex = rgb2hex(
|
||||
textRgb.split(",").map((a) => parseInt(a, 10)) as [
|
||||
number,
|
||||
number,
|
||||
number,
|
||||
]
|
||||
);
|
||||
const backgroundHex = rgb2hex(
|
||||
backgroundRgb.split(",").map((a) => parseInt(a, 10)) as [
|
||||
number,
|
||||
number,
|
||||
number,
|
||||
]
|
||||
);
|
||||
|
||||
QRCode.toCanvas(canvas, this.data, {
|
||||
errorCorrectionLevel:
|
||||
@@ -74,8 +95,8 @@ export class HaQrCode extends LitElement {
|
||||
margin: this.margin,
|
||||
maskPattern: this.maskPattern,
|
||||
color: {
|
||||
light: computedStyles.getPropertyValue("--card-background-color"),
|
||||
dark: computedStyles.getPropertyValue("--primary-text-color"),
|
||||
light: backgroundHex,
|
||||
dark: textHex,
|
||||
},
|
||||
}).catch((err) => {
|
||||
this._error = err.message;
|
||||
|
||||
@@ -186,6 +186,8 @@ class HaQrScanner extends LitElement {
|
||||
position: absolute;
|
||||
bottom: 8px;
|
||||
right: 8px;
|
||||
inset-inline-end: 8px;
|
||||
inset-inline-start: initial;
|
||||
background: #727272b2;
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
|
||||
@@ -102,7 +102,10 @@ export class HaSelectSelector extends LitElement {
|
||||
${this.label}
|
||||
${options.map(
|
||||
(item: SelectOption) => html`
|
||||
<ha-formfield .label=${item.label}>
|
||||
<ha-formfield
|
||||
.label=${item.label}
|
||||
.disabled=${item.disabled || this.disabled}
|
||||
>
|
||||
<ha-radio
|
||||
.checked=${item.value === this.value}
|
||||
.value=${item.value}
|
||||
|
||||
@@ -93,6 +93,8 @@ export class HaServiceControl extends LitElement {
|
||||
|
||||
@property({ type: Boolean, reflect: true }) public hidePicker = false;
|
||||
|
||||
@property({ type: Boolean }) public hideDescription = false;
|
||||
|
||||
@state() private _value!: this["value"];
|
||||
|
||||
@state() private _checkedKeys = new Set();
|
||||
@@ -373,7 +375,8 @@ export class HaServiceControl extends LitElement {
|
||||
)) ||
|
||||
serviceData?.description;
|
||||
|
||||
return html`${this.hidePicker
|
||||
return html`
|
||||
${this.hidePicker
|
||||
? nothing
|
||||
: html`<ha-service-picker
|
||||
.hass=${this.hass}
|
||||
@@ -381,29 +384,33 @@ export class HaServiceControl extends LitElement {
|
||||
.disabled=${this.disabled}
|
||||
@value-changed=${this._serviceChanged}
|
||||
></ha-service-picker>`}
|
||||
<div class="description">
|
||||
${description ? html`<p>${description}</p>` : ""}
|
||||
${this._manifest
|
||||
? html` <a
|
||||
href=${this._manifest.is_built_in
|
||||
? documentationUrl(
|
||||
this.hass,
|
||||
`/integrations/${this._manifest.domain}`
|
||||
)
|
||||
: this._manifest.documentation}
|
||||
title=${this.hass.localize(
|
||||
"ui.components.service-control.integration_doc"
|
||||
)}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<ha-icon-button
|
||||
.path=${mdiHelpCircle}
|
||||
class="help-icon"
|
||||
></ha-icon-button>
|
||||
</a>`
|
||||
: ""}
|
||||
</div>
|
||||
${this.hideDescription
|
||||
? nothing
|
||||
: html`
|
||||
<div class="description">
|
||||
${description ? html`<p>${description}</p>` : ""}
|
||||
${this._manifest
|
||||
? html` <a
|
||||
href=${this._manifest.is_built_in
|
||||
? documentationUrl(
|
||||
this.hass,
|
||||
`/integrations/${this._manifest.domain}`
|
||||
)
|
||||
: this._manifest.documentation}
|
||||
title=${this.hass.localize(
|
||||
"ui.components.service-control.integration_doc"
|
||||
)}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<ha-icon-button
|
||||
.path=${mdiHelpCircle}
|
||||
class="help-icon"
|
||||
></ha-icon-button>
|
||||
</a>`
|
||||
: nothing}
|
||||
</div>
|
||||
`}
|
||||
${serviceData && "target" in serviceData
|
||||
? html`<ha-settings-row .narrow=${this.narrow}>
|
||||
${hasOptional
|
||||
@@ -517,7 +524,8 @@ export class HaServiceControl extends LitElement {
|
||||
></ha-selector>
|
||||
</ha-settings-row>`
|
||||
: "";
|
||||
})}`;
|
||||
})}
|
||||
`;
|
||||
}
|
||||
|
||||
private _localizeValueCallback = (key: string) => {
|
||||
|
||||
@@ -114,11 +114,14 @@ class HaServicePicker extends LitElement {
|
||||
if (!filter) {
|
||||
return processedServices;
|
||||
}
|
||||
return processedServices.filter(
|
||||
(service) =>
|
||||
service.service.toLowerCase().includes(filter) ||
|
||||
service.name?.toLowerCase().includes(filter)
|
||||
);
|
||||
const split_filter = filter.split(" ");
|
||||
return processedServices.filter((service) => {
|
||||
const lower_service_name = service.name.toLowerCase();
|
||||
const lower_service = service.service.toLowerCase();
|
||||
return split_filter.every(
|
||||
(f) => lower_service_name.includes(f) || lower_service.includes(f)
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -1010,8 +1010,8 @@ class HaSidebar extends SubscribeMixin(LitElement) {
|
||||
}
|
||||
.profile paper-icon-item {
|
||||
padding-left: 4px;
|
||||
margin-inline-start: 4px;
|
||||
margin-inline-end: auto;
|
||||
padding-inline-start: 4px;
|
||||
padding-inline-end: auto;
|
||||
}
|
||||
.profile .item-text {
|
||||
margin-left: 8px;
|
||||
@@ -1040,6 +1040,8 @@ class HaSidebar extends SubscribeMixin(LitElement) {
|
||||
position: absolute;
|
||||
bottom: 14px;
|
||||
left: 26px;
|
||||
inset-inline-start: 26px;
|
||||
inset-inline-end: initial;
|
||||
font-size: 0.65em;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,9 +14,16 @@ declare global {
|
||||
oldPath?: ItemPath;
|
||||
newPath?: ItemPath;
|
||||
};
|
||||
"drag-start": undefined;
|
||||
"drag-end": undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export type HaSortableOptions = Omit<
|
||||
SortableInstance.SortableOptions,
|
||||
"onStart" | "onChoose" | "onEnd"
|
||||
>;
|
||||
|
||||
@customElement("ha-sortable")
|
||||
export class HaSortable extends LitElement {
|
||||
private _sortable?: SortableInstance;
|
||||
@@ -36,14 +43,17 @@ export class HaSortable extends LitElement {
|
||||
@property({ type: String, attribute: "handle-selector" })
|
||||
public handleSelector?: string;
|
||||
|
||||
@property({ type: String, attribute: "group" })
|
||||
public group?: string;
|
||||
|
||||
@property({ type: Number, attribute: "swap-threshold" })
|
||||
public swapThreshold?: number;
|
||||
@property({ type: String })
|
||||
public group?: string | SortableInstance.GroupOptions;
|
||||
|
||||
@property({ type: Boolean, attribute: "invert-swap" })
|
||||
public invertSwap?: boolean;
|
||||
public invertSwap: boolean = false;
|
||||
|
||||
@property({ attribute: false })
|
||||
public options?: HaSortableOptions;
|
||||
|
||||
@property({ type: Boolean })
|
||||
public rollback: boolean = true;
|
||||
|
||||
protected updated(changedProperties: PropertyValues<this>) {
|
||||
if (changedProperties.has("disabled")) {
|
||||
@@ -72,6 +82,9 @@ export class HaSortable extends LitElement {
|
||||
public connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this._shouldBeDestroy = false;
|
||||
if (this.hasUpdated) {
|
||||
this.requestUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
protected createRenderRoot() {
|
||||
@@ -114,26 +127,20 @@ export class HaSortable extends LitElement {
|
||||
|
||||
const options: SortableInstance.Options = {
|
||||
animation: 150,
|
||||
swapThreshold: 1,
|
||||
...this.options,
|
||||
onChoose: this._handleChoose,
|
||||
onStart: this._handleStart,
|
||||
onEnd: this._handleEnd,
|
||||
};
|
||||
|
||||
if (this.draggableSelector) {
|
||||
options.draggable = this.draggableSelector;
|
||||
}
|
||||
|
||||
if (this.swapThreshold !== undefined) {
|
||||
options.swapThreshold = this.swapThreshold;
|
||||
}
|
||||
if (this.invertSwap !== undefined) {
|
||||
options.invertSwap = this.invertSwap;
|
||||
}
|
||||
if (this.handleSelector) {
|
||||
options.handle = this.handleSelector;
|
||||
}
|
||||
if (this.draggableSelector) {
|
||||
options.draggable = this.draggableSelector;
|
||||
if (this.invertSwap !== undefined) {
|
||||
options.invertSwap = this.invertSwap;
|
||||
}
|
||||
if (this.group) {
|
||||
options.group = this.group;
|
||||
@@ -143,8 +150,9 @@ export class HaSortable extends LitElement {
|
||||
}
|
||||
|
||||
private _handleEnd = async (evt: SortableEvent) => {
|
||||
fireEvent(this, "drag-end");
|
||||
// put back in original location
|
||||
if ((evt.item as any).placeholder) {
|
||||
if (this.rollback && (evt.item as any).placeholder) {
|
||||
(evt.item as any).placeholder.replaceWith(evt.item);
|
||||
delete (evt.item as any).placeholder;
|
||||
}
|
||||
@@ -170,7 +178,12 @@ export class HaSortable extends LitElement {
|
||||
});
|
||||
};
|
||||
|
||||
private _handleStart = () => {
|
||||
fireEvent(this, "drag-start");
|
||||
};
|
||||
|
||||
private _handleChoose = (evt: SortableEvent) => {
|
||||
if (!this.rollback) return;
|
||||
(evt.item as any).placeholder = document.createComment("sort-placeholder");
|
||||
evt.item.after((evt.item as any).placeholder);
|
||||
};
|
||||
|
||||
@@ -90,7 +90,7 @@ export class HaTextField extends TextFieldBase {
|
||||
padding-right: var(--text-field-suffix-padding-right, 0px);
|
||||
padding-inline-start: var(--text-field-suffix-padding-left, 12px);
|
||||
padding-inline-end: var(--text-field-suffix-padding-right, 0px);
|
||||
direction: var(--direction);
|
||||
direction: ltr;
|
||||
}
|
||||
.mdc-text-field--with-leading-icon {
|
||||
padding-inline-start: var(--text-field-suffix-padding-left, 0px);
|
||||
@@ -199,7 +199,6 @@ export class HaTextField extends TextFieldBase {
|
||||
// safari workaround - must be explicit
|
||||
mainWindow.document.dir === "rtl"
|
||||
? css`
|
||||
.mdc-text-field__affix--suffix,
|
||||
.mdc-text-field--with-leading-icon,
|
||||
.mdc-text-field__icon--leading,
|
||||
.mdc-floating-label,
|
||||
|
||||
@@ -1,35 +1,8 @@
|
||||
import "@polymer/paper-toast/paper-toast";
|
||||
import type { PaperToastElement } from "@polymer/paper-toast/paper-toast";
|
||||
import { customElement } from "lit/decorators";
|
||||
import type { Constructor } from "../types";
|
||||
|
||||
const PaperToast = customElements.get(
|
||||
"paper-toast"
|
||||
) as Constructor<PaperToastElement>;
|
||||
import { Snackbar } from "@material/mwc-snackbar/mwc-snackbar";
|
||||
|
||||
@customElement("ha-toast")
|
||||
export class HaToast extends PaperToast {
|
||||
private _resizeListener?: (obj: { matches: boolean }) => unknown;
|
||||
|
||||
private _mediaq?: MediaQueryList;
|
||||
|
||||
public connectedCallback() {
|
||||
super.connectedCallback();
|
||||
|
||||
if (!this._resizeListener) {
|
||||
this._resizeListener = (ev) =>
|
||||
this.classList.toggle("fit-bottom", ev.matches);
|
||||
this._mediaq = window.matchMedia("(max-width: 599px");
|
||||
}
|
||||
this._mediaq!.addListener(this._resizeListener);
|
||||
this._resizeListener(this._mediaq!);
|
||||
}
|
||||
|
||||
public disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this._mediaq!.removeListener(this._resizeListener!);
|
||||
}
|
||||
}
|
||||
export class HaToast extends Snackbar {}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
|
||||
@@ -223,7 +223,6 @@ class DialogMediaPlayerBrowse extends LitElement {
|
||||
|
||||
ha-media-player-browse {
|
||||
--media-browser-max-height: calc(100vh - 65px);
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
:host(.opened) ha-media-player-browse {
|
||||
|
||||
@@ -879,6 +879,7 @@ export class HaMediaPlayerBrowse extends LitElement {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
ha-circular-progress {
|
||||
|
||||
@@ -163,21 +163,22 @@ export class HaTracePathDetails extends LitElement {
|
||||
}
|
||||
)}
|
||||
<br />
|
||||
${error
|
||||
? html`<div class="error">
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.automation.trace.path.error",
|
||||
{
|
||||
error: error,
|
||||
}
|
||||
)}
|
||||
</div>`
|
||||
: nothing}
|
||||
${result
|
||||
? html`${this.hass!.localize(
|
||||
"ui.panel.config.automation.trace.path.result"
|
||||
)}
|
||||
<pre>${dump(result)}</pre>`
|
||||
: error
|
||||
? html`<div class="error">
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.automation.trace.path.error",
|
||||
{
|
||||
error: error,
|
||||
}
|
||||
)}
|
||||
</div>`
|
||||
: nothing}
|
||||
: nothing}
|
||||
${Object.keys(rest).length === 0
|
||||
? nothing
|
||||
: html`<pre>${dump(rest)}</pre>`}
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import { mdiExclamationThick } from "@mdi/js";
|
||||
import {
|
||||
css,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
html,
|
||||
TemplateResult,
|
||||
css,
|
||||
html,
|
||||
nothing,
|
||||
svg,
|
||||
} from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { NODE_SIZE, SPACING } from "./hat-graph-const";
|
||||
import { isSafari } from "../../util/is_safari";
|
||||
import { NODE_SIZE, SPACING } from "./hat-graph-const";
|
||||
|
||||
/**
|
||||
* @attribute active
|
||||
@@ -20,6 +22,8 @@ export class HatGraphNode extends LitElement {
|
||||
|
||||
@property({ type: Boolean, reflect: true }) public disabled = false;
|
||||
|
||||
@property({ type: Boolean }) public error = false;
|
||||
|
||||
@property({ reflect: true, type: Boolean }) notEnabled = false;
|
||||
|
||||
@property({ reflect: true, type: Boolean }) graphStart = false;
|
||||
@@ -51,7 +55,7 @@ export class HatGraphNode extends LitElement {
|
||||
: Math.ceil((NODE_SIZE + SPACING * 2) / 2)} ${width} ${height}"
|
||||
>
|
||||
${this.graphStart
|
||||
? ``
|
||||
? nothing
|
||||
: svg`
|
||||
<path
|
||||
class="connector"
|
||||
@@ -64,26 +68,39 @@ export class HatGraphNode extends LitElement {
|
||||
`}
|
||||
<g class="node">
|
||||
<circle cx="0" cy="0" r=${NODE_SIZE / 2} />
|
||||
}
|
||||
${this.error
|
||||
? svg`
|
||||
<g class="error">
|
||||
<circle
|
||||
cx="-12"
|
||||
cy=${-NODE_SIZE / 2}
|
||||
r="8"
|
||||
></circle>
|
||||
<path transform="translate(-18 -21) scale(.5)" class="exclamation" d=${mdiExclamationThick}/>
|
||||
</g>
|
||||
`
|
||||
: nothing}
|
||||
${this.badge
|
||||
? svg`
|
||||
<g class="number">
|
||||
<circle
|
||||
cx="8"
|
||||
cx="12"
|
||||
cy=${-NODE_SIZE / 2}
|
||||
r="8"
|
||||
></circle>
|
||||
<text
|
||||
x="8"
|
||||
x="12"
|
||||
y=${-NODE_SIZE / 2}
|
||||
text-anchor="middle"
|
||||
alignment-baseline="middle"
|
||||
>${this.badge > 9 ? "9+" : this.badge}</text>
|
||||
</g>
|
||||
`
|
||||
: ""}
|
||||
<g style="pointer-events: none" transform="translate(${-12} ${-12})">
|
||||
${this.iconPath ? svg`<path class="icon" d=${this.iconPath}/>` : ""}
|
||||
: nothing}
|
||||
<g style="pointer-events: none" transform="translate(-12 -12)">
|
||||
${this.iconPath
|
||||
? svg`<path class="icon" d=${this.iconPath}/>`
|
||||
: svg`<foreignObject><span class="icon"><slot name="icon"></slot></span></foreignObject>`}
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
@@ -141,17 +158,33 @@ export class HatGraphNode extends LitElement {
|
||||
fill: var(--background-clr);
|
||||
stroke: var(--circle-clr, var(--stroke-clr));
|
||||
}
|
||||
.error circle {
|
||||
fill: var(--error-color);
|
||||
stroke: none;
|
||||
stroke-width: 0;
|
||||
}
|
||||
.error .exclamation {
|
||||
fill: var(--text-primary-color);
|
||||
}
|
||||
.number circle {
|
||||
fill: var(--track-clr);
|
||||
stroke: none;
|
||||
stroke-width: 0;
|
||||
}
|
||||
.number text {
|
||||
font-size: smaller;
|
||||
font-size: 10px;
|
||||
fill: var(--text-primary-color);
|
||||
}
|
||||
path.icon {
|
||||
fill: var(--icon-clr);
|
||||
}
|
||||
foreignObject {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
.icon {
|
||||
color: var(--icon-clr);
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,11 +17,10 @@ import {
|
||||
mdiRoomService,
|
||||
mdiShuffleDisabled,
|
||||
} from "@mdi/js";
|
||||
import { LitElement, PropertyValues, css, html } from "lit";
|
||||
import { LitElement, PropertyValues, css, html, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { ensureArray } from "../../common/array/ensure-array";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { ACTION_ICONS } from "../../data/action";
|
||||
import { Condition, Trigger } from "../../data/automation";
|
||||
import {
|
||||
Action,
|
||||
@@ -41,11 +40,14 @@ import {
|
||||
IfActionTraceStep,
|
||||
TraceExtended,
|
||||
} from "../../data/trace";
|
||||
import { HomeAssistant } from "../../types";
|
||||
import "../ha-icon-button";
|
||||
import "../ha-service-icon";
|
||||
import "./hat-graph-branch";
|
||||
import { BRANCH_HEIGHT, NODE_SIZE, SPACING } from "./hat-graph-const";
|
||||
import "./hat-graph-node";
|
||||
import "./hat-graph-spacer";
|
||||
import { ACTION_ICONS } from "../../data/action";
|
||||
|
||||
export interface NodeInfo {
|
||||
path: string;
|
||||
@@ -64,6 +66,8 @@ export class HatScriptGraph extends LitElement {
|
||||
|
||||
@property({ attribute: false }) public selected?: string;
|
||||
|
||||
public hass!: HomeAssistant;
|
||||
|
||||
public renderedNodes: Record<string, NodeInfo> = {};
|
||||
|
||||
public trackedNodes: Record<string, NodeInfo> = {};
|
||||
@@ -89,6 +93,7 @@ export class HatScriptGraph extends LitElement {
|
||||
?active=${this.selected === path}
|
||||
.iconPath=${mdiAsterisk}
|
||||
.notEnabled=${config.enabled === false}
|
||||
.error=${this.trace.trace[path]?.some((tr) => tr.error)}
|
||||
tabindex=${track ? "0" : "-1"}
|
||||
></hat-graph-node>
|
||||
`;
|
||||
@@ -167,6 +172,7 @@ export class HatScriptGraph extends LitElement {
|
||||
?track=${trace !== undefined}
|
||||
?active=${this.selected === path}
|
||||
.notEnabled=${disabled || config.enabled === false}
|
||||
.error=${this.trace.trace[path]?.some((tr) => tr.error)}
|
||||
slot="head"
|
||||
nofocus
|
||||
></hat-graph-node>
|
||||
@@ -415,13 +421,22 @@ export class HatScriptGraph extends LitElement {
|
||||
return html`
|
||||
<hat-graph-node
|
||||
.graphStart=${graphStart}
|
||||
.iconPath=${mdiRoomService}
|
||||
.iconPath=${node.service ? undefined : mdiRoomService}
|
||||
@focus=${this.selectNode(node, path)}
|
||||
?track=${path in this.trace.trace}
|
||||
?active=${this.selected === path}
|
||||
.notEnabled=${disabled || node.enabled === false}
|
||||
.error=${this.trace.trace[path]?.some((tr) => tr.error)}
|
||||
tabindex=${this.trace && path in this.trace.trace ? "0" : "-1"}
|
||||
></hat-graph-node>
|
||||
>
|
||||
${node.service
|
||||
? html`<ha-service-icon
|
||||
slot="icon"
|
||||
.hass=${this.hass}
|
||||
.service=${node.service}
|
||||
></ha-service-icon>`
|
||||
: nothing}
|
||||
</hat-graph-node>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -439,6 +454,7 @@ export class HatScriptGraph extends LitElement {
|
||||
?track=${path in this.trace.trace}
|
||||
?active=${this.selected === path}
|
||||
.notEnabled=${disabled || node.enabled === false}
|
||||
.error=${this.trace.trace[path]?.some((tr) => tr.error)}
|
||||
tabindex=${this.trace && path in this.trace.trace ? "0" : "-1"}
|
||||
></hat-graph-node>
|
||||
`;
|
||||
@@ -505,6 +521,7 @@ export class HatScriptGraph extends LitElement {
|
||||
@focus=${this.selectNode(node, path)}
|
||||
?track=${path in this.trace.trace}
|
||||
?active=${this.selected === path}
|
||||
.error=${this.trace.trace[path]?.some((tr) => tr.error)}
|
||||
.notEnabled=${disabled || node.enabled === false}
|
||||
></hat-graph-node>
|
||||
`;
|
||||
@@ -667,8 +684,6 @@ export class HatScriptGraph extends LitElement {
|
||||
}
|
||||
.parent {
|
||||
margin-left: 8px;
|
||||
margin-inline-start: 8px;
|
||||
margin-inline-end: initial;
|
||||
margin-top: 16px;
|
||||
}
|
||||
.error {
|
||||
|
||||
@@ -153,7 +153,7 @@ class LogbookRenderer {
|
||||
|
||||
const parts: TemplateResult[] = [];
|
||||
|
||||
let i;
|
||||
let i: number;
|
||||
|
||||
for (
|
||||
i = 0;
|
||||
@@ -232,7 +232,7 @@ class ActionRenderer {
|
||||
const value = this._getItem(index);
|
||||
|
||||
if (renderAllIterations) {
|
||||
let i;
|
||||
let i: number = 0;
|
||||
value.forEach((item) => {
|
||||
i = this._renderIteration(index, item, actionType);
|
||||
});
|
||||
@@ -270,7 +270,12 @@ class ActionRenderer {
|
||||
} catch (err: any) {
|
||||
this._renderEntry(
|
||||
path,
|
||||
`Unable to extract path ${path}. Download trace and report as bug`
|
||||
this.hass.localize(
|
||||
"ui.panel.config.automation.trace.messages.path_error",
|
||||
{
|
||||
path: path,
|
||||
}
|
||||
)
|
||||
);
|
||||
return index + 1;
|
||||
}
|
||||
@@ -324,20 +329,22 @@ class ActionRenderer {
|
||||
private _handleTrigger(index: number, triggerStep: TriggerTraceStep): number {
|
||||
this._renderEntry(
|
||||
triggerStep.path,
|
||||
`${
|
||||
triggerStep.changed_variables.trigger.alias
|
||||
? `${triggerStep.changed_variables.trigger.alias} triggered`
|
||||
: "Triggered"
|
||||
} ${
|
||||
triggerStep.path === "trigger"
|
||||
? "manually"
|
||||
: `by the ${this.trace.trigger}`
|
||||
} at
|
||||
${formatDateTimeWithSeconds(
|
||||
new Date(triggerStep.timestamp),
|
||||
this.hass.locale,
|
||||
this.hass.config
|
||||
)}`,
|
||||
this.hass.localize(
|
||||
"ui.panel.config.automation.trace.messages.triggered_by",
|
||||
{
|
||||
triggeredBy: triggerStep.changed_variables.trigger?.alias
|
||||
? "alias"
|
||||
: "other",
|
||||
alias: triggerStep.changed_variables.trigger?.alias,
|
||||
triggeredPath: triggerStep.path === "trigger" ? "manual" : "trigger",
|
||||
trigger: this.trace.trigger,
|
||||
time: formatDateTimeWithSeconds(
|
||||
new Date(triggerStep.timestamp),
|
||||
this.hass.locale,
|
||||
this.hass.config
|
||||
),
|
||||
}
|
||||
),
|
||||
mdiCircle
|
||||
);
|
||||
return index + 1;
|
||||
@@ -367,12 +374,17 @@ class ActionRenderer {
|
||||
this.keys[index]
|
||||
) as ChooseAction;
|
||||
const disabled = chooseConfig.enabled === false;
|
||||
const name = chooseConfig.alias || "Choose";
|
||||
const name =
|
||||
chooseConfig.alias ||
|
||||
this.hass.localize("ui.panel.config.automation.trace.messages.choose");
|
||||
|
||||
if (defaultExecuted) {
|
||||
this._renderEntry(
|
||||
choosePath,
|
||||
`${name}: Default action executed`,
|
||||
this.hass.localize(
|
||||
"ui.panel.config.automation.trace.messages.default_action_executed",
|
||||
{ name: name }
|
||||
),
|
||||
undefined,
|
||||
disabled
|
||||
);
|
||||
@@ -385,8 +397,17 @@ class ActionRenderer {
|
||||
`${this.keys[index]}/choose/${chooseTrace.result.choice}`
|
||||
) as ChooseActionChoice | undefined;
|
||||
const choiceName = choiceConfig
|
||||
? `${choiceConfig.alias || `Option ${choiceNumeric}`} executed`
|
||||
: `Error: ${chooseTrace.error}`;
|
||||
? `${
|
||||
choiceConfig.alias ||
|
||||
this.hass.localize(
|
||||
"ui.panel.config.automation.trace.messages.option_executed",
|
||||
{ option: choiceNumeric }
|
||||
)
|
||||
}`
|
||||
: this.hass.localize(
|
||||
"ui.panel.config.automation.trace.messages.error",
|
||||
{ error: chooseTrace.error }
|
||||
);
|
||||
this._renderEntry(
|
||||
choosePath,
|
||||
`${name}: ${choiceName}`,
|
||||
@@ -396,13 +417,16 @@ class ActionRenderer {
|
||||
} else {
|
||||
this._renderEntry(
|
||||
choosePath,
|
||||
`${name}: No action taken`,
|
||||
this.hass.localize(
|
||||
"ui.panel.config.automation.trace.messages.no_action_executed",
|
||||
{ name: name }
|
||||
),
|
||||
undefined,
|
||||
disabled
|
||||
);
|
||||
}
|
||||
|
||||
let i;
|
||||
let i: number;
|
||||
|
||||
// Skip over conditions
|
||||
for (i = index + 1; i < this.keys.length; i++) {
|
||||
@@ -479,26 +503,38 @@ class ActionRenderer {
|
||||
const ifTrace = this._getItem(index)[0] as IfActionTraceStep;
|
||||
const ifConfig = this._getDataFromPath(this.keys[index]) as IfAction;
|
||||
const disabled = ifConfig.enabled === false;
|
||||
const name = ifConfig.alias || "If";
|
||||
const name =
|
||||
ifConfig.alias ||
|
||||
this.hass.localize("ui.panel.config.automation.trace.messages.if");
|
||||
|
||||
if (ifTrace.result?.choice) {
|
||||
const choiceConfig = this._getDataFromPath(
|
||||
`${this.keys[index]}/${ifTrace.result.choice}/`
|
||||
) as any;
|
||||
const choiceName = choiceConfig
|
||||
? `${choiceConfig.alias || `${ifTrace.result.choice} action executed`}`
|
||||
: `Error: ${ifTrace.error}`;
|
||||
? choiceConfig.alias ||
|
||||
this.hass.localize(
|
||||
"ui.panel.config.automation.trace.messages.action_executed",
|
||||
{ action: ifTrace.result.choice }
|
||||
)
|
||||
: this.hass.localize(
|
||||
"ui.panel.config.automation.trace.messages.error",
|
||||
{ error: ifTrace.error }
|
||||
);
|
||||
this._renderEntry(ifPath, `${name}: ${choiceName}`, undefined, disabled);
|
||||
} else {
|
||||
this._renderEntry(
|
||||
ifPath,
|
||||
`${name}: No action taken`,
|
||||
this.hass.localize(
|
||||
"ui.panel.config.automation.trace.messages.no_action_executed",
|
||||
{ name: name }
|
||||
),
|
||||
undefined,
|
||||
disabled
|
||||
);
|
||||
}
|
||||
|
||||
let i;
|
||||
let i: number;
|
||||
|
||||
// Skip over conditions
|
||||
for (i = index + 1; i < this.keys.length; i++) {
|
||||
@@ -534,7 +570,11 @@ class ActionRenderer {
|
||||
|
||||
const disabled = parallelConfig.enabled === false;
|
||||
|
||||
const name = parallelConfig.alias || "Execute in parallel";
|
||||
const name =
|
||||
parallelConfig.alias ||
|
||||
this.hass.localize(
|
||||
"ui.panel.config.automation.trace.messages.execute_in_parallel"
|
||||
);
|
||||
|
||||
this._renderEntry(parallelPath, name, undefined, disabled);
|
||||
|
||||
@@ -564,7 +604,11 @@ class ActionRenderer {
|
||||
this.entries.push(html`
|
||||
<ha-timeline .icon=${icon} data-path=${path} .notEnabled=${disabled}>
|
||||
${description}${disabled
|
||||
? html`<span class="disabled"> (disabled)</span>`
|
||||
? html`<span class="disabled">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.trace.messages.disabled"
|
||||
)}</span
|
||||
>`
|
||||
: ""}
|
||||
</ha-timeline>
|
||||
`);
|
||||
@@ -636,13 +680,12 @@ export class HaAutomationTracer extends LitElement {
|
||||
this.hass.locale,
|
||||
this.hass.config
|
||||
);
|
||||
const renderRuntime = () => `(runtime:
|
||||
${(
|
||||
const renderRuntime = () =>
|
||||
(
|
||||
(new Date(this.trace!.timestamp.finish!).getTime() -
|
||||
new Date(this.trace!.timestamp.start).getTime()) /
|
||||
1000
|
||||
).toFixed(2)}
|
||||
seconds)`;
|
||||
).toFixed(2);
|
||||
|
||||
let entry: {
|
||||
description: TemplateResult | string;
|
||||
@@ -652,57 +695,90 @@ export class HaAutomationTracer extends LitElement {
|
||||
|
||||
if (this.trace.state === "running") {
|
||||
entry = {
|
||||
description: "Still running",
|
||||
description: this.hass.localize(
|
||||
"ui.panel.config.automation.trace.messages.still_running"
|
||||
),
|
||||
icon: mdiProgressClock,
|
||||
};
|
||||
} else if (this.trace.state === "debugged") {
|
||||
entry = {
|
||||
description: "Debugged",
|
||||
description: this.hass.localize(
|
||||
"ui.panel.config.automation.trace.messages.debugged"
|
||||
),
|
||||
icon: mdiProgressWrench,
|
||||
};
|
||||
} else if (this.trace.script_execution === "finished") {
|
||||
entry = {
|
||||
description: `Finished at ${renderFinishedAt()} ${renderRuntime()}`,
|
||||
description: this.hass.localize(
|
||||
"ui.panel.config.automation.trace.messages.finished",
|
||||
{
|
||||
time: renderFinishedAt(),
|
||||
executiontime: renderRuntime(),
|
||||
}
|
||||
),
|
||||
icon: mdiCircle,
|
||||
};
|
||||
} else if (this.trace.script_execution === "aborted") {
|
||||
entry = {
|
||||
description: `Aborted at ${renderFinishedAt()} ${renderRuntime()}`,
|
||||
description: this.hass.localize(
|
||||
"ui.panel.config.automation.trace.messages.aborted",
|
||||
{
|
||||
time: renderFinishedAt(),
|
||||
executiontime: renderRuntime(),
|
||||
}
|
||||
),
|
||||
icon: mdiAlertCircle,
|
||||
};
|
||||
} else if (this.trace.script_execution === "cancelled") {
|
||||
entry = {
|
||||
description: `Cancelled at ${renderFinishedAt()} ${renderRuntime()}`,
|
||||
description: this.hass.localize(
|
||||
"ui.panel.config.automation.trace.messages.cancelled",
|
||||
{
|
||||
time: renderFinishedAt(),
|
||||
executiontime: renderRuntime(),
|
||||
}
|
||||
),
|
||||
icon: mdiAlertCircle,
|
||||
};
|
||||
} else {
|
||||
let reason: string;
|
||||
let message:
|
||||
| "stopped_failed_conditions"
|
||||
| "stopped_failed_single"
|
||||
| "stopped_failed_max_runs"
|
||||
| "stopped_error"
|
||||
| "stopped_unknown_reason";
|
||||
let isError = false;
|
||||
let extra: TemplateResult | undefined;
|
||||
|
||||
switch (this.trace.script_execution) {
|
||||
case "failed_conditions":
|
||||
reason = "a condition failed";
|
||||
message = "stopped_failed_conditions";
|
||||
break;
|
||||
case "failed_single":
|
||||
reason = "only a single execution is allowed";
|
||||
message = "stopped_failed_single";
|
||||
break;
|
||||
case "failed_max_runs":
|
||||
reason = "maximum number of parallel runs reached";
|
||||
message = "stopped_failed_max_runs";
|
||||
break;
|
||||
case "error":
|
||||
reason = "an error was encountered";
|
||||
isError = true;
|
||||
message = "stopped_error";
|
||||
extra = html`<br /><br />${this.trace.error!}`;
|
||||
break;
|
||||
default:
|
||||
reason = `of unknown reason "${this.trace.script_execution}"`;
|
||||
isError = true;
|
||||
message = "stopped_unknown_reason";
|
||||
}
|
||||
|
||||
entry = {
|
||||
description: html`Stopped because ${reason} at ${renderFinishedAt()}
|
||||
${renderRuntime()}${extra || ""}`,
|
||||
description: html`${this.hass.localize(
|
||||
`ui.panel.config.automation.trace.messages.${message}`,
|
||||
{
|
||||
time: renderFinishedAt(),
|
||||
executiontime: renderRuntime(),
|
||||
}
|
||||
)}
|
||||
${extra || ""}`,
|
||||
icon: mdiAlertCircle,
|
||||
className: isError ? "error" : undefined,
|
||||
};
|
||||
|
||||
@@ -18,6 +18,7 @@ export interface CloudPreferences {
|
||||
google_enabled: boolean;
|
||||
alexa_enabled: boolean;
|
||||
remote_enabled: boolean;
|
||||
remote_allow_remote_enable: boolean;
|
||||
google_secure_devices_pin: string | undefined;
|
||||
cloudhooks: { [webhookId: string]: CloudWebhook };
|
||||
alexa_report_state: boolean;
|
||||
@@ -139,6 +140,7 @@ export const updateCloudPref = (
|
||||
google_report_state?: CloudPreferences["google_report_state"];
|
||||
google_secure_devices_pin?: CloudPreferences["google_secure_devices_pin"];
|
||||
tts_default_voice?: CloudPreferences["tts_default_voice"];
|
||||
remote_allow_remote_enable?: CloudPreferences["remote_allow_remote_enable"];
|
||||
}
|
||||
) =>
|
||||
hass.callWS({
|
||||
|
||||
@@ -33,6 +33,7 @@ export interface DataEntryFlowStepForm {
|
||||
description_placeholders?: Record<string, string>;
|
||||
last_step: boolean | null;
|
||||
preview?: string;
|
||||
translation_domain?: string;
|
||||
}
|
||||
|
||||
export interface DataEntryFlowStepExternal {
|
||||
@@ -42,6 +43,7 @@ export interface DataEntryFlowStepExternal {
|
||||
step_id: string;
|
||||
url: string;
|
||||
description_placeholders: Record<string, string>;
|
||||
translation_domain?: string;
|
||||
}
|
||||
|
||||
export interface DataEntryFlowStepCreateEntry {
|
||||
@@ -53,6 +55,7 @@ export interface DataEntryFlowStepCreateEntry {
|
||||
result?: ConfigEntry;
|
||||
description: string;
|
||||
description_placeholders?: Record<string, string>;
|
||||
translation_domain?: string;
|
||||
}
|
||||
|
||||
export interface DataEntryFlowStepAbort {
|
||||
@@ -61,6 +64,7 @@ export interface DataEntryFlowStepAbort {
|
||||
handler: string;
|
||||
reason: string;
|
||||
description_placeholders?: Record<string, string>;
|
||||
translation_domain?: string;
|
||||
}
|
||||
|
||||
export interface DataEntryFlowStepProgress {
|
||||
@@ -70,6 +74,7 @@ export interface DataEntryFlowStepProgress {
|
||||
step_id: string;
|
||||
progress_action: string;
|
||||
description_placeholders?: Record<string, string>;
|
||||
translation_domain?: string;
|
||||
}
|
||||
|
||||
export interface DataEntryFlowStepMenu {
|
||||
@@ -80,6 +85,7 @@ export interface DataEntryFlowStepMenu {
|
||||
/** If array, use value to lookup translations in strings.json */
|
||||
menu_options: string[] | Record<string, string>;
|
||||
description_placeholders?: Record<string, string>;
|
||||
translation_domain?: string;
|
||||
}
|
||||
|
||||
export type DataEntryFlowStep =
|
||||
|
||||
+5
-1
@@ -331,6 +331,9 @@ export const getReferencedStatisticIds = (
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!(includeTypes && !includeTypes.includes("device"))) {
|
||||
statIDs.push(...prefs.device_consumption.map((d) => d.stat_consumption));
|
||||
}
|
||||
|
||||
return statIDs;
|
||||
};
|
||||
@@ -383,6 +386,7 @@ const getEnergyData = async (
|
||||
"solar",
|
||||
"battery",
|
||||
"gas",
|
||||
"device",
|
||||
]);
|
||||
const waterStatIds = getReferencedStatisticIds(prefs, info, ["water"]);
|
||||
|
||||
@@ -777,7 +781,7 @@ export const getEnergyGasUnit = (
|
||||
: "ft³";
|
||||
};
|
||||
|
||||
export const getEnergyWaterUnit = (hass: HomeAssistant): string | undefined =>
|
||||
export const getEnergyWaterUnit = (hass: HomeAssistant): string =>
|
||||
hass.config.unit_system.length === "km" ? "L" : "gal";
|
||||
|
||||
export const energyStatisticHelpUrl =
|
||||
|
||||
+24
-4
@@ -81,7 +81,7 @@ export interface EntityHistoryState {
|
||||
/** attributes */
|
||||
a: { [key: string]: any };
|
||||
/** last_changed; if set, also applies to lu */
|
||||
lc: number;
|
||||
lc?: number;
|
||||
/** last_updated */
|
||||
lu: number;
|
||||
}
|
||||
@@ -419,17 +419,37 @@ const BLANK_UNIT = " ";
|
||||
export const computeHistory = (
|
||||
hass: HomeAssistant,
|
||||
stateHistory: HistoryStates,
|
||||
entityIds: string[],
|
||||
localize: LocalizeFunc,
|
||||
sensorNumericalDeviceClasses: string[],
|
||||
splitDeviceClasses = false
|
||||
): HistoryResult => {
|
||||
const lineChartDevices: { [unit: string]: HistoryStates } = {};
|
||||
const timelineDevices: TimelineEntity[] = [];
|
||||
if (!stateHistory) {
|
||||
|
||||
const localStateHistory: HistoryStates = {};
|
||||
|
||||
// Create a limited history from stateObj if entity has no recorded history.
|
||||
const allEntities = new Set([...entityIds, ...Object.keys(stateHistory)]);
|
||||
allEntities.forEach((entity) => {
|
||||
if (entity in stateHistory) {
|
||||
localStateHistory[entity] = stateHistory[entity];
|
||||
} else if (hass.states[entity]) {
|
||||
localStateHistory[entity] = [
|
||||
{
|
||||
s: hass.states[entity].state,
|
||||
a: hass.states[entity].attributes,
|
||||
lu: new Date(hass.states[entity].last_updated).getTime() / 1000,
|
||||
},
|
||||
];
|
||||
}
|
||||
});
|
||||
|
||||
if (!localStateHistory) {
|
||||
return { line: [], timeline: [] };
|
||||
}
|
||||
Object.keys(stateHistory).forEach((entityId) => {
|
||||
const stateInfo = stateHistory[entityId];
|
||||
Object.keys(localStateHistory).forEach((entityId) => {
|
||||
const stateInfo = localStateHistory[entityId];
|
||||
if (stateInfo.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
+21
-5
@@ -9,6 +9,7 @@ import {
|
||||
EntityRegistryEntry,
|
||||
} from "./entity_registry";
|
||||
import { isComponentLoaded } from "../common/config/is_component_loaded";
|
||||
import { atLeastVersion } from "../common/config/version";
|
||||
|
||||
const resources: {
|
||||
entity: Record<string, Promise<PlatformIcons>>;
|
||||
@@ -46,10 +47,10 @@ interface PlatformIcons {
|
||||
};
|
||||
}
|
||||
|
||||
interface ComponentIcons {
|
||||
export interface ComponentIcons {
|
||||
[device_class: string]: {
|
||||
state: Record<string, string>;
|
||||
state_attributes: Record<
|
||||
state?: Record<string, string>;
|
||||
state_attributes?: Record<
|
||||
string,
|
||||
{
|
||||
state: Record<string, string>;
|
||||
@@ -91,7 +92,10 @@ export const getPlatformIcons = async (
|
||||
if (!force && integration in resources.entity) {
|
||||
return resources.entity[integration];
|
||||
}
|
||||
if (!isComponentLoaded(hass, integration)) {
|
||||
if (
|
||||
!isComponentLoaded(hass, integration) ||
|
||||
!atLeastVersion(hass.connection.haVersion, 2024, 2)
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
const result = getHassIcons(hass, "entity", integration).then(
|
||||
@@ -106,6 +110,16 @@ export const getComponentIcons = async (
|
||||
domain: string,
|
||||
force = false
|
||||
): Promise<ComponentIcons | undefined> => {
|
||||
// For Cast, old instances can connect to it.
|
||||
if (
|
||||
__BACKWARDS_COMPAT__ &&
|
||||
!atLeastVersion(hass.connection.haVersion, 2024, 2)
|
||||
) {
|
||||
return import("../fake_data/entity_component_icons")
|
||||
.then((mod) => mod.ENTITY_COMPONENT_ICONS)
|
||||
.then((res) => res[domain]);
|
||||
}
|
||||
|
||||
if (
|
||||
!force &&
|
||||
resources.entity_component.resources &&
|
||||
@@ -113,6 +127,7 @@ export const getComponentIcons = async (
|
||||
) {
|
||||
return resources.entity_component.resources.then((res) => res[domain]);
|
||||
}
|
||||
|
||||
if (!isComponentLoaded(hass, domain)) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -183,8 +198,9 @@ export const entryIcon = async (
|
||||
if (entry.icon) {
|
||||
return entry.icon;
|
||||
}
|
||||
const stateObj = hass.states[entry.entity_id] as HassEntity | undefined;
|
||||
const domain = computeDomain(entry.entity_id);
|
||||
return getEntityIcon(hass, domain, undefined, undefined, entry);
|
||||
return getEntityIcon(hass, domain, stateObj, undefined, entry);
|
||||
};
|
||||
|
||||
const getEntityIcon = async (
|
||||
|
||||
@@ -10,8 +10,10 @@ import {
|
||||
LovelaceCard,
|
||||
} from "../panels/lovelace/types";
|
||||
import { HomeAssistant } from "../types";
|
||||
import { LovelaceSectionConfig } from "./lovelace/config/section";
|
||||
import { fetchConfig, LegacyLovelaceConfig } from "./lovelace/config/types";
|
||||
import { LovelaceViewConfig } from "./lovelace/config/view";
|
||||
import { HuiSection } from "../panels/lovelace/sections/hui-section";
|
||||
|
||||
export interface LovelacePanelConfig {
|
||||
mode: "yaml" | "storage";
|
||||
@@ -24,10 +26,21 @@ export interface LovelaceViewElement extends HTMLElement {
|
||||
index?: number;
|
||||
cards?: Array<LovelaceCard | HuiErrorCard>;
|
||||
badges?: LovelaceBadge[];
|
||||
sections?: HuiSection[];
|
||||
isStrategy: boolean;
|
||||
setConfig(config: LovelaceViewConfig): void;
|
||||
}
|
||||
|
||||
export interface LovelaceSectionElement extends HTMLElement {
|
||||
hass?: HomeAssistant;
|
||||
lovelace?: Lovelace;
|
||||
viewIndex?: number;
|
||||
index?: number;
|
||||
cards?: Array<LovelaceCard | HuiErrorCard>;
|
||||
isStrategy: boolean;
|
||||
setConfig(config: LovelaceSectionConfig): void;
|
||||
}
|
||||
|
||||
type LovelaceUpdatedEvent = HassEventBase & {
|
||||
event_type: "lovelace_updated";
|
||||
data: {
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import type { LovelaceCardConfig } from "./card";
|
||||
import type { LovelaceStrategyConfig } from "./strategy";
|
||||
|
||||
export interface LovelaceBaseSectionConfig {
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export interface LovelaceSectionConfig extends LovelaceBaseSectionConfig {
|
||||
type?: string;
|
||||
cards?: LovelaceCardConfig[];
|
||||
}
|
||||
|
||||
export interface LovelaceStrategySectionConfig
|
||||
extends LovelaceBaseSectionConfig {
|
||||
strategy: LovelaceStrategyConfig;
|
||||
}
|
||||
|
||||
export type LovelaceSectionRawConfig =
|
||||
| LovelaceSectionConfig
|
||||
| LovelaceStrategySectionConfig;
|
||||
|
||||
export function isStrategySection(
|
||||
section: LovelaceSectionRawConfig
|
||||
): section is LovelaceStrategySectionConfig {
|
||||
return "strategy" in section;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { LovelaceBadgeConfig } from "./badge";
|
||||
import type { LovelaceCardConfig } from "./card";
|
||||
import type { LovelaceSectionRawConfig } from "./section";
|
||||
import type { LovelaceStrategyConfig } from "./strategy";
|
||||
|
||||
export interface ShowViewConfig {
|
||||
@@ -23,6 +24,7 @@ export interface LovelaceViewConfig extends LovelaceBaseViewConfig {
|
||||
type?: string;
|
||||
badges?: Array<string | LovelaceBadgeConfig>;
|
||||
cards?: LovelaceCardConfig[];
|
||||
sections?: LovelaceSectionRawConfig[];
|
||||
}
|
||||
|
||||
export interface LovelaceStrategyViewConfig extends LovelaceBaseViewConfig {
|
||||
|
||||
@@ -35,6 +35,7 @@ export interface MatterNodeDiagnostics {
|
||||
mac_address?: string;
|
||||
available: boolean;
|
||||
active_fabrics: MatterFabricData[];
|
||||
active_fabric_index: number;
|
||||
}
|
||||
|
||||
export interface MatterPingResult {
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
import { Connection, createCollection } from "home-assistant-js-websocket";
|
||||
import { Store } from "home-assistant-js-websocket/dist/store";
|
||||
import { stringCompare } from "../common/string/compare";
|
||||
import { debounce } from "../common/util/debounce";
|
||||
import { AreaRegistryEntry } from "./area_registry";
|
||||
|
||||
const fetchAreaRegistry = (conn: Connection) =>
|
||||
conn.sendMessagePromise<AreaRegistryEntry[]>({
|
||||
type: "config/area_registry/list",
|
||||
});
|
||||
conn
|
||||
.sendMessagePromise<AreaRegistryEntry[]>({
|
||||
type: "config/area_registry/list",
|
||||
})
|
||||
.then((areas) =>
|
||||
areas.sort((ent1, ent2) => stringCompare(ent1.name, ent2.name))
|
||||
);
|
||||
|
||||
const subscribeAreaRegistryUpdates = (
|
||||
conn: Connection,
|
||||
|
||||
@@ -44,7 +44,7 @@ export const showConfigFlowDialog = (
|
||||
|
||||
renderAbortDescription(hass, step) {
|
||||
const description = hass.localize(
|
||||
`component.${step.handler}.config.abort.${step.reason}`,
|
||||
`component.${step.translation_domain || step.handler}.config.abort.${step.reason}`,
|
||||
step.description_placeholders
|
||||
);
|
||||
|
||||
@@ -58,7 +58,7 @@ export const showConfigFlowDialog = (
|
||||
renderShowFormStepHeader(hass, step) {
|
||||
return (
|
||||
hass.localize(
|
||||
`component.${step.handler}.config.step.${step.step_id}.title`,
|
||||
`component.${step.translation_domain || step.handler}.config.step.${step.step_id}.title`,
|
||||
step.description_placeholders
|
||||
) || hass.localize(`component.${step.handler}.title`)
|
||||
);
|
||||
@@ -66,7 +66,7 @@ export const showConfigFlowDialog = (
|
||||
|
||||
renderShowFormStepDescription(hass, step) {
|
||||
const description = hass.localize(
|
||||
`component.${step.handler}.config.step.${step.step_id}.description`,
|
||||
`component.${step.translation_domain || step.handler}.config.step.${step.step_id}.description`,
|
||||
step.description_placeholders
|
||||
);
|
||||
return description
|
||||
@@ -84,7 +84,7 @@ export const showConfigFlowDialog = (
|
||||
|
||||
renderShowFormStepFieldHelper(hass, step, field) {
|
||||
const description = hass.localize(
|
||||
`component.${step.handler}.config.step.${step.step_id}.data_description.${field.name}`,
|
||||
`component.${step.translation_domain || step.handler}.config.step.${step.step_id}.data_description.${field.name}`,
|
||||
step.description_placeholders
|
||||
);
|
||||
return description
|
||||
@@ -95,7 +95,7 @@ export const showConfigFlowDialog = (
|
||||
renderShowFormStepFieldError(hass, step, error) {
|
||||
return (
|
||||
hass.localize(
|
||||
`component.${step.handler}.config.error.${error}`,
|
||||
`component.${step.translation_domain || step.translation_domain || step.handler}.config.error.${error}`,
|
||||
step.description_placeholders
|
||||
) || error
|
||||
);
|
||||
@@ -131,7 +131,7 @@ export const showConfigFlowDialog = (
|
||||
|
||||
renderExternalStepDescription(hass, step) {
|
||||
const description = hass.localize(
|
||||
`component.${step.handler}.config.${step.step_id}.description`,
|
||||
`component.${step.translation_domain || step.handler}.config.${step.step_id}.description`,
|
||||
step.description_placeholders
|
||||
);
|
||||
|
||||
@@ -155,7 +155,7 @@ export const showConfigFlowDialog = (
|
||||
|
||||
renderCreateEntryDescription(hass, step) {
|
||||
const description = hass.localize(
|
||||
`component.${step.handler}.config.create_entry.${
|
||||
`component.${step.translation_domain || step.handler}.config.create_entry.${
|
||||
step.description || "default"
|
||||
}`,
|
||||
step.description_placeholders
|
||||
@@ -190,7 +190,7 @@ export const showConfigFlowDialog = (
|
||||
|
||||
renderShowFormProgressDescription(hass, step) {
|
||||
const description = hass.localize(
|
||||
`component.${step.handler}.config.progress.${step.progress_action}`,
|
||||
`component.${step.translation_domain || step.handler}.config.progress.${step.progress_action}`,
|
||||
step.description_placeholders
|
||||
);
|
||||
return description
|
||||
@@ -210,7 +210,7 @@ export const showConfigFlowDialog = (
|
||||
|
||||
renderMenuDescription(hass, step) {
|
||||
const description = hass.localize(
|
||||
`component.${step.handler}.config.step.${step.step_id}.description`,
|
||||
`component.${step.translation_domain || step.handler}.config.step.${step.step_id}.description`,
|
||||
step.description_placeholders
|
||||
);
|
||||
return description
|
||||
@@ -222,7 +222,7 @@ export const showConfigFlowDialog = (
|
||||
|
||||
renderMenuOption(hass, step, option) {
|
||||
return hass.localize(
|
||||
`component.${step.handler}.config.step.${step.step_id}.menu_options.${option}`,
|
||||
`component.${step.translation_domain || step.handler}.config.step.${step.step_id}.menu_options.${option}`,
|
||||
step.description_placeholders
|
||||
);
|
||||
},
|
||||
|
||||
@@ -53,7 +53,7 @@ export const showOptionsFlowDialog = (
|
||||
|
||||
renderAbortDescription(hass, step) {
|
||||
const description = hass.localize(
|
||||
`component.${configEntry.domain}.options.abort.${step.reason}`,
|
||||
`component.${step.translation_domain || configEntry.domain}.options.abort.${step.reason}`,
|
||||
step.description_placeholders
|
||||
);
|
||||
|
||||
@@ -71,7 +71,7 @@ export const showOptionsFlowDialog = (
|
||||
renderShowFormStepHeader(hass, step) {
|
||||
return (
|
||||
hass.localize(
|
||||
`component.${configEntry.domain}.options.step.${step.step_id}.title`,
|
||||
`component.${step.translation_domain || configEntry.domain}.options.step.${step.step_id}.title`,
|
||||
step.description_placeholders
|
||||
) || hass.localize(`ui.dialogs.options_flow.form.header`)
|
||||
);
|
||||
@@ -79,7 +79,7 @@ export const showOptionsFlowDialog = (
|
||||
|
||||
renderShowFormStepDescription(hass, step) {
|
||||
const description = hass.localize(
|
||||
`component.${configEntry.domain}.options.step.${step.step_id}.description`,
|
||||
`component.${step.translation_domain || configEntry.domain}.options.step.${step.step_id}.description`,
|
||||
step.description_placeholders
|
||||
);
|
||||
return description
|
||||
@@ -101,7 +101,7 @@ export const showOptionsFlowDialog = (
|
||||
|
||||
renderShowFormStepFieldHelper(hass, step, field) {
|
||||
const description = hass.localize(
|
||||
`component.${configEntry.domain}.options.step.${step.step_id}.data_description.${field.name}`,
|
||||
`component.${step.translation_domain || configEntry.domain}.options.step.${step.step_id}.data_description.${field.name}`,
|
||||
step.description_placeholders
|
||||
);
|
||||
return description
|
||||
@@ -112,7 +112,7 @@ export const showOptionsFlowDialog = (
|
||||
renderShowFormStepFieldError(hass, step, error) {
|
||||
return (
|
||||
hass.localize(
|
||||
`component.${configEntry.domain}.options.error.${error}`,
|
||||
`component.${step.translation_domain || configEntry.domain}.options.error.${error}`,
|
||||
step.description_placeholders
|
||||
) || error
|
||||
);
|
||||
@@ -159,7 +159,7 @@ export const showOptionsFlowDialog = (
|
||||
|
||||
renderShowFormProgressDescription(hass, step) {
|
||||
const description = hass.localize(
|
||||
`component.${configEntry.domain}.options.progress.${step.progress_action}`,
|
||||
`component.${step.translation_domain || configEntry.domain}.options.progress.${step.progress_action}`,
|
||||
step.description_placeholders
|
||||
);
|
||||
return description
|
||||
@@ -183,7 +183,7 @@ export const showOptionsFlowDialog = (
|
||||
|
||||
renderMenuDescription(hass, step) {
|
||||
const description = hass.localize(
|
||||
`component.${configEntry.domain}.options.step.${step.step_id}.description`,
|
||||
`component.${step.translation_domain || configEntry.domain}.options.step.${step.step_id}.description`,
|
||||
step.description_placeholders
|
||||
);
|
||||
return description
|
||||
@@ -199,7 +199,7 @@ export const showOptionsFlowDialog = (
|
||||
|
||||
renderMenuOption(hass, step, option) {
|
||||
return hass.localize(
|
||||
`component.${configEntry.domain}.options.step.${step.step_id}.menu_options.${option}`,
|
||||
`component.${step.translation_domain || configEntry.domain}.options.step.${step.step_id}.menu_options.${option}`,
|
||||
step.description_placeholders
|
||||
);
|
||||
},
|
||||
|
||||
@@ -89,7 +89,12 @@ class DialogBox extends LitElement {
|
||||
</div>
|
||||
${confirmPrompt &&
|
||||
html`
|
||||
<mwc-button @click=${this._dismiss} slot="secondaryAction">
|
||||
<mwc-button
|
||||
@click=${this._dismiss}
|
||||
slot="secondaryAction"
|
||||
?dialogInitialFocus=${!this._params.prompt &&
|
||||
this._params.destructive}
|
||||
>
|
||||
${this._params.dismissText
|
||||
? this._params.dismissText
|
||||
: this.hass.localize("ui.dialogs.generic.cancel")}
|
||||
@@ -97,7 +102,8 @@ class DialogBox extends LitElement {
|
||||
`}
|
||||
<mwc-button
|
||||
@click=${this._confirm}
|
||||
?dialogInitialFocus=${!this._params.prompt}
|
||||
?dialogInitialFocus=${!this._params.prompt &&
|
||||
!this._params.destructive}
|
||||
slot="primaryAction"
|
||||
class=${classMap({
|
||||
destructive: this._params.destructive || false,
|
||||
|
||||
@@ -16,6 +16,8 @@ export class HaMoreInfoStateHeader extends LitElement {
|
||||
|
||||
@property({ attribute: false }) public stateOverride?: string;
|
||||
|
||||
@property({ attribute: false }) public changedOverride?: number;
|
||||
|
||||
@state() private _absoluteTime = false;
|
||||
|
||||
private _localizeState(): TemplateResult | string {
|
||||
@@ -50,13 +52,13 @@ export class HaMoreInfoStateHeader extends LitElement {
|
||||
? html`
|
||||
<ha-absolute-time
|
||||
.hass=${this.hass}
|
||||
.datetime=${this.stateObj.last_changed}
|
||||
.datetime=${this.changedOverride ?? this.stateObj.last_changed}
|
||||
></ha-absolute-time>
|
||||
`
|
||||
: html`
|
||||
<ha-relative-time
|
||||
.hass=${this.hass}
|
||||
.datetime=${this.stateObj.last_changed}
|
||||
.datetime=${this.changedOverride ?? this.stateObj.last_changed}
|
||||
capitalize
|
||||
></ha-relative-time>
|
||||
`}
|
||||
|
||||
@@ -322,6 +322,8 @@ export class HaMoreInfoLightFavoriteColors extends LitElement {
|
||||
position: absolute;
|
||||
top: -6px;
|
||||
right: -6px;
|
||||
inset-inline-end: -6px;
|
||||
inset-inline-start: initial;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
outline: none;
|
||||
|
||||
@@ -446,6 +446,8 @@ class LightRgbColorPicker extends LitElement {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
inset-inline-end: 0;
|
||||
inset-inline-start: initial;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user