mirror of
https://github.com/home-assistant/frontend.git
synced 2025-10-02 16:29:45 +00:00
Compare commits
2 Commits
20230904.0
...
dashboard-
Author | SHA1 | Date | |
---|---|---|---|
![]() |
f3f2059634 | ||
![]() |
497484d419 |
4
.github/workflows/cast_deployment.yaml
vendored
4
.github/workflows/cast_deployment.yaml
vendored
@@ -21,7 +21,7 @@ jobs:
|
|||||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v3.6.0
|
uses: actions/checkout@v3.5.3
|
||||||
with:
|
with:
|
||||||
ref: dev
|
ref: dev
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ jobs:
|
|||||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v3.6.0
|
uses: actions/checkout@v3.5.3
|
||||||
with:
|
with:
|
||||||
ref: master
|
ref: master
|
||||||
|
|
||||||
|
8
.github/workflows/ci.yaml
vendored
8
.github/workflows/ci.yaml
vendored
@@ -24,7 +24,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v3.6.0
|
uses: actions/checkout@v3.5.3
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v3.8.1
|
uses: actions/setup-node@v3.8.1
|
||||||
with:
|
with:
|
||||||
@@ -55,7 +55,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v3.6.0
|
uses: actions/checkout@v3.5.3
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v3.8.1
|
uses: actions/setup-node@v3.8.1
|
||||||
with:
|
with:
|
||||||
@@ -73,7 +73,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v3.6.0
|
uses: actions/checkout@v3.5.3
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v3.8.1
|
uses: actions/setup-node@v3.8.1
|
||||||
with:
|
with:
|
||||||
@@ -91,7 +91,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v3.6.0
|
uses: actions/checkout@v3.5.3
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v3.8.1
|
uses: actions/setup-node@v3.8.1
|
||||||
with:
|
with:
|
||||||
|
2
.github/workflows/codeql-analysis.yml
vendored
2
.github/workflows/codeql-analysis.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3.6.0
|
uses: actions/checkout@v3.5.3
|
||||||
with:
|
with:
|
||||||
# We must fetch at least the immediate parents so that if this is
|
# We must fetch at least the immediate parents so that if this is
|
||||||
# a pull request then we can checkout the head.
|
# a pull request then we can checkout the head.
|
||||||
|
4
.github/workflows/demo_deployment.yaml
vendored
4
.github/workflows/demo_deployment.yaml
vendored
@@ -22,7 +22,7 @@ jobs:
|
|||||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v3.6.0
|
uses: actions/checkout@v3.5.3
|
||||||
with:
|
with:
|
||||||
ref: dev
|
ref: dev
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ jobs:
|
|||||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v3.6.0
|
uses: actions/checkout@v3.5.3
|
||||||
with:
|
with:
|
||||||
ref: master
|
ref: master
|
||||||
|
|
||||||
|
2
.github/workflows/design_deployment.yaml
vendored
2
.github/workflows/design_deployment.yaml
vendored
@@ -16,7 +16,7 @@ jobs:
|
|||||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v3.6.0
|
uses: actions/checkout@v3.5.3
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v3.8.1
|
uses: actions/setup-node@v3.8.1
|
||||||
|
2
.github/workflows/design_preview.yaml
vendored
2
.github/workflows/design_preview.yaml
vendored
@@ -21,7 +21,7 @@ jobs:
|
|||||||
if: github.repository == 'home-assistant/frontend' && contains(github.event.pull_request.labels.*.name, 'needs design preview')
|
if: github.repository == 'home-assistant/frontend' && contains(github.event.pull_request.labels.*.name, 'needs design preview')
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v3.6.0
|
uses: actions/checkout@v3.5.3
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v3.8.1
|
uses: actions/setup-node@v3.8.1
|
||||||
|
2
.github/workflows/nightly.yaml
vendored
2
.github/workflows/nightly.yaml
vendored
@@ -20,7 +20,7 @@ jobs:
|
|||||||
contents: write
|
contents: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repository
|
- name: Checkout the repository
|
||||||
uses: actions/checkout@v3.6.0
|
uses: actions/checkout@v3.5.3
|
||||||
|
|
||||||
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v4
|
||||||
|
2
.github/workflows/release.yaml
vendored
2
.github/workflows/release.yaml
vendored
@@ -23,7 +23,7 @@ jobs:
|
|||||||
contents: write # Required to upload release assets
|
contents: write # Required to upload release assets
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repository
|
- name: Checkout the repository
|
||||||
uses: actions/checkout@v3.6.0
|
uses: actions/checkout@v3.5.3
|
||||||
|
|
||||||
- name: Verify version
|
- name: Verify version
|
||||||
uses: home-assistant/actions/helpers/verify-version@master
|
uses: home-assistant/actions/helpers/verify-version@master
|
||||||
|
2
.github/workflows/translations.yaml
vendored
2
.github/workflows/translations.yaml
vendored
@@ -13,7 +13,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repository
|
- name: Checkout the repository
|
||||||
uses: actions/checkout@v3.6.0
|
uses: actions/checkout@v3.5.3
|
||||||
|
|
||||||
- name: Upload Translations
|
- name: Upload Translations
|
||||||
run: |
|
run: |
|
||||||
|
File diff suppressed because one or more lines are too long
@@ -8,4 +8,4 @@ plugins:
|
|||||||
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
|
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
|
||||||
spec: "@yarnpkg/plugin-interactive-tools"
|
spec: "@yarnpkg/plugin-interactive-tools"
|
||||||
|
|
||||||
yarnPath: .yarn/releases/yarn-3.6.3.cjs
|
yarnPath: .yarn/releases/yarn-3.6.2.cjs
|
||||||
|
@@ -1,3 +0,0 @@
|
|||||||
---
|
|
||||||
title: Control Number Buttons
|
|
||||||
---
|
|
@@ -1,100 +0,0 @@
|
|||||||
import { LitElement, TemplateResult, css, html } from "lit";
|
|
||||||
import { customElement, state } from "lit/decorators";
|
|
||||||
import "../../../../src/components/ha-card";
|
|
||||||
import "../../../../src/components/ha-control-number-buttons";
|
|
||||||
import { repeat } from "lit/directives/repeat";
|
|
||||||
import { ifDefined } from "lit/directives/if-defined";
|
|
||||||
|
|
||||||
const buttons: {
|
|
||||||
id: string;
|
|
||||||
label: string;
|
|
||||||
min?: number;
|
|
||||||
max?: number;
|
|
||||||
step?: number;
|
|
||||||
class?: string;
|
|
||||||
}[] = [
|
|
||||||
{
|
|
||||||
id: "basic",
|
|
||||||
label: "Basic",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "min_max_step",
|
|
||||||
label: "With min/max and step",
|
|
||||||
min: 5,
|
|
||||||
max: 25,
|
|
||||||
step: 0.5,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "custom",
|
|
||||||
label: "Custom",
|
|
||||||
class: "custom",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
@customElement("demo-components-ha-control-number-buttons")
|
|
||||||
export class DemoHarControlNumberButtons extends LitElement {
|
|
||||||
@state() value = 5;
|
|
||||||
|
|
||||||
private _valueChanged(ev) {
|
|
||||||
this.value = ev.detail.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
|
||||||
return html`
|
|
||||||
${repeat(buttons, (button) => {
|
|
||||||
const { id, label, ...config } = button;
|
|
||||||
return html`
|
|
||||||
<ha-card>
|
|
||||||
<div class="card-content">
|
|
||||||
<label id=${id}>${label}</label>
|
|
||||||
<pre>Config: ${JSON.stringify(config)}</pre>
|
|
||||||
<ha-control-number-buttons
|
|
||||||
.value=${this.value}
|
|
||||||
.min=${config.min}
|
|
||||||
.max=${config.max}
|
|
||||||
.step=${config.step}
|
|
||||||
class=${ifDefined(config.class)}
|
|
||||||
@value-changed=${this._valueChanged}
|
|
||||||
.label=${label}
|
|
||||||
>
|
|
||||||
</ha-control-number-buttons>
|
|
||||||
</div>
|
|
||||||
</ha-card>
|
|
||||||
`;
|
|
||||||
})}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles() {
|
|
||||||
return css`
|
|
||||||
ha-card {
|
|
||||||
max-width: 600px;
|
|
||||||
margin: 24px auto;
|
|
||||||
}
|
|
||||||
pre {
|
|
||||||
margin-top: 0;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
p {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
label {
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
.custom {
|
|
||||||
color: #2196f3;
|
|
||||||
--control-number-buttons-color: #2196f3;
|
|
||||||
--control-number-buttons-background-color: #2196f3;
|
|
||||||
--control-number-buttons-background-opacity: 0.1;
|
|
||||||
--control-number-buttons-thickness: 100px;
|
|
||||||
--control-number-buttons-border-radius: 24px;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
"demo-components-ha-control-number-buttons": DemoHarControlNumberButtons;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
title: Temp Color Picker
|
||||||
|
---
|
117
gallery/src/pages/components/ha-temp-color-picker.ts
Normal file
117
gallery/src/pages/components/ha-temp-color-picker.ts
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
import "../../../../src/components/ha-temp-color-picker";
|
||||||
|
|
||||||
|
import { css, html, LitElement, TemplateResult } from "lit";
|
||||||
|
import { customElement, state } from "lit/decorators";
|
||||||
|
|
||||||
|
import "../../../../src/components/ha-card";
|
||||||
|
import "../../../../src/components/ha-slider";
|
||||||
|
|
||||||
|
@customElement("demo-components-ha-temp-color-picker")
|
||||||
|
export class DemoHaTempColorPicker extends LitElement {
|
||||||
|
@state()
|
||||||
|
min = 3000;
|
||||||
|
|
||||||
|
@state()
|
||||||
|
max = 7000;
|
||||||
|
|
||||||
|
@state()
|
||||||
|
value = 4000;
|
||||||
|
|
||||||
|
@state()
|
||||||
|
liveValue?: number;
|
||||||
|
|
||||||
|
private _minChanged(ev) {
|
||||||
|
this.min = Number(ev.target.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _maxChanged(ev) {
|
||||||
|
this.max = Number(ev.target.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _valueChanged(ev) {
|
||||||
|
this.value = Number(ev.target.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _tempColorCursor(ev) {
|
||||||
|
this.liveValue = ev.detail.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _tempColorChanged(ev) {
|
||||||
|
this.value = ev.detail.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<ha-card>
|
||||||
|
<div class="card-content">
|
||||||
|
<p class="value">${this.liveValue ?? this.value} K</p>
|
||||||
|
<ha-temp-color-picker
|
||||||
|
.min=${this.min}
|
||||||
|
.max=${this.max}
|
||||||
|
.value=${this.value}
|
||||||
|
@value-changed=${this._tempColorChanged}
|
||||||
|
@cursor-moved=${this._tempColorCursor}
|
||||||
|
></ha-temp-color-picker>
|
||||||
|
<p>Min temp : ${this.min} K</p>
|
||||||
|
<ha-slider
|
||||||
|
step="1"
|
||||||
|
pin
|
||||||
|
min="2000"
|
||||||
|
max="10000"
|
||||||
|
.value=${this.min}
|
||||||
|
@change=${this._minChanged}
|
||||||
|
>
|
||||||
|
</ha-slider>
|
||||||
|
<p>Max temp : ${this.max} K</p>
|
||||||
|
<ha-slider
|
||||||
|
step="1"
|
||||||
|
pin
|
||||||
|
min="2000"
|
||||||
|
max="10000"
|
||||||
|
.value=${this.max}
|
||||||
|
@change=${this._maxChanged}
|
||||||
|
>
|
||||||
|
</ha-slider>
|
||||||
|
<p>Value : ${this.value} K</p>
|
||||||
|
<ha-slider
|
||||||
|
step="1"
|
||||||
|
pin
|
||||||
|
min=${this.min}
|
||||||
|
max=${this.max}
|
||||||
|
.value=${this.value}
|
||||||
|
@change=${this._valueChanged}
|
||||||
|
>
|
||||||
|
</ha-slider>
|
||||||
|
</div>
|
||||||
|
</ha-card>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return css`
|
||||||
|
ha-card {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 24px auto;
|
||||||
|
}
|
||||||
|
.card-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
ha-temp-color-picker {
|
||||||
|
width: 400px;
|
||||||
|
}
|
||||||
|
.value {
|
||||||
|
font-size: 22px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin: 0 0 12px 0;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"demo-components-ha-temp-color-picker": DemoHaTempColorPicker;
|
||||||
|
}
|
||||||
|
}
|
@@ -31,7 +31,6 @@ export class HassioUploadBackup extends LitElement {
|
|||||||
.icon=${mdiFolderUpload}
|
.icon=${mdiFolderUpload}
|
||||||
accept="application/x-tar"
|
accept="application/x-tar"
|
||||||
label="Upload backup"
|
label="Upload backup"
|
||||||
supports="Supports .TAR files"
|
|
||||||
@file-picked=${this._uploadFile}
|
@file-picked=${this._uploadFile}
|
||||||
auto-open-file-dialog
|
auto-open-file-dialog
|
||||||
></ha-file-upload>
|
></ha-file-upload>
|
||||||
|
@@ -173,7 +173,6 @@ class HassioBackupDialog
|
|||||||
private async _restoreClicked() {
|
private async _restoreClicked() {
|
||||||
const backupDetails = this._backupContent.backupDetails();
|
const backupDetails = this._backupContent.backupDetails();
|
||||||
this._restoringBackup = true;
|
this._restoringBackup = true;
|
||||||
this._dialogParams?.onRestoring?.();
|
|
||||||
if (this._backupContent.backupType === "full") {
|
if (this._backupContent.backupType === "full") {
|
||||||
await this._fullRestoreClicked(backupDetails);
|
await this._fullRestoreClicked(backupDetails);
|
||||||
} else {
|
} else {
|
||||||
@@ -220,7 +219,7 @@ class HassioBackupDialog
|
|||||||
this._error = error.body.message;
|
this._error = error.body.message;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this._dialogParams?.onRestoring?.();
|
fireEvent(this, "restoring");
|
||||||
await fetch(`/api/hassio/backups/${this._backup!.slug}/restore/partial`, {
|
await fetch(`/api/hassio/backups/${this._backup!.slug}/restore/partial`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify(backupDetails),
|
body: JSON.stringify(backupDetails),
|
||||||
@@ -269,7 +268,7 @@ class HassioBackupDialog
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
this._dialogParams?.onRestoring?.();
|
fireEvent(this, "restoring");
|
||||||
fetch(`/api/hassio/backups/${this._backup!.slug}/restore/full`, {
|
fetch(`/api/hassio/backups/${this._backup!.slug}/restore/full`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify(backupDetails),
|
body: JSON.stringify(backupDetails),
|
||||||
|
@@ -5,7 +5,6 @@ import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
|||||||
export interface HassioBackupDialogParams {
|
export interface HassioBackupDialogParams {
|
||||||
slug: string;
|
slug: string;
|
||||||
onDelete?: () => void;
|
onDelete?: () => void;
|
||||||
onRestoring?: () => void;
|
|
||||||
onboarding?: boolean;
|
onboarding?: boolean;
|
||||||
supervisor?: Supervisor;
|
supervisor?: Supervisor;
|
||||||
localize?: LocalizeFunc;
|
localize?: LocalizeFunc;
|
||||||
|
69
package.json
69
package.json
@@ -25,15 +25,15 @@
|
|||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "7.22.11",
|
"@babel/runtime": "7.22.10",
|
||||||
"@braintree/sanitize-url": "6.0.4",
|
"@braintree/sanitize-url": "6.0.4",
|
||||||
"@codemirror/autocomplete": "6.9.0",
|
"@codemirror/autocomplete": "6.9.0",
|
||||||
"@codemirror/commands": "6.2.5",
|
"@codemirror/commands": "6.2.4",
|
||||||
"@codemirror/language": "6.9.0",
|
"@codemirror/language": "6.9.0",
|
||||||
"@codemirror/legacy-modes": "6.3.3",
|
"@codemirror/legacy-modes": "6.3.3",
|
||||||
"@codemirror/search": "6.5.2",
|
"@codemirror/search": "6.5.1",
|
||||||
"@codemirror/state": "6.2.1",
|
"@codemirror/state": "6.2.1",
|
||||||
"@codemirror/view": "6.17.1",
|
"@codemirror/view": "6.16.0",
|
||||||
"@egjs/hammerjs": "2.0.17",
|
"@egjs/hammerjs": "2.0.17",
|
||||||
"@formatjs/intl-datetimeformat": "6.10.0",
|
"@formatjs/intl-datetimeformat": "6.10.0",
|
||||||
"@formatjs/intl-displaynames": "6.5.0",
|
"@formatjs/intl-displaynames": "6.5.0",
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
"@lezer/highlight": "1.1.6",
|
"@lezer/highlight": "1.1.6",
|
||||||
"@lit-labs/context": "0.4.0",
|
"@lit-labs/context": "0.4.0",
|
||||||
"@lit-labs/motion": "1.0.4",
|
"@lit-labs/motion": "1.0.4",
|
||||||
"@lit-labs/virtualizer": "2.0.7",
|
"@lit-labs/virtualizer": "2.0.5",
|
||||||
"@lrnwebcomponents/simple-tooltip": "7.0.16",
|
"@lrnwebcomponents/simple-tooltip": "7.0.16",
|
||||||
"@material/chips": "=14.0.0-canary.53b3cad2f.0",
|
"@material/chips": "=14.0.0-canary.53b3cad2f.0",
|
||||||
"@material/data-table": "=14.0.0-canary.53b3cad2f.0",
|
"@material/data-table": "=14.0.0-canary.53b3cad2f.0",
|
||||||
@@ -79,9 +79,10 @@
|
|||||||
"@material/mwc-top-app-bar": "0.27.0",
|
"@material/mwc-top-app-bar": "0.27.0",
|
||||||
"@material/mwc-top-app-bar-fixed": "0.27.0",
|
"@material/mwc-top-app-bar-fixed": "0.27.0",
|
||||||
"@material/top-app-bar": "=14.0.0-canary.53b3cad2f.0",
|
"@material/top-app-bar": "=14.0.0-canary.53b3cad2f.0",
|
||||||
"@material/web": "=1.0.0-pre.16",
|
"@material/web": "=1.0.0-pre.15",
|
||||||
"@mdi/js": "7.2.96",
|
"@mdi/js": "7.2.96",
|
||||||
"@mdi/svg": "7.2.96",
|
"@mdi/svg": "7.2.96",
|
||||||
|
"@polymer/app-layout": "3.1.0",
|
||||||
"@polymer/iron-flex-layout": "3.0.1",
|
"@polymer/iron-flex-layout": "3.0.1",
|
||||||
"@polymer/iron-input": "3.0.1",
|
"@polymer/iron-input": "3.0.1",
|
||||||
"@polymer/iron-resizable-behavior": "3.0.1",
|
"@polymer/iron-resizable-behavior": "3.0.1",
|
||||||
@@ -93,8 +94,8 @@
|
|||||||
"@polymer/paper-toast": "3.0.1",
|
"@polymer/paper-toast": "3.0.1",
|
||||||
"@polymer/polymer": "3.5.1",
|
"@polymer/polymer": "3.5.1",
|
||||||
"@thomasloven/round-slider": "0.6.0",
|
"@thomasloven/round-slider": "0.6.0",
|
||||||
"@vaadin/combo-box": "24.1.6",
|
"@vaadin/combo-box": "24.1.5",
|
||||||
"@vaadin/vaadin-themable-mixin": "24.1.6",
|
"@vaadin/vaadin-themable-mixin": "24.1.5",
|
||||||
"@vibrant/color": "3.2.1-alpha.1",
|
"@vibrant/color": "3.2.1-alpha.1",
|
||||||
"@vibrant/core": "3.2.1-alpha.1",
|
"@vibrant/core": "3.2.1-alpha.1",
|
||||||
"@vibrant/quantizer-mmcq": "3.2.1-alpha.1",
|
"@vibrant/quantizer-mmcq": "3.2.1-alpha.1",
|
||||||
@@ -102,17 +103,17 @@
|
|||||||
"@webcomponents/scoped-custom-element-registry": "0.0.9",
|
"@webcomponents/scoped-custom-element-registry": "0.0.9",
|
||||||
"@webcomponents/webcomponentsjs": "2.8.0",
|
"@webcomponents/webcomponentsjs": "2.8.0",
|
||||||
"app-datepicker": "5.1.1",
|
"app-datepicker": "5.1.1",
|
||||||
"chart.js": "4.3.3",
|
"chart.js": "3.3.2",
|
||||||
"comlink": "4.4.1",
|
"comlink": "4.4.1",
|
||||||
"core-js": "3.32.1",
|
"core-js": "3.32.1",
|
||||||
"cropperjs": "1.6.0",
|
"cropperjs": "1.5.13",
|
||||||
"date-fns": "2.30.0",
|
"date-fns": "2.30.0",
|
||||||
"date-fns-tz": "2.0.0",
|
"date-fns-tz": "2.0.0",
|
||||||
"deep-clone-simple": "1.1.1",
|
"deep-clone-simple": "1.1.1",
|
||||||
"deep-freeze": "0.0.1",
|
"deep-freeze": "0.0.1",
|
||||||
"fuse.js": "6.6.2",
|
"fuse.js": "6.6.2",
|
||||||
"google-timezones-json": "1.2.0",
|
"google-timezones-json": "1.2.0",
|
||||||
"hls.js": "1.4.12",
|
"hls.js": "1.4.10",
|
||||||
"home-assistant-js-websocket": "8.2.0",
|
"home-assistant-js-websocket": "8.2.0",
|
||||||
"idb-keyval": "6.2.1",
|
"idb-keyval": "6.2.1",
|
||||||
"intl-messageformat": "10.5.0",
|
"intl-messageformat": "10.5.0",
|
||||||
@@ -120,8 +121,8 @@
|
|||||||
"leaflet": "1.9.4",
|
"leaflet": "1.9.4",
|
||||||
"leaflet-draw": "1.0.4",
|
"leaflet-draw": "1.0.4",
|
||||||
"lit": "2.8.0",
|
"lit": "2.8.0",
|
||||||
"luxon": "3.4.2",
|
"luxon": "3.4.0",
|
||||||
"marked": "7.0.5",
|
"marked": "7.0.4",
|
||||||
"memoize-one": "6.0.0",
|
"memoize-one": "6.0.0",
|
||||||
"node-vibrant": "3.2.1-alpha.1",
|
"node-vibrant": "3.2.1-alpha.1",
|
||||||
"proxy-polyfill": "0.3.2",
|
"proxy-polyfill": "0.3.2",
|
||||||
@@ -153,11 +154,11 @@
|
|||||||
"xss": "1.0.14"
|
"xss": "1.0.14"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "7.22.11",
|
"@babel/core": "7.22.10",
|
||||||
"@babel/plugin-proposal-decorators": "7.22.10",
|
"@babel/plugin-proposal-decorators": "7.22.10",
|
||||||
"@babel/plugin-transform-runtime": "7.22.10",
|
"@babel/plugin-transform-runtime": "7.22.10",
|
||||||
"@babel/preset-env": "7.22.14",
|
"@babel/preset-env": "7.22.10",
|
||||||
"@babel/preset-typescript": "7.22.11",
|
"@babel/preset-typescript": "7.22.5",
|
||||||
"@koa/cors": "4.0.0",
|
"@koa/cors": "4.0.0",
|
||||||
"@octokit/auth-oauth-device": "6.0.0",
|
"@octokit/auth-oauth-device": "6.0.0",
|
||||||
"@octokit/plugin-retry": "6.0.0",
|
"@octokit/plugin-retry": "6.0.0",
|
||||||
@@ -166,7 +167,7 @@
|
|||||||
"@rollup/plugin-babel": "6.0.3",
|
"@rollup/plugin-babel": "6.0.3",
|
||||||
"@rollup/plugin-commonjs": "25.0.4",
|
"@rollup/plugin-commonjs": "25.0.4",
|
||||||
"@rollup/plugin-json": "6.0.0",
|
"@rollup/plugin-json": "6.0.0",
|
||||||
"@rollup/plugin-node-resolve": "15.2.1",
|
"@rollup/plugin-node-resolve": "15.2.0",
|
||||||
"@rollup/plugin-replace": "5.0.2",
|
"@rollup/plugin-replace": "5.0.2",
|
||||||
"@types/babel__plugin-transform-runtime": "7.9.2",
|
"@types/babel__plugin-transform-runtime": "7.9.2",
|
||||||
"@types/chromecast-caf-receiver": "6.0.9",
|
"@types/chromecast-caf-receiver": "6.0.9",
|
||||||
@@ -175,25 +176,25 @@
|
|||||||
"@types/glob": "8.1.0",
|
"@types/glob": "8.1.0",
|
||||||
"@types/html-minifier-terser": "7.0.0",
|
"@types/html-minifier-terser": "7.0.0",
|
||||||
"@types/js-yaml": "4.0.5",
|
"@types/js-yaml": "4.0.5",
|
||||||
"@types/leaflet": "1.9.4",
|
"@types/leaflet": "1.9.3",
|
||||||
"@types/leaflet-draw": "1.0.8",
|
"@types/leaflet-draw": "1.0.7",
|
||||||
"@types/luxon": "3.3.2",
|
"@types/luxon": "3.3.1",
|
||||||
"@types/mocha": "10.0.1",
|
"@types/mocha": "10.0.1",
|
||||||
"@types/qrcode": "1.5.2",
|
"@types/qrcode": "1.5.1",
|
||||||
"@types/serve-handler": "6.1.1",
|
"@types/serve-handler": "6.1.1",
|
||||||
"@types/sortablejs": "1.15.2",
|
"@types/sortablejs": "1.15.1",
|
||||||
"@types/tar": "6.1.5",
|
"@types/tar": "6.1.5",
|
||||||
"@types/ua-parser-js": "0.7.37",
|
"@types/ua-parser-js": "0.7.36",
|
||||||
"@types/webspeechapi": "0.0.29",
|
"@types/webspeechapi": "0.0.29",
|
||||||
"@typescript-eslint/eslint-plugin": "6.5.0",
|
"@typescript-eslint/eslint-plugin": "6.4.0",
|
||||||
"@typescript-eslint/parser": "6.5.0",
|
"@typescript-eslint/parser": "6.4.0",
|
||||||
"@web/dev-server": "0.1.38",
|
"@web/dev-server": "0.1.38",
|
||||||
"@web/dev-server-rollup": "0.4.1",
|
"@web/dev-server-rollup": "0.4.1",
|
||||||
"babel-loader": "9.1.3",
|
"babel-loader": "9.1.3",
|
||||||
"babel-plugin-template-html-minifier": "4.1.0",
|
"babel-plugin-template-html-minifier": "4.1.0",
|
||||||
"chai": "4.3.8",
|
"chai": "4.3.7",
|
||||||
"del": "7.1.0",
|
"del": "7.0.0",
|
||||||
"eslint": "8.48.0",
|
"eslint": "8.47.0",
|
||||||
"eslint-config-airbnb-base": "15.0.0",
|
"eslint-config-airbnb-base": "15.0.0",
|
||||||
"eslint-config-airbnb-typescript": "17.1.0",
|
"eslint-config-airbnb-typescript": "17.1.0",
|
||||||
"eslint-config-prettier": "9.0.0",
|
"eslint-config-prettier": "9.0.0",
|
||||||
@@ -207,7 +208,7 @@
|
|||||||
"esprima": "4.0.1",
|
"esprima": "4.0.1",
|
||||||
"fancy-log": "2.0.0",
|
"fancy-log": "2.0.0",
|
||||||
"fs-extra": "11.1.1",
|
"fs-extra": "11.1.1",
|
||||||
"glob": "10.3.4",
|
"glob": "10.3.3",
|
||||||
"gulp": "4.0.2",
|
"gulp": "4.0.2",
|
||||||
"gulp-flatmap": "1.0.2",
|
"gulp-flatmap": "1.0.2",
|
||||||
"gulp-json-transform": "0.4.8",
|
"gulp-json-transform": "0.4.8",
|
||||||
@@ -218,16 +219,16 @@
|
|||||||
"husky": "8.0.3",
|
"husky": "8.0.3",
|
||||||
"instant-mocha": "1.5.2",
|
"instant-mocha": "1.5.2",
|
||||||
"jszip": "3.10.1",
|
"jszip": "3.10.1",
|
||||||
"lint-staged": "14.0.1",
|
"lint-staged": "14.0.0",
|
||||||
"lit-analyzer": "2.0.0-pre.3",
|
"lit-analyzer": "2.0.0-pre.3",
|
||||||
"lodash.template": "4.5.0",
|
"lodash.template": "4.5.0",
|
||||||
"magic-string": "0.30.3",
|
"magic-string": "0.30.2",
|
||||||
"map-stream": "0.0.7",
|
"map-stream": "0.0.7",
|
||||||
"mocha": "10.2.0",
|
"mocha": "10.2.0",
|
||||||
"object-hash": "3.0.0",
|
"object-hash": "3.0.0",
|
||||||
"open": "9.1.0",
|
"open": "9.1.0",
|
||||||
"pinst": "3.0.0",
|
"pinst": "3.0.0",
|
||||||
"prettier": "3.0.3",
|
"prettier": "3.0.2",
|
||||||
"rollup": "2.79.1",
|
"rollup": "2.79.1",
|
||||||
"rollup-plugin-string": "3.0.0",
|
"rollup-plugin-string": "3.0.0",
|
||||||
"rollup-plugin-terser": "7.0.2",
|
"rollup-plugin-terser": "7.0.2",
|
||||||
@@ -239,7 +240,7 @@
|
|||||||
"tar": "6.1.15",
|
"tar": "6.1.15",
|
||||||
"terser-webpack-plugin": "5.3.9",
|
"terser-webpack-plugin": "5.3.9",
|
||||||
"ts-lit-plugin": "2.0.0-pre.1",
|
"ts-lit-plugin": "2.0.0-pre.1",
|
||||||
"typescript": "5.2.2",
|
"typescript": "5.1.6",
|
||||||
"vinyl-buffer": "1.0.1",
|
"vinyl-buffer": "1.0.1",
|
||||||
"vinyl-source-stream": "2.0.0",
|
"vinyl-source-stream": "2.0.0",
|
||||||
"webpack": "5.88.2",
|
"webpack": "5.88.2",
|
||||||
@@ -256,5 +257,5 @@
|
|||||||
"sortablejs@1.15.0": "patch:sortablejs@npm%3A1.15.0#./.yarn/patches/sortablejs-npm-1.15.0-f3a393abcc.patch",
|
"sortablejs@1.15.0": "patch:sortablejs@npm%3A1.15.0#./.yarn/patches/sortablejs-npm-1.15.0-f3a393abcc.patch",
|
||||||
"leaflet-draw@1.0.4": "patch:leaflet-draw@npm%3A1.0.4#./.yarn/patches/leaflet-draw-npm-1.0.4-0ca0ebcf65.patch"
|
"leaflet-draw@1.0.4": "patch:leaflet-draw@npm%3A1.0.4#./.yarn/patches/leaflet-draw-npm-1.0.4-0ca0ebcf65.patch"
|
||||||
},
|
},
|
||||||
"packageManager": "yarn@3.6.3"
|
"packageManager": "yarn@3.6.2"
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 1.3 KiB |
Binary file not shown.
Before Width: | Height: | Size: 1.2 KiB |
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "home-assistant-frontend"
|
name = "home-assistant-frontend"
|
||||||
version = "20230904.0"
|
version = "20230802.0"
|
||||||
license = {text = "Apache-2.0"}
|
license = {text = "Apache-2.0"}
|
||||||
description = "The Home Assistant frontend"
|
description = "The Home Assistant frontend"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
@@ -35,8 +35,6 @@ export class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
|
|||||||
|
|
||||||
@property() public oauth2State?: string;
|
@property() public oauth2State?: string;
|
||||||
|
|
||||||
@property() public translationFragment = "page-authorize";
|
|
||||||
|
|
||||||
@state() private _authProvider?: AuthProvider;
|
@state() private _authProvider?: AuthProvider;
|
||||||
|
|
||||||
@state() private _authProviders?: AuthProvider[];
|
@state() private _authProviders?: AuthProvider[];
|
||||||
@@ -47,6 +45,7 @@ export class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
|
|||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
this.translationFragment = "page-authorize";
|
||||||
const query = extractSearchParamsObject() as AuthUrlSearchParams;
|
const query = extractSearchParamsObject() as AuthUrlSearchParams;
|
||||||
if (query.client_id) {
|
if (query.client_id) {
|
||||||
this.clientId = query.client_id;
|
this.clientId = query.client_id;
|
||||||
@@ -103,6 +102,7 @@ export class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
|
|||||||
: nothing}
|
: nothing}
|
||||||
|
|
||||||
<ha-auth-flow
|
<ha-auth-flow
|
||||||
|
.resources=${this.resources}
|
||||||
.clientId=${this.clientId}
|
.clientId=${this.clientId}
|
||||||
.redirectUri=${this.redirectUri}
|
.redirectUri=${this.redirectUri}
|
||||||
.oauth2State=${this.oauth2State}
|
.oauth2State=${this.oauth2State}
|
||||||
|
@@ -107,7 +107,6 @@ export class HaPasswordManagerPolyfill extends LitElement {
|
|||||||
.value=${this.stepData[schema.name] || ""}
|
.value=${this.stepData[schema.name] || ""}
|
||||||
.autocomplete=${schema.autocomplete}
|
.autocomplete=${schema.autocomplete}
|
||||||
@input=${this._valueChanged}
|
@input=${this._valueChanged}
|
||||||
@change=${this._valueChanged}
|
|
||||||
/>
|
/>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@@ -4,7 +4,7 @@ export const clamp = (value: number, min: number, max: number) =>
|
|||||||
// Variant that only applies the clamping to a border if the border is defined
|
// Variant that only applies the clamping to a border if the border is defined
|
||||||
export const conditionalClamp = (value: number, min?: number, max?: number) => {
|
export const conditionalClamp = (value: number, min?: number, max?: number) => {
|
||||||
let result: number;
|
let result: number;
|
||||||
result = min != null ? Math.max(value, min) : value;
|
result = min ? Math.max(value, min) : value;
|
||||||
result = max != null ? Math.min(result, max) : result;
|
result = max ? Math.min(result, max) : result;
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
@@ -108,7 +108,7 @@ export const formatNumber = (
|
|||||||
* @returns An `Intl.NumberFormatOptions` object with `maximumFractionDigits` set to 0, or `undefined`
|
* @returns An `Intl.NumberFormatOptions` object with `maximumFractionDigits` set to 0, or `undefined`
|
||||||
*/
|
*/
|
||||||
export const getNumberFormatOptions = (
|
export const getNumberFormatOptions = (
|
||||||
entityState?: HassEntity,
|
entityState: HassEntity,
|
||||||
entity?: EntityRegistryDisplayEntry
|
entity?: EntityRegistryDisplayEntry
|
||||||
): Intl.NumberFormatOptions | undefined => {
|
): Intl.NumberFormatOptions | undefined => {
|
||||||
const precision = entity?.display_precision;
|
const precision = entity?.display_precision;
|
||||||
@@ -119,8 +119,8 @@ export const getNumberFormatOptions = (
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
Number.isInteger(Number(entityState?.attributes?.step)) &&
|
Number.isInteger(Number(entityState.attributes?.step)) &&
|
||||||
Number.isInteger(Number(entityState?.state))
|
Number.isInteger(Number(entityState.state))
|
||||||
) {
|
) {
|
||||||
return { maximumFractionDigits: 0 };
|
return { maximumFractionDigits: 0 };
|
||||||
}
|
}
|
||||||
|
@@ -15,20 +15,13 @@ import { HomeAssistant } from "../../types";
|
|||||||
|
|
||||||
export const MIN_TIME_BETWEEN_UPDATES = 60 * 5 * 1000;
|
export const MIN_TIME_BETWEEN_UPDATES = 60 * 5 * 1000;
|
||||||
|
|
||||||
export interface ChartResizeOptions {
|
interface Tooltip extends TooltipModel<any> {
|
||||||
aspectRatio?: number;
|
|
||||||
height?: number;
|
|
||||||
width?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Tooltip
|
|
||||||
extends Omit<TooltipModel<any>, "tooltipPosition" | "hasValue" | "getProps"> {
|
|
||||||
top: string;
|
top: string;
|
||||||
left: string;
|
left: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@customElement("ha-chart-base")
|
@customElement("ha-chart-base")
|
||||||
export class HaChartBase extends LitElement {
|
export default class HaChartBase extends LitElement {
|
||||||
public chart?: Chart;
|
public chart?: Chart;
|
||||||
|
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
@@ -52,6 +45,14 @@ export class HaChartBase extends LitElement {
|
|||||||
|
|
||||||
@state() private _hiddenDatasets: Set<number> = new Set();
|
@state() private _hiddenDatasets: Set<number> = new Set();
|
||||||
|
|
||||||
|
private _releaseCanvas() {
|
||||||
|
// release the canvas memory to prevent
|
||||||
|
// safari from running out of memory.
|
||||||
|
if (this.chart) {
|
||||||
|
this.chart.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public disconnectedCallback() {
|
public disconnectedCallback() {
|
||||||
this._releaseCanvas();
|
this._releaseCanvas();
|
||||||
super.disconnectedCallback();
|
super.disconnectedCallback();
|
||||||
@@ -64,36 +65,6 @@ export class HaChartBase extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateChart = (
|
|
||||||
mode:
|
|
||||||
| "resize"
|
|
||||||
| "reset"
|
|
||||||
| "none"
|
|
||||||
| "hide"
|
|
||||||
| "show"
|
|
||||||
| "default"
|
|
||||||
| "active"
|
|
||||||
| undefined
|
|
||||||
): void => {
|
|
||||||
this.chart?.update(mode);
|
|
||||||
};
|
|
||||||
|
|
||||||
public resize = (options?: ChartResizeOptions): void => {
|
|
||||||
if (options?.aspectRatio && !options.height) {
|
|
||||||
options.height = Math.round(
|
|
||||||
(options.width ?? this.clientWidth) / options.aspectRatio
|
|
||||||
);
|
|
||||||
} else if (options?.aspectRatio && !options.width) {
|
|
||||||
options.width = Math.round(
|
|
||||||
(options.height ?? this.clientHeight) * options.aspectRatio
|
|
||||||
);
|
|
||||||
}
|
|
||||||
this.chart?.resize(
|
|
||||||
options?.width ?? this.clientWidth,
|
|
||||||
options?.height ?? this.clientHeight
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
protected firstUpdated() {
|
protected firstUpdated() {
|
||||||
this._setupChart();
|
this._setupChart();
|
||||||
this.data.datasets.forEach((dataset, index) => {
|
this.data.datasets.forEach((dataset, index) => {
|
||||||
@@ -109,11 +80,14 @@ export class HaChartBase extends LitElement {
|
|||||||
if (!this.hasUpdated || !this.chart) {
|
if (!this.hasUpdated || !this.chart) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (changedProps.has("plugins") || changedProps.has("chartType")) {
|
if (changedProps.has("plugins")) {
|
||||||
this.chart.destroy();
|
this.chart.destroy();
|
||||||
this._setupChart();
|
this._setupChart();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (changedProps.has("chartType")) {
|
||||||
|
this.chart.config.type = this.chartType;
|
||||||
|
}
|
||||||
if (changedProps.has("data")) {
|
if (changedProps.has("data")) {
|
||||||
if (this._hiddenDatasets.size) {
|
if (this._hiddenDatasets.size) {
|
||||||
this.data.datasets.forEach((dataset, index) => {
|
this.data.datasets.forEach((dataset, index) => {
|
||||||
@@ -156,33 +130,19 @@ export class HaChartBase extends LitElement {
|
|||||||
</ul>
|
</ul>
|
||||||
</div>`
|
</div>`
|
||||||
: ""}
|
: ""}
|
||||||
<div
|
|
||||||
class="animationContainer"
|
|
||||||
style=${styleMap({
|
|
||||||
height: `${this.height || this._chartHeight || 0}px`,
|
|
||||||
overflow: this._chartHeight ? "initial" : "hidden",
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
class="chartContainer"
|
class="chartContainer"
|
||||||
style=${styleMap({
|
style=${styleMap({
|
||||||
height: `${
|
height: `${this.height ?? this._chartHeight}px`,
|
||||||
this.height ?? this._chartHeight ?? this.clientWidth / 2
|
overflow: this._chartHeight ? "initial" : "hidden",
|
||||||
}px`,
|
"padding-left": `${computeRTL(this.hass) ? 0 : this.paddingYAxis}px`,
|
||||||
"padding-left": `${
|
"padding-right": `${computeRTL(this.hass) ? this.paddingYAxis : 0}px`,
|
||||||
computeRTL(this.hass) ? 0 : this.paddingYAxis
|
|
||||||
}px`,
|
|
||||||
"padding-right": `${
|
|
||||||
computeRTL(this.hass) ? this.paddingYAxis : 0
|
|
||||||
}px`,
|
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<canvas></canvas>
|
<canvas></canvas>
|
||||||
${this._tooltip
|
${this._tooltip
|
||||||
? html`<div
|
? html`<div
|
||||||
class="chartTooltip ${classMap({
|
class="chartTooltip ${classMap({ [this._tooltip.yAlign]: true })}"
|
||||||
[this._tooltip.yAlign]: true,
|
|
||||||
})}"
|
|
||||||
style=${styleMap({
|
style=${styleMap({
|
||||||
top: this._tooltip.top,
|
top: this._tooltip.top,
|
||||||
left: this._tooltip.left,
|
left: this._tooltip.left,
|
||||||
@@ -221,7 +181,6 @@ export class HaChartBase extends LitElement {
|
|||||||
</div>`
|
</div>`
|
||||||
: ""}
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,7 +213,6 @@ export class HaChartBase extends LitElement {
|
|||||||
|
|
||||||
private _createOptions() {
|
private _createOptions() {
|
||||||
return {
|
return {
|
||||||
maintainAspectRatio: false,
|
|
||||||
...this.options,
|
...this.options,
|
||||||
plugins: {
|
plugins: {
|
||||||
...this.options?.plugins,
|
...this.options?.plugins,
|
||||||
@@ -275,10 +233,10 @@ export class HaChartBase extends LitElement {
|
|||||||
return [
|
return [
|
||||||
...(this.plugins || []),
|
...(this.plugins || []),
|
||||||
{
|
{
|
||||||
id: "resizeHook",
|
id: "afterRenderHook",
|
||||||
resize: (chart) => {
|
afterRender: (chart) => {
|
||||||
const change = chart.height - (this._chartHeight ?? 0);
|
const change = chart.height - (this._chartHeight ?? 0);
|
||||||
if (!this._chartHeight || change > 12 || change < -12) {
|
if (!this._chartHeight || change > 0 || change < -12) {
|
||||||
// hysteresis to prevent infinite render loops
|
// hysteresis to prevent infinite render loops
|
||||||
this._chartHeight = chart.height;
|
this._chartHeight = chart.height;
|
||||||
}
|
}
|
||||||
@@ -330,13 +288,21 @@ export class HaChartBase extends LitElement {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private _releaseCanvas() {
|
public updateChart = (
|
||||||
// release the canvas memory to prevent
|
mode:
|
||||||
// safari from running out of memory.
|
| "resize"
|
||||||
|
| "reset"
|
||||||
|
| "none"
|
||||||
|
| "hide"
|
||||||
|
| "show"
|
||||||
|
| "normal"
|
||||||
|
| "active"
|
||||||
|
| undefined
|
||||||
|
): void => {
|
||||||
if (this.chart) {
|
if (this.chart) {
|
||||||
this.chart.destroy();
|
this.chart.update(mode);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return css`
|
return css`
|
||||||
@@ -344,14 +310,11 @@ export class HaChartBase extends LitElement {
|
|||||||
display: block;
|
display: block;
|
||||||
position: var(--chart-base-position, relative);
|
position: var(--chart-base-position, relative);
|
||||||
}
|
}
|
||||||
.animationContainer {
|
.chartContainer {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
height: 0;
|
height: 0;
|
||||||
transition: height 300ms cubic-bezier(0.4, 0, 0.2, 1);
|
transition: height 300ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
}
|
}
|
||||||
.chartContainer {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
canvas {
|
canvas {
|
||||||
max-height: var(--chart-max-height, 400px);
|
max-height: var(--chart-max-height, 400px);
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import type { ChartData, ChartDataset, ChartOptions } from "chart.js";
|
import type { ChartData, ChartDataset, ChartOptions } from "chart.js";
|
||||||
import { html, LitElement, PropertyValues } from "lit";
|
import { html, LitElement, PropertyValues } from "lit";
|
||||||
import { property, query, state } from "lit/decorators";
|
import { property, state } from "lit/decorators";
|
||||||
import { getGraphColorByIndex } from "../../common/color/colors";
|
import { getGraphColorByIndex } from "../../common/color/colors";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
import { computeRTL } from "../../common/util/compute_rtl";
|
import { computeRTL } from "../../common/util/compute_rtl";
|
||||||
@@ -11,18 +11,14 @@ import {
|
|||||||
} from "../../common/number/format_number";
|
} from "../../common/number/format_number";
|
||||||
import { LineChartEntity, LineChartState } from "../../data/history";
|
import { LineChartEntity, LineChartState } from "../../data/history";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
import {
|
import { MIN_TIME_BETWEEN_UPDATES } from "./ha-chart-base";
|
||||||
ChartResizeOptions,
|
|
||||||
HaChartBase,
|
|
||||||
MIN_TIME_BETWEEN_UPDATES,
|
|
||||||
} from "./ha-chart-base";
|
|
||||||
|
|
||||||
const safeParseFloat = (value) => {
|
const safeParseFloat = (value) => {
|
||||||
const parsed = parseFloat(value);
|
const parsed = parseFloat(value);
|
||||||
return isFinite(parsed) ? parsed : null;
|
return isFinite(parsed) ? parsed : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export class StateHistoryChartLine extends LitElement {
|
class StateHistoryChartLine extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property({ attribute: false }) public data: LineChartEntity[] = [];
|
@property({ attribute: false }) public data: LineChartEntity[] = [];
|
||||||
@@ -51,12 +47,6 @@ export class StateHistoryChartLine extends LitElement {
|
|||||||
|
|
||||||
private _chartTime: Date = new Date();
|
private _chartTime: Date = new Date();
|
||||||
|
|
||||||
@query("ha-chart-base") private _chart?: HaChartBase;
|
|
||||||
|
|
||||||
public resize = (options?: ChartResizeOptions): void => {
|
|
||||||
this._chart?.resize(options);
|
|
||||||
};
|
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
return html`
|
return html`
|
||||||
<ha-chart-base
|
<ha-chart-base
|
||||||
@@ -137,16 +127,12 @@ export class StateHistoryChartLine extends LitElement {
|
|||||||
`${context.dataset.label}: ${formatNumber(
|
`${context.dataset.label}: ${formatNumber(
|
||||||
context.parsed.y,
|
context.parsed.y,
|
||||||
this.hass.locale,
|
this.hass.locale,
|
||||||
this.data[context.datasetIndex]?.entity_id
|
getNumberFormatOptions(
|
||||||
? getNumberFormatOptions(
|
this.hass.states[this.data[context.datasetIndex].entity_id],
|
||||||
this.hass.states[
|
|
||||||
this.data[context.datasetIndex].entity_id
|
|
||||||
],
|
|
||||||
this.hass.entities[
|
this.hass.entities[
|
||||||
this.data[context.datasetIndex].entity_id
|
this.data[context.datasetIndex].entity_id
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
: undefined
|
|
||||||
)} ${this.unit}`,
|
)} ${this.unit}`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import type { ChartData, ChartDataset, ChartOptions } from "chart.js";
|
import type { ChartData, ChartDataset, ChartOptions } from "chart.js";
|
||||||
import { css, CSSResultGroup, html, LitElement, PropertyValues } from "lit";
|
import { css, CSSResultGroup, html, LitElement, PropertyValues } from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { formatDateTimeWithSeconds } from "../../common/datetime/format_date_time";
|
import { formatDateTimeWithSeconds } from "../../common/datetime/format_date_time";
|
||||||
import millisecondsToDuration from "../../common/datetime/milliseconds_to_duration";
|
import millisecondsToDuration from "../../common/datetime/milliseconds_to_duration";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
@@ -8,11 +8,7 @@ import { numberFormatToLocale } from "../../common/number/format_number";
|
|||||||
import { computeRTL } from "../../common/util/compute_rtl";
|
import { computeRTL } from "../../common/util/compute_rtl";
|
||||||
import { TimelineEntity } from "../../data/history";
|
import { TimelineEntity } from "../../data/history";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
import {
|
import { MIN_TIME_BETWEEN_UPDATES } from "./ha-chart-base";
|
||||||
ChartResizeOptions,
|
|
||||||
HaChartBase,
|
|
||||||
MIN_TIME_BETWEEN_UPDATES,
|
|
||||||
} from "./ha-chart-base";
|
|
||||||
import type { TimeLineData } from "./timeline-chart/const";
|
import type { TimeLineData } from "./timeline-chart/const";
|
||||||
import { computeTimelineColor } from "./timeline-chart/timeline-color";
|
import { computeTimelineColor } from "./timeline-chart/timeline-color";
|
||||||
|
|
||||||
@@ -50,12 +46,6 @@ export class StateHistoryChartTimeline extends LitElement {
|
|||||||
|
|
||||||
private _chartTime: Date = new Date();
|
private _chartTime: Date = new Date();
|
||||||
|
|
||||||
@query("ha-chart-base") private _chart?: HaChartBase;
|
|
||||||
|
|
||||||
public resize = (options?: ChartResizeOptions): void => {
|
|
||||||
this._chart?.resize(options);
|
|
||||||
};
|
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
return html`
|
return html`
|
||||||
<ha-chart-base
|
<ha-chart-base
|
||||||
|
@@ -6,13 +6,7 @@ import {
|
|||||||
nothing,
|
nothing,
|
||||||
PropertyValues,
|
PropertyValues,
|
||||||
} from "lit";
|
} from "lit";
|
||||||
import {
|
import { customElement, eventOptions, property, state } from "lit/decorators";
|
||||||
customElement,
|
|
||||||
eventOptions,
|
|
||||||
property,
|
|
||||||
queryAll,
|
|
||||||
state,
|
|
||||||
} from "lit/decorators";
|
|
||||||
import { isComponentLoaded } from "../../common/config/is_component_loaded";
|
import { isComponentLoaded } from "../../common/config/is_component_loaded";
|
||||||
import { restoreScroll } from "../../common/decorators/restore-scroll";
|
import { restoreScroll } from "../../common/decorators/restore-scroll";
|
||||||
import {
|
import {
|
||||||
@@ -24,9 +18,6 @@ import { loadVirtualizer } from "../../resources/virtualizer";
|
|||||||
import type { HomeAssistant } from "../../types";
|
import type { HomeAssistant } from "../../types";
|
||||||
import "./state-history-chart-line";
|
import "./state-history-chart-line";
|
||||||
import "./state-history-chart-timeline";
|
import "./state-history-chart-timeline";
|
||||||
import type { StateHistoryChartLine } from "./state-history-chart-line";
|
|
||||||
import type { StateHistoryChartTimeline } from "./state-history-chart-timeline";
|
|
||||||
import { ChartResizeOptions } from "./ha-chart-base";
|
|
||||||
|
|
||||||
const CANVAS_TIMELINE_ROWS_CHUNK = 10; // Split up the canvases to avoid hitting the render limit
|
const CANVAS_TIMELINE_ROWS_CHUNK = 10; // Split up the canvases to avoid hitting the render limit
|
||||||
|
|
||||||
@@ -84,16 +75,6 @@ export class StateHistoryCharts extends LitElement {
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@restoreScroll(".container") private _savedScrollPos?: number;
|
@restoreScroll(".container") private _savedScrollPos?: number;
|
||||||
|
|
||||||
@queryAll("state-history-chart-line")
|
|
||||||
private _charts?: StateHistoryChartLine[];
|
|
||||||
|
|
||||||
public resize = (options?: ChartResizeOptions): void => {
|
|
||||||
this._charts?.forEach(
|
|
||||||
(chart: StateHistoryChartLine | StateHistoryChartTimeline) =>
|
|
||||||
chart.resize(options)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
if (!isComponentLoaded(this.hass, "history")) {
|
if (!isComponentLoaded(this.hass, "history")) {
|
||||||
return html`<div class="info">
|
return html`<div class="info">
|
||||||
|
@@ -12,7 +12,7 @@ import {
|
|||||||
PropertyValues,
|
PropertyValues,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit";
|
} from "lit";
|
||||||
import { customElement, property, state, query } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { getGraphColorByIndex } from "../../common/color/colors";
|
import { getGraphColorByIndex } from "../../common/color/colors";
|
||||||
import { isComponentLoaded } from "../../common/config/is_component_loaded";
|
import { isComponentLoaded } from "../../common/config/is_component_loaded";
|
||||||
@@ -31,7 +31,6 @@ import {
|
|||||||
} from "../../data/recorder";
|
} from "../../data/recorder";
|
||||||
import type { HomeAssistant } from "../../types";
|
import type { HomeAssistant } from "../../types";
|
||||||
import "./ha-chart-base";
|
import "./ha-chart-base";
|
||||||
import type { ChartResizeOptions, HaChartBase } from "./ha-chart-base";
|
|
||||||
|
|
||||||
export const supportedStatTypeMap: Record<StatisticType, StatisticType> = {
|
export const supportedStatTypeMap: Record<StatisticType, StatisticType> = {
|
||||||
mean: "mean",
|
mean: "mean",
|
||||||
@@ -43,7 +42,7 @@ export const supportedStatTypeMap: Record<StatisticType, StatisticType> = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
@customElement("statistics-chart")
|
@customElement("statistics-chart")
|
||||||
export class StatisticsChart extends LitElement {
|
class StatisticsChart extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property({ attribute: false }) public statisticsData?: Statistics;
|
@property({ attribute: false }) public statisticsData?: Statistics;
|
||||||
@@ -76,14 +75,8 @@ export class StatisticsChart extends LitElement {
|
|||||||
|
|
||||||
@state() private _chartOptions?: ChartOptions;
|
@state() private _chartOptions?: ChartOptions;
|
||||||
|
|
||||||
@query("ha-chart-base") private _chart?: HaChartBase;
|
|
||||||
|
|
||||||
private _computedStyle?: CSSStyleDeclaration;
|
private _computedStyle?: CSSStyleDeclaration;
|
||||||
|
|
||||||
public resize = (options?: ChartResizeOptions): void => {
|
|
||||||
this._chart?.resize(options);
|
|
||||||
};
|
|
||||||
|
|
||||||
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
||||||
return changedProps.size > 1 || !changedProps.has("hass");
|
return changedProps.size > 1 || !changedProps.has("hass");
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,3 @@
|
|||||||
import type {
|
|
||||||
BarControllerChartOptions,
|
|
||||||
BarControllerDatasetOptions,
|
|
||||||
} from "chart.js";
|
|
||||||
|
|
||||||
export interface TimeLineData {
|
export interface TimeLineData {
|
||||||
start: Date;
|
start: Date;
|
||||||
end: Date;
|
end: Date;
|
||||||
|
@@ -16,7 +16,7 @@ export interface TextBaroptions extends BarOptions {
|
|||||||
export class TextBarElement extends BarElement {
|
export class TextBarElement extends BarElement {
|
||||||
static id = "textbar";
|
static id = "textbar";
|
||||||
|
|
||||||
draw(ctx: CanvasRenderingContext2D) {
|
draw(ctx) {
|
||||||
super.draw(ctx);
|
super.draw(ctx);
|
||||||
const options = this.options as TextBaroptions;
|
const options = this.options as TextBaroptions;
|
||||||
const { x, y, base, width, text } = (
|
const { x, y, base, width, text } = (
|
||||||
|
@@ -2,95 +2,6 @@ import { BarController, BarElement } from "chart.js";
|
|||||||
import { TimeLineData } from "./const";
|
import { TimeLineData } from "./const";
|
||||||
import { TextBarProps } from "./textbar-element";
|
import { TextBarProps } from "./textbar-element";
|
||||||
|
|
||||||
function borderProps(properties) {
|
|
||||||
let reverse;
|
|
||||||
let start;
|
|
||||||
let end;
|
|
||||||
let top;
|
|
||||||
let bottom;
|
|
||||||
if (properties.horizontal) {
|
|
||||||
reverse = properties.base > properties.x;
|
|
||||||
start = "left";
|
|
||||||
end = "right";
|
|
||||||
} else {
|
|
||||||
reverse = properties.base < properties.y;
|
|
||||||
start = "bottom";
|
|
||||||
end = "top";
|
|
||||||
}
|
|
||||||
if (reverse) {
|
|
||||||
top = "end";
|
|
||||||
bottom = "start";
|
|
||||||
} else {
|
|
||||||
top = "start";
|
|
||||||
bottom = "end";
|
|
||||||
}
|
|
||||||
return { start, end, reverse, top, bottom };
|
|
||||||
}
|
|
||||||
|
|
||||||
function setBorderSkipped(properties, options, stack, index) {
|
|
||||||
let edge = options.borderSkipped;
|
|
||||||
const res = {};
|
|
||||||
|
|
||||||
if (!edge) {
|
|
||||||
properties.borderSkipped = res;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (edge === true) {
|
|
||||||
properties.borderSkipped = {
|
|
||||||
top: true,
|
|
||||||
right: true,
|
|
||||||
bottom: true,
|
|
||||||
left: true,
|
|
||||||
};
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { start, end, reverse, top, bottom } = borderProps(properties);
|
|
||||||
|
|
||||||
if (edge === "middle" && stack) {
|
|
||||||
properties.enableBorderRadius = true;
|
|
||||||
if ((stack._top || 0) === index) {
|
|
||||||
edge = top;
|
|
||||||
} else if ((stack._bottom || 0) === index) {
|
|
||||||
edge = bottom;
|
|
||||||
} else {
|
|
||||||
res[parseEdge(bottom, start, end, reverse)] = true;
|
|
||||||
edge = top;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
res[parseEdge(edge, start, end, reverse)] = true;
|
|
||||||
properties.borderSkipped = res;
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseEdge(edge, a, b, reverse) {
|
|
||||||
if (reverse) {
|
|
||||||
edge = swap(edge, a, b);
|
|
||||||
edge = startEnd(edge, b, a);
|
|
||||||
} else {
|
|
||||||
edge = startEnd(edge, a, b);
|
|
||||||
}
|
|
||||||
return edge;
|
|
||||||
}
|
|
||||||
|
|
||||||
function swap(orig, v1, v2) {
|
|
||||||
return orig === v1 ? v2 : orig === v2 ? v1 : orig;
|
|
||||||
}
|
|
||||||
|
|
||||||
function startEnd(v, start, end) {
|
|
||||||
return v === "start" ? start : v === "end" ? end : v;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setInflateAmount(
|
|
||||||
properties,
|
|
||||||
{ inflateAmount }: { inflateAmount?: string | number },
|
|
||||||
ratio
|
|
||||||
) {
|
|
||||||
properties.inflateAmount =
|
|
||||||
inflateAmount === "auto" ? (ratio === 1 ? 0.33 : 0) : inflateAmount;
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseValue(entry, item, vScale, i) {
|
function parseValue(entry, item, vScale, i) {
|
||||||
const startValue = vScale.parse(entry.start, i);
|
const startValue = vScale.parse(entry.start, i);
|
||||||
const endValue = vScale.parse(entry.end, i);
|
const endValue = vScale.parse(entry.end, i);
|
||||||
@@ -186,7 +97,7 @@ export class TimelineController extends BarController {
|
|||||||
bars: BarElement[],
|
bars: BarElement[],
|
||||||
start: number,
|
start: number,
|
||||||
count: number,
|
count: number,
|
||||||
mode: "reset" | "resize" | "none" | "hide" | "show" | "default" | "active"
|
mode: "reset" | "resize" | "none" | "hide" | "show" | "normal" | "active"
|
||||||
) {
|
) {
|
||||||
const vScale = this._cachedMeta.vScale!;
|
const vScale = this._cachedMeta.vScale!;
|
||||||
const iScale = this._cachedMeta.iScale!;
|
const iScale = this._cachedMeta.iScale!;
|
||||||
@@ -203,15 +114,15 @@ export class TimelineController extends BarController {
|
|||||||
for (let index = start; index < start + count; index++) {
|
for (let index = start; index < start + count; index++) {
|
||||||
const data = dataset.data[index] as TimeLineData;
|
const data = dataset.data[index] as TimeLineData;
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
const y = vScale.getPixelForValue(this.index);
|
const y = vScale.getPixelForValue(this.index);
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
const xStart = iScale.getPixelForValue(data.start.getTime());
|
const xStart = iScale.getPixelForValue(data.start.getTime());
|
||||||
|
// @ts-ignore
|
||||||
const xEnd = iScale.getPixelForValue(data.end.getTime());
|
const xEnd = iScale.getPixelForValue(data.end.getTime());
|
||||||
const width = xEnd - xStart;
|
const width = xEnd - xStart;
|
||||||
|
|
||||||
const parsed = this.getParsed(index);
|
|
||||||
const stack = (parsed._stacks || {})[vScale.axis];
|
|
||||||
|
|
||||||
const height = 10;
|
const height = 10;
|
||||||
|
|
||||||
const properties: TextBarProps = {
|
const properties: TextBarProps = {
|
||||||
@@ -234,10 +145,7 @@ export class TimelineController extends BarController {
|
|||||||
backgroundColor: data.color,
|
backgroundColor: data.color,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const options = properties.options || bars[index].options;
|
|
||||||
|
|
||||||
setBorderSkipped(properties, options, stack, index);
|
|
||||||
setInflateAmount(properties, options, 1);
|
|
||||||
this.updateElement(bars[index], index, properties as any, mode);
|
this.updateElement(bars[index], index, properties as any, mode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -458,8 +458,7 @@ export class HaDataTable extends LitElement {
|
|||||||
filteredData,
|
filteredData,
|
||||||
this._sortColumns[this._sortColumn],
|
this._sortColumns[this._sortColumn],
|
||||||
this._sortDirection,
|
this._sortDirection,
|
||||||
this._sortColumn,
|
this._sortColumn
|
||||||
this.hass.locale.language
|
|
||||||
)
|
)
|
||||||
: filteredData;
|
: filteredData;
|
||||||
|
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
// To use comlink under ES5
|
// To use comlink under ES5
|
||||||
import { expose } from "comlink";
|
|
||||||
import "proxy-polyfill";
|
import "proxy-polyfill";
|
||||||
import { stringCompare } from "../../common/string/compare";
|
import { expose } from "comlink";
|
||||||
import type {
|
import type {
|
||||||
ClonedDataTableColumnData,
|
ClonedDataTableColumnData,
|
||||||
DataTableRowData,
|
DataTableRowData,
|
||||||
@@ -40,8 +39,7 @@ const sortData = (
|
|||||||
data: DataTableRowData[],
|
data: DataTableRowData[],
|
||||||
column: ClonedDataTableColumnData,
|
column: ClonedDataTableColumnData,
|
||||||
direction: SortingDirection,
|
direction: SortingDirection,
|
||||||
sortColumn: string,
|
sortColumn: string
|
||||||
language?: string
|
|
||||||
) =>
|
) =>
|
||||||
data.sort((a, b) => {
|
data.sort((a, b) => {
|
||||||
let sort = 1;
|
let sort = 1;
|
||||||
@@ -60,8 +58,13 @@ const sortData = (
|
|||||||
if (column.type === "numeric") {
|
if (column.type === "numeric") {
|
||||||
valA = isNaN(valA) ? undefined : Number(valA);
|
valA = isNaN(valA) ? undefined : Number(valA);
|
||||||
valB = isNaN(valB) ? undefined : Number(valB);
|
valB = isNaN(valB) ? undefined : Number(valB);
|
||||||
} else if (typeof valA === "string" && typeof valB === "string") {
|
} else {
|
||||||
return sort * stringCompare(valA, valB, language);
|
if (typeof valA === "string") {
|
||||||
|
valA = valA.toUpperCase();
|
||||||
|
}
|
||||||
|
if (typeof valB === "string") {
|
||||||
|
valB = valB.toUpperCase();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure "undefined" and "null" are always sorted to the bottom
|
// Ensure "undefined" and "null" are always sorted to the bottom
|
||||||
|
@@ -27,12 +27,10 @@ export const filterData = (
|
|||||||
filter: FilterDataParamTypes[2]
|
filter: FilterDataParamTypes[2]
|
||||||
): Promise<ReturnType<FilterDataType>> =>
|
): Promise<ReturnType<FilterDataType>> =>
|
||||||
getWorker().filterData(data, columns, filter);
|
getWorker().filterData(data, columns, filter);
|
||||||
|
|
||||||
export const sortData = (
|
export const sortData = (
|
||||||
data: SortDataParamTypes[0],
|
data: SortDataParamTypes[0],
|
||||||
columns: SortDataParamTypes[1],
|
columns: SortDataParamTypes[1],
|
||||||
direction: SortDataParamTypes[2],
|
direction: SortDataParamTypes[2],
|
||||||
sortColumn: SortDataParamTypes[3],
|
sortColumn: SortDataParamTypes[3]
|
||||||
language?: SortDataParamTypes[4]
|
|
||||||
): Promise<ReturnType<SortDataType>> =>
|
): Promise<ReturnType<SortDataType>> =>
|
||||||
getWorker().sortData(data, columns, direction, sortColumn, language);
|
getWorker().sortData(data, columns, direction, sortColumn);
|
||||||
|
@@ -324,7 +324,6 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
|
|||||||
.renderer=${rowRenderer}
|
.renderer=${rowRenderer}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
.required=${this.required}
|
.required=${this.required}
|
||||||
item-id-path="id"
|
|
||||||
item-value-path="id"
|
item-value-path="id"
|
||||||
item-label-path="name"
|
item-label-path="name"
|
||||||
@opened-changed=${this._openedChanged}
|
@opened-changed=${this._openedChanged}
|
||||||
|
@@ -1,9 +1,11 @@
|
|||||||
import { HassEntity } from "home-assistant-js-websocket";
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
import { LitElement, PropertyValues, html, nothing } from "lit";
|
import { html, LitElement, PropertyValues, nothing } from "lit";
|
||||||
import { customElement, property, query } from "lit/decorators";
|
import { customElement, property, query } from "lit/decorators";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
|
import { computeStateDisplay } from "../../common/entity/compute_state_display";
|
||||||
import { getStates } from "../../common/entity/get_states";
|
import { getStates } from "../../common/entity/get_states";
|
||||||
import { HomeAssistant, ValueChangedEvent } from "../../types";
|
import { computeAttributeValueDisplay } from "../../common/entity/compute_attribute_display";
|
||||||
|
import { ValueChangedEvent, HomeAssistant } from "../../types";
|
||||||
import "../ha-combo-box";
|
import "../ha-combo-box";
|
||||||
import type { HaComboBox } from "../ha-combo-box";
|
import type { HaComboBox } from "../ha-combo-box";
|
||||||
|
|
||||||
@@ -56,9 +58,20 @@ class HaEntityStatePicker extends LitElement {
|
|||||||
? getStates(state, this.attribute).map((key) => ({
|
? getStates(state, this.attribute).map((key) => ({
|
||||||
value: key,
|
value: key,
|
||||||
label: !this.attribute
|
label: !this.attribute
|
||||||
? this.hass.formatEntityState(state, key)
|
? computeStateDisplay(
|
||||||
: this.hass.formatEntityAttributeValue(
|
this.hass.localize,
|
||||||
state,
|
state,
|
||||||
|
this.hass.locale,
|
||||||
|
this.hass.config,
|
||||||
|
this.hass.entities,
|
||||||
|
key
|
||||||
|
)
|
||||||
|
: computeAttributeValueDisplay(
|
||||||
|
this.hass.localize,
|
||||||
|
state,
|
||||||
|
this.hass.locale,
|
||||||
|
this.hass.config,
|
||||||
|
this.hass.entities,
|
||||||
this.attribute,
|
this.attribute,
|
||||||
key
|
key
|
||||||
),
|
),
|
||||||
|
@@ -12,6 +12,7 @@ import { customElement, property, state } from "lit/decorators";
|
|||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
import { arrayLiteralIncludes } from "../../common/array/literal-includes";
|
import { arrayLiteralIncludes } from "../../common/array/literal-includes";
|
||||||
import secondsToDuration from "../../common/datetime/seconds_to_duration";
|
import secondsToDuration from "../../common/datetime/seconds_to_duration";
|
||||||
|
import { computeStateDisplay } from "../../common/entity/compute_state_display";
|
||||||
import { computeStateDomain } from "../../common/entity/compute_state_domain";
|
import { computeStateDomain } from "../../common/entity/compute_state_domain";
|
||||||
import { computeStateName } from "../../common/entity/compute_state_name";
|
import { computeStateName } from "../../common/entity/compute_state_name";
|
||||||
import { FIXED_DOMAIN_STATES } from "../../common/entity/get_states";
|
import { FIXED_DOMAIN_STATES } from "../../common/entity/get_states";
|
||||||
@@ -191,7 +192,13 @@ export class HaStateLabelBadge extends LitElement {
|
|||||||
this.hass!.locale,
|
this.hass!.locale,
|
||||||
getNumberFormatOptions(entityState, entry)
|
getNumberFormatOptions(entityState, entry)
|
||||||
)
|
)
|
||||||
: this.hass!.formatEntityState(entityState);
|
: computeStateDisplay(
|
||||||
|
this.hass!.localize,
|
||||||
|
entityState,
|
||||||
|
this.hass!.locale,
|
||||||
|
this.hass!.config,
|
||||||
|
this.hass!.entities
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,19 +1,12 @@
|
|||||||
import { HassEntity } from "home-assistant-js-websocket";
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
import {
|
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
||||||
css,
|
|
||||||
CSSResultGroup,
|
|
||||||
html,
|
|
||||||
LitElement,
|
|
||||||
nothing,
|
|
||||||
PropertyValues,
|
|
||||||
} from "lit";
|
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { computeAttributeNameDisplay } from "../common/entity/compute_attribute_display";
|
import { computeAttributeNameDisplay } from "../common/entity/compute_attribute_display";
|
||||||
import { STATE_ATTRIBUTES } from "../data/entity_attributes";
|
import { STATE_ATTRIBUTES } from "../data/entity_attributes";
|
||||||
import { haStyle } from "../resources/styles";
|
import { haStyle } from "../resources/styles";
|
||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
import "./ha-attribute-value";
|
|
||||||
import "./ha-expansion-panel";
|
import "./ha-expansion-panel";
|
||||||
|
import "./ha-attribute-value";
|
||||||
|
|
||||||
@customElement("ha-attributes")
|
@customElement("ha-attributes")
|
||||||
class HaAttributes extends LitElement {
|
class HaAttributes extends LitElement {
|
||||||
@@ -25,30 +18,16 @@ class HaAttributes extends LitElement {
|
|||||||
|
|
||||||
@state() private _expanded = false;
|
@state() private _expanded = false;
|
||||||
|
|
||||||
private get _filteredAttributes() {
|
|
||||||
return this.computeDisplayAttributes(
|
|
||||||
STATE_ATTRIBUTES.concat(
|
|
||||||
this.extraFilters ? this.extraFilters.split(",") : []
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected willUpdate(changedProperties: PropertyValues): void {
|
|
||||||
if (
|
|
||||||
changedProperties.has("extraFilters") ||
|
|
||||||
changedProperties.has("stateObj")
|
|
||||||
) {
|
|
||||||
this.toggleAttribute("empty", this._filteredAttributes.length === 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
if (!this.stateObj) {
|
if (!this.stateObj) {
|
||||||
return nothing;
|
return nothing;
|
||||||
}
|
}
|
||||||
|
|
||||||
const attributes = this._filteredAttributes;
|
const attributes = this.computeDisplayAttributes(
|
||||||
|
STATE_ATTRIBUTES.concat(
|
||||||
|
this.extraFilters ? this.extraFilters.split(",") : []
|
||||||
|
)
|
||||||
|
);
|
||||||
if (attributes.length === 0) {
|
if (attributes.length === 0) {
|
||||||
return nothing;
|
return nothing;
|
||||||
}
|
}
|
||||||
|
@@ -1,14 +1,9 @@
|
|||||||
import {
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
css,
|
|
||||||
CSSResultGroup,
|
|
||||||
html,
|
|
||||||
LitElement,
|
|
||||||
nothing,
|
|
||||||
TemplateResult,
|
|
||||||
} from "lit";
|
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import { computeAttributeValueDisplay } from "../common/entity/compute_attribute_display";
|
||||||
|
import { computeStateDisplay } from "../common/entity/compute_state_display";
|
||||||
import { CLIMATE_PRESET_NONE, ClimateEntity } from "../data/climate";
|
import { CLIMATE_PRESET_NONE, ClimateEntity } from "../data/climate";
|
||||||
import { isUnavailableState, OFF } from "../data/entity";
|
import { isUnavailableState } from "../data/entity";
|
||||||
import type { HomeAssistant } from "../types";
|
import type { HomeAssistant } from "../types";
|
||||||
|
|
||||||
@customElement("ha-climate-state")
|
@customElement("ha-climate-state")
|
||||||
@@ -27,24 +22,26 @@ class HaClimateState extends LitElement {
|
|||||||
${this.stateObj.attributes.preset_mode &&
|
${this.stateObj.attributes.preset_mode &&
|
||||||
this.stateObj.attributes.preset_mode !== CLIMATE_PRESET_NONE
|
this.stateObj.attributes.preset_mode !== CLIMATE_PRESET_NONE
|
||||||
? html`-
|
? html`-
|
||||||
${this.hass.formatEntityAttributeValue(
|
${computeAttributeValueDisplay(
|
||||||
|
this.hass.localize,
|
||||||
this.stateObj,
|
this.stateObj,
|
||||||
|
this.hass.locale,
|
||||||
|
this.hass.config,
|
||||||
|
this.hass.entities,
|
||||||
"preset_mode"
|
"preset_mode"
|
||||||
)}`
|
)}`
|
||||||
: nothing}
|
: ""}
|
||||||
</span>
|
</span>
|
||||||
<div class="unit">${this._computeTarget()}</div>`
|
<div class="unit">${this._computeTarget()}</div>`
|
||||||
: this._localizeState()}
|
: this._localizeState()}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
${currentStatus && !isUnavailableState(this.stateObj.state)
|
${currentStatus && !isUnavailableState(this.stateObj.state)
|
||||||
? html`
|
? html`<div class="current">
|
||||||
<div class="current">
|
|
||||||
${this.hass.localize("ui.card.climate.currently")}:
|
${this.hass.localize("ui.card.climate.currently")}:
|
||||||
<div class="unit">${currentStatus}</div>
|
<div class="unit">${currentStatus}</div>
|
||||||
</div>
|
</div>`
|
||||||
`
|
: ""}`;
|
||||||
: nothing}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _computeCurrentStatus(): string | undefined {
|
private _computeCurrentStatus(): string | undefined {
|
||||||
@@ -128,17 +125,24 @@ class HaClimateState extends LitElement {
|
|||||||
return this.hass.localize(`state.default.${this.stateObj.state}`);
|
return this.hass.localize(`state.default.${this.stateObj.state}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const stateString = this.hass.formatEntityState(this.stateObj);
|
const stateString = computeStateDisplay(
|
||||||
|
this.hass.localize,
|
||||||
if (this.stateObj.attributes.hvac_action && this.stateObj.state !== OFF) {
|
|
||||||
const actionString = this.hass.formatEntityAttributeValue(
|
|
||||||
this.stateObj,
|
this.stateObj,
|
||||||
"hvac_action"
|
this.hass.locale,
|
||||||
|
this.hass.config,
|
||||||
|
this.hass.entities
|
||||||
);
|
);
|
||||||
return `${actionString} (${stateString})`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return stateString;
|
return this.stateObj.attributes.hvac_action
|
||||||
|
? `${computeAttributeValueDisplay(
|
||||||
|
this.hass.localize,
|
||||||
|
this.stateObj,
|
||||||
|
this.hass.locale,
|
||||||
|
this.hass.config,
|
||||||
|
this.hass.entities,
|
||||||
|
"hvac_action"
|
||||||
|
)} (${stateString})`
|
||||||
|
: stateString;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
|
@@ -244,6 +244,7 @@ export class HaComboBox extends LitElement {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (overlay) {
|
if (overlay) {
|
||||||
|
overlay.setAttribute("required-vertical-space", "0");
|
||||||
this._removeInert(overlay);
|
this._removeInert(overlay);
|
||||||
}
|
}
|
||||||
this._observeBody();
|
this._observeBody();
|
||||||
@@ -311,10 +312,6 @@ export class HaComboBox extends LitElement {
|
|||||||
|
|
||||||
private _valueChanged(ev: ComboBoxLightValueChangedEvent) {
|
private _valueChanged(ev: ComboBoxLightValueChangedEvent) {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
if (!this.allowCustomValue) {
|
|
||||||
// @ts-ignore
|
|
||||||
this._comboBox._closeOnBlurIsPrevented = true;
|
|
||||||
}
|
|
||||||
const newValue = ev.detail.value;
|
const newValue = ev.detail.value;
|
||||||
|
|
||||||
if (newValue !== this.value) {
|
if (newValue !== this.value) {
|
||||||
@@ -330,7 +327,7 @@ export class HaComboBox extends LitElement {
|
|||||||
}
|
}
|
||||||
vaadin-combo-box-light {
|
vaadin-combo-box-light {
|
||||||
position: relative;
|
position: relative;
|
||||||
--vaadin-combo-box-overlay-max-height: calc(45vh - 56px);
|
--vaadin-combo-box-overlay-max-height: calc(45vh);
|
||||||
}
|
}
|
||||||
ha-textfield {
|
ha-textfield {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@@ -1,260 +0,0 @@
|
|||||||
import { mdiMinus, mdiPlus } from "@mdi/js";
|
|
||||||
import { CSSResultGroup, LitElement, TemplateResult, css, html } from "lit";
|
|
||||||
import { customElement, property, query } from "lit/decorators";
|
|
||||||
import { ifDefined } from "lit/directives/if-defined";
|
|
||||||
import { conditionalClamp } from "../common/number/clamp";
|
|
||||||
import { formatNumber } from "../common/number/format_number";
|
|
||||||
import { FrontendLocaleData } from "../data/translation";
|
|
||||||
import { fireEvent } from "../common/dom/fire_event";
|
|
||||||
|
|
||||||
const A11Y_KEY_CODES = new Set([
|
|
||||||
"ArrowRight",
|
|
||||||
"ArrowUp",
|
|
||||||
"ArrowLeft",
|
|
||||||
"ArrowDown",
|
|
||||||
"PageUp",
|
|
||||||
"PageDown",
|
|
||||||
"Home",
|
|
||||||
"End",
|
|
||||||
]);
|
|
||||||
|
|
||||||
@customElement("ha-control-number-buttons")
|
|
||||||
export class HaControlNumberButton extends LitElement {
|
|
||||||
@property({ attribute: false }) public locale?: FrontendLocaleData;
|
|
||||||
|
|
||||||
@property({ type: Boolean, reflect: true }) disabled = false;
|
|
||||||
|
|
||||||
@property() public label?: string;
|
|
||||||
|
|
||||||
@property({ type: Number }) public step?: number;
|
|
||||||
|
|
||||||
@property({ type: Number }) public value?: number;
|
|
||||||
|
|
||||||
@property({ type: Number }) public min?: number;
|
|
||||||
|
|
||||||
@property({ type: Number }) public max?: number;
|
|
||||||
|
|
||||||
@property({ attribute: "false" })
|
|
||||||
public formatOptions: Intl.NumberFormatOptions = {};
|
|
||||||
|
|
||||||
@query("#input") _input!: HTMLDivElement;
|
|
||||||
|
|
||||||
private boundedValue(value: number) {
|
|
||||||
const clamped = conditionalClamp(value, this.min, this.max);
|
|
||||||
return Math.round(clamped / this._step) * this._step;
|
|
||||||
}
|
|
||||||
|
|
||||||
private get _step() {
|
|
||||||
return this.step ?? 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private get _value() {
|
|
||||||
return this.value ?? 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private get _tenPercentStep() {
|
|
||||||
if (this.max == null || this.min == null) return this._step;
|
|
||||||
const range = this.max - this.min / 10;
|
|
||||||
|
|
||||||
if (range <= this._step) return this._step;
|
|
||||||
return Math.max(range / 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _handlePlusButton() {
|
|
||||||
this._increment();
|
|
||||||
fireEvent(this, "value-changed", { value: this.value });
|
|
||||||
this._input.focus();
|
|
||||||
}
|
|
||||||
|
|
||||||
private _handleMinusButton() {
|
|
||||||
this._decrement();
|
|
||||||
fireEvent(this, "value-changed", { value: this.value });
|
|
||||||
this._input.focus();
|
|
||||||
}
|
|
||||||
|
|
||||||
private _increment() {
|
|
||||||
this.value = this.boundedValue(this._value + this._step);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _decrement() {
|
|
||||||
this.value = this.boundedValue(this._value - this._step);
|
|
||||||
}
|
|
||||||
|
|
||||||
_handleKeyDown(e: KeyboardEvent) {
|
|
||||||
if (this.disabled) return;
|
|
||||||
if (!A11Y_KEY_CODES.has(e.code)) return;
|
|
||||||
e.preventDefault();
|
|
||||||
switch (e.code) {
|
|
||||||
case "ArrowRight":
|
|
||||||
case "ArrowUp":
|
|
||||||
this._increment();
|
|
||||||
break;
|
|
||||||
case "ArrowLeft":
|
|
||||||
case "ArrowDown":
|
|
||||||
this._decrement();
|
|
||||||
break;
|
|
||||||
case "PageUp":
|
|
||||||
this.value = this.boundedValue(this._value + this._tenPercentStep);
|
|
||||||
break;
|
|
||||||
case "PageDown":
|
|
||||||
this.value = this.boundedValue(this._value - this._tenPercentStep);
|
|
||||||
break;
|
|
||||||
case "Home":
|
|
||||||
if (this.min != null) {
|
|
||||||
this.value = this.min;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "End":
|
|
||||||
if (this.max != null) {
|
|
||||||
this.value = this.max;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
fireEvent(this, "value-changed", { value: this.value });
|
|
||||||
}
|
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
|
||||||
const displayedValue =
|
|
||||||
this.value != null
|
|
||||||
? formatNumber(this.value, this.locale, this.formatOptions)
|
|
||||||
: "";
|
|
||||||
|
|
||||||
return html`
|
|
||||||
<div class="container">
|
|
||||||
<div
|
|
||||||
id="input"
|
|
||||||
class="value"
|
|
||||||
role="number-button"
|
|
||||||
.tabIndex=${this.disabled ? "-1" : "0"}
|
|
||||||
aria-valuenow=${this.value}
|
|
||||||
aria-valuemin=${this.min}
|
|
||||||
aria-valuemax=${this.max}
|
|
||||||
aria-label=${ifDefined(this.label)}
|
|
||||||
?disabled=${this.disabled}
|
|
||||||
@keydown=${this._handleKeyDown}
|
|
||||||
>
|
|
||||||
${displayedValue}
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
class="button minus"
|
|
||||||
type="button"
|
|
||||||
tabindex="-1"
|
|
||||||
aria-label="decrement"
|
|
||||||
@click=${this._handleMinusButton}
|
|
||||||
.disabled=${this.disabled ||
|
|
||||||
(this.min != null && this._value <= this.min)}
|
|
||||||
>
|
|
||||||
<ha-svg-icon aria-hidden .path=${mdiMinus}></ha-svg-icon>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="button plus"
|
|
||||||
type="button"
|
|
||||||
tabindex="-1"
|
|
||||||
aria-label="increment"
|
|
||||||
@click=${this._handlePlusButton}
|
|
||||||
.disabled=${this.disabled ||
|
|
||||||
(this.max != null && this._value >= this.max)}
|
|
||||||
>
|
|
||||||
<ha-svg-icon aria-hidden .path=${mdiPlus}></ha-svg-icon>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
|
||||||
return css`
|
|
||||||
:host {
|
|
||||||
display: block;
|
|
||||||
--control-number-buttons-focus-color: var(--primary-color);
|
|
||||||
--control-number-buttons-background-color: var(--disabled-color);
|
|
||||||
--control-number-buttons-background-opacity: 0.2;
|
|
||||||
--control-number-buttons-border-radius: 10px;
|
|
||||||
--mdc-icon-size: 16px;
|
|
||||||
height: 40px;
|
|
||||||
width: 200px;
|
|
||||||
color: var(--primary-text-color);
|
|
||||||
-webkit-tap-highlight-color: transparent;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 500;
|
|
||||||
transition: color 180ms ease-in-out;
|
|
||||||
}
|
|
||||||
:host([disabled]) {
|
|
||||||
color: var(--disabled-color);
|
|
||||||
}
|
|
||||||
.container {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
.value {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
overflow: hidden;
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
padding: 0 44px;
|
|
||||||
border-radius: var(--control-number-buttons-border-radius);
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
line-height: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
/* For safari border-radius overflow */
|
|
||||||
z-index: 0;
|
|
||||||
font-size: inherit;
|
|
||||||
color: inherit;
|
|
||||||
user-select: none;
|
|
||||||
-webkit-tap-highlight-color: transparent;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
.value::before {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
background-color: var(--control-number-buttons-background-color);
|
|
||||||
transition:
|
|
||||||
background-color 180ms ease-in-out,
|
|
||||||
opacity 180ms ease-in-out;
|
|
||||||
opacity: var(--control-number-buttons-background-opacity);
|
|
||||||
}
|
|
||||||
.value:focus-visible {
|
|
||||||
box-shadow: 0 0 0 2px var(--control-number-buttons-focus-color);
|
|
||||||
}
|
|
||||||
.button {
|
|
||||||
color: inherit;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
padding: 0;
|
|
||||||
width: 35px;
|
|
||||||
height: 40px;
|
|
||||||
border: none;
|
|
||||||
background: none;
|
|
||||||
cursor: pointer;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
.button[disabled] {
|
|
||||||
opacity: 0.4;
|
|
||||||
pointer-events: none;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
.button.minus {
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
.button.plus {
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
"ha-control-number-buttons": HaControlNumberButton;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -171,12 +171,15 @@ export class HaControlSelectMenu extends SelectBase {
|
|||||||
--control-select-menu-background-color: var(--disabled-color);
|
--control-select-menu-background-color: var(--disabled-color);
|
||||||
--control-select-menu-background-opacity: 0.2;
|
--control-select-menu-background-opacity: 0.2;
|
||||||
--control-select-menu-border-radius: 14px;
|
--control-select-menu-border-radius: 14px;
|
||||||
|
--control-select-menu-min-width: 120px;
|
||||||
|
--control-select-menu-max-width: 200px;
|
||||||
|
--control-select-menu-width: 100%;
|
||||||
--mdc-icon-size: 20px;
|
--mdc-icon-size: 20px;
|
||||||
width: auto;
|
|
||||||
color: var(--primary-text-color);
|
color: var(--primary-text-color);
|
||||||
-webkit-tap-highlight-color: transparent;
|
-webkit-tap-highlight-color: transparent;
|
||||||
}
|
}
|
||||||
.select-anchor {
|
.select-anchor {
|
||||||
|
color: var(--control-select-menu-text-color);
|
||||||
height: 48px;
|
height: 48px;
|
||||||
padding: 6px 10px;
|
padding: 6px 10px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -195,8 +198,11 @@ export class HaControlSelectMenu extends SelectBase {
|
|||||||
z-index: 0;
|
z-index: 0;
|
||||||
font-size: inherit;
|
font-size: inherit;
|
||||||
transition: color 180ms ease-in-out;
|
transition: color 180ms ease-in-out;
|
||||||
|
color: var(--control-text-icon-color);
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
width: 100%;
|
min-width: var(--control-select-menu-min-width);
|
||||||
|
max-width: var(--control-select-menu-max-width);
|
||||||
|
width: var(--control-select-menu-width);
|
||||||
user-select: none;
|
user-select: none;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
|
@@ -217,7 +217,6 @@ export class HaControlSelect extends LitElement {
|
|||||||
transition: box-shadow 180ms ease-in-out;
|
transition: box-shadow 180ms ease-in-out;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: var(--primary-text-color);
|
|
||||||
user-select: none;
|
user-select: none;
|
||||||
-webkit-tap-highlight-color: transparent;
|
-webkit-tap-highlight-color: transparent;
|
||||||
}
|
}
|
||||||
@@ -268,6 +267,7 @@ export class HaControlSelect extends LitElement {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
border-radius: var(--control-select-button-border-radius);
|
border-radius: var(--control-select-button-border-radius);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
color: var(--primary-text-color);
|
||||||
/* For safari border-radius overflow */
|
/* For safari border-radius overflow */
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
}
|
}
|
||||||
@@ -331,7 +331,6 @@ export class HaControlSelect extends LitElement {
|
|||||||
:host([disabled]) {
|
:host([disabled]) {
|
||||||
--control-select-color: var(--disabled-color);
|
--control-select-color: var(--disabled-color);
|
||||||
--control-select-focused-opacity: 0;
|
--control-select-focused-opacity: 0;
|
||||||
color: var(--disabled-color);
|
|
||||||
}
|
}
|
||||||
:host([disabled]) .option {
|
:host([disabled]) .option {
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
|
@@ -10,12 +10,12 @@ import "./ha-icon-button";
|
|||||||
const SUPPRESS_DEFAULT_PRESS_SELECTOR = ["button", "ha-list-item"];
|
const SUPPRESS_DEFAULT_PRESS_SELECTOR = ["button", "ha-list-item"];
|
||||||
|
|
||||||
export const createCloseHeading = (
|
export const createCloseHeading = (
|
||||||
hass: HomeAssistant | undefined,
|
hass: HomeAssistant,
|
||||||
title: string | TemplateResult
|
title: string | TemplateResult
|
||||||
) => html`
|
) => html`
|
||||||
<div class="header_title">${title}</div>
|
<div class="header_title">${title}</div>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
.label=${hass?.localize("ui.dialogs.generic.close") ?? "Close"}
|
.label=${hass.localize("ui.dialogs.generic.close")}
|
||||||
.path=${mdiClose}
|
.path=${mdiClose}
|
||||||
dialogAction="close"
|
dialogAction="close"
|
||||||
class="header_button"
|
class="header_button"
|
||||||
|
@@ -1,19 +1,16 @@
|
|||||||
import "@material/mwc-linear-progress/mwc-linear-progress";
|
import { styles } from "@material/mwc-textfield/mwc-textfield.css";
|
||||||
import { mdiDelete, mdiFileUpload } from "@mdi/js";
|
import { mdiClose } from "@mdi/js";
|
||||||
import { LitElement, PropertyValues, TemplateResult, css, html } from "lit";
|
import { css, html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
import { fireEvent } from "../common/dom/fire_event";
|
import { fireEvent } from "../common/dom/fire_event";
|
||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
import "./ha-button";
|
import "./ha-circular-progress";
|
||||||
import "./ha-icon-button";
|
import "./ha-icon-button";
|
||||||
import { blankBeforePercent } from "../common/translations/blank_before_percent";
|
|
||||||
import { ensureArray } from "../common/array/ensure-array";
|
|
||||||
import { bytesToString } from "../util/bytes-to-string";
|
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HASSDomEvents {
|
interface HASSDomEvents {
|
||||||
"file-picked": { files: File[] };
|
"file-picked": { files: FileList };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,22 +22,12 @@ export class HaFileUpload extends LitElement {
|
|||||||
|
|
||||||
@property() public icon?: string;
|
@property() public icon?: string;
|
||||||
|
|
||||||
@property() public label?: string;
|
@property() public label!: string;
|
||||||
|
|
||||||
@property() public secondary?: string;
|
@property() public value: string | TemplateResult | null = null;
|
||||||
|
|
||||||
@property() public supports?: string;
|
|
||||||
|
|
||||||
@property() public value?: File | File[] | FileList | string;
|
|
||||||
|
|
||||||
@property({ type: Boolean }) private multiple = false;
|
|
||||||
|
|
||||||
@property({ type: Boolean, reflect: true }) public disabled: boolean = false;
|
|
||||||
|
|
||||||
@property({ type: Boolean }) private uploading = false;
|
@property({ type: Boolean }) private uploading = false;
|
||||||
|
|
||||||
@property({ type: Number }) private progress?: number;
|
|
||||||
|
|
||||||
@property({ type: Boolean, attribute: "auto-open-file-dialog" })
|
@property({ type: Boolean, attribute: "auto-open-file-dialog" })
|
||||||
private autoOpenFileDialog = false;
|
private autoOpenFileDialog = false;
|
||||||
|
|
||||||
@@ -58,102 +45,72 @@ export class HaFileUpload extends LitElement {
|
|||||||
public render(): TemplateResult {
|
public render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
${this.uploading
|
${this.uploading
|
||||||
? html`<div class="container">
|
? html`<ha-circular-progress
|
||||||
<div class="row">
|
alt="Uploading"
|
||||||
<span class="header"
|
size="large"
|
||||||
>${this.value
|
active
|
||||||
? this.hass?.localize(
|
></ha-circular-progress>`
|
||||||
"ui.components.file-upload.uploading_name",
|
: html`
|
||||||
{ name: this.value }
|
<label
|
||||||
)
|
for="input"
|
||||||
: this.hass?.localize(
|
class="mdc-text-field mdc-text-field--filled ${classMap({
|
||||||
"ui.components.file-upload.uploading"
|
"mdc-text-field--focused": this._drag,
|
||||||
)}</span
|
"mdc-text-field--with-leading-icon": Boolean(this.icon),
|
||||||
>
|
"mdc-text-field--with-trailing-icon": Boolean(this.value),
|
||||||
${this.progress
|
|
||||||
? html`<span class="progress"
|
|
||||||
>${this.progress}${blankBeforePercent(
|
|
||||||
this.hass!.locale
|
|
||||||
)}%</span
|
|
||||||
>`
|
|
||||||
: ""}
|
|
||||||
</div>
|
|
||||||
<mwc-linear-progress
|
|
||||||
.indeterminate=${!this.progress}
|
|
||||||
.progress=${this.progress ? this.progress / 100 : undefined}
|
|
||||||
></mwc-linear-progress>
|
|
||||||
</div>`
|
|
||||||
: html`<label
|
|
||||||
for=${this.value ? "" : "input"}
|
|
||||||
class="container ${classMap({
|
|
||||||
dragged: this._drag,
|
|
||||||
multiple: this.multiple,
|
|
||||||
value: Boolean(this.value),
|
|
||||||
})}"
|
})}"
|
||||||
@drop=${this._handleDrop}
|
@drop=${this._handleDrop}
|
||||||
@dragenter=${this._handleDragStart}
|
@dragenter=${this._handleDragStart}
|
||||||
@dragover=${this._handleDragStart}
|
@dragover=${this._handleDragStart}
|
||||||
@dragleave=${this._handleDragEnd}
|
@dragleave=${this._handleDragEnd}
|
||||||
@dragend=${this._handleDragEnd}
|
@dragend=${this._handleDragEnd}
|
||||||
>${!this.value
|
|
||||||
? html`<ha-svg-icon
|
|
||||||
class="big-icon"
|
|
||||||
.path=${this.icon || mdiFileUpload}
|
|
||||||
></ha-svg-icon>
|
|
||||||
<ha-button unelevated @click=${this._openFilePicker}>
|
|
||||||
${this.label ||
|
|
||||||
this.hass?.localize("ui.components.file-upload.label")}
|
|
||||||
</ha-button>
|
|
||||||
<span class="secondary"
|
|
||||||
>${this.secondary ||
|
|
||||||
this.hass?.localize(
|
|
||||||
"ui.components.file-upload.secondary"
|
|
||||||
)}</span
|
|
||||||
>
|
>
|
||||||
<span class="supports">${this.supports}</span>`
|
<span class="mdc-text-field__ripple"></span>
|
||||||
: typeof this.value === "string"
|
<span
|
||||||
? html`<div class="row">
|
class="mdc-floating-label ${this.value || this._drag
|
||||||
<div class="value" @click=${this._openFilePicker}>
|
? "mdc-floating-label--float-above"
|
||||||
<ha-svg-icon
|
: ""}"
|
||||||
.path=${this.icon || mdiFileUpload}
|
id="label"
|
||||||
></ha-svg-icon>
|
>${this.label}</span
|
||||||
${this.value}
|
>
|
||||||
</div>
|
${this.icon
|
||||||
|
? html`<span
|
||||||
|
class="mdc-text-field__icon mdc-text-field__icon--leading"
|
||||||
|
>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
@click=${this._clearValue}
|
@click=${this._openFilePicker}
|
||||||
.label=${this.hass?.localize("ui.common.delete") ||
|
.path=${this.icon}
|
||||||
"Delete"}
|
|
||||||
.path=${mdiDelete}
|
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
</div>`
|
</span>`
|
||||||
: (this.value instanceof FileList
|
: ""}
|
||||||
? Array.from(this.value)
|
<div class="value">${this.value}</div>
|
||||||
: ensureArray(this.value)
|
|
||||||
).map(
|
|
||||||
(file) =>
|
|
||||||
html`<div class="row">
|
|
||||||
<div class="value" @click=${this._openFilePicker}>
|
|
||||||
<ha-svg-icon
|
|
||||||
.path=${this.icon || mdiFileUpload}
|
|
||||||
></ha-svg-icon>
|
|
||||||
${file.name} - ${bytesToString(file.size)}
|
|
||||||
</div>
|
|
||||||
<ha-icon-button
|
|
||||||
@click=${this._clearValue}
|
|
||||||
.label=${this.hass?.localize("ui.common.delete") ||
|
|
||||||
"Delete"}
|
|
||||||
.path=${mdiDelete}
|
|
||||||
></ha-icon-button>
|
|
||||||
</div>`
|
|
||||||
)}
|
|
||||||
<input
|
<input
|
||||||
id="input"
|
id="input"
|
||||||
type="file"
|
type="file"
|
||||||
class="file"
|
class="mdc-text-field__input file"
|
||||||
.accept=${this.accept}
|
accept=${this.accept}
|
||||||
.multiple=${this.multiple}
|
|
||||||
@change=${this._handleFilePicked}
|
@change=${this._handleFilePicked}
|
||||||
/></label>`}
|
aria-labelledby="label"
|
||||||
|
/>
|
||||||
|
${this.value
|
||||||
|
? html`<span
|
||||||
|
class="mdc-text-field__icon mdc-text-field__icon--trailing"
|
||||||
|
>
|
||||||
|
<ha-icon-button
|
||||||
|
slot="suffix"
|
||||||
|
@click=${this._clearValue}
|
||||||
|
.label=${this.hass?.localize("ui.common.close") ||
|
||||||
|
"close"}
|
||||||
|
.path=${mdiClose}
|
||||||
|
></ha-icon-button>
|
||||||
|
</span>`
|
||||||
|
: ""}
|
||||||
|
<span
|
||||||
|
class="mdc-line-ripple ${this._drag
|
||||||
|
? "mdc-line-ripple--active"
|
||||||
|
: ""}"
|
||||||
|
></span>
|
||||||
|
</label>
|
||||||
|
`}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,12 +122,7 @@ export class HaFileUpload extends LitElement {
|
|||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
if (ev.dataTransfer?.files) {
|
if (ev.dataTransfer?.files) {
|
||||||
fireEvent(this, "file-picked", {
|
fireEvent(this, "file-picked", { files: ev.dataTransfer.files });
|
||||||
files:
|
|
||||||
this.multiple || ev.dataTransfer.files.length === 1
|
|
||||||
? Array.from(ev.dataTransfer.files)
|
|
||||||
: [ev.dataTransfer.files[0]],
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
this._drag = false;
|
this._drag = false;
|
||||||
}
|
}
|
||||||
@@ -188,121 +140,93 @@ export class HaFileUpload extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _handleFilePicked(ev) {
|
private _handleFilePicked(ev) {
|
||||||
if (ev.target.files.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.value = ev.target.files;
|
|
||||||
fireEvent(this, "file-picked", { files: ev.target.files });
|
fireEvent(this, "file-picked", { files: ev.target.files });
|
||||||
}
|
}
|
||||||
|
|
||||||
private _clearValue(ev: Event) {
|
private _clearValue(ev: Event) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
this.value = null;
|
||||||
this._input!.value = "";
|
this._input!.value = "";
|
||||||
this.value = undefined;
|
|
||||||
fireEvent(this, "change");
|
fireEvent(this, "change");
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return css`
|
return [
|
||||||
|
styles,
|
||||||
|
css`
|
||||||
:host {
|
:host {
|
||||||
display: block;
|
display: block;
|
||||||
height: 240px;
|
|
||||||
}
|
}
|
||||||
:host([disabled]) {
|
.mdc-text-field--filled {
|
||||||
pointer-events: none;
|
height: auto;
|
||||||
color: var(--disabled-text-color);
|
padding-top: 16px;
|
||||||
}
|
|
||||||
.container {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
border: solid 1px
|
|
||||||
var(--mdc-text-field-idle-line-color, rgba(0, 0, 0, 0.42));
|
|
||||||
border-radius: var(--mdc-shape-small, 4px);
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
label.container {
|
|
||||||
border: dashed 1px
|
|
||||||
var(--mdc-text-field-idle-line-color, rgba(0, 0, 0, 0.42));
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
:host([disabled]) .container {
|
.mdc-text-field--filled.mdc-text-field--with-trailing-icon {
|
||||||
border-color: var(--disabled-color);
|
padding-top: 28px;
|
||||||
}
|
}
|
||||||
label.dragged {
|
.mdc-text-field:not(.mdc-text-field--disabled) .mdc-text-field__icon {
|
||||||
border-color: var(--primary-color);
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
|
.mdc-text-field--filled.mdc-text-field--with-trailing-icon
|
||||||
|
.mdc-text-field__icon {
|
||||||
|
align-self: flex-end;
|
||||||
|
}
|
||||||
|
.mdc-text-field__icon--leading {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
inset-inline-start: initial;
|
||||||
|
inset-inline-end: 0px;
|
||||||
|
direction: var(--direction);
|
||||||
|
}
|
||||||
|
.mdc-text-field--filled .mdc-floating-label--float-above {
|
||||||
|
transform: scale(0.75);
|
||||||
|
top: 8px;
|
||||||
|
}
|
||||||
|
.mdc-floating-label {
|
||||||
|
inset-inline-start: 16px !important;
|
||||||
|
inset-inline-end: initial !important;
|
||||||
|
direction: var(--direction);
|
||||||
|
}
|
||||||
|
.mdc-text-field--filled .mdc-floating-label {
|
||||||
|
inset-inline-start: 48px !important;
|
||||||
|
inset-inline-end: initial !important;
|
||||||
|
direction: var(--direction);
|
||||||
|
}
|
||||||
|
.mdc-text-field__icon--trailing {
|
||||||
|
pointer-events: auto !important;
|
||||||
}
|
}
|
||||||
.dragged:before {
|
.dragged:before {
|
||||||
position: absolute;
|
position: var(--layout-fit_-_position);
|
||||||
top: 0;
|
top: var(--layout-fit_-_top);
|
||||||
right: 0;
|
right: var(--layout-fit_-_right);
|
||||||
bottom: 0;
|
bottom: var(--layout-fit_-_bottom);
|
||||||
left: 0;
|
left: var(--layout-fit_-_left);
|
||||||
background-color: var(--primary-color);
|
background: currentColor;
|
||||||
content: "";
|
content: "";
|
||||||
opacity: var(--dark-divider-opacity);
|
opacity: var(--dark-divider-opacity);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
border-radius: var(--mdc-shape-small, 4px);
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
label.value {
|
.value {
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
label.value.multiple {
|
|
||||||
justify-content: unset;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
.highlight {
|
|
||||||
color: var(--primary-color);
|
|
||||||
}
|
|
||||||
.row {
|
|
||||||
display: flex;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: 0 16px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
ha-button {
|
|
||||||
margin-bottom: 4px;
|
|
||||||
}
|
|
||||||
.supports {
|
|
||||||
color: var(--secondary-text-color);
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
:host([disabled]) .secondary {
|
|
||||||
color: var(--disabled-text-color);
|
|
||||||
}
|
}
|
||||||
input.file {
|
input.file {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
.value {
|
img {
|
||||||
cursor: pointer;
|
max-width: 100%;
|
||||||
|
max-height: 125px;
|
||||||
}
|
}
|
||||||
.value ha-svg-icon {
|
ha-icon-button {
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
.big-icon {
|
|
||||||
--mdc-icon-size: 48px;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
ha-button {
|
|
||||||
--mdc-button-outline-color: var(--primary-color);
|
|
||||||
--mdc-icon-button-size: 24px;
|
--mdc-icon-button-size: 24px;
|
||||||
|
--mdc-icon-size: 20px;
|
||||||
}
|
}
|
||||||
mwc-linear-progress {
|
ha-circular-progress {
|
||||||
width: 100%;
|
display: block;
|
||||||
padding: 16px;
|
text-align-last: center;
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
}
|
||||||
.header {
|
`,
|
||||||
font-weight: 500;
|
];
|
||||||
}
|
|
||||||
.progress {
|
|
||||||
color: var(--secondary-text-color);
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -68,7 +68,6 @@ export class HaFormString extends LitElement implements HaFormElement {
|
|||||||
: this.schema.description?.suffix}
|
: this.schema.description?.suffix}
|
||||||
.validationMessage=${this.schema.required ? "Required" : undefined}
|
.validationMessage=${this.schema.required ? "Required" : undefined}
|
||||||
@input=${this._valueChanged}
|
@input=${this._valueChanged}
|
||||||
@change=${this._valueChanged}
|
|
||||||
></ha-textfield>
|
></ha-textfield>
|
||||||
${isPassword
|
${isPassword
|
||||||
? html`<ha-icon-button
|
? html`<ha-icon-button
|
||||||
|
@@ -7,12 +7,6 @@ import { hsv2rgb, rgb2hex } from "../common/color/convert-color";
|
|||||||
import { rgbw2rgb, rgbww2rgb } from "../common/color/convert-light-color";
|
import { rgbw2rgb, rgbww2rgb } from "../common/color/convert-light-color";
|
||||||
import { fireEvent } from "../common/dom/fire_event";
|
import { fireEvent } from "../common/dom/fire_event";
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HASSDomEvents {
|
|
||||||
"cursor-moved": { value?: any };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function xy2polar(x: number, y: number) {
|
function xy2polar(x: number, y: number) {
|
||||||
const r = Math.sqrt(x * x + y * y);
|
const r = Math.sqrt(x * x + y * y);
|
||||||
const phi = Math.atan2(y, x);
|
const phi = Math.atan2(y, x);
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import { computeAttributeValueDisplay } from "../common/entity/compute_attribute_display";
|
||||||
|
import { computeStateDisplay } from "../common/entity/compute_state_display";
|
||||||
import { isUnavailableState, OFF } from "../data/entity";
|
import { isUnavailableState, OFF } from "../data/entity";
|
||||||
import { HumidifierEntity } from "../data/humidifier";
|
import { HumidifierEntity } from "../data/humidifier";
|
||||||
import type { HomeAssistant } from "../types";
|
import type { HomeAssistant } from "../types";
|
||||||
@@ -19,8 +21,12 @@ class HaHumidifierState extends LitElement {
|
|||||||
${this._localizeState()}
|
${this._localizeState()}
|
||||||
${this.stateObj.attributes.mode
|
${this.stateObj.attributes.mode
|
||||||
? html`-
|
? html`-
|
||||||
${this.hass.formatEntityAttributeValue(
|
${computeAttributeValueDisplay(
|
||||||
|
this.hass.localize,
|
||||||
this.stateObj,
|
this.stateObj,
|
||||||
|
this.hass.locale,
|
||||||
|
this.hass.config,
|
||||||
|
this.hass.entities,
|
||||||
"mode"
|
"mode"
|
||||||
)}`
|
)}`
|
||||||
: ""}
|
: ""}
|
||||||
@@ -72,17 +78,24 @@ class HaHumidifierState extends LitElement {
|
|||||||
return this.hass.localize(`state.default.${this.stateObj.state}`);
|
return this.hass.localize(`state.default.${this.stateObj.state}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const stateString = this.hass.formatEntityState(this.stateObj);
|
const stateString = computeStateDisplay(
|
||||||
|
this.hass.localize,
|
||||||
if (this.stateObj.attributes.action && this.stateObj.state !== OFF) {
|
|
||||||
const actionString = this.hass.formatEntityAttributeValue(
|
|
||||||
this.stateObj,
|
this.stateObj,
|
||||||
"action"
|
this.hass.locale,
|
||||||
|
this.hass.config,
|
||||||
|
this.hass.entities
|
||||||
);
|
);
|
||||||
return `${actionString} (${stateString})`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return stateString;
|
return this.stateObj.attributes.action && this.stateObj.state !== OFF
|
||||||
|
? `${computeAttributeValueDisplay(
|
||||||
|
this.hass.localize,
|
||||||
|
this.stateObj,
|
||||||
|
this.hass.locale,
|
||||||
|
this.hass.config,
|
||||||
|
this.hass.entities,
|
||||||
|
"action"
|
||||||
|
)} (${stateString})`
|
||||||
|
: stateString;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
|
@@ -7,7 +7,6 @@ import { formatLanguageCode } from "../common/language/format_language";
|
|||||||
import { caseInsensitiveStringCompare } from "../common/string/compare";
|
import { caseInsensitiveStringCompare } from "../common/string/compare";
|
||||||
import { FrontendLocaleData } from "../data/translation";
|
import { FrontendLocaleData } from "../data/translation";
|
||||||
import "../resources/intl-polyfill";
|
import "../resources/intl-polyfill";
|
||||||
import { translationMetadata } from "../resources/translations-metadata";
|
|
||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
import "./ha-list-item";
|
import "./ha-list-item";
|
||||||
import "./ha-select";
|
import "./ha-select";
|
||||||
@@ -21,7 +20,7 @@ export class HaLanguagePicker extends LitElement {
|
|||||||
|
|
||||||
@property() public languages?: string[];
|
@property() public languages?: string[];
|
||||||
|
|
||||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property({ type: Boolean, reflect: true }) public disabled = false;
|
@property({ type: Boolean, reflect: true }) public disabled = false;
|
||||||
|
|
||||||
@@ -42,18 +41,7 @@ export class HaLanguagePicker extends LitElement {
|
|||||||
|
|
||||||
protected updated(changedProperties: PropertyValues) {
|
protected updated(changedProperties: PropertyValues) {
|
||||||
super.updated(changedProperties);
|
super.updated(changedProperties);
|
||||||
|
if (changedProperties.has("languages") || changedProperties.has("value")) {
|
||||||
const localeChanged =
|
|
||||||
changedProperties.has("hass") &&
|
|
||||||
this.hass &&
|
|
||||||
changedProperties.get("hass") &&
|
|
||||||
changedProperties.get("hass").locale.language !==
|
|
||||||
this.hass.locale.language;
|
|
||||||
if (
|
|
||||||
changedProperties.has("languages") ||
|
|
||||||
changedProperties.has("value") ||
|
|
||||||
localeChanged
|
|
||||||
) {
|
|
||||||
this._select.layoutOptions();
|
this._select.layoutOptions();
|
||||||
if (this._select.value !== this.value) {
|
if (this._select.value !== this.value) {
|
||||||
fireEvent(this, "value-changed", { value: this._select.value });
|
fireEvent(this, "value-changed", { value: this._select.value });
|
||||||
@@ -63,27 +51,24 @@ export class HaLanguagePicker extends LitElement {
|
|||||||
}
|
}
|
||||||
const languageOptions = this._getLanguagesOptions(
|
const languageOptions = this._getLanguagesOptions(
|
||||||
this.languages ?? this._defaultLanguages,
|
this.languages ?? this._defaultLanguages,
|
||||||
this.nativeName,
|
this.hass.locale,
|
||||||
this.hass?.locale
|
this.nativeName
|
||||||
);
|
);
|
||||||
const selectedItemIndex = languageOptions.findIndex(
|
const selectedItem = languageOptions.find(
|
||||||
(option) => option.value === this.value
|
(option) => option.value === this.value
|
||||||
);
|
);
|
||||||
if (selectedItemIndex === -1) {
|
if (!selectedItem) {
|
||||||
this.value = undefined;
|
this.value = undefined;
|
||||||
}
|
}
|
||||||
if (localeChanged) {
|
|
||||||
this._select.select(selectedItemIndex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getLanguagesOptions = memoizeOne(
|
private _getLanguagesOptions = memoizeOne(
|
||||||
(languages: string[], nativeName: boolean, locale?: FrontendLocaleData) => {
|
(languages: string[], locale: FrontendLocaleData, nativeName: boolean) => {
|
||||||
let options: { label: string; value: string }[] = [];
|
let options: { label: string; value: string }[] = [];
|
||||||
|
|
||||||
if (nativeName) {
|
if (nativeName) {
|
||||||
const translations = translationMetadata.translations;
|
const translations = this.hass.translationMetadata.translations;
|
||||||
options = languages.map((lang) => {
|
options = languages.map((lang) => {
|
||||||
let label = translations[lang]?.nativeName;
|
let label = translations[lang]?.nativeName;
|
||||||
if (!label) {
|
if (!label) {
|
||||||
@@ -102,14 +87,14 @@ export class HaLanguagePicker extends LitElement {
|
|||||||
label,
|
label,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
} else if (locale) {
|
} else {
|
||||||
options = languages.map((lang) => ({
|
options = languages.map((lang) => ({
|
||||||
value: lang,
|
value: lang,
|
||||||
label: formatLanguageCode(lang, locale),
|
label: formatLanguageCode(lang, locale),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.noSort && locale) {
|
if (!this.noSort) {
|
||||||
options.sort((a, b) =>
|
options.sort((a, b) =>
|
||||||
caseInsensitiveStringCompare(a.label, b.label, locale.language)
|
caseInsensitiveStringCompare(a.label, b.label, locale.language)
|
||||||
);
|
);
|
||||||
@@ -119,14 +104,20 @@ export class HaLanguagePicker extends LitElement {
|
|||||||
);
|
);
|
||||||
|
|
||||||
private _computeDefaultLanguageOptions() {
|
private _computeDefaultLanguageOptions() {
|
||||||
this._defaultLanguages = Object.keys(translationMetadata.translations);
|
if (!this.hass.translationMetadata?.translations) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._defaultLanguages = Object.keys(
|
||||||
|
this.hass.translationMetadata.translations
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
const languageOptions = this._getLanguagesOptions(
|
const languageOptions = this._getLanguagesOptions(
|
||||||
this.languages ?? this._defaultLanguages,
|
this.languages ?? this._defaultLanguages,
|
||||||
this.nativeName,
|
this.hass.locale,
|
||||||
this.hass?.locale
|
this.nativeName
|
||||||
);
|
);
|
||||||
|
|
||||||
const value =
|
const value =
|
||||||
@@ -134,10 +125,9 @@ export class HaLanguagePicker extends LitElement {
|
|||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-select
|
<ha-select
|
||||||
.label=${this.label ??
|
.label=${this.label ||
|
||||||
(this.hass?.localize("ui.components.language-picker.language") ||
|
this.hass.localize("ui.components.language-picker.language")}
|
||||||
"Language")}
|
.value=${value}
|
||||||
.value=${value || ""}
|
|
||||||
.required=${this.required}
|
.required=${this.required}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
@selected=${this._changed}
|
@selected=${this._changed}
|
||||||
@@ -147,9 +137,9 @@ export class HaLanguagePicker extends LitElement {
|
|||||||
>
|
>
|
||||||
${languageOptions.length === 0
|
${languageOptions.length === 0
|
||||||
? html`<ha-list-item value=""
|
? html`<ha-list-item value=""
|
||||||
>${this.hass?.localize(
|
>${this.hass.localize(
|
||||||
"ui.components.language-picker.no_languages"
|
"ui.components.language-picker.no_languages"
|
||||||
) || "No languages"}</ha-list-item
|
)}</ha-list-item
|
||||||
>`
|
>`
|
||||||
: languageOptions.map(
|
: languageOptions.map(
|
||||||
(option) => html`
|
(option) => html`
|
||||||
@@ -172,7 +162,7 @@ export class HaLanguagePicker extends LitElement {
|
|||||||
|
|
||||||
private _changed(ev): void {
|
private _changed(ev): void {
|
||||||
const target = ev.target as HaSelect;
|
const target = ev.target as HaSelect;
|
||||||
if (target.value === "" || target.value === this.value) {
|
if (!this.hass || target.value === "" || target.value === this.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.value = target.value;
|
this.value = target.value;
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { mdiImagePlus } from "@mdi/js";
|
import { mdiImagePlus } from "@mdi/js";
|
||||||
import { LitElement, TemplateResult, css, html } from "lit";
|
import { html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { fireEvent } from "../common/dom/fire_event";
|
import { fireEvent } from "../common/dom/fire_event";
|
||||||
import { createImage, generateImageThumbnailUrl } from "../data/image_upload";
|
import { createImage, generateImageThumbnailUrl } from "../data/image_upload";
|
||||||
@@ -9,7 +9,6 @@ import {
|
|||||||
showImageCropperDialog,
|
showImageCropperDialog,
|
||||||
} from "../dialogs/image-cropper-dialog/show-image-cropper-dialog";
|
} from "../dialogs/image-cropper-dialog/show-image-cropper-dialog";
|
||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
import "./ha-button";
|
|
||||||
import "./ha-circular-progress";
|
import "./ha-circular-progress";
|
||||||
import "./ha-file-upload";
|
import "./ha-file-upload";
|
||||||
|
|
||||||
@@ -21,12 +20,6 @@ export class HaPictureUpload extends LitElement {
|
|||||||
|
|
||||||
@property() public label?: string;
|
@property() public label?: string;
|
||||||
|
|
||||||
@property() public secondary?: string;
|
|
||||||
|
|
||||||
@property() public supports?: string;
|
|
||||||
|
|
||||||
@property() public currentImageAltText?: string;
|
|
||||||
|
|
||||||
@property({ type: Boolean }) public crop = false;
|
@property({ type: Boolean }) public crop = false;
|
||||||
|
|
||||||
@property({ attribute: false }) public cropOptions?: CropOptions;
|
@property({ attribute: false }) public cropOptions?: CropOptions;
|
||||||
@@ -36,45 +29,20 @@ export class HaPictureUpload extends LitElement {
|
|||||||
@state() private _uploading = false;
|
@state() private _uploading = false;
|
||||||
|
|
||||||
public render(): TemplateResult {
|
public render(): TemplateResult {
|
||||||
if (!this.value) {
|
|
||||||
return html`
|
return html`
|
||||||
<ha-file-upload
|
<ha-file-upload
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.icon=${mdiImagePlus}
|
.icon=${mdiImagePlus}
|
||||||
.label=${this.label ||
|
.label=${this.label ||
|
||||||
this.hass.localize("ui.components.picture-upload.label")}
|
this.hass.localize("ui.components.picture-upload.label")}
|
||||||
.secondary=${this.secondary}
|
|
||||||
.supports=${this.supports ||
|
|
||||||
this.hass.localize("ui.components.picture-upload.supported_formats")}
|
|
||||||
.uploading=${this._uploading}
|
.uploading=${this._uploading}
|
||||||
|
.value=${this.value ? html`<img .src=${this.value} />` : ""}
|
||||||
@file-picked=${this._handleFilePicked}
|
@file-picked=${this._handleFilePicked}
|
||||||
@change=${this._handleFileCleared}
|
@change=${this._handleFileCleared}
|
||||||
accept="image/png, image/jpeg, image/gif"
|
accept="image/png, image/jpeg, image/gif"
|
||||||
></ha-file-upload>
|
></ha-file-upload>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
return html`<div class="center-vertical">
|
|
||||||
<div class="value">
|
|
||||||
<img
|
|
||||||
.src=${this.value}
|
|
||||||
alt=${this.currentImageAltText ||
|
|
||||||
this.hass.localize("ui.components.picture-upload.current_image_alt")}
|
|
||||||
/>
|
|
||||||
<ha-button
|
|
||||||
@click=${this._handleChangeClick}
|
|
||||||
.label=${this.hass.localize(
|
|
||||||
"ui.components.picture-upload.change_picture"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
</ha-button>
|
|
||||||
</div>
|
|
||||||
</div>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _handleChangeClick() {
|
|
||||||
this.value = null;
|
|
||||||
fireEvent(this, "change");
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _handleFilePicked(ev) {
|
private async _handleFilePicked(ev) {
|
||||||
const file = ev.detail.files[0];
|
const file = ev.detail.files[0];
|
||||||
@@ -132,35 +100,6 @@ export class HaPictureUpload extends LitElement {
|
|||||||
this._uploading = false;
|
this._uploading = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles() {
|
|
||||||
return css`
|
|
||||||
:host {
|
|
||||||
display: block;
|
|
||||||
height: 240px;
|
|
||||||
}
|
|
||||||
ha-file-upload {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
.center-vertical {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
.value {
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
img {
|
|
||||||
max-width: 100%;
|
|
||||||
max-height: 200px;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
border-radius: var(--file-upload-image-border-radius);
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
@@ -47,9 +47,6 @@ export class HaSelect extends SelectBase {
|
|||||||
.mdc-select__anchor {
|
.mdc-select__anchor {
|
||||||
width: var(--ha-select-min-width, 200px);
|
width: var(--ha-select-min-width, 200px);
|
||||||
}
|
}
|
||||||
.mdc-select--filled .mdc-select__anchor {
|
|
||||||
height: var(--ha-select-height, 56px);
|
|
||||||
}
|
|
||||||
.mdc-select--filled .mdc-floating-label {
|
.mdc-select--filled .mdc-floating-label {
|
||||||
inset-inline-start: 12px;
|
inset-inline-start: 12px;
|
||||||
inset-inline-end: initial;
|
inset-inline-end: initial;
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
import { css, CSSResultGroup, html, LitElement } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import { Action } from "../../data/script";
|
import { Action } from "../../data/script";
|
||||||
import { ActionSelector } from "../../data/selector";
|
import { ActionSelector } from "../../data/selector";
|
||||||
@@ -19,13 +19,10 @@ export class HaActionSelector extends LitElement {
|
|||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
return html`
|
return html`
|
||||||
${this.label ? html`<label>${this.label}</label>` : nothing}
|
|
||||||
<ha-automation-action
|
<ha-automation-action
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
.actions=${this.value || []}
|
.actions=${this.value || []}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.nested=${this.selector.action?.nested}
|
|
||||||
.reOrderMode=${this.selector.action?.reorder_mode}
|
|
||||||
></ha-automation-action>
|
></ha-automation-action>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@@ -40,11 +37,6 @@ export class HaActionSelector extends LitElement {
|
|||||||
opacity: var(--light-disabled-opacity);
|
opacity: var(--light-disabled-opacity);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
label {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
import { css, CSSResultGroup, html, LitElement } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import { Condition } from "../../data/automation";
|
import { Condition } from "../../data/automation";
|
||||||
import { ConditionSelector } from "../../data/selector";
|
import { ConditionSelector } from "../../data/selector";
|
||||||
@@ -19,13 +19,10 @@ export class HaConditionSelector extends LitElement {
|
|||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
return html`
|
return html`
|
||||||
${this.label ? html`<label>${this.label}</label>` : nothing}
|
|
||||||
<ha-automation-condition
|
<ha-automation-condition
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
.conditions=${this.value || []}
|
.conditions=${this.value || []}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.nested=${this.selector.condition?.nested}
|
|
||||||
.reOrderMode=${this.selector.condition?.reorder_mode}
|
|
||||||
></ha-automation-condition>
|
></ha-automation-condition>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@@ -40,11 +37,6 @@ export class HaConditionSelector extends LitElement {
|
|||||||
opacity: var(--light-disabled-opacity);
|
opacity: var(--light-disabled-opacity);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
label {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -37,12 +37,9 @@ export class HaFileSelector extends LitElement {
|
|||||||
.label=${this.label}
|
.label=${this.label}
|
||||||
.required=${this.required}
|
.required=${this.required}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
.supports=${this.helper}
|
.helper=${this.helper}
|
||||||
.uploading=${this._busy}
|
.uploading=${this._busy}
|
||||||
.value=${this.value
|
.value=${this.value ? this._filename?.name || "Unknown file" : ""}
|
||||||
? this._filename?.name ||
|
|
||||||
this.hass.localize("ui.components.selectors.file.unknown_file")
|
|
||||||
: undefined}
|
|
||||||
@file-picked=${this._uploadFile}
|
@file-picked=${this._uploadFile}
|
||||||
@change=${this._removeFile}
|
@change=${this._removeFile}
|
||||||
></ha-file-upload>
|
></ha-file-upload>
|
||||||
|
440
src/components/ha-temp-color-picker.ts
Normal file
440
src/components/ha-temp-color-picker.ts
Normal file
@@ -0,0 +1,440 @@
|
|||||||
|
import { DIRECTION_ALL, Manager, Pan, Tap } from "@egjs/hammerjs";
|
||||||
|
import { LitElement, PropertyValues, css, html, svg } from "lit";
|
||||||
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
|
import { classMap } from "lit/directives/class-map";
|
||||||
|
import { styleMap } from "lit/directives/style-map";
|
||||||
|
import { rgb2hex } from "../common/color/convert-color";
|
||||||
|
import {
|
||||||
|
DEFAULT_MAX_KELVIN,
|
||||||
|
DEFAULT_MIN_KELVIN,
|
||||||
|
temperature2rgb,
|
||||||
|
} from "../common/color/convert-light-color";
|
||||||
|
import { fireEvent } from "../common/dom/fire_event";
|
||||||
|
|
||||||
|
const SAFE_ZONE_FACTOR = 0.9;
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HASSDomEvents {
|
||||||
|
"cursor-moved": { value?: any };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const A11Y_KEY_CODES = new Set([
|
||||||
|
"ArrowRight",
|
||||||
|
"ArrowUp",
|
||||||
|
"ArrowLeft",
|
||||||
|
"ArrowDown",
|
||||||
|
"PageUp",
|
||||||
|
"PageDown",
|
||||||
|
"Home",
|
||||||
|
"End",
|
||||||
|
]);
|
||||||
|
|
||||||
|
function xy2polar(x: number, y: number) {
|
||||||
|
const r = Math.sqrt(x * x + y * y);
|
||||||
|
const phi = Math.atan2(y, x);
|
||||||
|
return [r, phi];
|
||||||
|
}
|
||||||
|
|
||||||
|
function polar2xy(r: number, phi: number) {
|
||||||
|
const x = Math.cos(phi) * r;
|
||||||
|
const y = Math.sin(phi) * r;
|
||||||
|
return [x, y];
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawColorWheel(
|
||||||
|
ctx: CanvasRenderingContext2D,
|
||||||
|
minTemp: number,
|
||||||
|
maxTemp: number
|
||||||
|
) {
|
||||||
|
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
||||||
|
const radius = ctx.canvas.width / 2;
|
||||||
|
|
||||||
|
const min = Math.max(minTemp, 2000);
|
||||||
|
const max = Math.min(maxTemp, 40000);
|
||||||
|
|
||||||
|
for (let y = -radius; y < radius; y += 1) {
|
||||||
|
const x = radius * Math.sqrt(1 - (y / radius) ** 2);
|
||||||
|
|
||||||
|
const fraction = (y / (radius * SAFE_ZONE_FACTOR) + 1) / 2;
|
||||||
|
|
||||||
|
const temperature = Math.max(
|
||||||
|
Math.min(min + fraction * (max - min), max),
|
||||||
|
min
|
||||||
|
);
|
||||||
|
|
||||||
|
const color = rgb2hex(temperature2rgb(temperature));
|
||||||
|
|
||||||
|
ctx.fillStyle = color;
|
||||||
|
ctx.fillRect(radius - x, radius + y - 0.5, 2 * x, 2);
|
||||||
|
ctx.fill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@customElement("ha-temp-color-picker")
|
||||||
|
class HaTempColorPicker extends LitElement {
|
||||||
|
@property({ type: Boolean, reflect: true })
|
||||||
|
public disabled = false;
|
||||||
|
|
||||||
|
@property({ type: Number, attribute: false })
|
||||||
|
public renderSize?: number;
|
||||||
|
|
||||||
|
@property({ type: Number })
|
||||||
|
public value?: number;
|
||||||
|
|
||||||
|
@property({ type: Number })
|
||||||
|
public min = DEFAULT_MIN_KELVIN;
|
||||||
|
|
||||||
|
@property({ type: Number })
|
||||||
|
public max = DEFAULT_MAX_KELVIN;
|
||||||
|
|
||||||
|
@query("#canvas") private _canvas!: HTMLCanvasElement;
|
||||||
|
|
||||||
|
private _mc?: HammerManager;
|
||||||
|
|
||||||
|
@state()
|
||||||
|
private _pressed?: string;
|
||||||
|
|
||||||
|
@state()
|
||||||
|
private _cursorPosition?: [number, number];
|
||||||
|
|
||||||
|
@state()
|
||||||
|
private _localValue?: number;
|
||||||
|
|
||||||
|
protected firstUpdated(changedProps: PropertyValues): void {
|
||||||
|
super.firstUpdated(changedProps);
|
||||||
|
this._setupListeners();
|
||||||
|
this._generateColorWheel();
|
||||||
|
this.setAttribute("role", "slider");
|
||||||
|
this.setAttribute("aria-orientation", "vertical");
|
||||||
|
if (!this.hasAttribute("tabindex")) {
|
||||||
|
this.setAttribute("tabindex", "0");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _generateColorWheel() {
|
||||||
|
const ctx = this._canvas.getContext("2d")!;
|
||||||
|
drawColorWheel(ctx, this.min, this.max);
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback(): void {
|
||||||
|
super.connectedCallback();
|
||||||
|
this._setupListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectedCallback(): void {
|
||||||
|
super.disconnectedCallback();
|
||||||
|
this._destroyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected updated(changedProps: PropertyValues): void {
|
||||||
|
super.updated(changedProps);
|
||||||
|
if (changedProps.has("_localValue")) {
|
||||||
|
this.setAttribute("aria-valuenow", this._localValue?.toString() ?? "");
|
||||||
|
}
|
||||||
|
if (changedProps.has("min") || changedProps.has("max")) {
|
||||||
|
this._generateColorWheel();
|
||||||
|
this._resetPosition();
|
||||||
|
}
|
||||||
|
if (changedProps.has("min")) {
|
||||||
|
this.setAttribute("aria-valuemin", this.min.toString());
|
||||||
|
}
|
||||||
|
if (changedProps.has("max")) {
|
||||||
|
this.setAttribute("aria-valuemax", this.max.toString());
|
||||||
|
}
|
||||||
|
if (changedProps.has("value")) {
|
||||||
|
if (this._localValue !== this.value) {
|
||||||
|
this._resetPosition();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _setupListeners() {
|
||||||
|
if (this._canvas && !this._mc) {
|
||||||
|
this._mc = new Manager(this._canvas);
|
||||||
|
this._mc.add(
|
||||||
|
new Pan({
|
||||||
|
direction: DIRECTION_ALL,
|
||||||
|
enable: true,
|
||||||
|
threshold: 0,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
this._mc.add(new Tap({ event: "singletap" }));
|
||||||
|
|
||||||
|
let savedPosition;
|
||||||
|
this._mc.on("panstart", (e) => {
|
||||||
|
if (this.disabled) return;
|
||||||
|
this._pressed = e.pointerType;
|
||||||
|
savedPosition = this._cursorPosition;
|
||||||
|
});
|
||||||
|
this._mc.on("pancancel", () => {
|
||||||
|
if (this.disabled) return;
|
||||||
|
this._pressed = undefined;
|
||||||
|
this._cursorPosition = savedPosition;
|
||||||
|
});
|
||||||
|
this._mc.on("panmove", (e) => {
|
||||||
|
if (this.disabled) return;
|
||||||
|
this._cursorPosition = this._getPositionFromEvent(e);
|
||||||
|
this._localValue = this._getValueFromCoord(...this._cursorPosition);
|
||||||
|
fireEvent(this, "cursor-moved", { value: this._localValue });
|
||||||
|
});
|
||||||
|
this._mc.on("panend", (e) => {
|
||||||
|
if (this.disabled) return;
|
||||||
|
this._pressed = undefined;
|
||||||
|
this._cursorPosition = this._getPositionFromEvent(e);
|
||||||
|
this._localValue = this._getValueFromCoord(...this._cursorPosition);
|
||||||
|
fireEvent(this, "cursor-moved", { value: undefined });
|
||||||
|
fireEvent(this, "value-changed", { value: this._localValue });
|
||||||
|
});
|
||||||
|
|
||||||
|
this._mc.on("singletap", (e) => {
|
||||||
|
if (this.disabled) return;
|
||||||
|
this._cursorPosition = this._getPositionFromEvent(e);
|
||||||
|
this._localValue = this._getValueFromCoord(...this._cursorPosition);
|
||||||
|
fireEvent(this, "value-changed", { value: this._localValue });
|
||||||
|
});
|
||||||
|
|
||||||
|
this.addEventListener("keydown", this._handleKeyDown);
|
||||||
|
this.addEventListener("keyup", this._handleKeyUp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _resetPosition() {
|
||||||
|
if (this.value === undefined) {
|
||||||
|
this._cursorPosition = undefined;
|
||||||
|
this._localValue = undefined;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const [, y] = this._getCoordsFromValue(this.value);
|
||||||
|
const currentX = this._cursorPosition?.[0] ?? 0;
|
||||||
|
const x =
|
||||||
|
Math.sign(currentX) * Math.min(Math.sqrt(1 - y ** 2), Math.abs(currentX));
|
||||||
|
this._cursorPosition = [x, y];
|
||||||
|
this._localValue = this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _getCoordsFromValue = (temperature: number): [number, number] => {
|
||||||
|
if (this.value === this.min) {
|
||||||
|
return [0, -1];
|
||||||
|
}
|
||||||
|
if (this.value === this.max) {
|
||||||
|
return [0, 1];
|
||||||
|
}
|
||||||
|
const fraction = (temperature - this.min) / (this.max - this.min);
|
||||||
|
const y = (2 * fraction - 1) * SAFE_ZONE_FACTOR;
|
||||||
|
return [0, y];
|
||||||
|
};
|
||||||
|
|
||||||
|
private _getValueFromCoord = (_x: number, y: number): number => {
|
||||||
|
const fraction = (y / SAFE_ZONE_FACTOR + 1) / 2;
|
||||||
|
const temperature = Math.max(
|
||||||
|
Math.min(this.min + fraction * (this.max - this.min), this.max),
|
||||||
|
this.min
|
||||||
|
);
|
||||||
|
return Math.round(temperature);
|
||||||
|
};
|
||||||
|
|
||||||
|
private _getPositionFromEvent = (e: HammerInput): [number, number] => {
|
||||||
|
const x = e.center.x;
|
||||||
|
const y = e.center.y;
|
||||||
|
const boundingRect = e.target.getBoundingClientRect();
|
||||||
|
const offsetX = boundingRect.left;
|
||||||
|
const offsetY = boundingRect.top;
|
||||||
|
const maxX = e.target.clientWidth;
|
||||||
|
const maxY = e.target.clientHeight;
|
||||||
|
|
||||||
|
const _x = (2 * (x - offsetX)) / maxX - 1;
|
||||||
|
const _y = (2 * (y - offsetY)) / maxY - 1;
|
||||||
|
|
||||||
|
const [r, phi] = xy2polar(_x, _y);
|
||||||
|
const [__x, __y] = polar2xy(Math.min(1, r), phi);
|
||||||
|
return [__x, __y];
|
||||||
|
};
|
||||||
|
|
||||||
|
private _destroyListeners() {
|
||||||
|
if (this._mc) {
|
||||||
|
this._mc.destroy();
|
||||||
|
this._mc = undefined;
|
||||||
|
}
|
||||||
|
this.removeEventListener("keydown", this._handleKeyDown);
|
||||||
|
this.removeEventListener("keyup", this._handleKeyDown);
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleKeyDown(e: KeyboardEvent) {
|
||||||
|
if (!A11Y_KEY_CODES.has(e.code)) return;
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const step = 1;
|
||||||
|
const tenPercentStep = Math.max(step, (this.max - this.min) / 10);
|
||||||
|
const currentValue =
|
||||||
|
this._localValue ?? Math.round((this.max + this.min) / 2);
|
||||||
|
switch (e.code) {
|
||||||
|
case "ArrowRight":
|
||||||
|
case "ArrowUp":
|
||||||
|
this._localValue = Math.round(Math.min(currentValue + step, this.max));
|
||||||
|
break;
|
||||||
|
case "ArrowLeft":
|
||||||
|
case "ArrowDown":
|
||||||
|
this._localValue = Math.round(Math.max(currentValue - step, this.min));
|
||||||
|
break;
|
||||||
|
case "PageUp":
|
||||||
|
this._localValue = Math.round(
|
||||||
|
Math.min(currentValue + tenPercentStep, this.max)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case "PageDown":
|
||||||
|
this._localValue = Math.round(
|
||||||
|
Math.max(currentValue - tenPercentStep, this.min)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case "Home":
|
||||||
|
this._localValue = this.min;
|
||||||
|
break;
|
||||||
|
case "End":
|
||||||
|
this._localValue = this.max;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (this._localValue != null) {
|
||||||
|
const [_, y] = this._getCoordsFromValue(this._localValue);
|
||||||
|
const currentX = this._cursorPosition?.[0] ?? 0;
|
||||||
|
const x =
|
||||||
|
Math.sign(currentX) *
|
||||||
|
Math.min(Math.sqrt(1 - y ** 2), Math.abs(currentX));
|
||||||
|
this._cursorPosition = [x, y];
|
||||||
|
fireEvent(this, "cursor-moved", { value: this._localValue });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleKeyUp(e: KeyboardEvent) {
|
||||||
|
if (!A11Y_KEY_CODES.has(e.code)) return;
|
||||||
|
e.preventDefault();
|
||||||
|
this.value = this._localValue;
|
||||||
|
fireEvent(this, "value-changed", { value: this._localValue });
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const size = this.renderSize || 400;
|
||||||
|
const canvasSize = size * window.devicePixelRatio;
|
||||||
|
|
||||||
|
const rgb = temperature2rgb(
|
||||||
|
this._localValue ?? Math.round((this.max + this.min) / 2)
|
||||||
|
);
|
||||||
|
|
||||||
|
const [x, y] = this._cursorPosition ?? [0, 0];
|
||||||
|
|
||||||
|
const cx = ((x + 1) * size) / 2;
|
||||||
|
const cy = ((y + 1) * size) / 2;
|
||||||
|
|
||||||
|
const markerPosition = `${cx}px, ${cy}px`;
|
||||||
|
const markerScale = this._pressed
|
||||||
|
? this._pressed === "touch"
|
||||||
|
? "2.5"
|
||||||
|
: "1.5"
|
||||||
|
: "1";
|
||||||
|
const markerOffset =
|
||||||
|
this._pressed === "touch" ? `0px, -${size / 16}px` : "0px, 0px";
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<div class="container ${classMap({ pressed: Boolean(this._pressed) })}">
|
||||||
|
<canvas id="canvas" .width=${canvasSize} .height=${canvasSize}></canvas>
|
||||||
|
<svg
|
||||||
|
id="interaction"
|
||||||
|
viewBox="0 0 ${size} ${size}"
|
||||||
|
overflow="visible"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<defs>${this.renderSVGFilter()}</defs>
|
||||||
|
<g
|
||||||
|
style=${styleMap({
|
||||||
|
fill: rgb2hex(rgb),
|
||||||
|
transform: `translate(${markerPosition})`,
|
||||||
|
})}
|
||||||
|
class="cursor"
|
||||||
|
>
|
||||||
|
<circle
|
||||||
|
cx="0"
|
||||||
|
cy="0"
|
||||||
|
r="16"
|
||||||
|
style=${styleMap({
|
||||||
|
fill: rgb2hex(rgb),
|
||||||
|
transform: `translate(${markerOffset}) scale(${markerScale})`,
|
||||||
|
visibility: this._cursorPosition ? undefined : "hidden",
|
||||||
|
})}
|
||||||
|
></circle>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSVGFilter() {
|
||||||
|
return svg`
|
||||||
|
<filter
|
||||||
|
id="marker-shadow"
|
||||||
|
x="-50%"
|
||||||
|
y="-50%"
|
||||||
|
width="200%"
|
||||||
|
height="200%"
|
||||||
|
filterUnits="objectBoundingBox"
|
||||||
|
>
|
||||||
|
<feDropShadow dx="0" dy="1" stdDeviation="2" flood-opacity="0.3" flood-color="rgba(0, 0, 0, 1)"/>
|
||||||
|
<feDropShadow dx="0" dy="1" stdDeviation="3" flood-opacity="0.15" flood-color="rgba(0, 0, 0, 1)"/>
|
||||||
|
</filter>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return css`
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
canvas {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: contain;
|
||||||
|
border-radius: 50%;
|
||||||
|
transition: box-shadow 180ms ease-in-out;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
:host(:focus-visible) canvas {
|
||||||
|
box-shadow: 0 0 0 2px rgb(255, 160, 0);
|
||||||
|
}
|
||||||
|
svg {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
circle {
|
||||||
|
fill: black;
|
||||||
|
stroke: white;
|
||||||
|
stroke-width: 2;
|
||||||
|
filter: url(#marker-shadow);
|
||||||
|
}
|
||||||
|
.container:not(.pressed) circle {
|
||||||
|
transition:
|
||||||
|
transform 100ms ease-in-out,
|
||||||
|
fill 100ms ease-in-out;
|
||||||
|
}
|
||||||
|
.container:not(.pressed) .cursor {
|
||||||
|
transition: transform 200ms ease-in-out;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-temp-color-picker": HaTempColorPicker;
|
||||||
|
}
|
||||||
|
}
|
@@ -1,6 +1,7 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
/* eslint-plugin-disable lit */
|
/* eslint-plugin-disable lit */
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
|
import { computeStateDisplay } from "../common/entity/compute_state_display";
|
||||||
import { formatNumber } from "../common/number/format_number";
|
import { formatNumber } from "../common/number/format_number";
|
||||||
import LocalizeMixin from "../mixins/localize-mixin";
|
import LocalizeMixin from "../mixins/localize-mixin";
|
||||||
|
|
||||||
@@ -83,7 +84,12 @@ class HaWaterHeaterState extends LocalizeMixin(PolymerElement) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_localizeState(stateObj) {
|
_localizeState(stateObj) {
|
||||||
return this.hass.formatEntityState(stateObj);
|
return computeStateDisplay(
|
||||||
|
this.hass.localize,
|
||||||
|
stateObj,
|
||||||
|
this.hass.locale,
|
||||||
|
this.hass.entities
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
customElements.define("ha-water_heater-state", HaWaterHeaterState);
|
customElements.define("ha-water_heater-state", HaWaterHeaterState);
|
||||||
|
15
src/components/language-datalist.ts
Normal file
15
src/components/language-datalist.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { HomeAssistant } from "../types";
|
||||||
|
|
||||||
|
export const createLanguageListEl = (hass: HomeAssistant) => {
|
||||||
|
const list = document.createElement("datalist");
|
||||||
|
list.id = "languages";
|
||||||
|
for (const [language, metadata] of Object.entries(
|
||||||
|
hass.translationMetadata.translations
|
||||||
|
)) {
|
||||||
|
const option = document.createElement("option");
|
||||||
|
option.value = language;
|
||||||
|
option.innerText = metadata.nativeName;
|
||||||
|
list.appendChild(option);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
};
|
@@ -61,18 +61,7 @@ export const createAuthForUser = async (
|
|||||||
password,
|
password,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const changePassword = (
|
export const adminChangePassword = async (
|
||||||
hass: HomeAssistant,
|
|
||||||
current_password: string,
|
|
||||||
new_password: string
|
|
||||||
) =>
|
|
||||||
hass.callWS({
|
|
||||||
type: "config/auth_provider/homeassistant/change_password",
|
|
||||||
current_password,
|
|
||||||
new_password,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const adminChangePassword = (
|
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
userId: string,
|
userId: string,
|
||||||
password: string
|
password: string
|
||||||
@@ -82,8 +71,3 @@ export const adminChangePassword = (
|
|||||||
user_id: userId,
|
user_id: userId,
|
||||||
password,
|
password,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const deleteAllRefreshTokens = (hass: HomeAssistant) =>
|
|
||||||
hass.callWS({
|
|
||||||
type: "auth/delete_all_refresh_tokens",
|
|
||||||
});
|
|
||||||
|
@@ -6,7 +6,11 @@ import {
|
|||||||
formatTimeWithSeconds,
|
formatTimeWithSeconds,
|
||||||
} from "../common/datetime/format_time";
|
} from "../common/datetime/format_time";
|
||||||
import secondsToDuration from "../common/datetime/seconds_to_duration";
|
import secondsToDuration from "../common/datetime/seconds_to_duration";
|
||||||
import { computeAttributeNameDisplay } from "../common/entity/compute_attribute_display";
|
import {
|
||||||
|
computeAttributeNameDisplay,
|
||||||
|
computeAttributeValueDisplay,
|
||||||
|
} from "../common/entity/compute_attribute_display";
|
||||||
|
import { computeStateDisplay } from "../common/entity/compute_state_display";
|
||||||
import { computeStateName } from "../common/entity/compute_state_name";
|
import { computeStateName } from "../common/entity/compute_state_name";
|
||||||
import "../resources/intl-polyfill";
|
import "../resources/intl-polyfill";
|
||||||
import type { HomeAssistant } from "../types";
|
import type { HomeAssistant } from "../types";
|
||||||
@@ -231,14 +235,23 @@ const tryDescribeTrigger = (
|
|||||||
for (const state of trigger.from.values()) {
|
for (const state of trigger.from.values()) {
|
||||||
from.push(
|
from.push(
|
||||||
trigger.attribute
|
trigger.attribute
|
||||||
? hass
|
? computeAttributeValueDisplay(
|
||||||
.formatEntityAttributeValue(
|
hass.localize,
|
||||||
stateObj,
|
stateObj,
|
||||||
|
hass.locale,
|
||||||
|
hass.config,
|
||||||
|
hass.entities,
|
||||||
trigger.attribute,
|
trigger.attribute,
|
||||||
state
|
state
|
||||||
|
).toString()
|
||||||
|
: computeStateDisplay(
|
||||||
|
hass.localize,
|
||||||
|
stateObj,
|
||||||
|
hass.locale,
|
||||||
|
hass.config,
|
||||||
|
hass.entities,
|
||||||
|
state
|
||||||
)
|
)
|
||||||
.toString()
|
|
||||||
: hass.formatEntityState(stateObj, state)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (from.length !== 0) {
|
if (from.length !== 0) {
|
||||||
@@ -248,16 +261,23 @@ const tryDescribeTrigger = (
|
|||||||
} else {
|
} else {
|
||||||
base += ` from ${
|
base += ` from ${
|
||||||
trigger.attribute
|
trigger.attribute
|
||||||
? hass
|
? computeAttributeValueDisplay(
|
||||||
.formatEntityAttributeValue(
|
hass.localize,
|
||||||
stateObj,
|
stateObj,
|
||||||
|
hass.locale,
|
||||||
|
hass.config,
|
||||||
|
hass.entities,
|
||||||
trigger.attribute,
|
trigger.attribute,
|
||||||
trigger.from
|
trigger.from
|
||||||
)
|
).toString()
|
||||||
.toString()
|
: computeStateDisplay(
|
||||||
: hass
|
hass.localize,
|
||||||
.formatEntityState(stateObj, trigger.from.toString())
|
stateObj,
|
||||||
.toString()
|
hass.locale,
|
||||||
|
hass.config,
|
||||||
|
hass.entities,
|
||||||
|
trigger.from.toString()
|
||||||
|
).toString()
|
||||||
}`;
|
}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -272,14 +292,23 @@ const tryDescribeTrigger = (
|
|||||||
for (const state of trigger.to.values()) {
|
for (const state of trigger.to.values()) {
|
||||||
to.push(
|
to.push(
|
||||||
trigger.attribute
|
trigger.attribute
|
||||||
? hass
|
? computeAttributeValueDisplay(
|
||||||
.formatEntityAttributeValue(
|
hass.localize,
|
||||||
stateObj,
|
stateObj,
|
||||||
|
hass.locale,
|
||||||
|
hass.config,
|
||||||
|
hass.entities,
|
||||||
trigger.attribute,
|
trigger.attribute,
|
||||||
state
|
state
|
||||||
)
|
).toString()
|
||||||
.toString()
|
: computeStateDisplay(
|
||||||
: hass.formatEntityState(stateObj, state).toString()
|
hass.localize,
|
||||||
|
stateObj,
|
||||||
|
hass.locale,
|
||||||
|
hass.config,
|
||||||
|
hass.entities,
|
||||||
|
state
|
||||||
|
).toString()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (to.length !== 0) {
|
if (to.length !== 0) {
|
||||||
@@ -289,14 +318,23 @@ const tryDescribeTrigger = (
|
|||||||
} else {
|
} else {
|
||||||
base += ` to ${
|
base += ` to ${
|
||||||
trigger.attribute
|
trigger.attribute
|
||||||
? hass
|
? computeAttributeValueDisplay(
|
||||||
.formatEntityAttributeValue(
|
hass.localize,
|
||||||
stateObj,
|
stateObj,
|
||||||
|
hass.locale,
|
||||||
|
hass.config,
|
||||||
|
hass.entities,
|
||||||
trigger.attribute,
|
trigger.attribute,
|
||||||
trigger.to
|
trigger.to
|
||||||
|
).toString()
|
||||||
|
: computeStateDisplay(
|
||||||
|
hass.localize,
|
||||||
|
stateObj,
|
||||||
|
hass.locale,
|
||||||
|
hass.config,
|
||||||
|
hass.entities,
|
||||||
|
trigger.to.toString()
|
||||||
)
|
)
|
||||||
.toString()
|
|
||||||
: hass.formatEntityState(stateObj, trigger.to.toString())
|
|
||||||
}`;
|
}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -784,27 +822,45 @@ const tryDescribeCondition = (
|
|||||||
for (const state of condition.state.values()) {
|
for (const state of condition.state.values()) {
|
||||||
states.push(
|
states.push(
|
||||||
condition.attribute
|
condition.attribute
|
||||||
? hass
|
? computeAttributeValueDisplay(
|
||||||
.formatEntityAttributeValue(
|
hass.localize,
|
||||||
stateObj,
|
stateObj,
|
||||||
|
hass.locale,
|
||||||
|
hass.config,
|
||||||
|
hass.entities,
|
||||||
condition.attribute,
|
condition.attribute,
|
||||||
state
|
state
|
||||||
|
).toString()
|
||||||
|
: computeStateDisplay(
|
||||||
|
hass.localize,
|
||||||
|
stateObj,
|
||||||
|
hass.locale,
|
||||||
|
hass.config,
|
||||||
|
hass.entities,
|
||||||
|
state
|
||||||
)
|
)
|
||||||
.toString()
|
|
||||||
: hass.formatEntityState(stateObj, state)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (condition.state !== "") {
|
} else if (condition.state !== "") {
|
||||||
states.push(
|
states.push(
|
||||||
condition.attribute
|
condition.attribute
|
||||||
? hass
|
? computeAttributeValueDisplay(
|
||||||
.formatEntityAttributeValue(
|
hass.localize,
|
||||||
stateObj,
|
stateObj,
|
||||||
|
hass.locale,
|
||||||
|
hass.config,
|
||||||
|
hass.entities,
|
||||||
condition.attribute,
|
condition.attribute,
|
||||||
condition.state
|
condition.state
|
||||||
|
).toString()
|
||||||
|
: computeStateDisplay(
|
||||||
|
hass.localize,
|
||||||
|
stateObj,
|
||||||
|
hass.locale,
|
||||||
|
hass.config,
|
||||||
|
hass.entities,
|
||||||
|
condition.state.toString()
|
||||||
)
|
)
|
||||||
.toString()
|
|
||||||
: hass.formatEntityState(stateObj, condition.state.toString())
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -67,7 +67,7 @@ export const DOMAIN_ATTRIBUTES_UNITS: Record<string, Record<string, string>> = {
|
|||||||
sun: {
|
sun: {
|
||||||
elevation: "°",
|
elevation: "°",
|
||||||
},
|
},
|
||||||
vacuum: {
|
vaccum: {
|
||||||
battery_level: "%",
|
battery_level: "%",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@@ -6,7 +6,6 @@ import { caseInsensitiveStringCompare } from "../common/string/compare";
|
|||||||
import { debounce } from "../common/util/debounce";
|
import { debounce } from "../common/util/debounce";
|
||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
import { LightColor } from "./light";
|
import { LightColor } from "./light";
|
||||||
import { computeDomain } from "../common/entity/compute_domain";
|
|
||||||
|
|
||||||
type entityCategory = "config" | "diagnostic";
|
type entityCategory = "config" | "diagnostic";
|
||||||
|
|
||||||
@@ -130,29 +129,15 @@ export interface EntityRegistryEntryUpdateParams {
|
|||||||
aliases?: string[];
|
aliases?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const batteryPriorities = ["sensor", "binary_sensor"];
|
|
||||||
export const findBatteryEntity = <T extends { entity_id: string }>(
|
export const findBatteryEntity = <T extends { entity_id: string }>(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
entities: T[]
|
entities: T[]
|
||||||
): T | undefined => {
|
): T | undefined =>
|
||||||
const batteryEntities = entities
|
entities.find(
|
||||||
.filter(
|
|
||||||
(entity) =>
|
(entity) =>
|
||||||
hass.states[entity.entity_id] &&
|
hass.states[entity.entity_id] &&
|
||||||
hass.states[entity.entity_id].attributes.device_class === "battery" &&
|
hass.states[entity.entity_id].attributes.device_class === "battery"
|
||||||
batteryPriorities.includes(computeDomain(entity.entity_id))
|
|
||||||
)
|
|
||||||
.sort(
|
|
||||||
(a, b) =>
|
|
||||||
batteryPriorities.indexOf(computeDomain(a.entity_id)) -
|
|
||||||
batteryPriorities.indexOf(computeDomain(b.entity_id))
|
|
||||||
);
|
);
|
||||||
if (batteryEntities.length > 0) {
|
|
||||||
return batteryEntities[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
return undefined;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const findBatteryChargingEntity = <T extends { entity_id: string }>(
|
export const findBatteryChargingEntity = <T extends { entity_id: string }>(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
|
@@ -17,11 +17,6 @@ export interface GroupEntity extends HassEntityBase {
|
|||||||
attributes: GroupEntityAttributes;
|
attributes: GroupEntityAttributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GroupPreview {
|
|
||||||
state: string;
|
|
||||||
attributes: Record<string, any>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const computeGroupDomain = (
|
export const computeGroupDomain = (
|
||||||
stateObj: GroupEntity
|
stateObj: GroupEntity
|
||||||
): string | undefined => {
|
): string | undefined => {
|
||||||
@@ -32,15 +27,35 @@ export const computeGroupDomain = (
|
|||||||
return uniqueDomains.length === 1 ? uniqueDomains[0] : undefined;
|
return uniqueDomains.length === 1 ? uniqueDomains[0] : undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const subscribePreviewGroup = (
|
export const subscribePreviewGroupSensor = (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
flow_id: string,
|
flow_id: string,
|
||||||
flow_type: "config_flow" | "options_flow",
|
flow_type: "config_flow" | "options_flow",
|
||||||
user_input: Record<string, any>,
|
user_input: Record<string, any>,
|
||||||
callback: (preview: GroupPreview) => void
|
callback: (preview: {
|
||||||
|
state: string;
|
||||||
|
attributes: Record<string, any>;
|
||||||
|
}) => void
|
||||||
): Promise<UnsubscribeFunc> =>
|
): Promise<UnsubscribeFunc> =>
|
||||||
hass.connection.subscribeMessage(callback, {
|
hass.connection.subscribeMessage(callback, {
|
||||||
type: "group/start_preview",
|
type: "group/sensor/start_preview",
|
||||||
|
flow_id,
|
||||||
|
flow_type,
|
||||||
|
user_input,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const subscribePreviewGroupBinarySensor = (
|
||||||
|
hass: HomeAssistant,
|
||||||
|
flow_id: string,
|
||||||
|
flow_type: "config_flow" | "options_flow",
|
||||||
|
user_input: Record<string, any>,
|
||||||
|
callback: (preview: {
|
||||||
|
state: string;
|
||||||
|
attributes: Record<string, any>;
|
||||||
|
}) => void
|
||||||
|
): Promise<UnsubscribeFunc> =>
|
||||||
|
hass.connection.subscribeMessage(callback, {
|
||||||
|
type: "group/binary_sensor/start_preview",
|
||||||
flow_id,
|
flow_id,
|
||||||
flow_type,
|
flow_type,
|
||||||
user_input,
|
user_input,
|
||||||
|
@@ -5,12 +5,14 @@ import {
|
|||||||
DOMAINS_WITH_DYNAMIC_PICTURE,
|
DOMAINS_WITH_DYNAMIC_PICTURE,
|
||||||
} from "../common/const";
|
} from "../common/const";
|
||||||
import { computeDomain } from "../common/entity/compute_domain";
|
import { computeDomain } from "../common/entity/compute_domain";
|
||||||
|
import { computeStateDisplay } from "../common/entity/compute_state_display";
|
||||||
import { computeStateDomain } from "../common/entity/compute_state_domain";
|
import { computeStateDomain } from "../common/entity/compute_state_domain";
|
||||||
import { autoCaseNoun } from "../common/translations/auto_case_noun";
|
import { autoCaseNoun } from "../common/translations/auto_case_noun";
|
||||||
import { LocalizeFunc } from "../common/translations/localize";
|
import { LocalizeFunc } from "../common/translations/localize";
|
||||||
import { HaEntityPickerEntityFilterFunc } from "../components/entity/ha-entity-picker";
|
import { HaEntityPickerEntityFilterFunc } from "../components/entity/ha-entity-picker";
|
||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
import { UNAVAILABLE, UNKNOWN } from "./entity";
|
import { UNAVAILABLE, UNKNOWN } from "./entity";
|
||||||
|
import { computeAttributeValueDisplay } from "../common/entity/compute_attribute_display";
|
||||||
|
|
||||||
const LOGBOOK_LOCALIZE_PATH = "ui.components.logbook.messages";
|
const LOGBOOK_LOCALIZE_PATH = "ui.components.logbook.messages";
|
||||||
export const CONTINUOUS_DOMAINS = ["counter", "proximity", "sensor", "zone"];
|
export const CONTINUOUS_DOMAINS = ["counter", "proximity", "sensor", "zone"];
|
||||||
@@ -337,9 +339,14 @@ export const localizeStateMessage = (
|
|||||||
|
|
||||||
// TODO: This is not working yet, as we don't get historic attribute values
|
// TODO: This is not working yet, as we don't get historic attribute values
|
||||||
|
|
||||||
const event_type = hass
|
const event_type = computeAttributeValueDisplay(
|
||||||
.formatEntityAttributeValue(stateObj, "event_type")
|
hass!.localize,
|
||||||
?.toString();
|
stateObj,
|
||||||
|
hass.locale,
|
||||||
|
hass.config,
|
||||||
|
hass.entities,
|
||||||
|
"event_type"
|
||||||
|
)?.toString();
|
||||||
|
|
||||||
if (!event_type) {
|
if (!event_type) {
|
||||||
return localize(`${LOGBOOK_LOCALIZE_PATH}.detected_unknown_event`);
|
return localize(`${LOGBOOK_LOCALIZE_PATH}.detected_unknown_event`);
|
||||||
@@ -385,7 +392,16 @@ export const localizeStateMessage = (
|
|||||||
return hass.localize(
|
return hass.localize(
|
||||||
`${LOGBOOK_LOCALIZE_PATH}.changed_to_state`,
|
`${LOGBOOK_LOCALIZE_PATH}.changed_to_state`,
|
||||||
"state",
|
"state",
|
||||||
stateObj ? hass.formatEntityState(stateObj, state) : state
|
stateObj
|
||||||
|
? computeStateDisplay(
|
||||||
|
localize,
|
||||||
|
stateObj,
|
||||||
|
hass.locale,
|
||||||
|
hass.config,
|
||||||
|
hass.entities,
|
||||||
|
state
|
||||||
|
)
|
||||||
|
: state
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -54,10 +54,8 @@ export type Selector =
|
|||||||
| UiColorSelector;
|
| UiColorSelector;
|
||||||
|
|
||||||
export interface ActionSelector {
|
export interface ActionSelector {
|
||||||
action: {
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||||
reorder_mode?: boolean;
|
action: {} | null;
|
||||||
nested?: boolean;
|
|
||||||
} | null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AddonSelector {
|
export interface AddonSelector {
|
||||||
@@ -100,10 +98,8 @@ export interface ColorTempSelector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ConditionSelector {
|
export interface ConditionSelector {
|
||||||
condition: {
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||||
reorder_mode?: boolean;
|
condition: {} | null;
|
||||||
nested?: boolean;
|
|
||||||
} | null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ConversationAgentSelector {
|
export interface ConversationAgentSelector {
|
||||||
|
@@ -5,6 +5,7 @@ import {
|
|||||||
} from "home-assistant-js-websocket";
|
} from "home-assistant-js-websocket";
|
||||||
import durationToSeconds from "../common/datetime/duration_to_seconds";
|
import durationToSeconds from "../common/datetime/duration_to_seconds";
|
||||||
import secondsToDuration from "../common/datetime/seconds_to_duration";
|
import secondsToDuration from "../common/datetime/seconds_to_duration";
|
||||||
|
import { computeStateDisplay } from "../common/entity/compute_state_display";
|
||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
|
|
||||||
export type TimerEntity = HassEntityBase & {
|
export type TimerEntity = HassEntityBase & {
|
||||||
@@ -89,13 +90,25 @@ export const computeDisplayTimer = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (stateObj.state === "idle" || timeRemaining === 0) {
|
if (stateObj.state === "idle" || timeRemaining === 0) {
|
||||||
return hass.formatEntityState(stateObj);
|
return computeStateDisplay(
|
||||||
|
hass.localize,
|
||||||
|
stateObj,
|
||||||
|
hass.locale,
|
||||||
|
hass.config,
|
||||||
|
hass.entities
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let display = secondsToDuration(timeRemaining || 0);
|
let display = secondsToDuration(timeRemaining || 0);
|
||||||
|
|
||||||
if (stateObj.state === "paused") {
|
if (stateObj.state === "paused") {
|
||||||
display = `${display} (${hass.formatEntityState(stateObj)})`;
|
display = `${display} (${computeStateDisplay(
|
||||||
|
hass.localize,
|
||||||
|
stateObj,
|
||||||
|
hass.locale,
|
||||||
|
hass.config,
|
||||||
|
hass.entities
|
||||||
|
)})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return display;
|
return display;
|
||||||
|
@@ -649,11 +649,11 @@ export const getDefaultForecastType = (stateObj: HassEntityBase) => {
|
|||||||
if (supportsFeature(stateObj, WeatherEntityFeature.FORECAST_DAILY)) {
|
if (supportsFeature(stateObj, WeatherEntityFeature.FORECAST_DAILY)) {
|
||||||
return "daily";
|
return "daily";
|
||||||
}
|
}
|
||||||
if (supportsFeature(stateObj, WeatherEntityFeature.FORECAST_TWICE_DAILY)) {
|
|
||||||
return "twice_daily";
|
|
||||||
}
|
|
||||||
if (supportsFeature(stateObj, WeatherEntityFeature.FORECAST_HOURLY)) {
|
if (supportsFeature(stateObj, WeatherEntityFeature.FORECAST_HOURLY)) {
|
||||||
return "hourly";
|
return "hourly";
|
||||||
}
|
}
|
||||||
|
if (supportsFeature(stateObj, WeatherEntityFeature.FORECAST_TWICE_DAILY)) {
|
||||||
|
return "twice_daily";
|
||||||
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
import { Connection, UnsubscribeFunc } from "home-assistant-js-websocket";
|
import { Connection, UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||||
import { HomeAssistant } from "../types";
|
|
||||||
|
|
||||||
export interface RenderTemplateResult {
|
export interface RenderTemplateResult {
|
||||||
result: string;
|
result: string;
|
||||||
@@ -13,17 +12,6 @@ interface TemplateListeners {
|
|||||||
time: boolean;
|
time: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TemplatePreview = TemplatePreviewState | TemplatePreviewError;
|
|
||||||
|
|
||||||
interface TemplatePreviewState {
|
|
||||||
state: string;
|
|
||||||
attributes: Record<string, any>;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface TemplatePreviewError {
|
|
||||||
error: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const subscribeRenderTemplate = (
|
export const subscribeRenderTemplate = (
|
||||||
conn: Connection,
|
conn: Connection,
|
||||||
onChange: (result: RenderTemplateResult) => void,
|
onChange: (result: RenderTemplateResult) => void,
|
||||||
@@ -39,17 +27,3 @@ export const subscribeRenderTemplate = (
|
|||||||
type: "render_template",
|
type: "render_template",
|
||||||
...params,
|
...params,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const subscribePreviewTemplate = (
|
|
||||||
hass: HomeAssistant,
|
|
||||||
flow_id: string,
|
|
||||||
flow_type: "config_flow" | "options_flow",
|
|
||||||
user_input: Record<string, any>,
|
|
||||||
callback: (preview: TemplatePreview) => void
|
|
||||||
): Promise<UnsubscribeFunc> =>
|
|
||||||
hass.connection.subscribeMessage(callback, {
|
|
||||||
type: "template/start_preview",
|
|
||||||
flow_id,
|
|
||||||
flow_type,
|
|
||||||
user_input,
|
|
||||||
});
|
|
||||||
|
@@ -404,6 +404,8 @@ export interface RequestedGrant {
|
|||||||
clientSideAuth: boolean;
|
clientSideAuth: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const nodeStatus = ["unknown", "asleep", "awake", "dead", "alive"];
|
||||||
|
|
||||||
export const fetchZwaveNetworkStatus = (
|
export const fetchZwaveNetworkStatus = (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
device_or_entry_id: {
|
device_or_entry_id: {
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import { HassEntity } from "home-assistant-js-websocket";
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
import { CSSResultGroup, LitElement, css, html, nothing } from "lit";
|
import { CSSResultGroup, LitElement, css, html, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import { computeStateDisplay } from "../../../common/entity/compute_state_display";
|
||||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||||
import { isUnavailableState } from "../../../data/entity";
|
import { isUnavailableState } from "../../../data/entity";
|
||||||
import { SENSOR_DEVICE_CLASS_TIMESTAMP } from "../../../data/sensor";
|
import { SENSOR_DEVICE_CLASS_TIMESTAMP } from "../../../data/sensor";
|
||||||
@@ -20,7 +21,6 @@ class EntityPreviewRow extends LitElement {
|
|||||||
return html`<state-badge
|
return html`<state-badge
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.stateObj=${stateObj}
|
.stateObj=${stateObj}
|
||||||
stateColor
|
|
||||||
></state-badge>
|
></state-badge>
|
||||||
<div class="name" .title=${computeStateName(stateObj)}>
|
<div class="name" .title=${computeStateName(stateObj)}>
|
||||||
${computeStateName(stateObj)}
|
${computeStateName(stateObj)}
|
||||||
@@ -35,7 +35,13 @@ class EntityPreviewRow extends LitElement {
|
|||||||
capitalize
|
capitalize
|
||||||
></hui-timestamp-display>
|
></hui-timestamp-display>
|
||||||
`
|
`
|
||||||
: this.hass.formatEntityState(stateObj)}
|
: computeStateDisplay(
|
||||||
|
this.hass!.localize,
|
||||||
|
stateObj,
|
||||||
|
this.hass.locale,
|
||||||
|
this.hass.config,
|
||||||
|
this.hass.entities
|
||||||
|
)}
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2,20 +2,19 @@ import { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket";
|
|||||||
import { LitElement, html } from "lit";
|
import { LitElement, html } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { FlowType } from "../../../data/data_entry_flow";
|
import { FlowType } from "../../../data/data_entry_flow";
|
||||||
import { GroupPreview, subscribePreviewGroup } from "../../../data/group";
|
import { subscribePreviewGroupBinarySensor } from "../../../data/group";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import "./entity-preview-row";
|
import "./entity-preview-row";
|
||||||
import { debounce } from "../../../common/util/debounce";
|
|
||||||
|
|
||||||
@customElement("flow-preview-group")
|
@customElement("flow-preview-group_binary_sensor")
|
||||||
class FlowPreviewGroup extends LitElement {
|
class FlowPreviewGroupBinarySensor extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property() public flowType!: FlowType;
|
@property() public flowType!: FlowType;
|
||||||
|
|
||||||
public handler!: string;
|
public handler!: string;
|
||||||
|
|
||||||
@property() public stepId!: string;
|
public stepId!: string;
|
||||||
|
|
||||||
@property() public flowId!: string;
|
@property() public flowId!: string;
|
||||||
|
|
||||||
@@ -35,7 +34,7 @@ class FlowPreviewGroup extends LitElement {
|
|||||||
|
|
||||||
willUpdate(changedProps) {
|
willUpdate(changedProps) {
|
||||||
if (changedProps.has("stepData")) {
|
if (changedProps.has("stepData")) {
|
||||||
this._debouncedSubscribePreview();
|
this._subscribePreview();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,10 +45,13 @@ class FlowPreviewGroup extends LitElement {
|
|||||||
></entity-preview-row>`;
|
></entity-preview-row>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _setPreview = (preview: GroupPreview) => {
|
private _setPreview = (preview: {
|
||||||
|
state: string;
|
||||||
|
attributes: Record<string, any>;
|
||||||
|
}) => {
|
||||||
const now = new Date().toISOString();
|
const now = new Date().toISOString();
|
||||||
this._preview = {
|
this._preview = {
|
||||||
entity_id: `${this.stepId}.flow_preview`,
|
entity_id: "binary_sensor.flow_preview",
|
||||||
last_changed: now,
|
last_changed: now,
|
||||||
last_updated: now,
|
last_updated: now,
|
||||||
context: { id: "", parent_id: null, user_id: null },
|
context: { id: "", parent_id: null, user_id: null },
|
||||||
@@ -57,10 +59,6 @@ class FlowPreviewGroup extends LitElement {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
private _debouncedSubscribePreview = debounce(() => {
|
|
||||||
this._subscribePreview();
|
|
||||||
}, 250);
|
|
||||||
|
|
||||||
private async _subscribePreview() {
|
private async _subscribePreview() {
|
||||||
if (this._unsub) {
|
if (this._unsub) {
|
||||||
(await this._unsub)();
|
(await this._unsub)();
|
||||||
@@ -70,7 +68,7 @@ class FlowPreviewGroup extends LitElement {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
this._unsub = subscribePreviewGroup(
|
this._unsub = subscribePreviewGroupBinarySensor(
|
||||||
this.hass,
|
this.hass,
|
||||||
this.flowId,
|
this.flowId,
|
||||||
this.flowType,
|
this.flowType,
|
||||||
@@ -85,6 +83,6 @@ class FlowPreviewGroup extends LitElement {
|
|||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"flow-preview-group": FlowPreviewGroup;
|
"flow-preview-group_binary_sensor": FlowPreviewGroupBinarySensor;
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,25 +1,20 @@
|
|||||||
import { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket";
|
import { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||||
import { LitElement, html } from "lit";
|
import { LitElement, html } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { debounce } from "../../../common/util/debounce";
|
|
||||||
import { FlowType } from "../../../data/data_entry_flow";
|
import { FlowType } from "../../../data/data_entry_flow";
|
||||||
import {
|
import { subscribePreviewGroupSensor } from "../../../data/group";
|
||||||
TemplatePreview,
|
|
||||||
subscribePreviewTemplate,
|
|
||||||
} from "../../../data/ws-templates";
|
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import "./entity-preview-row";
|
import "./entity-preview-row";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
|
||||||
|
|
||||||
@customElement("flow-preview-template")
|
@customElement("flow-preview-group_sensor")
|
||||||
class FlowPreviewTemplate extends LitElement {
|
class FlowPreviewGroupSensor extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property() public flowType!: FlowType;
|
@property() public flowType!: FlowType;
|
||||||
|
|
||||||
public handler!: string;
|
public handler!: string;
|
||||||
|
|
||||||
@property() public stepId!: string;
|
public stepId!: string;
|
||||||
|
|
||||||
@property() public flowId!: string;
|
@property() public flowId!: string;
|
||||||
|
|
||||||
@@ -27,8 +22,6 @@ class FlowPreviewTemplate extends LitElement {
|
|||||||
|
|
||||||
@state() private _preview?: HassEntity;
|
@state() private _preview?: HassEntity;
|
||||||
|
|
||||||
@state() private _error?: string;
|
|
||||||
|
|
||||||
private _unsub?: Promise<UnsubscribeFunc>;
|
private _unsub?: Promise<UnsubscribeFunc>;
|
||||||
|
|
||||||
disconnectedCallback(): void {
|
disconnectedCallback(): void {
|
||||||
@@ -41,30 +34,24 @@ class FlowPreviewTemplate extends LitElement {
|
|||||||
|
|
||||||
willUpdate(changedProps) {
|
willUpdate(changedProps) {
|
||||||
if (changedProps.has("stepData")) {
|
if (changedProps.has("stepData")) {
|
||||||
this._debouncedSubscribePreview();
|
this._subscribePreview();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
if (this._error) {
|
|
||||||
return html`<ha-alert alert-type="error">${this._error}</ha-alert>`;
|
|
||||||
}
|
|
||||||
return html`<entity-preview-row
|
return html`<entity-preview-row
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.stateObj=${this._preview}
|
.stateObj=${this._preview}
|
||||||
></entity-preview-row>`;
|
></entity-preview-row>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _setPreview = (preview: TemplatePreview) => {
|
private _setPreview = (preview: {
|
||||||
if ("error" in preview) {
|
state: string;
|
||||||
this._error = preview.error;
|
attributes: Record<string, any>;
|
||||||
this._preview = undefined;
|
}) => {
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._error = undefined;
|
|
||||||
const now = new Date().toISOString();
|
const now = new Date().toISOString();
|
||||||
this._preview = {
|
this._preview = {
|
||||||
entity_id: `${this.stepId}.flow_preview`,
|
entity_id: "sensor.flow_preview",
|
||||||
last_changed: now,
|
last_changed: now,
|
||||||
last_updated: now,
|
last_updated: now,
|
||||||
context: { id: "", parent_id: null, user_id: null },
|
context: { id: "", parent_id: null, user_id: null },
|
||||||
@@ -72,10 +59,6 @@ class FlowPreviewTemplate extends LitElement {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
private _debouncedSubscribePreview = debounce(() => {
|
|
||||||
this._subscribePreview();
|
|
||||||
}, 250);
|
|
||||||
|
|
||||||
private async _subscribePreview() {
|
private async _subscribePreview() {
|
||||||
if (this._unsub) {
|
if (this._unsub) {
|
||||||
(await this._unsub)();
|
(await this._unsub)();
|
||||||
@@ -84,24 +67,19 @@ class FlowPreviewTemplate extends LitElement {
|
|||||||
if (this.flowType === "repair_flow") {
|
if (this.flowType === "repair_flow") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!this.stepData.type) {
|
||||||
|
this._preview = undefined;
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
this._unsub = subscribePreviewTemplate(
|
this._unsub = subscribePreviewGroupSensor(
|
||||||
this.hass,
|
this.hass,
|
||||||
this.flowId,
|
this.flowId,
|
||||||
this.flowType,
|
this.flowType,
|
||||||
this.stepData,
|
this.stepData,
|
||||||
this._setPreview
|
this._setPreview
|
||||||
);
|
);
|
||||||
await this._unsub;
|
} catch (err) {
|
||||||
fireEvent(this, "set-flow-errors", { errors: {} });
|
|
||||||
} catch (err: any) {
|
|
||||||
if (typeof err.message === "string") {
|
|
||||||
this._error = err.message;
|
|
||||||
} else {
|
|
||||||
this._error = undefined;
|
|
||||||
fireEvent(this, "set-flow-errors", err.message);
|
|
||||||
}
|
|
||||||
this._unsub = undefined;
|
|
||||||
this._preview = undefined;
|
this._preview = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -109,6 +87,6 @@ class FlowPreviewTemplate extends LitElement {
|
|||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"flow-preview-template": FlowPreviewTemplate;
|
"flow-preview-group_sensor": FlowPreviewGroupSensor;
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -70,7 +70,7 @@ class StepFlowForm extends LitElement {
|
|||||||
></ha-form>
|
></ha-form>
|
||||||
</div>
|
</div>
|
||||||
${step.preview
|
${step.preview
|
||||||
? html`<div class="preview" @set-flow-errors=${this._setError}>
|
? html`<div class="preview">
|
||||||
<h3>
|
<h3>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.integrations.config_flow.preview"
|
"ui.panel.config.integrations.config_flow.preview"
|
||||||
@@ -107,10 +107,6 @@ class StepFlowForm extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _setError(ev: CustomEvent) {
|
|
||||||
this.step = { ...this.step, errors: ev.detail };
|
|
||||||
}
|
|
||||||
|
|
||||||
protected firstUpdated(changedProps: PropertyValues) {
|
protected firstUpdated(changedProps: PropertyValues) {
|
||||||
super.firstUpdated(changedProps);
|
super.firstUpdated(changedProps);
|
||||||
setTimeout(() => this.shadowRoot!.querySelector("ha-form")!.focus(), 0);
|
setTimeout(() => this.shadowRoot!.querySelector("ha-form")!.focus(), 0);
|
||||||
@@ -257,9 +253,6 @@ class StepFlowForm extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HASSDomEvents {
|
|
||||||
"set-flow-errors": { errors: DataEntryFlowStepForm["errors"] };
|
|
||||||
}
|
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"step-flow-form": StepFlowForm;
|
"step-flow-form": StepFlowForm;
|
||||||
}
|
}
|
||||||
|
@@ -10,7 +10,7 @@ import {
|
|||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
import { styleMap } from "lit/directives/style-map";
|
import { styleMap } from "lit/directives/style-map";
|
||||||
import { UNIT_F } from "../../../../common/const";
|
import { computeAttributeValueDisplay } from "../../../../common/entity/compute_attribute_display";
|
||||||
import { stateActive } from "../../../../common/entity/state_active";
|
import { stateActive } from "../../../../common/entity/state_active";
|
||||||
import { stateColorCss } from "../../../../common/entity/state_color";
|
import { stateColorCss } from "../../../../common/entity/state_color";
|
||||||
import { supportsFeature } from "../../../../common/entity/supports-feature";
|
import { supportsFeature } from "../../../../common/entity/supports-feature";
|
||||||
@@ -67,7 +67,7 @@ export class HaMoreInfoClimateTemperature extends LitElement {
|
|||||||
private get _step() {
|
private get _step() {
|
||||||
return (
|
return (
|
||||||
this.stateObj.attributes.target_temp_step ||
|
this.stateObj.attributes.target_temp_step ||
|
||||||
(this.hass.config.unit_system.temperature === UNIT_F ? 1 : 0.5)
|
(this.hass.config.unit_system.temperature.indexOf("F") === -1 ? 0.5 : 1)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,10 +161,14 @@ export class HaMoreInfoClimateTemperature extends LitElement {
|
|||||||
|
|
||||||
const action = this.stateObj.attributes.hvac_action;
|
const action = this.stateObj.attributes.hvac_action;
|
||||||
|
|
||||||
const actionLabel = this.hass.formatEntityAttributeValue(
|
const actionLabel = computeAttributeValueDisplay(
|
||||||
|
this.hass.localize,
|
||||||
this.stateObj,
|
this.stateObj,
|
||||||
|
this.hass.locale,
|
||||||
|
this.hass.config,
|
||||||
|
this.hass.entities,
|
||||||
"hvac_action"
|
"hvac_action"
|
||||||
);
|
) as string;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<p class="label">
|
<p class="label">
|
||||||
@@ -275,21 +279,15 @@ export class HaMoreInfoClimateTemperature extends LitElement {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const activeModes = this.stateObj.attributes.hvac_modes.filter(
|
||||||
|
(m) => m !== "off"
|
||||||
|
);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
supportsTargetTemperature &&
|
supportsTargetTemperature &&
|
||||||
this._targetTemperature.value != null &&
|
this._targetTemperature.value != null &&
|
||||||
this.stateObj.state !== UNAVAILABLE
|
this.stateObj.state !== UNAVAILABLE
|
||||||
) {
|
) {
|
||||||
const heatCoolModes = this.stateObj.attributes.hvac_modes.filter((m) =>
|
|
||||||
["heat", "cool", "heat_cool"].includes(m)
|
|
||||||
);
|
|
||||||
const sliderMode =
|
|
||||||
SLIDER_MODES[
|
|
||||||
heatCoolModes.length === 1 && ["off", "auto"].includes(mode)
|
|
||||||
? heatCoolModes[0]
|
|
||||||
: mode
|
|
||||||
];
|
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div
|
<div
|
||||||
class="container"
|
class="container"
|
||||||
@@ -300,7 +298,9 @@ export class HaMoreInfoClimateTemperature extends LitElement {
|
|||||||
>
|
>
|
||||||
<ha-control-circular-slider
|
<ha-control-circular-slider
|
||||||
.inactive=${!active}
|
.inactive=${!active}
|
||||||
.mode=${sliderMode}
|
.mode=${mode === "off" && activeModes.length === 1
|
||||||
|
? SLIDER_MODES[activeModes[0]]
|
||||||
|
: SLIDER_MODES[mode]}
|
||||||
.value=${this._targetTemperature.value}
|
.value=${this._targetTemperature.value}
|
||||||
.min=${this._min}
|
.min=${this._min}
|
||||||
.max=${this._max}
|
.max=${this._max}
|
||||||
|
@@ -35,9 +35,7 @@ export class HaMoreInfoCoverPosition extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
const forcedState = this.stateObj.state === "closed" ? "open" : undefined;
|
const color = stateColorCss(this.stateObj);
|
||||||
|
|
||||||
const color = stateColorCss(this.stateObj, forcedState);
|
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-control-slider
|
<ha-control-slider
|
||||||
@@ -70,6 +68,8 @@ export class HaMoreInfoCoverPosition extends LitElement {
|
|||||||
height: 45vh;
|
height: 45vh;
|
||||||
max-height: 320px;
|
max-height: 320px;
|
||||||
min-height: 200px;
|
min-height: 200px;
|
||||||
|
/* Force inactive state to be colored for the slider */
|
||||||
|
--state-cover-inactive-color: var(--state-cover-active-color);
|
||||||
--control-slider-thickness: 100px;
|
--control-slider-thickness: 100px;
|
||||||
--control-slider-border-radius: 24px;
|
--control-slider-border-radius: 24px;
|
||||||
--control-slider-color: var(--primary-color);
|
--control-slider-color: var(--primary-color);
|
||||||
|
@@ -72,9 +72,8 @@ export class HaMoreInfoCoverTiltPosition extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
const forcedState = this.stateObj.state === "closed" ? "open" : undefined;
|
const color = stateColorCss(this.stateObj);
|
||||||
|
const isUnavailable = this.stateObj.state === UNAVAILABLE;
|
||||||
const color = stateColorCss(this.stateObj, forcedState);
|
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-control-slider
|
<ha-control-slider
|
||||||
@@ -94,7 +93,7 @@ export class HaMoreInfoCoverTiltPosition extends LitElement {
|
|||||||
"--control-slider-color": color,
|
"--control-slider-color": color,
|
||||||
"--control-slider-background": color,
|
"--control-slider-background": color,
|
||||||
})}
|
})}
|
||||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
.disabled=${isUnavailable}
|
||||||
>
|
>
|
||||||
<div slot="background" class="gradient"></div>
|
<div slot="background" class="gradient"></div>
|
||||||
</ha-control-slider>
|
</ha-control-slider>
|
||||||
@@ -107,6 +106,8 @@ export class HaMoreInfoCoverTiltPosition extends LitElement {
|
|||||||
height: 45vh;
|
height: 45vh;
|
||||||
max-height: 320px;
|
max-height: 320px;
|
||||||
min-height: 200px;
|
min-height: 200px;
|
||||||
|
/* Force inactive state to be colored for the slider */
|
||||||
|
--state-cover-inactive-color: var(--state-cover-active-color);
|
||||||
--control-slider-thickness: 100px;
|
--control-slider-thickness: 100px;
|
||||||
--control-slider-border-radius: 24px;
|
--control-slider-border-radius: 24px;
|
||||||
--control-slider-color: var(--primary-color);
|
--control-slider-color: var(--primary-color);
|
||||||
|
@@ -2,6 +2,7 @@ import { css, CSSResultGroup, html, LitElement } from "lit";
|
|||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { styleMap } from "lit/directives/style-map";
|
import { styleMap } from "lit/directives/style-map";
|
||||||
import { computeAttributeNameDisplay } from "../../../../common/entity/compute_attribute_display";
|
import { computeAttributeNameDisplay } from "../../../../common/entity/compute_attribute_display";
|
||||||
|
import { computeStateDisplay } from "../../../../common/entity/compute_state_display";
|
||||||
import { stateActive } from "../../../../common/entity/state_active";
|
import { stateActive } from "../../../../common/entity/state_active";
|
||||||
import { stateColorCss } from "../../../../common/entity/state_color";
|
import { stateColorCss } from "../../../../common/entity/state_color";
|
||||||
import "../../../../components/ha-control-select";
|
import "../../../../components/ha-control-select";
|
||||||
@@ -11,12 +12,12 @@ import { UNAVAILABLE } from "../../../../data/entity";
|
|||||||
import {
|
import {
|
||||||
computeFanSpeedCount,
|
computeFanSpeedCount,
|
||||||
computeFanSpeedIcon,
|
computeFanSpeedIcon,
|
||||||
FAN_SPEED_COUNT_MAX_FOR_BUTTONS,
|
|
||||||
FAN_SPEEDS,
|
|
||||||
FanEntity,
|
FanEntity,
|
||||||
fanPercentageToSpeed,
|
fanPercentageToSpeed,
|
||||||
FanSpeed,
|
FanSpeed,
|
||||||
fanSpeedToPercentage,
|
fanSpeedToPercentage,
|
||||||
|
FAN_SPEEDS,
|
||||||
|
FAN_SPEED_COUNT_MAX_FOR_BUTTONS,
|
||||||
} from "../../../../data/fan";
|
} from "../../../../data/fan";
|
||||||
import { HomeAssistant } from "../../../../types";
|
import { HomeAssistant } from "../../../../types";
|
||||||
|
|
||||||
@@ -67,7 +68,14 @@ export class HaMoreInfoFanSpeed extends LitElement {
|
|||||||
|
|
||||||
private _localizeSpeed(speed: FanSpeed) {
|
private _localizeSpeed(speed: FanSpeed) {
|
||||||
if (speed === "on" || speed === "off") {
|
if (speed === "on" || speed === "off") {
|
||||||
return this.hass.formatEntityState(this.stateObj, speed);
|
return computeStateDisplay(
|
||||||
|
this.hass.localize,
|
||||||
|
this.stateObj,
|
||||||
|
this.hass.locale,
|
||||||
|
this.hass.config,
|
||||||
|
this.hass.entities,
|
||||||
|
speed
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
this.hass.localize(`ui.dialogs.more_info_control.fan.speed.${speed}`) ||
|
this.hass.localize(`ui.dialogs.more_info_control.fan.speed.${speed}`) ||
|
||||||
|
@@ -1,79 +0,0 @@
|
|||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
|
||||||
import { customElement } from "lit/decorators";
|
|
||||||
import { classMap } from "lit/directives/class-map";
|
|
||||||
|
|
||||||
@customElement("ha-more-info-control-select-container")
|
|
||||||
export class HaMoreInfoControlSelectContainer extends LitElement {
|
|
||||||
protected render(): TemplateResult {
|
|
||||||
const classname = `items-${this.childElementCount}`;
|
|
||||||
|
|
||||||
return html`
|
|
||||||
<div class="controls">
|
|
||||||
<div
|
|
||||||
class="controls-scroll ${classMap({
|
|
||||||
[classname]: true,
|
|
||||||
multiline: this.childElementCount >= 4,
|
|
||||||
})}"
|
|
||||||
>
|
|
||||||
<slot></slot>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
|
||||||
return css`
|
|
||||||
.controls {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
.controls-scroll {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: flex-start;
|
|
||||||
gap: 12px;
|
|
||||||
margin: auto;
|
|
||||||
overflow: auto;
|
|
||||||
-ms-overflow-style: none; /* IE and Edge */
|
|
||||||
scrollbar-width: none; /* Firefox */
|
|
||||||
margin: 0 -24px;
|
|
||||||
padding: 0 24px;
|
|
||||||
}
|
|
||||||
.controls-scroll::-webkit-scrollbar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
::slotted(*) {
|
|
||||||
min-width: 120px;
|
|
||||||
max-width: 160px;
|
|
||||||
flex: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media all and (hover: hover),
|
|
||||||
all and (min-width: 600px) and (min-height: 501px) {
|
|
||||||
.controls-scroll {
|
|
||||||
justify-content: center;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 450px;
|
|
||||||
}
|
|
||||||
.controls-scroll.items-4 {
|
|
||||||
max-width: 300px;
|
|
||||||
}
|
|
||||||
.controls-scroll.items-3 ::slotted(*) {
|
|
||||||
max-width: 140px;
|
|
||||||
}
|
|
||||||
.multiline ::slotted(*) {
|
|
||||||
width: 140px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
"ha-more-info-control-select-container": HaMoreInfoControlSelectContainer;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -22,6 +22,35 @@ export const moreInfoControlStyle = css`
|
|||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.secondary-controls {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.secondary-controls-scroll {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-start;
|
||||||
|
gap: 12px;
|
||||||
|
margin: auto;
|
||||||
|
overflow: auto;
|
||||||
|
-ms-overflow-style: none; /* IE and Edge */
|
||||||
|
scrollbar-width: none; /* Firefox */
|
||||||
|
margin: 0 -24px;
|
||||||
|
padding: 0 24px;
|
||||||
|
}
|
||||||
|
.secondary-controls-scroll::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Don't use scroll on device without touch support */
|
||||||
|
@media (hover: hover) {
|
||||||
|
.secondary-controls-scroll {
|
||||||
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.buttons {
|
.buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -34,10 +63,6 @@ export const moreInfoControlStyle = css`
|
|||||||
}
|
}
|
||||||
|
|
||||||
ha-attributes {
|
ha-attributes {
|
||||||
display: block;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
ha-more-info-control-select-container + ha-attributes:not([empty]) {
|
|
||||||
margin-top: 16px;
|
|
||||||
}
|
|
||||||
`;
|
`;
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import { computeStateDisplay } from "../../../common/entity/compute_state_display";
|
||||||
import "../../../components/ha-absolute-time";
|
import "../../../components/ha-absolute-time";
|
||||||
import "../../../components/ha-relative-time";
|
import "../../../components/ha-relative-time";
|
||||||
import { isUnavailableState } from "../../../data/entity";
|
import { isUnavailableState } from "../../../data/entity";
|
||||||
@@ -18,22 +20,30 @@ export class HaMoreInfoStateHeader extends LitElement {
|
|||||||
|
|
||||||
@state() private _absoluteTime = false;
|
@state() private _absoluteTime = false;
|
||||||
|
|
||||||
private _localizeState(): TemplateResult | string {
|
private _computeStateDisplay(stateObj: HassEntity): TemplateResult | string {
|
||||||
if (
|
if (
|
||||||
this.stateObj.attributes.device_class === SENSOR_DEVICE_CLASS_TIMESTAMP &&
|
stateObj.attributes.device_class === SENSOR_DEVICE_CLASS_TIMESTAMP &&
|
||||||
!isUnavailableState(this.stateObj.state)
|
!isUnavailableState(stateObj.state)
|
||||||
) {
|
) {
|
||||||
return html`
|
return html`
|
||||||
<hui-timestamp-display
|
<hui-timestamp-display
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.ts=${new Date(this.stateObj.state)}
|
.ts=${new Date(stateObj.state)}
|
||||||
format="relative"
|
format="relative"
|
||||||
capitalize
|
capitalize
|
||||||
></hui-timestamp-display>
|
></hui-timestamp-display>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.hass.formatEntityState(this.stateObj);
|
const stateDisplay = computeStateDisplay(
|
||||||
|
this.hass!.localize,
|
||||||
|
stateObj,
|
||||||
|
this.hass!.locale,
|
||||||
|
this.hass!.config,
|
||||||
|
this.hass!.entities
|
||||||
|
);
|
||||||
|
|
||||||
|
return stateDisplay;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _toggleAbsolute() {
|
private _toggleAbsolute() {
|
||||||
@@ -41,7 +51,8 @@ export class HaMoreInfoStateHeader extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
const stateDisplay = this.stateOverride ?? this._localizeState();
|
const stateDisplay =
|
||||||
|
this.stateOverride ?? this._computeStateDisplay(this.stateObj);
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<p class="state">${stateDisplay}</p>
|
<p class="state">${stateDisplay}</p>
|
||||||
|
@@ -2,6 +2,7 @@ import { mdiMinus, mdiPlus } from "@mdi/js";
|
|||||||
import { CSSResultGroup, LitElement, PropertyValues, css, html } from "lit";
|
import { CSSResultGroup, LitElement, PropertyValues, css, html } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { styleMap } from "lit/directives/style-map";
|
import { styleMap } from "lit/directives/style-map";
|
||||||
|
import { computeAttributeValueDisplay } from "../../../../common/entity/compute_attribute_display";
|
||||||
import { stateActive } from "../../../../common/entity/state_active";
|
import { stateActive } from "../../../../common/entity/state_active";
|
||||||
import { stateColorCss } from "../../../../common/entity/state_color";
|
import { stateColorCss } from "../../../../common/entity/state_color";
|
||||||
import { clamp } from "../../../../common/number/clamp";
|
import { clamp } from "../../../../common/number/clamp";
|
||||||
@@ -91,10 +92,14 @@ export class HaMoreInfoHumidifierHumidity extends LitElement {
|
|||||||
|
|
||||||
const action = this.stateObj.attributes.action;
|
const action = this.stateObj.attributes.action;
|
||||||
|
|
||||||
const actionLabel = this.hass.formatEntityAttributeValue(
|
const actionLabel = computeAttributeValueDisplay(
|
||||||
|
this.hass.localize,
|
||||||
this.stateObj,
|
this.stateObj,
|
||||||
|
this.hass.locale,
|
||||||
|
this.hass.config,
|
||||||
|
this.hass.entities,
|
||||||
"action"
|
"action"
|
||||||
);
|
) as string;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<p class="label">
|
<p class="label">
|
||||||
|
@@ -26,6 +26,7 @@ import "../../../../components/ha-hs-color-picker";
|
|||||||
import "../../../../components/ha-icon";
|
import "../../../../components/ha-icon";
|
||||||
import "../../../../components/ha-icon-button-prev";
|
import "../../../../components/ha-icon-button-prev";
|
||||||
import "../../../../components/ha-labeled-slider";
|
import "../../../../components/ha-labeled-slider";
|
||||||
|
import "../../../../components/ha-temp-color-picker";
|
||||||
import {
|
import {
|
||||||
getLightCurrentModeRgbColor,
|
getLightCurrentModeRgbColor,
|
||||||
LightColor,
|
LightColor,
|
||||||
|
@@ -1,31 +1,25 @@
|
|||||||
import {
|
import {
|
||||||
CSSResultGroup,
|
|
||||||
LitElement,
|
|
||||||
PropertyValues,
|
|
||||||
css,
|
css,
|
||||||
|
CSSResultGroup,
|
||||||
html,
|
html,
|
||||||
|
LitElement,
|
||||||
nothing,
|
nothing,
|
||||||
|
PropertyValues,
|
||||||
} from "lit";
|
} from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { styleMap } from "lit/directives/style-map";
|
|
||||||
import memoizeOne from "memoize-one";
|
|
||||||
import { rgb2hex } from "../../../../common/color/convert-color";
|
|
||||||
import {
|
|
||||||
DEFAULT_MAX_KELVIN,
|
|
||||||
DEFAULT_MIN_KELVIN,
|
|
||||||
temperature2rgb,
|
|
||||||
} from "../../../../common/color/convert-light-color";
|
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
import { stateColorCss } from "../../../../common/entity/state_color";
|
|
||||||
import { throttle } from "../../../../common/util/throttle";
|
import { throttle } from "../../../../common/util/throttle";
|
||||||
import "../../../../components/ha-control-slider";
|
import "../../../../components/ha-temp-color-picker";
|
||||||
import { UNAVAILABLE } from "../../../../data/entity";
|
|
||||||
import {
|
import {
|
||||||
LightColor,
|
LightColor,
|
||||||
LightColorMode,
|
LightColorMode,
|
||||||
LightEntity,
|
LightEntity,
|
||||||
} from "../../../../data/light";
|
} from "../../../../data/light";
|
||||||
import { HomeAssistant } from "../../../../types";
|
import { HomeAssistant } from "../../../../types";
|
||||||
|
import {
|
||||||
|
DEFAULT_MAX_KELVIN,
|
||||||
|
DEFAULT_MIN_KELVIN,
|
||||||
|
} from "../../../../common/color/convert-light-color";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HASSDomEvents {
|
interface HASSDomEvents {
|
||||||
@@ -34,26 +28,6 @@ declare global {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const generateColorTemperatureGradient = (min: number, max: number) => {
|
|
||||||
const count = 10;
|
|
||||||
|
|
||||||
const gradient: [number, string][] = [];
|
|
||||||
|
|
||||||
const step = (max - min) / count;
|
|
||||||
const percentageStep = 1 / count;
|
|
||||||
|
|
||||||
for (let i = 0; i < count + 1; i++) {
|
|
||||||
const value = min + step * i;
|
|
||||||
|
|
||||||
const hex = rgb2hex(temperature2rgb(value));
|
|
||||||
gradient.push([percentageStep * i, hex]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return gradient
|
|
||||||
.map(([stop, color]) => `${color} ${(stop as number) * 100}%`)
|
|
||||||
.join(", ");
|
|
||||||
};
|
|
||||||
|
|
||||||
@customElement("light-color-temp-picker")
|
@customElement("light-color-temp-picker")
|
||||||
class LightColorTempPicker extends LitElement {
|
class LightColorTempPicker extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
@@ -72,36 +46,18 @@ class LightColorTempPicker extends LitElement {
|
|||||||
const maxKelvin =
|
const maxKelvin =
|
||||||
this.stateObj.attributes.max_color_temp_kelvin ?? DEFAULT_MAX_KELVIN;
|
this.stateObj.attributes.max_color_temp_kelvin ?? DEFAULT_MAX_KELVIN;
|
||||||
|
|
||||||
const gradient = this._generateTemperatureGradient(minKelvin!, maxKelvin);
|
|
||||||
const color = stateColorCss(this.stateObj);
|
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-control-slider
|
<ha-temp-color-picker
|
||||||
inverted
|
@value-changed=${this._ctColorChanged}
|
||||||
vertical
|
@cursor-moved=${this._ctColorCursorMoved}
|
||||||
.value=${this._ctPickerValue}
|
|
||||||
.min=${minKelvin}
|
.min=${minKelvin}
|
||||||
.max=${maxKelvin}
|
.max=${maxKelvin}
|
||||||
mode="cursor"
|
.value=${this._ctPickerValue}
|
||||||
@value-changed=${this._ctColorChanged}
|
|
||||||
@slider-moved=${this._ctColorCursorMoved}
|
|
||||||
.ariaLabel=${this.hass.localize(
|
|
||||||
"ui.dialogs.more_info_control.light.color_temp"
|
|
||||||
)}
|
|
||||||
style=${styleMap({
|
|
||||||
"--control-slider-color": color,
|
|
||||||
"--gradient": gradient,
|
|
||||||
})}
|
|
||||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
|
||||||
>
|
>
|
||||||
</ha-control-slider>
|
</ha-temp-color-picker>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _generateTemperatureGradient = memoizeOne(
|
|
||||||
(min: number, max: number) => generateColorTemperatureGradient(min, max)
|
|
||||||
);
|
|
||||||
|
|
||||||
public _updateSliderValues() {
|
public _updateSliderValues() {
|
||||||
const stateObj = this.stateObj;
|
const stateObj = this.stateObj;
|
||||||
|
|
||||||
@@ -182,18 +138,10 @@ class LightColorTempPicker extends LitElement {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
ha-control-slider {
|
ha-temp-color-picker {
|
||||||
height: 45vh;
|
height: 45vh;
|
||||||
max-height: 320px;
|
max-height: 320px;
|
||||||
min-height: 200px;
|
min-height: 200px;
|
||||||
--control-slider-thickness: 100px;
|
|
||||||
--control-slider-border-radius: 24px;
|
|
||||||
--control-slider-color: var(--primary-color);
|
|
||||||
--control-slider-background: -webkit-linear-gradient(
|
|
||||||
top,
|
|
||||||
var(--gradient)
|
|
||||||
);
|
|
||||||
--control-slider-background-opacity: 1;
|
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
|
@@ -9,7 +9,6 @@ import {
|
|||||||
} from "lit";
|
} from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { styleMap } from "lit/directives/style-map";
|
import { styleMap } from "lit/directives/style-map";
|
||||||
import { UNIT_F } from "../../../../common/const";
|
|
||||||
import { stateActive } from "../../../../common/entity/state_active";
|
import { stateActive } from "../../../../common/entity/state_active";
|
||||||
import { stateColorCss } from "../../../../common/entity/state_color";
|
import { stateColorCss } from "../../../../common/entity/state_color";
|
||||||
import { supportsFeature } from "../../../../common/entity/supports-feature";
|
import { supportsFeature } from "../../../../common/entity/supports-feature";
|
||||||
@@ -45,7 +44,7 @@ export class HaMoreInfoWaterHeaterTemperature extends LitElement {
|
|||||||
private get _step() {
|
private get _step() {
|
||||||
return (
|
return (
|
||||||
this.stateObj.attributes.target_temp_step ||
|
this.stateObj.attributes.target_temp_step ||
|
||||||
(this.hass.config.unit_system.temperature === UNIT_F ? 1 : 0.5)
|
(this.hass.config.unit_system.temperature.indexOf("F") === -1 ? 0.5 : 1)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -38,7 +38,6 @@ import { haOscillating } from "../../../data/icons/haOscillating";
|
|||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import "../components/climate/ha-more-info-climate-humidity";
|
import "../components/climate/ha-more-info-climate-humidity";
|
||||||
import "../components/climate/ha-more-info-climate-temperature";
|
import "../components/climate/ha-more-info-climate-temperature";
|
||||||
import "../components/ha-more-info-control-select-container";
|
|
||||||
import { moreInfoControlStyle } from "../components/ha-more-info-control-style";
|
import { moreInfoControlStyle } from "../components/ha-more-info-control-style";
|
||||||
|
|
||||||
type MainControl = "temperature" | "humidity";
|
type MainControl = "temperature" | "humidity";
|
||||||
@@ -142,7 +141,7 @@ class MoreInfoClimate extends LitElement {
|
|||||||
.selected=${this._mainControl === "temperature"}
|
.selected=${this._mainControl === "temperature"}
|
||||||
.disabled=${this.stateObj!.state === UNAVAILABLE}
|
.disabled=${this.stateObj!.state === UNAVAILABLE}
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.dialogs.more_info_control.climate.temperature"
|
"ui.dialogs.more_info_control.light.color"
|
||||||
)}
|
)}
|
||||||
.control=${"temperature"}
|
.control=${"temperature"}
|
||||||
@click=${this._setMainControl}
|
@click=${this._setMainControl}
|
||||||
@@ -153,7 +152,7 @@ class MoreInfoClimate extends LitElement {
|
|||||||
.selected=${this._mainControl === "humidity"}
|
.selected=${this._mainControl === "humidity"}
|
||||||
.disabled=${this.stateObj!.state === UNAVAILABLE}
|
.disabled=${this.stateObj!.state === UNAVAILABLE}
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.dialogs.more_info_control.climate.humidity"
|
"ui.dialogs.more_info_control.light.color_temp"
|
||||||
)}
|
)}
|
||||||
.control=${"humidity"}
|
.control=${"humidity"}
|
||||||
@click=${this._setMainControl}
|
@click=${this._setMainControl}
|
||||||
@@ -164,9 +163,13 @@ class MoreInfoClimate extends LitElement {
|
|||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
</div>
|
</div>
|
||||||
<ha-more-info-control-select-container>
|
<div class="secondary-controls">
|
||||||
|
<div class="secondary-controls-scroll">
|
||||||
<ha-control-select-menu
|
<ha-control-select-menu
|
||||||
.label=${this.hass.localize("ui.card.climate.mode")}
|
.label=${this.hass.formatEntityAttributeName(
|
||||||
|
this.stateObj,
|
||||||
|
"hvac_mode"
|
||||||
|
)}
|
||||||
.value=${stateObj.state}
|
.value=${stateObj.state}
|
||||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||||
fixedMenuPosition
|
fixedMenuPosition
|
||||||
@@ -204,7 +207,10 @@ class MoreInfoClimate extends LitElement {
|
|||||||
@selected=${this._handlePresetmodeChanged}
|
@selected=${this._handlePresetmodeChanged}
|
||||||
@closed=${stopPropagation}
|
@closed=${stopPropagation}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiTuneVariant}></ha-svg-icon>
|
<ha-svg-icon
|
||||||
|
slot="icon"
|
||||||
|
.path=${mdiTuneVariant}
|
||||||
|
></ha-svg-icon>
|
||||||
${stateObj.attributes.preset_modes!.map(
|
${stateObj.attributes.preset_modes!.map(
|
||||||
(mode) => html`
|
(mode) => html`
|
||||||
<ha-list-item .value=${mode} graphic="icon">
|
<ha-list-item .value=${mode} graphic="icon">
|
||||||
@@ -289,7 +295,8 @@ class MoreInfoClimate extends LitElement {
|
|||||||
</ha-control-select-menu>
|
</ha-control-select-menu>
|
||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
</ha-more-info-control-select-container>
|
</div>
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,21 +1,22 @@
|
|||||||
import { mdiMenu, mdiSwapVertical } from "@mdi/js";
|
import { mdiMenu, mdiSwapVertical } from "@mdi/js";
|
||||||
import {
|
import {
|
||||||
CSSResultGroup,
|
|
||||||
LitElement,
|
|
||||||
PropertyValues,
|
|
||||||
css,
|
css,
|
||||||
|
CSSResultGroup,
|
||||||
html,
|
html,
|
||||||
|
LitElement,
|
||||||
nothing,
|
nothing,
|
||||||
|
PropertyValues,
|
||||||
} from "lit";
|
} from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import { computeStateDisplay } from "../../../common/entity/compute_state_display";
|
||||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||||
import "../../../components/ha-attributes";
|
import "../../../components/ha-attributes";
|
||||||
import "../../../components/ha-icon-button-group";
|
import "../../../components/ha-icon-button-group";
|
||||||
import "../../../components/ha-icon-button-toggle";
|
import "../../../components/ha-icon-button-toggle";
|
||||||
import {
|
import {
|
||||||
|
computeCoverPositionStateDisplay,
|
||||||
CoverEntity,
|
CoverEntity,
|
||||||
CoverEntityFeature,
|
CoverEntityFeature,
|
||||||
computeCoverPositionStateDisplay,
|
|
||||||
} from "../../../data/cover";
|
} from "../../../data/cover";
|
||||||
import type { HomeAssistant } from "../../../types";
|
import type { HomeAssistant } from "../../../types";
|
||||||
import "../components/cover/ha-more-info-cover-buttons";
|
import "../components/cover/ha-more-info-cover-buttons";
|
||||||
@@ -82,8 +83,12 @@ class MoreInfoCover extends LitElement {
|
|||||||
const forcedState =
|
const forcedState =
|
||||||
liveValue != null ? (liveValue ? "open" : "closed") : undefined;
|
liveValue != null ? (liveValue ? "open" : "closed") : undefined;
|
||||||
|
|
||||||
const stateDisplay = this.hass.formatEntityState(
|
const stateDisplay = computeStateDisplay(
|
||||||
|
this.hass.localize,
|
||||||
this.stateObj!,
|
this.stateObj!,
|
||||||
|
this.hass.locale,
|
||||||
|
this.hass.config,
|
||||||
|
this.hass.entities,
|
||||||
forcedState
|
forcedState
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@@ -27,7 +27,6 @@ import { haOscillating } from "../../../data/icons/haOscillating";
|
|||||||
import { haOscillatingOff } from "../../../data/icons/haOscillatingOff";
|
import { haOscillatingOff } from "../../../data/icons/haOscillatingOff";
|
||||||
import type { HomeAssistant } from "../../../types";
|
import type { HomeAssistant } from "../../../types";
|
||||||
import "../components/fan/ha-more-info-fan-speed";
|
import "../components/fan/ha-more-info-fan-speed";
|
||||||
import "../components/ha-more-info-control-select-container";
|
|
||||||
import { moreInfoControlStyle } from "../components/ha-more-info-control-style";
|
import { moreInfoControlStyle } from "../components/ha-more-info-control-style";
|
||||||
import "../components/ha-more-info-state-header";
|
import "../components/ha-more-info-state-header";
|
||||||
import "../components/ha-more-info-toggle";
|
import "../components/ha-more-info-toggle";
|
||||||
@@ -86,7 +85,7 @@ class MoreInfoFan extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_handleOscillating(ev) {
|
_handleOscillating(ev) {
|
||||||
const newVal = ev.target.value === "true";
|
const newVal = ev.target.value === "on";
|
||||||
|
|
||||||
this.hass.callService("fan", "oscillate", {
|
this.hass.callService("fan", "oscillate", {
|
||||||
entity_id: this.stateObj!.entity_id,
|
entity_id: this.stateObj!.entity_id,
|
||||||
@@ -192,7 +191,8 @@ class MoreInfoFan extends LitElement {
|
|||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
</div>
|
</div>
|
||||||
<ha-more-info-control-select-container>
|
<div class="secondary-controls">
|
||||||
|
<div class="secondary-controls-scroll">
|
||||||
${supportsPresetMode && this.stateObj.attributes.preset_modes
|
${supportsPresetMode && this.stateObj.attributes.preset_modes
|
||||||
? html`
|
? html`
|
||||||
<ha-control-select-menu
|
<ha-control-select-menu
|
||||||
@@ -207,7 +207,10 @@ class MoreInfoFan extends LitElement {
|
|||||||
@selected=${this._handlePresetMode}
|
@selected=${this._handlePresetMode}
|
||||||
@closed=${stopPropagation}
|
@closed=${stopPropagation}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiTuneVariant}></ha-svg-icon>
|
<ha-svg-icon
|
||||||
|
slot="icon"
|
||||||
|
.path=${mdiTuneVariant}
|
||||||
|
></ha-svg-icon>
|
||||||
${this.stateObj.attributes.preset_modes?.map(
|
${this.stateObj.attributes.preset_modes?.map(
|
||||||
(mode) => html`
|
(mode) => html`
|
||||||
<ha-list-item .value=${mode}>
|
<ha-list-item .value=${mode}>
|
||||||
@@ -269,9 +272,7 @@ class MoreInfoFan extends LitElement {
|
|||||||
this.stateObj,
|
this.stateObj,
|
||||||
"oscillating"
|
"oscillating"
|
||||||
)}
|
)}
|
||||||
.value=${this.stateObj.attributes.oscillating
|
.value=${this.stateObj.attributes.oscillating ? "on" : "off"}
|
||||||
? "true"
|
|
||||||
: "false"}
|
|
||||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||||
fixedMenuPosition
|
fixedMenuPosition
|
||||||
naturalMenuWidth
|
naturalMenuWidth
|
||||||
@@ -282,32 +283,25 @@ class MoreInfoFan extends LitElement {
|
|||||||
slot="icon"
|
slot="icon"
|
||||||
.path=${haOscillatingOff}
|
.path=${haOscillatingOff}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
<ha-list-item value="true" graphic="icon">
|
<ha-list-item value="on" graphic="icon">
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="graphic"
|
slot="graphic"
|
||||||
.path=${haOscillating}
|
.path=${haOscillating}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
${this.hass.formatEntityAttributeValue(
|
${this.hass.localize("state.default.on")}
|
||||||
this.stateObj,
|
|
||||||
"oscillating",
|
|
||||||
true
|
|
||||||
)}
|
|
||||||
</ha-list-item>
|
</ha-list-item>
|
||||||
<ha-list-item value="false" graphic="icon">
|
<ha-list-item value="off" graphic="icon">
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="graphic"
|
slot="graphic"
|
||||||
.path=${haOscillatingOff}
|
.path=${haOscillatingOff}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
${this.hass.formatEntityAttributeValue(
|
${this.hass.localize("state.default.off")}
|
||||||
this.stateObj,
|
|
||||||
"oscillating",
|
|
||||||
false
|
|
||||||
)}
|
|
||||||
</ha-list-item>
|
</ha-list-item>
|
||||||
</ha-control-select-menu>
|
</ha-control-select-menu>
|
||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
</ha-more-info-control-select-container>
|
</div>
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -10,6 +10,11 @@ import {
|
|||||||
import { property, state } from "lit/decorators";
|
import { property, state } from "lit/decorators";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import { stopPropagation } from "../../../common/dom/stop_propagation";
|
import { stopPropagation } from "../../../common/dom/stop_propagation";
|
||||||
|
import {
|
||||||
|
computeAttributeNameDisplay,
|
||||||
|
computeAttributeValueDisplay,
|
||||||
|
} from "../../../common/entity/compute_attribute_display";
|
||||||
|
import { computeStateDisplay } from "../../../common/entity/compute_state_display";
|
||||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||||
import "../../../components/ha-control-select-menu";
|
import "../../../components/ha-control-select-menu";
|
||||||
import "../../../components/ha-list-item";
|
import "../../../components/ha-list-item";
|
||||||
@@ -20,7 +25,6 @@ import {
|
|||||||
computeHumidiferModeIcon,
|
computeHumidiferModeIcon,
|
||||||
} from "../../../data/humidifier";
|
} from "../../../data/humidifier";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import "../components/ha-more-info-control-select-container";
|
|
||||||
import { moreInfoControlStyle } from "../components/ha-more-info-control-style";
|
import { moreInfoControlStyle } from "../components/ha-more-info-control-style";
|
||||||
import "../components/humidifier/ha-more-info-humidifier-humidity";
|
import "../components/humidifier/ha-more-info-humidifier-humidity";
|
||||||
|
|
||||||
@@ -53,21 +57,26 @@ class MoreInfoHumidifier extends LitElement {
|
|||||||
HumidifierEntityFeature.MODES
|
HumidifierEntityFeature.MODES
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const currentHumidity = this.stateObj.attributes.current_humidity as number;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div class="current">
|
<div class="current">
|
||||||
${this.stateObj.attributes.current_humidity != null
|
${currentHumidity != null
|
||||||
? html`
|
? html`
|
||||||
<div>
|
<div>
|
||||||
<p class="label">
|
<p class="label">
|
||||||
${this.hass.formatEntityAttributeName(
|
${computeAttributeNameDisplay(
|
||||||
|
this.hass.localize,
|
||||||
this.stateObj,
|
this.stateObj,
|
||||||
|
this.hass.entities,
|
||||||
"current_humidity"
|
"current_humidity"
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
<p class="value">
|
<p class="value">
|
||||||
${this.hass.formatEntityAttributeValue(
|
${this.hass.formatEntityAttributeValue(
|
||||||
this.stateObj,
|
this.stateObj,
|
||||||
"current_humidity"
|
"current_humidity",
|
||||||
|
currentHumidity
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -82,7 +91,8 @@ class MoreInfoHumidifier extends LitElement {
|
|||||||
></ha-more-info-humidifier-humidity>
|
></ha-more-info-humidifier-humidity>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ha-more-info-control-select-container>
|
<div class="secondary-controls">
|
||||||
|
<div class="secondary-controls-scroll">
|
||||||
<ha-control-select-menu
|
<ha-control-select-menu
|
||||||
.label=${this.hass.localize("ui.card.humidifier.state")}
|
.label=${this.hass.localize("ui.card.humidifier.state")}
|
||||||
.value=${this.stateObj.state}
|
.value=${this.stateObj.state}
|
||||||
@@ -94,10 +104,24 @@ class MoreInfoHumidifier extends LitElement {
|
|||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiPower}></ha-svg-icon>
|
<ha-svg-icon slot="icon" .path=${mdiPower}></ha-svg-icon>
|
||||||
<ha-list-item value="off">
|
<ha-list-item value="off">
|
||||||
${this.hass.formatEntityState(this.stateObj, "off")}
|
${computeStateDisplay(
|
||||||
|
this.hass.localize,
|
||||||
|
this.stateObj,
|
||||||
|
this.hass.locale,
|
||||||
|
this.hass.config,
|
||||||
|
this.hass.entities,
|
||||||
|
"off"
|
||||||
|
)}
|
||||||
</ha-list-item>
|
</ha-list-item>
|
||||||
<ha-list-item value="on">
|
<ha-list-item value="on">
|
||||||
${this.hass.formatEntityState(this.stateObj, "on")}
|
${computeStateDisplay(
|
||||||
|
this.hass.localize,
|
||||||
|
this.stateObj,
|
||||||
|
this.hass.locale,
|
||||||
|
this.hass.config,
|
||||||
|
this.hass.entities,
|
||||||
|
"on"
|
||||||
|
)}
|
||||||
</ha-list-item>
|
</ha-list-item>
|
||||||
</ha-control-select-menu>
|
</ha-control-select-menu>
|
||||||
|
|
||||||
@@ -112,7 +136,10 @@ class MoreInfoHumidifier extends LitElement {
|
|||||||
@selected=${this._handleModeChanged}
|
@selected=${this._handleModeChanged}
|
||||||
@closed=${stopPropagation}
|
@closed=${stopPropagation}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiTuneVariant}></ha-svg-icon>
|
<ha-svg-icon
|
||||||
|
slot="icon"
|
||||||
|
.path=${mdiTuneVariant}
|
||||||
|
></ha-svg-icon>
|
||||||
${stateObj.attributes.available_modes!.map(
|
${stateObj.attributes.available_modes!.map(
|
||||||
(mode) => html`
|
(mode) => html`
|
||||||
<ha-list-item .value=${mode} graphic="icon">
|
<ha-list-item .value=${mode} graphic="icon">
|
||||||
@@ -120,8 +147,12 @@ class MoreInfoHumidifier extends LitElement {
|
|||||||
slot="graphic"
|
slot="graphic"
|
||||||
.path=${computeHumidiferModeIcon(mode)}
|
.path=${computeHumidiferModeIcon(mode)}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
${this.hass.formatEntityAttributeValue(
|
${computeAttributeValueDisplay(
|
||||||
|
hass.localize,
|
||||||
stateObj!,
|
stateObj!,
|
||||||
|
hass.locale,
|
||||||
|
hass.config,
|
||||||
|
hass.entities,
|
||||||
"mode",
|
"mode",
|
||||||
mode
|
mode
|
||||||
)}
|
)}
|
||||||
@@ -131,7 +162,8 @@ class MoreInfoHumidifier extends LitElement {
|
|||||||
</ha-control-select-menu>
|
</ha-control-select-menu>
|
||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
</ha-more-info-control-select-container>
|
</div>
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2,6 +2,7 @@ import { mdiHomeImportOutline, mdiPause, mdiPlay } from "@mdi/js";
|
|||||||
import { CSSResultGroup, LitElement, css, html, nothing } from "lit";
|
import { CSSResultGroup, LitElement, css, html, nothing } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
|
import { computeStateDisplay } from "../../../common/entity/compute_state_display";
|
||||||
import { computeStateDomain } from "../../../common/entity/compute_state_domain";
|
import { computeStateDomain } from "../../../common/entity/compute_state_domain";
|
||||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||||
import { blankBeforePercent } from "../../../common/translations/blank_before_percent";
|
import { blankBeforePercent } from "../../../common/translations/blank_before_percent";
|
||||||
@@ -73,7 +74,15 @@ class MoreInfoLawnMower extends LitElement {
|
|||||||
)}:
|
)}:
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
<strong>${this.hass.formatEntityState(stateObj)}</strong>
|
<strong>
|
||||||
|
${computeStateDisplay(
|
||||||
|
this.hass.localize,
|
||||||
|
stateObj,
|
||||||
|
this.hass.locale,
|
||||||
|
this.hass.config,
|
||||||
|
this.hass.entities
|
||||||
|
)}
|
||||||
|
</strong>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
${this._renderBattery()}
|
${this._renderBattery()}
|
||||||
|
@@ -36,7 +36,6 @@ import {
|
|||||||
lightSupportsFavoriteColors,
|
lightSupportsFavoriteColors,
|
||||||
} from "../../../data/light";
|
} from "../../../data/light";
|
||||||
import type { HomeAssistant } from "../../../types";
|
import type { HomeAssistant } from "../../../types";
|
||||||
import "../components/ha-more-info-control-select-container";
|
|
||||||
import { moreInfoControlStyle } from "../components/ha-more-info-control-style";
|
import { moreInfoControlStyle } from "../components/ha-more-info-control-style";
|
||||||
import "../components/ha-more-info-state-header";
|
import "../components/ha-more-info-state-header";
|
||||||
import "../components/ha-more-info-toggle";
|
import "../components/ha-more-info-toggle";
|
||||||
@@ -287,7 +286,8 @@ class MoreInfoLight extends LitElement {
|
|||||||
: nothing}
|
: nothing}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<ha-more-info-control-select-container>
|
<div class="secondary-controls">
|
||||||
|
<div class="secondary-controls-scroll">
|
||||||
${supportsEffects && this.stateObj.attributes.effect_list
|
${supportsEffects && this.stateObj.attributes.effect_list
|
||||||
? html`
|
? html`
|
||||||
<ha-control-select-menu
|
<ha-control-select-menu
|
||||||
@@ -317,7 +317,8 @@ class MoreInfoLight extends LitElement {
|
|||||||
</ha-control-select-menu>
|
</ha-control-select-menu>
|
||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
</ha-more-info-control-select-container>
|
</div>
|
||||||
|
</div>
|
||||||
<ha-attributes
|
<ha-attributes
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.stateObj=${this.stateObj}
|
.stateObj=${this.stateObj}
|
||||||
|
@@ -9,23 +9,24 @@ import {
|
|||||||
mdiVolumeOff,
|
mdiVolumeOff,
|
||||||
mdiVolumePlus,
|
mdiVolumePlus,
|
||||||
} from "@mdi/js";
|
} from "@mdi/js";
|
||||||
import { CSSResultGroup, LitElement, css, html, nothing } from "lit";
|
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import { stopPropagation } from "../../../common/dom/stop_propagation";
|
import { stopPropagation } from "../../../common/dom/stop_propagation";
|
||||||
import { stateActive } from "../../../common/entity/state_active";
|
import { computeAttributeValueDisplay } from "../../../common/entity/compute_attribute_display";
|
||||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||||
import { computeRTLDirection } from "../../../common/util/compute_rtl";
|
import { computeRTLDirection } from "../../../common/util/compute_rtl";
|
||||||
|
import { stateActive } from "../../../common/entity/state_active";
|
||||||
import "../../../components/ha-icon-button";
|
import "../../../components/ha-icon-button";
|
||||||
import "../../../components/ha-select";
|
import "../../../components/ha-select";
|
||||||
import "../../../components/ha-slider";
|
import "../../../components/ha-slider";
|
||||||
import "../../../components/ha-svg-icon";
|
import "../../../components/ha-svg-icon";
|
||||||
import { showMediaBrowserDialog } from "../../../components/media-player/show-media-browser-dialog";
|
import { showMediaBrowserDialog } from "../../../components/media-player/show-media-browser-dialog";
|
||||||
import {
|
import {
|
||||||
|
computeMediaControls,
|
||||||
|
handleMediaControlClick,
|
||||||
MediaPickedEvent,
|
MediaPickedEvent,
|
||||||
MediaPlayerEntity,
|
MediaPlayerEntity,
|
||||||
MediaPlayerEntityFeature,
|
MediaPlayerEntityFeature,
|
||||||
computeMediaControls,
|
|
||||||
handleMediaControlClick,
|
|
||||||
mediaPlayerPlayMedia,
|
mediaPlayerPlayMedia,
|
||||||
} from "../../../data/media-player";
|
} from "../../../data/media-player";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
@@ -156,20 +157,24 @@ class MoreInfoMediaPlayer extends LitElement {
|
|||||||
>
|
>
|
||||||
${stateObj.attributes.source_list!.map(
|
${stateObj.attributes.source_list!.map(
|
||||||
(source) => html`
|
(source) => html`
|
||||||
<mwc-list-item .value=${source}>
|
<mwc-list-item .value=${source}
|
||||||
${this.hass.formatEntityAttributeValue(
|
>${computeAttributeValueDisplay(
|
||||||
|
this.hass.localize,
|
||||||
stateObj,
|
stateObj,
|
||||||
|
this.hass.locale,
|
||||||
|
this.hass.config,
|
||||||
|
this.hass.entities,
|
||||||
"source",
|
"source",
|
||||||
source
|
source
|
||||||
)}
|
)}</mwc-list-item
|
||||||
</mwc-list-item>
|
>
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
<ha-svg-icon .path=${mdiLoginVariant} slot="icon"></ha-svg-icon>
|
<ha-svg-icon .path=${mdiLoginVariant} slot="icon"></ha-svg-icon>
|
||||||
</ha-select>
|
</ha-select>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: nothing}
|
: ""}
|
||||||
${stateActive(stateObj) &&
|
${stateActive(stateObj) &&
|
||||||
supportsFeature(stateObj, MediaPlayerEntityFeature.SELECT_SOUND_MODE) &&
|
supportsFeature(stateObj, MediaPlayerEntityFeature.SELECT_SOUND_MODE) &&
|
||||||
stateObj.attributes.sound_mode_list?.length
|
stateObj.attributes.sound_mode_list?.length
|
||||||
@@ -186,13 +191,17 @@ class MoreInfoMediaPlayer extends LitElement {
|
|||||||
>
|
>
|
||||||
${stateObj.attributes.sound_mode_list.map(
|
${stateObj.attributes.sound_mode_list.map(
|
||||||
(mode) => html`
|
(mode) => html`
|
||||||
<mwc-list-item .value=${mode}>
|
<mwc-list-item .value=${mode}
|
||||||
${this.hass.formatEntityAttributeValue(
|
>${computeAttributeValueDisplay(
|
||||||
|
this.hass.localize,
|
||||||
stateObj,
|
stateObj,
|
||||||
|
this.hass.locale,
|
||||||
|
this.hass.config,
|
||||||
|
this.hass.entities,
|
||||||
"sound_mode",
|
"sound_mode",
|
||||||
mode
|
mode
|
||||||
)}
|
)}</mwc-list-item
|
||||||
</mwc-list-item>
|
>
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
<ha-svg-icon .path=${mdiMusicNote} slot="icon"></ha-svg-icon>
|
<ha-svg-icon .path=${mdiMusicNote} slot="icon"></ha-svg-icon>
|
||||||
|
@@ -2,10 +2,11 @@ import "@material/mwc-list/mwc-list";
|
|||||||
import "@material/mwc-list/mwc-list-item";
|
import "@material/mwc-list/mwc-list-item";
|
||||||
import { html, LitElement, nothing } from "lit";
|
import { html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import { computeAttributeValueDisplay } from "../../../common/entity/compute_attribute_display";
|
||||||
import { stopPropagation } from "../../../common/dom/stop_propagation";
|
import { stopPropagation } from "../../../common/dom/stop_propagation";
|
||||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||||
import "../../../components/ha-attributes";
|
import "../../../components/ha-attributes";
|
||||||
import { REMOTE_SUPPORT_ACTIVITY, RemoteEntity } from "../../../data/remote";
|
import { RemoteEntity, REMOTE_SUPPORT_ACTIVITY } from "../../../data/remote";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
|
|
||||||
const filterExtraAttributes = "activity_list,current_activity";
|
const filterExtraAttributes = "activity_list,current_activity";
|
||||||
@@ -39,8 +40,12 @@ class MoreInfoRemote extends LitElement {
|
|||||||
${stateObj.attributes.activity_list!.map(
|
${stateObj.attributes.activity_list!.map(
|
||||||
(activity) => html`
|
(activity) => html`
|
||||||
<mwc-list-item .value=${activity}>
|
<mwc-list-item .value=${activity}>
|
||||||
${this.hass.formatEntityAttributeValue(
|
${computeAttributeValueDisplay(
|
||||||
|
this.hass.localize,
|
||||||
stateObj,
|
stateObj,
|
||||||
|
this.hass.locale,
|
||||||
|
this.hass.config,
|
||||||
|
this.hass.entities,
|
||||||
"activity",
|
"activity",
|
||||||
activity
|
activity
|
||||||
)}
|
)}
|
||||||
@@ -49,7 +54,7 @@ class MoreInfoRemote extends LitElement {
|
|||||||
)}
|
)}
|
||||||
</mwc-list>
|
</mwc-list>
|
||||||
`
|
`
|
||||||
: nothing}
|
: ""}
|
||||||
|
|
||||||
<ha-attributes
|
<ha-attributes
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
|
@@ -13,6 +13,8 @@ import { CSSResultGroup, LitElement, css, html, nothing } from "lit";
|
|||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { stopPropagation } from "../../../common/dom/stop_propagation";
|
import { stopPropagation } from "../../../common/dom/stop_propagation";
|
||||||
|
import { computeAttributeValueDisplay } from "../../../common/entity/compute_attribute_display";
|
||||||
|
import { computeStateDisplay } from "../../../common/entity/compute_state_display";
|
||||||
import { computeStateDomain } from "../../../common/entity/compute_state_domain";
|
import { computeStateDomain } from "../../../common/entity/compute_state_domain";
|
||||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||||
import "../../../components/entity/ha-battery-icon";
|
import "../../../components/entity/ha-battery-icon";
|
||||||
@@ -125,8 +127,21 @@ class MoreInfoVacuum extends LitElement {
|
|||||||
<strong>
|
<strong>
|
||||||
${supportsFeature(stateObj, VacuumEntityFeature.STATUS) &&
|
${supportsFeature(stateObj, VacuumEntityFeature.STATUS) &&
|
||||||
stateObj.attributes.status
|
stateObj.attributes.status
|
||||||
? this.hass.formatEntityAttributeValue(stateObj, "status")
|
? computeAttributeValueDisplay(
|
||||||
: this.hass.formatEntityState(stateObj)}
|
this.hass.localize,
|
||||||
|
stateObj,
|
||||||
|
this.hass.locale,
|
||||||
|
this.hass.config,
|
||||||
|
this.hass.entities,
|
||||||
|
"status"
|
||||||
|
)
|
||||||
|
: computeStateDisplay(
|
||||||
|
this.hass.localize,
|
||||||
|
stateObj,
|
||||||
|
this.hass.locale,
|
||||||
|
this.hass.config,
|
||||||
|
this.hass.entities
|
||||||
|
)}
|
||||||
</strong>
|
</strong>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -182,8 +197,12 @@ class MoreInfoVacuum extends LitElement {
|
|||||||
${stateObj.attributes.fan_speed_list!.map(
|
${stateObj.attributes.fan_speed_list!.map(
|
||||||
(mode) => html`
|
(mode) => html`
|
||||||
<mwc-list-item .value=${mode}>
|
<mwc-list-item .value=${mode}>
|
||||||
${this.hass.formatEntityAttributeValue(
|
${computeAttributeValueDisplay(
|
||||||
|
this.hass.localize,
|
||||||
stateObj,
|
stateObj,
|
||||||
|
this.hass.locale,
|
||||||
|
this.hass.config,
|
||||||
|
this.hass.entities,
|
||||||
"fan_speed",
|
"fan_speed",
|
||||||
mode
|
mode
|
||||||
)}
|
)}
|
||||||
@@ -196,8 +215,12 @@ class MoreInfoVacuum extends LitElement {
|
|||||||
>
|
>
|
||||||
<span>
|
<span>
|
||||||
<ha-svg-icon .path=${mdiFan}></ha-svg-icon>
|
<ha-svg-icon .path=${mdiFan}></ha-svg-icon>
|
||||||
${this.hass.formatEntityAttributeValue(
|
${computeAttributeValueDisplay(
|
||||||
|
this.hass.localize,
|
||||||
stateObj,
|
stateObj,
|
||||||
|
this.hass.locale,
|
||||||
|
this.hass.config,
|
||||||
|
this.hass.entities,
|
||||||
"fan_speed"
|
"fan_speed"
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
@@ -239,13 +262,12 @@ class MoreInfoVacuum extends LitElement {
|
|||||||
const battery = batteryEntity
|
const battery = batteryEntity
|
||||||
? this.hass.states[batteryEntity.entity_id]
|
? this.hass.states[batteryEntity.entity_id]
|
||||||
: undefined;
|
: undefined;
|
||||||
const batteryDomain = battery ? computeStateDomain(battery) : undefined;
|
|
||||||
|
const batteryIsBinary =
|
||||||
|
battery && computeStateDomain(battery) === "binary_sensor";
|
||||||
|
|
||||||
// Use device battery entity
|
// Use device battery entity
|
||||||
if (
|
if (battery && (batteryIsBinary || !isNaN(battery.state as any))) {
|
||||||
battery &&
|
|
||||||
(batteryDomain === "binary_sensor" || !isNaN(battery.state as any))
|
|
||||||
) {
|
|
||||||
const batteryChargingEntity = findBatteryChargingEntity(
|
const batteryChargingEntity = findBatteryChargingEntity(
|
||||||
this.hass,
|
this.hass,
|
||||||
entities
|
entities
|
||||||
@@ -257,9 +279,7 @@ class MoreInfoVacuum extends LitElement {
|
|||||||
return html`
|
return html`
|
||||||
<div>
|
<div>
|
||||||
<span>
|
<span>
|
||||||
${batteryDomain === "sensor"
|
${batteryIsBinary ? "" : this.hass.formatEntityState(battery)}
|
||||||
? this.hass.formatEntityState(battery)
|
|
||||||
: nothing}
|
|
||||||
<ha-battery-icon
|
<ha-battery-icon
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.batteryStateObj=${battery}
|
.batteryStateObj=${battery}
|
||||||
|
@@ -13,7 +13,6 @@ import {
|
|||||||
computeOperationModeIcon,
|
computeOperationModeIcon,
|
||||||
} from "../../../data/water_heater";
|
} from "../../../data/water_heater";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import "../components/ha-more-info-control-select-container";
|
|
||||||
import { moreInfoControlStyle } from "../components/ha-more-info-control-style";
|
import { moreInfoControlStyle } from "../components/ha-more-info-control-style";
|
||||||
import "../components/water_heater/ha-more-info-water_heater-temperature";
|
import "../components/water_heater/ha-more-info-water_heater-temperature";
|
||||||
|
|
||||||
@@ -69,11 +68,15 @@ class MoreInfoWaterHeater extends LitElement {
|
|||||||
.stateObj=${this.stateObj}
|
.stateObj=${this.stateObj}
|
||||||
></ha-more-info-water_heater-temperature>
|
></ha-more-info-water_heater-temperature>
|
||||||
</div>
|
</div>
|
||||||
<ha-more-info-control-select-container>
|
<div class="secondary-controls">
|
||||||
|
<div class="secondary-controls-scroll">
|
||||||
${supportOperationMode && stateObj.attributes.operation_list
|
${supportOperationMode && stateObj.attributes.operation_list
|
||||||
? html`
|
? html`
|
||||||
<ha-control-select-menu
|
<ha-control-select-menu
|
||||||
.label=${this.hass.localize("ui.card.water_heater.mode")}
|
.label=${this.hass.formatEntityAttributeName(
|
||||||
|
stateObj,
|
||||||
|
"operation"
|
||||||
|
)}
|
||||||
.value=${stateObj.state}
|
.value=${stateObj.state}
|
||||||
.disabled=${stateObj.state === UNAVAILABLE}
|
.disabled=${stateObj.state === UNAVAILABLE}
|
||||||
fixedMenuPosition
|
fixedMenuPosition
|
||||||
@@ -81,7 +84,10 @@ class MoreInfoWaterHeater extends LitElement {
|
|||||||
@selected=${this._handleOperationModeChanged}
|
@selected=${this._handleOperationModeChanged}
|
||||||
@closed=${stopPropagation}
|
@closed=${stopPropagation}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiWaterBoiler}></ha-svg-icon>
|
<ha-svg-icon
|
||||||
|
slot="icon"
|
||||||
|
.path=${mdiWaterBoiler}
|
||||||
|
></ha-svg-icon>
|
||||||
${stateObj.attributes.operation_list
|
${stateObj.attributes.operation_list
|
||||||
.concat()
|
.concat()
|
||||||
.sort(compareWaterHeaterOperationMode)
|
.sort(compareWaterHeaterOperationMode)
|
||||||
@@ -119,24 +125,20 @@ class MoreInfoWaterHeater extends LitElement {
|
|||||||
slot="graphic"
|
slot="graphic"
|
||||||
.path=${mdiAccountArrowRight}
|
.path=${mdiAccountArrowRight}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
${this.hass.formatEntityAttributeValue(
|
${this.hass.localize("state.default.on")}
|
||||||
stateObj,
|
|
||||||
"away_mode",
|
|
||||||
"on"
|
|
||||||
)}
|
|
||||||
</ha-list-item>
|
</ha-list-item>
|
||||||
<ha-list-item value="off" graphic="icon">
|
<ha-list-item value="off" graphic="icon">
|
||||||
<ha-svg-icon slot="graphic" .path=${mdiAccount}></ha-svg-icon>
|
<ha-svg-icon
|
||||||
${this.hass.formatEntityAttributeValue(
|
slot="graphic"
|
||||||
stateObj,
|
.path=${mdiAccount}
|
||||||
"away_mode",
|
></ha-svg-icon>
|
||||||
"off"
|
${this.hass.localize("state.default.off")}
|
||||||
)}
|
|
||||||
</ha-list-item>
|
</ha-list-item>
|
||||||
</ha-control-select-menu>
|
</ha-control-select-menu>
|
||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
</ha-more-info-control-select-container>
|
</div>
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -252,8 +252,7 @@ class MoreInfoWeather extends LitElement {
|
|||||||
${this._showValue(item.templow)
|
${this._showValue(item.templow)
|
||||||
? this.hass.formatEntityAttributeValue(
|
? this.hass.formatEntityAttributeValue(
|
||||||
this.stateObj!,
|
this.stateObj!,
|
||||||
"templow",
|
"templow"
|
||||||
item.templow
|
|
||||||
)
|
)
|
||||||
: hourly
|
: hourly
|
||||||
? ""
|
? ""
|
||||||
@@ -263,8 +262,7 @@ class MoreInfoWeather extends LitElement {
|
|||||||
${this._showValue(item.temperature)
|
${this._showValue(item.temperature)
|
||||||
? this.hass.formatEntityAttributeValue(
|
? this.hass.formatEntityAttributeValue(
|
||||||
this.stateObj!,
|
this.stateObj!,
|
||||||
"temperature",
|
"temperature"
|
||||||
item.temperature
|
|
||||||
)
|
)
|
||||||
: "—"}
|
: "—"}
|
||||||
</div>
|
</div>
|
||||||
|
@@ -10,8 +10,8 @@ import {
|
|||||||
mdiPencilOutline,
|
mdiPencilOutline,
|
||||||
} from "@mdi/js";
|
} from "@mdi/js";
|
||||||
import type { HassEntity } from "home-assistant-js-websocket";
|
import type { HassEntity } from "home-assistant-js-websocket";
|
||||||
import { LitElement, PropertyValues, css, html, nothing } from "lit";
|
import { css, html, LitElement, nothing, PropertyValues } from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { cache } from "lit/directives/cache";
|
import { cache } from "lit/directives/cache";
|
||||||
import { dynamicElement } from "../../common/dom/dynamic-element-directive";
|
import { dynamicElement } from "../../common/dom/dynamic-element-directive";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
@@ -38,17 +38,15 @@ import { haStyleDialog } from "../../resources/styles";
|
|||||||
import "../../state-summary/state-card-content";
|
import "../../state-summary/state-card-content";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
import {
|
import {
|
||||||
|
computeShowHistoryComponent,
|
||||||
|
computeShowLogBookComponent,
|
||||||
DOMAINS_WITH_MORE_INFO,
|
DOMAINS_WITH_MORE_INFO,
|
||||||
EDITABLE_DOMAINS_WITH_ID,
|
EDITABLE_DOMAINS_WITH_ID,
|
||||||
EDITABLE_DOMAINS_WITH_UNIQUE_ID,
|
EDITABLE_DOMAINS_WITH_UNIQUE_ID,
|
||||||
computeShowHistoryComponent,
|
|
||||||
computeShowLogBookComponent,
|
|
||||||
} from "./const";
|
} from "./const";
|
||||||
import "./controls/more-info-default";
|
import "./controls/more-info-default";
|
||||||
import "./ha-more-info-history-and-logbook";
|
import "./ha-more-info-history-and-logbook";
|
||||||
import type { MoreInfoHistoryAndLogbook } from "./ha-more-info-history-and-logbook";
|
|
||||||
import "./ha-more-info-info";
|
import "./ha-more-info-info";
|
||||||
import type { MoreInfoInfo } from "./ha-more-info-info";
|
|
||||||
import "./ha-more-info-settings";
|
import "./ha-more-info-settings";
|
||||||
import "./more-info-content";
|
import "./more-info-content";
|
||||||
|
|
||||||
@@ -93,9 +91,6 @@ export class MoreInfoDialog extends LitElement {
|
|||||||
|
|
||||||
@state() private _infoEditMode = false;
|
@state() private _infoEditMode = false;
|
||||||
|
|
||||||
@query("ha-more-info-info, ha-more-info-history-and-logbook")
|
|
||||||
private _history?: MoreInfoInfo | MoreInfoHistoryAndLogbook;
|
|
||||||
|
|
||||||
public showDialog(params: MoreInfoDialogParams) {
|
public showDialog(params: MoreInfoDialogParams) {
|
||||||
this._entityId = params.entityId;
|
this._entityId = params.entityId;
|
||||||
if (!this._entityId) {
|
if (!this._entityId) {
|
||||||
@@ -268,7 +263,6 @@ export class MoreInfoDialog extends LitElement {
|
|||||||
<ha-dialog
|
<ha-dialog
|
||||||
open
|
open
|
||||||
@closed=${this.closeDialog}
|
@closed=${this.closeDialog}
|
||||||
@opened=${this._handleOpened}
|
|
||||||
.heading=${title}
|
.heading=${title}
|
||||||
hideActions
|
hideActions
|
||||||
flexContent
|
flexContent
|
||||||
@@ -491,10 +485,6 @@ export class MoreInfoDialog extends LitElement {
|
|||||||
this.large = !this.large;
|
this.large = !this.large;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleOpened() {
|
|
||||||
this._history?.resize({ aspectRatio: 2 });
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return [
|
return [
|
||||||
haStyleDialog,
|
haStyleDialog,
|
||||||
|
@@ -1,13 +1,11 @@
|
|||||||
import { css, CSSResultGroup, html, LitElement } from "lit";
|
import { css, CSSResultGroup, html, LitElement } from "lit";
|
||||||
import { customElement, property, query } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import { ChartResizeOptions } from "../../components/chart/ha-chart-base";
|
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
import {
|
import {
|
||||||
computeShowHistoryComponent,
|
computeShowHistoryComponent,
|
||||||
computeShowLogBookComponent,
|
computeShowLogBookComponent,
|
||||||
} from "./const";
|
} from "./const";
|
||||||
import "./ha-more-info-history";
|
import "./ha-more-info-history";
|
||||||
import type { MoreInfoHistory } from "./ha-more-info-history";
|
|
||||||
import "./ha-more-info-logbook";
|
import "./ha-more-info-logbook";
|
||||||
|
|
||||||
@customElement("ha-more-info-history-and-logbook")
|
@customElement("ha-more-info-history-and-logbook")
|
||||||
@@ -16,13 +14,6 @@ export class MoreInfoHistoryAndLogbook extends LitElement {
|
|||||||
|
|
||||||
@property() public entityId!: string;
|
@property() public entityId!: string;
|
||||||
|
|
||||||
@query("ha-more-info-history")
|
|
||||||
private _history?: MoreInfoHistory;
|
|
||||||
|
|
||||||
public resize(options?: ChartResizeOptions) {
|
|
||||||
this._history?.resize(options);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
return html`
|
return html`
|
||||||
${computeShowHistoryComponent(this.hass, this.entityId)
|
${computeShowHistoryComponent(this.hass, this.entityId)
|
||||||
|
@@ -1,12 +1,11 @@
|
|||||||
import { startOfYesterday, subHours } from "date-fns/esm";
|
import { startOfYesterday, subHours } from "date-fns/esm";
|
||||||
import { css, html, LitElement, PropertyValues, nothing } from "lit";
|
import { css, html, LitElement, PropertyValues, nothing } from "lit";
|
||||||
import { customElement, property, state, query } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { isComponentLoaded } from "../../common/config/is_component_loaded";
|
import { isComponentLoaded } from "../../common/config/is_component_loaded";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
import { computeDomain } from "../../common/entity/compute_domain";
|
import { computeDomain } from "../../common/entity/compute_domain";
|
||||||
import { createSearchParam } from "../../common/url/search-params";
|
import { createSearchParam } from "../../common/url/search-params";
|
||||||
import "../../components/chart/state-history-charts";
|
import "../../components/chart/state-history-charts";
|
||||||
import type { StateHistoryCharts } from "../../components/chart/state-history-charts";
|
|
||||||
import "../../components/chart/statistics-chart";
|
import "../../components/chart/statistics-chart";
|
||||||
import {
|
import {
|
||||||
computeHistory,
|
computeHistory,
|
||||||
@@ -21,8 +20,6 @@ import {
|
|||||||
StatisticsTypes,
|
StatisticsTypes,
|
||||||
} from "../../data/recorder";
|
} from "../../data/recorder";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
import type { StatisticsChart } from "../../components/chart/statistics-chart";
|
|
||||||
import { ChartResizeOptions } from "../../components/chart/ha-chart-base";
|
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HASSDomEvents {
|
interface HASSDomEvents {
|
||||||
@@ -54,16 +51,6 @@ export class MoreInfoHistory extends LitElement {
|
|||||||
|
|
||||||
private _metadata?: Record<string, StatisticsMetaData>;
|
private _metadata?: Record<string, StatisticsMetaData>;
|
||||||
|
|
||||||
@query("statistics-chart, state-history-charts") private _chart?:
|
|
||||||
| StateHistoryCharts
|
|
||||||
| StatisticsChart;
|
|
||||||
|
|
||||||
public resize = (options?: ChartResizeOptions): void => {
|
|
||||||
if (this._chart) {
|
|
||||||
this._chart.resize(options);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
if (!this.entityId) {
|
if (!this.entityId) {
|
||||||
return nothing;
|
return nothing;
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user