mirror of
https://github.com/home-assistant/frontend.git
synced 2025-08-13 03:09:26 +00:00
Compare commits
1 Commits
update-int
...
20220223.0
Author | SHA1 | Date | |
---|---|---|---|
![]() |
09f8f816d1 |
631
.yarn/releases/yarn-3.0.2.cjs
vendored
Executable file
631
.yarn/releases/yarn-3.0.2.cjs
vendored
Executable file
File diff suppressed because one or more lines are too long
785
.yarn/releases/yarn-3.2.0.cjs
vendored
785
.yarn/releases/yarn-3.2.0.cjs
vendored
File diff suppressed because one or more lines are too long
@@ -6,4 +6,4 @@ plugins:
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
|
||||
spec: "@yarnpkg/plugin-interactive-tools"
|
||||
|
||||
yarnPath: .yarn/releases/yarn-3.2.0.cjs
|
||||
yarnPath: .yarn/releases/yarn-3.0.2.cjs
|
||||
|
@@ -3,7 +3,7 @@
|
||||
const gulp = require("gulp");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const { marked } = require("marked");
|
||||
const marked = require("marked");
|
||||
const glob = require("glob");
|
||||
const yaml = require("js-yaml");
|
||||
|
||||
|
@@ -7,7 +7,7 @@ const source = require("vinyl-source-stream");
|
||||
const vinylBuffer = require("vinyl-buffer");
|
||||
const gulp = require("gulp");
|
||||
const fs = require("fs");
|
||||
const flatmap = require("gulp-flatmap");
|
||||
const foreach = require("gulp-foreach");
|
||||
const merge = require("gulp-merge-json");
|
||||
const rename = require("gulp-rename");
|
||||
const transform = require("gulp-json-transform");
|
||||
@@ -183,7 +183,7 @@ gulp.task("build-merged-translations", () =>
|
||||
})
|
||||
.pipe(transform((data, file) => lokaliseTransform(data, data, file)))
|
||||
.pipe(
|
||||
flatmap((stream, file) => {
|
||||
foreach((stream, file) => {
|
||||
// For each language generate a merged json file. It begins with the master
|
||||
// translation as a failsafe for untranslated strings, and merges all parent
|
||||
// tags into one file for each specific subtag
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||
import { LovelaceConfig } from "../../../../src/data/lovelace";
|
||||
import { Lovelace } from "../../../../src/panels/lovelace/types";
|
||||
@@ -20,8 +20,6 @@ class HcLovelace extends LitElement {
|
||||
|
||||
@property() public urlPath: string | null = null;
|
||||
|
||||
@query("hui-view") private _huiView?: HTMLElement;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
const index = this._viewIndex;
|
||||
if (index === undefined) {
|
||||
@@ -80,12 +78,12 @@ class HcLovelace extends LitElement {
|
||||
this.lovelaceConfig.background;
|
||||
|
||||
if (configBackground) {
|
||||
this._huiView!.style.setProperty(
|
||||
(this.shadowRoot!.querySelector(
|
||||
"hui-view"
|
||||
) as HTMLElement)!.style.setProperty(
|
||||
"--lovelace-background",
|
||||
configBackground
|
||||
);
|
||||
} else {
|
||||
this._huiView!.style.removeProperty("--lovelace-background");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -118,9 +116,6 @@ class HcLovelace extends LitElement {
|
||||
:host > * {
|
||||
flex: 1;
|
||||
}
|
||||
hui-view {
|
||||
background: var(--lovelace-background, var(--primary-background-color));
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
@@ -279,7 +279,7 @@ class DemoHaSelector extends LitElement implements ProvideHassElement {
|
||||
can_play: true,
|
||||
can_expand: false,
|
||||
children_media_class: null,
|
||||
thumbnail: "https://brands.home-assistant.io/_/image/logo.png",
|
||||
thumbnail: null,
|
||||
},
|
||||
{
|
||||
title: "movie.mp4",
|
||||
|
@@ -14,7 +14,7 @@ import memoizeOne from "memoize-one";
|
||||
import { atLeastVersion } from "../../../src/common/config/version";
|
||||
import { fireEvent } from "../../../src/common/dom/fire_event";
|
||||
import { navigate } from "../../../src/common/navigate";
|
||||
import "../../../src/components/search-input";
|
||||
import "../../../src/common/search/search-input";
|
||||
import { extractSearchParam } from "../../../src/common/url/search-params";
|
||||
import "../../../src/components/ha-button-menu";
|
||||
import "../../../src/components/ha-icon-button";
|
||||
@@ -110,6 +110,8 @@ class HassioAddonStore extends LitElement {
|
||||
<div class="search">
|
||||
<search-input
|
||||
.hass=${this.hass}
|
||||
no-label-float
|
||||
no-underline
|
||||
.filter=${this._filter}
|
||||
@value-changed=${this._filterChanged}
|
||||
></search-input>
|
||||
|
@@ -3,7 +3,7 @@ import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||
import "../../../../src/components/search-input";
|
||||
import "../../../../src/common/search/search-input";
|
||||
import { stringCompare } from "../../../../src/common/string/compare";
|
||||
import "../../../../src/components/ha-dialog";
|
||||
import "../../../../src/components/ha-expansion-panel";
|
||||
@@ -80,6 +80,8 @@ class HassioHardwareDialog extends LitElement {
|
||||
></ha-icon-button>
|
||||
<search-input
|
||||
.hass=${this.hass}
|
||||
dialogInitialFocus
|
||||
no-label-float
|
||||
.filter=${this._filter}
|
||||
@value-changed=${this._handleSearchChange}
|
||||
.label=${this._dialogParams.supervisor.localize(
|
||||
|
@@ -106,9 +106,6 @@ class HassioRepositoriesDialog extends LitElement {
|
||||
</paper-item-body>
|
||||
<div class="delete">
|
||||
<ha-icon-button
|
||||
.label=${this._dialogParams!.supervisor.localize(
|
||||
"dialog.repositories.remove"
|
||||
)}
|
||||
.disabled=${usedRepositories.includes(repo.slug)}
|
||||
.slug=${repo.slug}
|
||||
.path=${usedRepositories.includes(repo.slug)
|
||||
|
@@ -1,12 +1,9 @@
|
||||
// Compat needs to be first import
|
||||
import "../../src/resources/compatibility";
|
||||
import { setCancelSyntheticClickEvents } from "@polymer/polymer/lib/utils/settings";
|
||||
import "../../src/resources/roboto";
|
||||
import "../../src/resources/safari-14-attachshadow-patch";
|
||||
import "./hassio-main";
|
||||
|
||||
setCancelSyntheticClickEvents(false);
|
||||
|
||||
const styleEl = document.createElement("style");
|
||||
styleEl.innerHTML = `
|
||||
body {
|
||||
|
@@ -45,6 +45,7 @@ import { showAlertDialog } from "../../../src/dialogs/generic/show-dialog-box";
|
||||
import "../../../src/layouts/hass-loading-screen";
|
||||
import "../../../src/layouts/hass-subpage";
|
||||
import "../../../src/layouts/hass-tabs-subpage";
|
||||
import { SUPERVISOR_UPDATE_NAMES } from "../../../src/panels/config/dashboard/ha-config-updates";
|
||||
import { HomeAssistant, Route } from "../../../src/types";
|
||||
import { addonArchIsSupported, extractChangelog } from "../util/addon";
|
||||
|
||||
@@ -56,12 +57,6 @@ declare global {
|
||||
|
||||
type updateType = "os" | "supervisor" | "core" | "addon";
|
||||
|
||||
const SUPERVISOR_UPDATE_NAMES = {
|
||||
core: "Home Assistant Core",
|
||||
os: "Home Assistant Operating System",
|
||||
supervisor: "Home Assistant Supervisor",
|
||||
};
|
||||
|
||||
const changelogUrl = (
|
||||
entry: updateType,
|
||||
version: string
|
||||
|
23
package.json
23
package.json
@@ -117,7 +117,7 @@
|
||||
"leaflet-draw": "^1.0.4",
|
||||
"lit": "^2.1.2",
|
||||
"lit-vaadin-helpers": "^0.3.0",
|
||||
"marked": "^4.0.12",
|
||||
"marked": "^3.0.2",
|
||||
"memoize-one": "^5.2.1",
|
||||
"node-vibrant": "3.2.1-alpha.1",
|
||||
"proxy-polyfill": "^0.3.2",
|
||||
@@ -137,12 +137,12 @@
|
||||
"vue": "^2.6.12",
|
||||
"vue2-daterange-picker": "^0.5.1",
|
||||
"web-animations-js": "^2.3.2",
|
||||
"workbox-cacheable-response": "^6.4.2",
|
||||
"workbox-core": "^6.4.2",
|
||||
"workbox-expiration": "^6.4.2",
|
||||
"workbox-precaching": "^6.4.2",
|
||||
"workbox-routing": "^6.4.2",
|
||||
"workbox-strategies": "^6.4.2",
|
||||
"workbox-cacheable-response": "^6.1.5",
|
||||
"workbox-core": "^6.1.5",
|
||||
"workbox-expiration": "^6.1.5",
|
||||
"workbox-precaching": "^6.1.5",
|
||||
"workbox-routing": "^6.1.5",
|
||||
"workbox-strategies": "^6.1.5",
|
||||
"xss": "^1.0.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -171,7 +171,7 @@
|
||||
"@types/js-yaml": "^4",
|
||||
"@types/leaflet": "^1",
|
||||
"@types/leaflet-draw": "^1",
|
||||
"@types/marked": "^4",
|
||||
"@types/marked": "^2",
|
||||
"@types/mocha": "^8",
|
||||
"@types/qrcode": "^1.4.2",
|
||||
"@types/sortablejs": "^1",
|
||||
@@ -198,7 +198,7 @@
|
||||
"fs-extra": "^7.0.1",
|
||||
"glob": "^7.2.0",
|
||||
"gulp": "^4.0.2",
|
||||
"gulp-flatmap": "^1.0.2",
|
||||
"gulp-foreach": "^0.1.0",
|
||||
"gulp-json-transform": "^0.4.6",
|
||||
"gulp-merge-json": "^1.3.1",
|
||||
"gulp-rename": "^2.0.0",
|
||||
@@ -235,7 +235,7 @@
|
||||
"webpack-dev-server": "^4.3.0",
|
||||
"webpack-manifest-plugin": "^4.0.2",
|
||||
"webpackbar": "^5.0.0-3",
|
||||
"workbox-build": "^6.4.2"
|
||||
"workbox-build": "^6.1.5"
|
||||
},
|
||||
"_comment": "Polymer 3.2 contained a bug, fixed in https://github.com/Polymer/polymer/pull/5569, add as patch",
|
||||
"resolutions": {
|
||||
@@ -255,6 +255,5 @@
|
||||
"prettier": {
|
||||
"trailingComma": "es5",
|
||||
"arrowParens": "always"
|
||||
},
|
||||
"packageManager": "yarn@3.2.0"
|
||||
}
|
||||
}
|
||||
|
@@ -2,6 +2,6 @@
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def where() -> Path:
|
||||
def where():
|
||||
"""Return path to the frontend."""
|
||||
return Path(__file__).parent
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[metadata]
|
||||
name = home-assistant-frontend
|
||||
version = 20220301.0
|
||||
version = 20220223.0
|
||||
author = The Home Assistant Authors
|
||||
author_email = hello@home-assistant.io
|
||||
license = Apache-2.0
|
||||
@@ -19,8 +19,3 @@ python_requires = >= 3.4.0
|
||||
[options.packages.find]
|
||||
include =
|
||||
hass_frontend*
|
||||
|
||||
[mypy]
|
||||
python_version = 3.4
|
||||
show_error_codes = True
|
||||
strict = True
|
||||
|
@@ -1,12 +1,12 @@
|
||||
import { mdiClose, mdiMagnify } from "@mdi/js";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import "./ha-icon-button";
|
||||
import "./ha-svg-icon";
|
||||
import "./ha-textfield";
|
||||
import type { HaTextField } from "./ha-textfield";
|
||||
import { HomeAssistant } from "../types";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import "../../components/ha-icon-button";
|
||||
import "../../components/ha-svg-icon";
|
||||
import "../../components/ha-textfield";
|
||||
import type { HaTextField } from "../../components/ha-textfield";
|
||||
import { HomeAssistant } from "../../types";
|
||||
import { fireEvent } from "../dom/fire_event";
|
||||
|
||||
@customElement("search-input")
|
||||
class SearchInput extends LitElement {
|
@@ -21,7 +21,7 @@ import { styleMap } from "lit/directives/style-map";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { restoreScroll } from "../../common/decorators/restore-scroll";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import "../search-input";
|
||||
import "../../common/search/search-input";
|
||||
import { debounce } from "../../common/util/debounce";
|
||||
import { nextRender } from "../../common/util/render-status";
|
||||
import { haStyleScrollbar } from "../../resources/styles";
|
||||
|
@@ -115,9 +115,6 @@ class DateRangePickerElement extends WrappedElement {
|
||||
color: var(--primary-text-color);
|
||||
min-width: initial !important;
|
||||
}
|
||||
.daterangepicker:before {
|
||||
display: none;
|
||||
}
|
||||
.daterangepicker:after {
|
||||
border-bottom: 6px solid var(--card-background-color);
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import { SelectBase } from "@material/mwc-select/mwc-select-base";
|
||||
import { styles } from "@material/mwc-select/mwc-select.css";
|
||||
import { css, html, nothing } from "lit";
|
||||
import { html, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { debounce } from "../common/util/debounce";
|
||||
import { nextRender } from "../common/util/render-status";
|
||||
@@ -20,6 +20,8 @@ export class HaSelect extends SelectBase {
|
||||
></span>`;
|
||||
}
|
||||
|
||||
static override styles = [styles];
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
window.addEventListener("translations-updated", this._translationsUpdated);
|
||||
@@ -37,15 +39,6 @@ export class HaSelect extends SelectBase {
|
||||
await nextRender();
|
||||
this.layoutOptions();
|
||||
}, 500);
|
||||
|
||||
static override styles = [
|
||||
styles,
|
||||
css`
|
||||
.mdc-select:not(.mdc-select--disabled) .mdc-select__icon {
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
|
@@ -51,11 +51,10 @@ export class HaEntitySelector extends SubscribeMixin(LitElement) {
|
||||
private _filterEntities = (entity: HassEntity): boolean => {
|
||||
if (this.selector.entity?.domain) {
|
||||
const filterDomain = this.selector.entity.domain;
|
||||
const filterDomainIsArray = Array.isArray(filterDomain);
|
||||
const entityDomain = computeStateDomain(entity);
|
||||
if (
|
||||
(filterDomainIsArray && !filterDomain.includes(entityDomain)) ||
|
||||
(!filterDomainIsArray && entityDomain !== filterDomain)
|
||||
(Array.isArray(filterDomain) && !filterDomain.includes(entityDomain)) ||
|
||||
entityDomain !== filterDomain
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
@@ -12,7 +12,6 @@ import {
|
||||
} from "../../data/media-player";
|
||||
import type { MediaSelector, MediaSelectorValue } from "../../data/selector";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import { brandsUrl, extractDomainFromBrandUrl } from "../../util/brands-url";
|
||||
import "../ha-alert";
|
||||
import "../ha-form/ha-form";
|
||||
import type { HaFormSchema } from "../ha-form/types";
|
||||
@@ -51,18 +50,6 @@ export class HaMediaSelector extends LitElement {
|
||||
getSignedPath(this.hass, thumbnail).then((signedPath) => {
|
||||
this._thumbnailUrl = signedPath.path;
|
||||
});
|
||||
} else if (
|
||||
thumbnail &&
|
||||
thumbnail.startsWith("https://brands.home-assistant.io")
|
||||
) {
|
||||
// The backend is not aware of the theme used by the users,
|
||||
// so we rewrite the URL to show a proper icon
|
||||
this._thumbnailUrl = brandsUrl({
|
||||
domain: extractDomainFromBrandUrl(thumbnail),
|
||||
type: "icon",
|
||||
useFallback: true,
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
});
|
||||
} else {
|
||||
this._thumbnailUrl = thumbnail;
|
||||
}
|
||||
|
@@ -42,7 +42,9 @@ export class HaTab extends LitElement {
|
||||
@keydown=${this._handleKeyDown}
|
||||
>
|
||||
${this.narrow ? html`<slot name="icon"></slot>` : ""}
|
||||
<span class="name">${this.name}</span>
|
||||
${!this.narrow || this.active
|
||||
? html`<span class="name">${this.name}</span>`
|
||||
: ""}
|
||||
${this._shouldRenderRipple ? html`<mwc-ripple></mwc-ripple>` : ""}
|
||||
</div>
|
||||
`;
|
||||
|
@@ -53,10 +53,6 @@ export class HaTextField extends TextFieldBase {
|
||||
padding-right: var(--text-field-suffix-padding-right, 0px);
|
||||
}
|
||||
|
||||
.mdc-text-field__icon {
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
|
||||
input {
|
||||
text-align: var(--text-field-text-align);
|
||||
}
|
||||
|
@@ -34,24 +34,23 @@ import {
|
||||
MediaPickedEvent,
|
||||
MediaPlayerBrowseAction,
|
||||
} from "../../data/media-player";
|
||||
import { browseLocalMediaPlayer } from "../../data/media_source";
|
||||
import { isTTSMediaSource } from "../../data/tts";
|
||||
import { showAlertDialog } from "../../dialogs/generic/show-dialog-box";
|
||||
import { installResizeObserver } from "../../panels/lovelace/common/install-resize-observer";
|
||||
import { haStyle } from "../../resources/styles";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import { brandsUrl, extractDomainFromBrandUrl } from "../../util/brands-url";
|
||||
import { documentationUrl } from "../../util/documentation-url";
|
||||
import "../entity/ha-entity-picker";
|
||||
import "../ha-button-menu";
|
||||
import "../ha-card";
|
||||
import type { HaCard } from "../ha-card";
|
||||
import "../ha-circular-progress";
|
||||
import "../ha-fab";
|
||||
import "../ha-icon-button";
|
||||
import "../ha-svg-icon";
|
||||
import "./ha-browse-media-tts";
|
||||
import "../ha-fab";
|
||||
import { browseLocalMediaPlayer } from "../../data/media_source";
|
||||
import { isTTSMediaSource } from "../../data/tts";
|
||||
import type { TtsMediaPickedEvent } from "./ha-browse-media-tts";
|
||||
import "./ha-browse-media-tts";
|
||||
|
||||
declare global {
|
||||
interface HASSDomEvents {
|
||||
@@ -682,17 +681,6 @@ export class HaMediaPlayerBrowse extends LitElement {
|
||||
// Thumbnails served by local API require authentication
|
||||
const signedPath = await getSignedPath(this.hass, thumbnailUrl);
|
||||
thumbnailUrl = signedPath.path;
|
||||
} else if (
|
||||
thumbnailUrl.startsWith("https://brands.home-assistant.io")
|
||||
) {
|
||||
// The backend is not aware of the theme used by the users,
|
||||
// so we rewrite the URL to show a proper icon
|
||||
thumbnailUrl = brandsUrl({
|
||||
domain: extractDomainFromBrandUrl(thumbnailUrl),
|
||||
type: "icon",
|
||||
useFallback: true,
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
});
|
||||
}
|
||||
thumbnailCard.style.backgroundImage = `url(${thumbnailUrl})`;
|
||||
observer.unobserve(thumbnailCard); // loaded, so no need to observe anymore
|
||||
|
@@ -1,13 +1,8 @@
|
||||
import { HomeAssistant } from "../types";
|
||||
|
||||
interface ValidConfig {
|
||||
valid: true;
|
||||
error: null;
|
||||
}
|
||||
|
||||
interface InvalidConfig {
|
||||
valid: false;
|
||||
error: string;
|
||||
interface ValidationResult {
|
||||
valid: boolean;
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
type ValidKeys = "trigger" | "action" | "condition";
|
||||
@@ -17,7 +12,7 @@ export const validateConfig = <
|
||||
>(
|
||||
hass: HomeAssistant,
|
||||
config: T
|
||||
): Promise<Record<keyof T, ValidConfig | InvalidConfig>> =>
|
||||
): Promise<Record<keyof T, ValidationResult>> =>
|
||||
hass.callWS({
|
||||
type: "validate_config",
|
||||
...config,
|
||||
|
@@ -1,4 +1,3 @@
|
||||
import { HassEntity } from "home-assistant-js-websocket";
|
||||
import { HomeAssistant } from "../types";
|
||||
|
||||
export interface InputDateTime {
|
||||
@@ -18,19 +17,6 @@ export interface InputDateTimeMutableParams {
|
||||
has_date: boolean;
|
||||
}
|
||||
|
||||
export const stateToIsoDateString = (entityState: HassEntity) =>
|
||||
`${entityState.attributes.year || "1970"}-${String(
|
||||
entityState.attributes.month || "01"
|
||||
).padStart(2, "0")}-${String(entityState.attributes.day || "01").padStart(
|
||||
2,
|
||||
"0"
|
||||
)}T${String(entityState.attributes.hour || "00").padStart(2, "0")}:${String(
|
||||
entityState.attributes.minute || "00"
|
||||
).padStart(2, "0")}:${String(entityState.attributes.second || "00").padStart(
|
||||
2,
|
||||
"0"
|
||||
)}`;
|
||||
|
||||
export const setInputDateTimeValue = (
|
||||
hass: HomeAssistant,
|
||||
entityId: string,
|
||||
|
@@ -1,10 +1,7 @@
|
||||
import { HomeAssistant } from "../types";
|
||||
import { Action } from "./script";
|
||||
|
||||
export const callExecuteScript = (
|
||||
hass: HomeAssistant,
|
||||
sequence: Action | Action[]
|
||||
) =>
|
||||
export const callExecuteScript = (hass: HomeAssistant, sequence: Action[]) =>
|
||||
hass.callWS({
|
||||
type: "execute_script",
|
||||
sequence,
|
||||
|
58
src/data/supervisor/root.ts
Normal file
58
src/data/supervisor/root.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { HomeAssistant } from "../../types";
|
||||
|
||||
interface SupervisorBaseAvailableUpdates {
|
||||
panel_path?: string;
|
||||
update_type?: string;
|
||||
version_latest?: string;
|
||||
}
|
||||
|
||||
interface SupervisorAddonAvailableUpdates
|
||||
extends SupervisorBaseAvailableUpdates {
|
||||
update_type?: "addon";
|
||||
icon?: string;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
interface SupervisorCoreAvailableUpdates
|
||||
extends SupervisorBaseAvailableUpdates {
|
||||
update_type?: "core";
|
||||
}
|
||||
|
||||
interface SupervisorOsAvailableUpdates extends SupervisorBaseAvailableUpdates {
|
||||
update_type?: "os";
|
||||
}
|
||||
|
||||
interface SupervisorSupervisorAvailableUpdates
|
||||
extends SupervisorBaseAvailableUpdates {
|
||||
update_type?: "supervisor";
|
||||
}
|
||||
|
||||
export type SupervisorAvailableUpdates =
|
||||
| SupervisorAddonAvailableUpdates
|
||||
| SupervisorCoreAvailableUpdates
|
||||
| SupervisorOsAvailableUpdates
|
||||
| SupervisorSupervisorAvailableUpdates;
|
||||
|
||||
export interface SupervisorAvailableUpdatesResponse {
|
||||
available_updates: SupervisorAvailableUpdates[];
|
||||
}
|
||||
|
||||
export const fetchSupervisorAvailableUpdates = async (
|
||||
hass: HomeAssistant
|
||||
): Promise<SupervisorAvailableUpdates[]> =>
|
||||
(
|
||||
await hass.callWS<SupervisorAvailableUpdatesResponse>({
|
||||
type: "supervisor/api",
|
||||
endpoint: "/available_updates",
|
||||
method: "get",
|
||||
})
|
||||
).available_updates;
|
||||
|
||||
export const refreshSupervisorAvailableUpdates = async (
|
||||
hass: HomeAssistant
|
||||
): Promise<void> =>
|
||||
hass.callWS<void>({
|
||||
type: "supervisor/api",
|
||||
endpoint: "/refresh_updates",
|
||||
method: "post",
|
||||
});
|
@@ -1,37 +0,0 @@
|
||||
import { HomeAssistant } from "../types";
|
||||
|
||||
export interface UpdateDescription {
|
||||
identifier: string;
|
||||
name: string;
|
||||
domain: string;
|
||||
current_version: string;
|
||||
available_version: string;
|
||||
changelog_content: string | null;
|
||||
changelog_url: string | null;
|
||||
icon_url: string | null;
|
||||
supports_backup: boolean;
|
||||
}
|
||||
|
||||
export interface SkipUpdateParams {
|
||||
domain: string;
|
||||
version: string;
|
||||
identifier: string;
|
||||
}
|
||||
|
||||
export interface PerformUpdateParams extends SkipUpdateParams {
|
||||
backup?: boolean;
|
||||
}
|
||||
|
||||
export const fetchUpdateInfo = (
|
||||
hass: HomeAssistant
|
||||
): Promise<UpdateDescription[]> => hass.callWS({ type: "update/info" });
|
||||
|
||||
export const skipUpdate = (
|
||||
hass: HomeAssistant,
|
||||
params: SkipUpdateParams
|
||||
): Promise<void> => hass.callWS({ type: "update/skip", ...params });
|
||||
|
||||
export const performUpdate = (
|
||||
hass: HomeAssistant,
|
||||
params: PerformUpdateParams
|
||||
): Promise<void> => hass.callWS({ type: "update/update", ...params });
|
@@ -117,17 +117,13 @@ class DataEntryFlowDialog extends LitElement {
|
||||
);
|
||||
} catch (err: any) {
|
||||
this.closeDialog();
|
||||
let message = err.message || err.body || "Unknown error";
|
||||
if (typeof message !== "string") {
|
||||
message = JSON.stringify(message);
|
||||
}
|
||||
showAlertDialog(this, {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.integrations.config_flow.error"
|
||||
),
|
||||
text: `${this.hass.localize(
|
||||
"ui.panel.config.integrations.config_flow.could_not_load"
|
||||
)}: ${message}`,
|
||||
)}: ${err.message || err.body}`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@@ -15,7 +15,7 @@ import memoizeOne from "memoize-one";
|
||||
import { isComponentLoaded } from "../../common/config/is_component_loaded";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { navigate } from "../../common/navigate";
|
||||
import "../../components/search-input";
|
||||
import "../../common/search/search-input";
|
||||
import { caseInsensitiveStringCompare } from "../../common/string/compare";
|
||||
import { LocalizeFunc } from "../../common/translations/localize";
|
||||
import "../../components/ha-icon-next";
|
||||
|
@@ -4,10 +4,7 @@ import { customElement, property } from "lit/decorators";
|
||||
import "../../../components/ha-date-input";
|
||||
import "../../../components/ha-time-input";
|
||||
import { UNAVAILABLE_STATES, UNKNOWN } from "../../../data/entity";
|
||||
import {
|
||||
setInputDateTimeValue,
|
||||
stateToIsoDateString,
|
||||
} from "../../../data/input_datetime";
|
||||
import { setInputDateTimeValue } from "../../../data/input_datetime";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
|
||||
@customElement("more-info-input_datetime")
|
||||
@@ -27,7 +24,7 @@ class MoreInfoInputDatetime extends LitElement {
|
||||
? html`
|
||||
<ha-date-input
|
||||
.locale=${this.hass.locale}
|
||||
.value=${stateToIsoDateString(this.stateObj)}
|
||||
.value=${`${this.stateObj.attributes.year}-${this.stateObj.attributes.month}-${this.stateObj.attributes.day}`}
|
||||
.disabled=${UNAVAILABLE_STATES.includes(this.stateObj.state)}
|
||||
@value-changed=${this._dateChanged}
|
||||
>
|
||||
|
@@ -50,40 +50,42 @@ class MoreInfoMediaPlayer extends LitElement {
|
||||
const controls = computeMediaControls(stateObj);
|
||||
|
||||
return html`
|
||||
<div class="controls">
|
||||
<div class="basic-controls">
|
||||
${!controls
|
||||
? ""
|
||||
: controls.map(
|
||||
(control) => html`
|
||||
<ha-icon-button
|
||||
action=${control.action}
|
||||
@click=${this._handleClick}
|
||||
.path=${control.icon}
|
||||
.label=${this.hass.localize(
|
||||
`ui.card.media_player.${control.action}`
|
||||
)}
|
||||
>
|
||||
</ha-icon-button>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
${supportsFeature(stateObj, SUPPORT_BROWSE_MEDIA)
|
||||
? html`
|
||||
<mwc-button
|
||||
.label=${this.hass.localize(
|
||||
"ui.card.media_player.browse_media"
|
||||
${!controls
|
||||
? ""
|
||||
: html`
|
||||
<div class="controls">
|
||||
<div class="basic-controls">
|
||||
${controls!.map(
|
||||
(control) => html`
|
||||
<ha-icon-button
|
||||
action=${control.action}
|
||||
@click=${this._handleClick}
|
||||
.path=${control.icon}
|
||||
.label=${this.hass.localize(
|
||||
`ui.card.media_player.${control.action}`
|
||||
)}
|
||||
>
|
||||
</ha-icon-button>
|
||||
`
|
||||
)}
|
||||
@click=${this._showBrowseMedia}
|
||||
>
|
||||
<ha-svg-icon
|
||||
.path=${mdiPlayBoxMultiple}
|
||||
slot="icon"
|
||||
></ha-svg-icon>
|
||||
</mwc-button>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
</div>
|
||||
${supportsFeature(stateObj, SUPPORT_BROWSE_MEDIA)
|
||||
? html`
|
||||
<mwc-button
|
||||
.label=${this.hass.localize(
|
||||
"ui.card.media_player.browse_media"
|
||||
)}
|
||||
@click=${this._showBrowseMedia}
|
||||
>
|
||||
<ha-svg-icon
|
||||
.path=${mdiPlayBoxMultiple}
|
||||
slot="icon"
|
||||
></ha-svg-icon>
|
||||
</mwc-button>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
`}
|
||||
${(supportsFeature(stateObj, SUPPORT_VOLUME_SET) ||
|
||||
supportsFeature(stateObj, SUPPORT_VOLUME_BUTTONS)) &&
|
||||
![UNAVAILABLE, UNKNOWN, "off"].includes(stateObj.state)
|
||||
|
@@ -86,11 +86,11 @@ export class QuickBar extends LitElement {
|
||||
|
||||
@state() private _search = "";
|
||||
|
||||
@state() private _open = false;
|
||||
@state() private _opened = false;
|
||||
|
||||
@state() private _commandMode = false;
|
||||
|
||||
@state() private _opened = false;
|
||||
@state() private _done = false;
|
||||
|
||||
@state() private _narrow = false;
|
||||
|
||||
@@ -109,12 +109,12 @@ export class QuickBar extends LitElement {
|
||||
"all and (max-width: 450px), all and (max-height: 500px)"
|
||||
).matches;
|
||||
this._initializeItemsIfNeeded();
|
||||
this._open = true;
|
||||
this._opened = true;
|
||||
}
|
||||
|
||||
public closeDialog() {
|
||||
this._open = false;
|
||||
this._opened = false;
|
||||
this._done = false;
|
||||
this._focusSet = false;
|
||||
this._filter = "";
|
||||
this._search = "";
|
||||
@@ -133,7 +133,7 @@ export class QuickBar extends LitElement {
|
||||
);
|
||||
|
||||
protected render() {
|
||||
if (!this._open) {
|
||||
if (!this._opened) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
@@ -218,26 +218,24 @@ export class QuickBar extends LitElement {
|
||||
`
|
||||
: html`
|
||||
<mwc-list>
|
||||
${this._opened
|
||||
? html`<lit-virtualizer
|
||||
scroller
|
||||
@keydown=${this._handleListItemKeyDown}
|
||||
@rangechange=${this._handleRangeChanged}
|
||||
@click=${this._handleItemClick}
|
||||
class="ha-scrollbar"
|
||||
style=${styleMap({
|
||||
height: this._narrow
|
||||
? "calc(100vh - 56px)"
|
||||
: `${Math.min(
|
||||
items.length * (this._commandMode ? 56 : 72) + 26,
|
||||
500
|
||||
)}px`,
|
||||
})}
|
||||
.items=${items}
|
||||
.renderItem=${this._renderItem}
|
||||
>
|
||||
</lit-virtualizer>`
|
||||
: ""}
|
||||
<lit-virtualizer
|
||||
scroller
|
||||
@keydown=${this._handleListItemKeyDown}
|
||||
@rangechange=${this._handleRangeChanged}
|
||||
@click=${this._handleItemClick}
|
||||
class="ha-scrollbar"
|
||||
style=${styleMap({
|
||||
height: this._narrow
|
||||
? "calc(100vh - 56px)"
|
||||
: `${Math.min(
|
||||
items.length * (this._commandMode ? 56 : 72) + 26,
|
||||
this._done ? 500 : 0
|
||||
)}px`,
|
||||
})}
|
||||
.items=${items}
|
||||
.renderItem=${this._renderItem}
|
||||
>
|
||||
</lit-virtualizer>
|
||||
</mwc-list>
|
||||
`}
|
||||
${this._hint ? html`<div class="hint">${this._hint}</div>` : ""}
|
||||
@@ -254,7 +252,9 @@ export class QuickBar extends LitElement {
|
||||
}
|
||||
|
||||
private _handleOpened() {
|
||||
this._opened = true;
|
||||
this.updateComplete.then(() => {
|
||||
this._done = true;
|
||||
});
|
||||
}
|
||||
|
||||
private async _handleRangeChanged(e) {
|
||||
@@ -454,10 +454,9 @@ export class QuickBar extends LitElement {
|
||||
}
|
||||
|
||||
private _handleItemClick(ev) {
|
||||
const listItem = ev.target.closest("mwc-list-item");
|
||||
this.processItemAndCloseDialog(
|
||||
listItem.item,
|
||||
Number(listItem.getAttribute("index"))
|
||||
(ev.target as any).item,
|
||||
Number((ev.target as HTMLElement).getAttribute("index"))
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -1,211 +0,0 @@
|
||||
import "@material/mwc-button/mwc-button";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { computeRTL } from "../../common/util/compute_rtl";
|
||||
import "../../components/ha-alert";
|
||||
import "../../components/ha-checkbox";
|
||||
import "../../components/ha-circular-progress";
|
||||
import { createCloseHeading } from "../../components/ha-dialog";
|
||||
import "../../components/ha-faded";
|
||||
import "../../components/ha-formfield";
|
||||
import "../../components/ha-icon-button";
|
||||
import "../../components/ha-markdown";
|
||||
import {
|
||||
performUpdate,
|
||||
skipUpdate,
|
||||
UpdateDescription,
|
||||
} from "../../data/update";
|
||||
import { haStyleDialog } from "../../resources/styles";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import { UpdateDialogParams } from "./show-ha-update-dialog";
|
||||
|
||||
@customElement("ha-update-dialog")
|
||||
export class HaUpdateDialog extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@state() private _opened = false;
|
||||
|
||||
@state() private _updating = false;
|
||||
|
||||
@state() private _error?: string;
|
||||
|
||||
@state() private _update!: UpdateDescription;
|
||||
|
||||
_refreshCallback!: () => void;
|
||||
|
||||
public async showDialog(dialogParams: UpdateDialogParams): Promise<void> {
|
||||
this._opened = true;
|
||||
this._update = dialogParams.update;
|
||||
this._refreshCallback = dialogParams.refreshCallback;
|
||||
}
|
||||
|
||||
public async closeDialog(): Promise<void> {
|
||||
this._opened = false;
|
||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this._opened) {
|
||||
return html``;
|
||||
}
|
||||
return html`
|
||||
<ha-dialog
|
||||
open
|
||||
@closed=${this.closeDialog}
|
||||
scrimClickAction
|
||||
.heading=${createCloseHeading(
|
||||
this.hass,
|
||||
this.hass.localize("ui.panel.config.updates.dialog.title", {
|
||||
name: this._update.name,
|
||||
})
|
||||
)}
|
||||
>
|
||||
<div>
|
||||
${this._error
|
||||
? html`<ha-alert alert-type="error" .rtl=${computeRTL(this.hass)}>
|
||||
${this._error}
|
||||
</ha-alert>`
|
||||
: ""}
|
||||
${!this._updating
|
||||
? html`
|
||||
${this._update.changelog_content
|
||||
? html`
|
||||
<ha-faded>
|
||||
<ha-markdown .content=${this._update.changelog_content}>
|
||||
</ha-markdown>
|
||||
</ha-faded>
|
||||
`
|
||||
: ""}
|
||||
${this._update.changelog_url
|
||||
? html`<a href=${this._update.changelog_url} target="_blank">
|
||||
Full changelog
|
||||
</a> `
|
||||
: ""}
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.updates.dialog.description",
|
||||
{
|
||||
name: this._update.name,
|
||||
version: this._update.current_version,
|
||||
newest_version: this._update.available_version,
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
${this._update.supports_backup
|
||||
? html`
|
||||
<ha-formfield
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.updates.dialog.create_backup"
|
||||
)}
|
||||
>
|
||||
<ha-checkbox checked></ha-checkbox>
|
||||
</ha-formfield>
|
||||
`
|
||||
: ""}
|
||||
`
|
||||
: html`<ha-circular-progress alt="Updating" size="large" active>
|
||||
</ha-circular-progress>
|
||||
<p class="progress-text">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.updates.dialog.updating",
|
||||
{
|
||||
name: this._update.name,
|
||||
version: this._update.available_version,
|
||||
}
|
||||
)}
|
||||
</p>`}
|
||||
</div>
|
||||
${!this._updating
|
||||
? html`
|
||||
<mwc-button slot="secondaryAction" @click=${this._skipUpdate}>
|
||||
${this.hass.localize("ui.common.skip")}
|
||||
</mwc-button>
|
||||
<mwc-button
|
||||
.disabled=${this._updating}
|
||||
slot="primaryAction"
|
||||
@click=${this._performUpdate}
|
||||
>
|
||||
${this.hass.localize("ui.panel.config.updates.dialog.update")}
|
||||
</mwc-button>
|
||||
`
|
||||
: ""}
|
||||
</ha-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
get _shouldCreateBackup(): boolean {
|
||||
if (!this._update.supports_backup) {
|
||||
return false;
|
||||
}
|
||||
const checkbox = this.shadowRoot?.querySelector("ha-checkbox");
|
||||
if (checkbox) {
|
||||
return checkbox.checked;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private async _performUpdate() {
|
||||
this._error = undefined;
|
||||
this._updating = true;
|
||||
try {
|
||||
await performUpdate(this.hass, {
|
||||
domain: this._update.domain,
|
||||
identifier: this._update.identifier,
|
||||
version: this._update.available_version,
|
||||
backup: this._shouldCreateBackup,
|
||||
});
|
||||
} catch (err: any) {
|
||||
this._error = err.message;
|
||||
this._updating = false;
|
||||
return;
|
||||
}
|
||||
this._updating = false;
|
||||
this._refreshCallback();
|
||||
this.closeDialog();
|
||||
}
|
||||
|
||||
private async _skipUpdate() {
|
||||
this._error = undefined;
|
||||
try {
|
||||
await skipUpdate(this.hass, {
|
||||
domain: this._update.domain,
|
||||
identifier: this._update.identifier,
|
||||
version: this._update.available_version,
|
||||
});
|
||||
} catch (err: any) {
|
||||
this._error = err.message;
|
||||
return;
|
||||
}
|
||||
|
||||
this._refreshCallback();
|
||||
this.closeDialog();
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
haStyleDialog,
|
||||
css`
|
||||
ha-circular-progress {
|
||||
display: block;
|
||||
margin: 32px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.progress-text {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
ha-markdown {
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-update-dialog": HaUpdateDialog;
|
||||
}
|
||||
}
|
@@ -1,18 +0,0 @@
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { UpdateDescription } from "../../data/update";
|
||||
|
||||
export interface UpdateDialogParams {
|
||||
update: UpdateDescription;
|
||||
refreshCallback: () => void;
|
||||
}
|
||||
|
||||
export const showUpdateDialog = (
|
||||
element: HTMLElement,
|
||||
dialogParams: UpdateDialogParams
|
||||
): void => {
|
||||
fireEvent(element, "show-dialog", {
|
||||
dialogTag: "ha-update-dialog",
|
||||
dialogImport: () => import("./ha-update-dialog"),
|
||||
dialogParams,
|
||||
});
|
||||
};
|
@@ -13,9 +13,6 @@ export const SubscribeMixin = <T extends Constructor<ReactiveElement>>(
|
||||
class SubscribeClass extends superClass {
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
// we wait with subscribing till these properties are set on the host element
|
||||
protected hassSubscribeRequiredHostProps?: string[];
|
||||
|
||||
private __unsubs?: Array<UnsubscribeFunc | Promise<UnsubscribeFunc>>;
|
||||
|
||||
public connectedCallback() {
|
||||
@@ -42,16 +39,6 @@ export const SubscribeMixin = <T extends Constructor<ReactiveElement>>(
|
||||
super.updated(changedProps);
|
||||
if (changedProps.has("hass")) {
|
||||
this.__checkSubscribed();
|
||||
return;
|
||||
}
|
||||
if (!this.hassSubscribeRequiredHostProps) {
|
||||
return;
|
||||
}
|
||||
for (const key of changedProps.keys()) {
|
||||
if (this.hassSubscribeRequiredHostProps.includes(key as string)) {
|
||||
this.__checkSubscribed();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,10 +52,7 @@ export const SubscribeMixin = <T extends Constructor<ReactiveElement>>(
|
||||
if (
|
||||
this.__unsubs !== undefined ||
|
||||
!(this as unknown as Element).isConnected ||
|
||||
this.hass === undefined ||
|
||||
this.hassSubscribeRequiredHostProps?.some(
|
||||
(prop) => this[prop] === undefined
|
||||
)
|
||||
this.hass === undefined
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
@@ -16,16 +16,10 @@ import "../../../../components/ha-icon-button";
|
||||
import "../../../../components/ha-select";
|
||||
import type { HaSelect } from "../../../../components/ha-select";
|
||||
import type { HaYamlEditor } from "../../../../components/ha-yaml-editor";
|
||||
import { validateConfig } from "../../../../data/config";
|
||||
import { Action, getActionType } from "../../../../data/script";
|
||||
import { callExecuteScript } from "../../../../data/service";
|
||||
import {
|
||||
showAlertDialog,
|
||||
showConfirmationDialog,
|
||||
} from "../../../../dialogs/generic/show-dialog-box";
|
||||
import { showConfirmationDialog } from "../../../../dialogs/generic/show-dialog-box";
|
||||
import { haStyle } from "../../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import { showToast } from "../../../../util/toast";
|
||||
import "./types/ha-automation-action-activate_scene";
|
||||
import "./types/ha-automation-action-choose";
|
||||
import "./types/ha-automation-action-condition";
|
||||
@@ -186,11 +180,6 @@ export default class HaAutomationActionRow extends LitElement {
|
||||
.label=${this.hass.localize("ui.common.menu")}
|
||||
.path=${mdiDotsVertical}
|
||||
></ha-icon-button>
|
||||
<mwc-list-item>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.run_action"
|
||||
)}
|
||||
</mwc-list-item>
|
||||
<mwc-list-item .disabled=${!this._uiModeAvailable}>
|
||||
${yamlMode
|
||||
? this.hass.localize(
|
||||
@@ -301,54 +290,17 @@ export default class HaAutomationActionRow extends LitElement {
|
||||
private _handleAction(ev: CustomEvent<ActionDetail>) {
|
||||
switch (ev.detail.index) {
|
||||
case 0:
|
||||
this._runAction();
|
||||
break;
|
||||
case 1:
|
||||
this._switchYamlMode();
|
||||
break;
|
||||
case 2:
|
||||
case 1:
|
||||
fireEvent(this, "duplicate");
|
||||
break;
|
||||
case 3:
|
||||
case 2:
|
||||
this._onDelete();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private async _runAction() {
|
||||
const validated = await validateConfig(this.hass, {
|
||||
action: this.action,
|
||||
});
|
||||
|
||||
if (!validated.action.valid) {
|
||||
showAlertDialog(this, {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.invalid_action"
|
||||
),
|
||||
text: validated.action.error,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await callExecuteScript(this.hass, this.action);
|
||||
} catch (err: any) {
|
||||
showAlertDialog(this, {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.run_action_error"
|
||||
),
|
||||
text: err.message || err,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
showToast(this, {
|
||||
message: this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.run_action_success"
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
private _onDelete() {
|
||||
showConfirmationDialog(this, {
|
||||
text: this.hass.localize(
|
||||
|
@@ -145,7 +145,7 @@ export class HaDeviceAction extends LitElement {
|
||||
static styles = css`
|
||||
ha-device-picker {
|
||||
display: block;
|
||||
margin-bottom: 16px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
ha-device-action-picker {
|
||||
display: block;
|
||||
|
@@ -50,7 +50,7 @@ export class HaEventAction extends LitElement implements ActionElement {
|
||||
<ha-yaml-editor
|
||||
.hass=${this.hass}
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.type.event.event_data"
|
||||
"ui.panel.config.automation.editor.actions.type.event.service_data"
|
||||
)}
|
||||
.name=${"event_data"}
|
||||
.defaultValue=${event_data}
|
||||
|
@@ -162,8 +162,8 @@ export class HaRepeatAction extends LitElement implements ActionElement {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
ha-textfield {
|
||||
margin-top: 16px;
|
||||
ha-select {
|
||||
margin-top: 8px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
@@ -33,6 +33,7 @@ export class HaWaitForTriggerAction
|
||||
.value=${timeout || ""}
|
||||
@change=${this._valueChanged}
|
||||
></ha-textfield>
|
||||
<br />
|
||||
<ha-formfield
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.type.wait_for_trigger.continue_timeout"
|
||||
|
@@ -117,8 +117,8 @@ export class HaTimeCondition extends LitElement implements ConditionElement {
|
||||
);
|
||||
|
||||
const data = {
|
||||
mode_before: inputModeBefore ? "input" : "value",
|
||||
mode_after: inputModeAfter ? "input" : "value",
|
||||
mode_before: "value",
|
||||
mode_after: "value",
|
||||
...this.condition,
|
||||
};
|
||||
|
||||
@@ -137,11 +137,18 @@ export class HaTimeCondition extends LitElement implements ConditionElement {
|
||||
ev.stopPropagation();
|
||||
const newValue = ev.detail.value;
|
||||
|
||||
this._inputModeAfter = newValue.mode_after === "input";
|
||||
this._inputModeBefore = newValue.mode_before === "input";
|
||||
const newModeAfter = newValue.mode_after === "input";
|
||||
const newModeBefore = newValue.mode_before === "input";
|
||||
|
||||
delete newValue.mode_after;
|
||||
delete newValue.mode_before;
|
||||
if (newModeAfter !== this._inputModeAfter) {
|
||||
this._inputModeAfter = newModeAfter;
|
||||
newValue.after = undefined;
|
||||
}
|
||||
|
||||
if (newModeBefore !== this._inputModeBefore) {
|
||||
this._inputModeBefore = newModeBefore;
|
||||
newValue.before = undefined;
|
||||
}
|
||||
|
||||
Object.keys(newValue).forEach((key) =>
|
||||
newValue[key] === undefined || newValue[key] === ""
|
||||
|
@@ -346,8 +346,7 @@ export class HaManualAutomationEditor extends LitElement {
|
||||
.link-button-row {
|
||||
padding: 14px;
|
||||
}
|
||||
ha-textarea,
|
||||
ha-textfield {
|
||||
ha-textarea {
|
||||
display: block;
|
||||
}
|
||||
span[slot="introduction"] a {
|
||||
|
@@ -23,9 +23,9 @@ export class HaTimeTrigger extends LitElement implements TriggerElement {
|
||||
|
||||
private _schema = memoizeOne(
|
||||
(localize: LocalizeFunc, inputMode?: boolean): HaFormSchema[] => {
|
||||
const atSelector = inputMode
|
||||
? { entity: { domain: "input_datetime" } }
|
||||
: { time: {} };
|
||||
const modeSchema = inputMode
|
||||
? { name: "at", selector: { entity: { domain: "input_datetime" } } }
|
||||
: { name: "at", selector: { time: {} } };
|
||||
|
||||
return [
|
||||
{
|
||||
@@ -47,7 +47,7 @@ export class HaTimeTrigger extends LitElement implements TriggerElement {
|
||||
],
|
||||
],
|
||||
},
|
||||
{ name: "at", selector: atSelector },
|
||||
modeSchema,
|
||||
];
|
||||
}
|
||||
);
|
||||
@@ -80,7 +80,7 @@ export class HaTimeTrigger extends LitElement implements TriggerElement {
|
||||
const schema: HaFormSchema[] = this._schema(this.hass.localize, inputMode);
|
||||
|
||||
const data = {
|
||||
mode: inputMode ? "input" : "value",
|
||||
mode: "value",
|
||||
...this.trigger,
|
||||
};
|
||||
|
||||
@@ -99,8 +99,7 @@ export class HaTimeTrigger extends LitElement implements TriggerElement {
|
||||
ev.stopPropagation();
|
||||
const newValue = ev.detail.value;
|
||||
|
||||
this._inputMode = newValue.mode === "input";
|
||||
delete newValue.mode;
|
||||
this._inputMode = newValue.mode.value === "input";
|
||||
|
||||
Object.keys(newValue).forEach((key) =>
|
||||
newValue[key] === undefined || newValue[key] === ""
|
||||
|
@@ -29,7 +29,6 @@ import "./cloud-remote-pref";
|
||||
import "./cloud-tts-pref";
|
||||
import "./cloud-webhooks";
|
||||
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
|
||||
import { showConfirmationDialog } from "../../../../dialogs/generic/show-dialog-box";
|
||||
|
||||
@customElement("cloud-account")
|
||||
export class CloudAccount extends SubscribeMixin(LitElement) {
|
||||
@@ -277,22 +276,11 @@ export class CloudAccount extends SubscribeMixin(LitElement) {
|
||||
private async _handleMenuAction(ev: CustomEvent<ActionDetail>) {
|
||||
switch (ev.detail.index) {
|
||||
case 0:
|
||||
showConfirmationDialog(this, {
|
||||
text: this.hass.localize(
|
||||
"ui.panel.config.cloud.account.sign_out_confirm"
|
||||
),
|
||||
confirmText: this.hass!.localize("ui.common.yes"),
|
||||
dismissText: this.hass!.localize("ui.common.no"),
|
||||
confirm: () => this._logoutFromCloud(),
|
||||
});
|
||||
await cloudLogout(this.hass);
|
||||
fireEvent(this, "ha-refresh-cloud-status");
|
||||
}
|
||||
}
|
||||
|
||||
private async _logoutFromCloud() {
|
||||
await cloudLogout(this.hass);
|
||||
fireEvent(this, "ha-refresh-cloud-status");
|
||||
}
|
||||
|
||||
_computeRTLDirection(hass) {
|
||||
return computeRTLDirection(hass);
|
||||
}
|
||||
|
@@ -40,7 +40,7 @@ class ConfigNameForm extends LitElement {
|
||||
)}
|
||||
.disabled=${disabled}
|
||||
.value=${this._nameValue}
|
||||
@change=${this._handleChange}
|
||||
@changed=${this._handleChange}
|
||||
></ha-textfield>
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
|
@@ -17,7 +17,7 @@ import {
|
||||
PropertyValues,
|
||||
TemplateResult,
|
||||
} from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-icon-next";
|
||||
@@ -26,6 +26,10 @@ import "../../../components/ha-menu-button";
|
||||
import "../../../components/ha-button-menu";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import { CloudStatus } from "../../../data/cloud";
|
||||
import {
|
||||
refreshSupervisorAvailableUpdates,
|
||||
SupervisorAvailableUpdates,
|
||||
} from "../../../data/supervisor/root";
|
||||
import { showQuickBar } from "../../../dialogs/quick-bar/show-dialog-quick-bar";
|
||||
import "../../../layouts/ha-app-layout";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
@@ -34,70 +38,10 @@ import "../ha-config-section";
|
||||
import { configSections } from "../ha-panel-config";
|
||||
import "./ha-config-navigation";
|
||||
import "./ha-config-updates";
|
||||
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
|
||||
import { documentationUrl } from "../../../util/documentation-url";
|
||||
import { UpdateDescription } from "../../../data/update";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { computeRTL } from "../../../common/util/compute_rtl";
|
||||
|
||||
const randomTip = (hass: HomeAssistant) => {
|
||||
const weighted: string[] = [];
|
||||
const tips = [
|
||||
{
|
||||
content: hass.localize(
|
||||
"ui.panel.config.tips.join",
|
||||
"forums",
|
||||
html`<a
|
||||
href="https://community.home-assistant.io"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>Forums</a
|
||||
>`,
|
||||
"twitter",
|
||||
html`<a
|
||||
href=${documentationUrl(hass, `/twitter`)}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>Twitter</a
|
||||
>`,
|
||||
"discord",
|
||||
html`<a
|
||||
href=${documentationUrl(hass, `/join-chat`)}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>Chat</a
|
||||
>`,
|
||||
"blog",
|
||||
html`<a
|
||||
href=${documentationUrl(hass, `/blog`)}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>Blog</a
|
||||
>`,
|
||||
"newsletter",
|
||||
html`<span class="keep-together"
|
||||
><a
|
||||
href=${documentationUrl(hass, `/newsletter`)}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>Newsletter</a
|
||||
>
|
||||
<ha-svg-icon class="new" .path=${mdiNewBox}></ha-svg-icon
|
||||
></span>`
|
||||
),
|
||||
weight: 2,
|
||||
},
|
||||
{ content: hass.localize("ui.dialogs.quick-bar.key_c_hint"), weight: 1 },
|
||||
];
|
||||
|
||||
tips.forEach((tip) => {
|
||||
for (let i = 0; i < tip.weight; i++) {
|
||||
weighted.push(tip.content);
|
||||
}
|
||||
});
|
||||
|
||||
return weighted[Math.floor(Math.random() * weighted.length)];
|
||||
};
|
||||
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
|
||||
import { showToast } from "../../../util/toast";
|
||||
import { documentationUrl } from "../../../util/documentation-url";
|
||||
|
||||
@customElement("ha-config-dashboard")
|
||||
class HaConfigDashboard extends LitElement {
|
||||
@@ -111,11 +55,11 @@ class HaConfigDashboard extends LitElement {
|
||||
@property() public cloudStatus?: CloudStatus;
|
||||
|
||||
// null means not available
|
||||
@property() public updates?: UpdateDescription[] | null;
|
||||
@property() public supervisorUpdates?: SupervisorAvailableUpdates[] | null;
|
||||
|
||||
@property() public showAdvanced!: boolean;
|
||||
|
||||
@state() private _tip?: string;
|
||||
private _notifyUpdates = false;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
@@ -128,7 +72,6 @@ class HaConfigDashboard extends LitElement {
|
||||
></ha-menu-button>
|
||||
<div main-title>${this.hass.localize("panel.config")}</div>
|
||||
<ha-icon-button
|
||||
.label=${this.hass.localize("ui.dialogs.quick-bar.title")}
|
||||
.path=${mdiMagnify}
|
||||
@click=${this._showQuickBar}
|
||||
></ha-icon-button>
|
||||
@@ -155,57 +98,94 @@ class HaConfigDashboard extends LitElement {
|
||||
.isWide=${this.isWide}
|
||||
full-width
|
||||
>
|
||||
${this.updates === undefined
|
||||
? html`<ha-alert .rtl=${computeRTL(this.hass)}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.updates.checking_updates"
|
||||
)}
|
||||
</ha-alert>`
|
||||
: this.updates?.length
|
||||
? html`<ha-card>
|
||||
<ha-config-updates
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.updates=${this.updates}
|
||||
></ha-config-updates>
|
||||
</ha-card>`
|
||||
: ""}
|
||||
<ha-card>
|
||||
${this.narrow && this.updates?.length
|
||||
? html`<div class="title">
|
||||
${this.hass.localize("panel.config")}
|
||||
</div>`
|
||||
: ""}
|
||||
${this.cloudStatus && isComponentLoaded(this.hass, "cloud")
|
||||
? html`
|
||||
${this.supervisorUpdates === undefined
|
||||
? // Hide everything until updates loaded
|
||||
html``
|
||||
: html`${this.supervisorUpdates?.length
|
||||
? html`<ha-card>
|
||||
<ha-config-updates
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.supervisorUpdates=${this.supervisorUpdates}
|
||||
></ha-config-updates>
|
||||
</ha-card>`
|
||||
: ""}
|
||||
<ha-card>
|
||||
${this.narrow && this.supervisorUpdates?.length
|
||||
? html`<div class="title">
|
||||
${this.hass.localize("panel.config")}
|
||||
</div>`
|
||||
: ""}
|
||||
${this.cloudStatus && isComponentLoaded(this.hass, "cloud")
|
||||
? html`
|
||||
<ha-config-navigation
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.showAdvanced=${this.showAdvanced}
|
||||
.pages=${[
|
||||
{
|
||||
component: "cloud",
|
||||
path: "/config/cloud",
|
||||
name: "Home Assistant Cloud",
|
||||
info: this.cloudStatus,
|
||||
iconPath: mdiCloudLock,
|
||||
iconColor: "#3B808E",
|
||||
},
|
||||
]}
|
||||
></ha-config-navigation>
|
||||
`
|
||||
: ""}
|
||||
<ha-config-navigation
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.showAdvanced=${this.showAdvanced}
|
||||
.pages=${[
|
||||
{
|
||||
component: "cloud",
|
||||
path: "/config/cloud",
|
||||
name: "Home Assistant Cloud",
|
||||
info: this.cloudStatus,
|
||||
iconPath: mdiCloudLock,
|
||||
iconColor: "#3B808E",
|
||||
},
|
||||
]}
|
||||
.pages=${configSections.dashboard}
|
||||
></ha-config-navigation>
|
||||
`
|
||||
: ""}
|
||||
<ha-config-navigation
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.showAdvanced=${this.showAdvanced}
|
||||
.pages=${configSections.dashboard}
|
||||
></ha-config-navigation>
|
||||
</ha-card>
|
||||
</ha-card>`}
|
||||
<div class="tips">
|
||||
<ha-svg-icon .path=${mdiLightbulbOutline}></ha-svg-icon>
|
||||
<span class="tip-word">Tip!</span>
|
||||
<span class="text">${this._tip}</span>
|
||||
<span class="text">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.tips.join",
|
||||
"forums",
|
||||
html`<a
|
||||
href="https://community.home-assistant.io"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>Forums</a
|
||||
>`,
|
||||
"twitter",
|
||||
html`<a
|
||||
href=${documentationUrl(this.hass, `/twitter`)}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>Twitter</a
|
||||
>`,
|
||||
"discord",
|
||||
html`<a
|
||||
href=${documentationUrl(this.hass, `/join-chat`)}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>Chat</a
|
||||
>`,
|
||||
"blog",
|
||||
html`<a
|
||||
href=${documentationUrl(this.hass, `/blog`)}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>Blog</a
|
||||
>`,
|
||||
"newsletter",
|
||||
html`<a
|
||||
href=${documentationUrl(this.hass, `/newsletter`)}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>Newsletter</a
|
||||
>
|
||||
<ha-svg-icon class="new" .path=${mdiNewBox}></ha-svg-icon>`
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
</ha-config-section>
|
||||
</ha-app-layout>
|
||||
@@ -215,8 +195,20 @@ class HaConfigDashboard extends LitElement {
|
||||
protected override updated(changedProps: PropertyValues): void {
|
||||
super.updated(changedProps);
|
||||
|
||||
if (!this._tip && changedProps.has("hass")) {
|
||||
this._tip = randomTip(this.hass);
|
||||
if (!changedProps.has("supervisorUpdates") || !this._notifyUpdates) {
|
||||
return;
|
||||
}
|
||||
this._notifyUpdates = false;
|
||||
if (this.supervisorUpdates?.length) {
|
||||
showToast(this, {
|
||||
message: this.hass.localize(
|
||||
"ui.panel.config.updates.updates_refreshed"
|
||||
),
|
||||
});
|
||||
} else {
|
||||
showToast(this, {
|
||||
message: this.hass.localize("ui.panel.config.updates.no_new_updates"),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,16 +222,18 @@ class HaConfigDashboard extends LitElement {
|
||||
private async _handleMenuAction(ev: CustomEvent<ActionDetail>) {
|
||||
switch (ev.detail.index) {
|
||||
case 0:
|
||||
if (isComponentLoaded(this.hass, "update")) {
|
||||
fireEvent(this, "ha-refresh-updates");
|
||||
if (isComponentLoaded(this.hass, "hassio")) {
|
||||
this._notifyUpdates = true;
|
||||
await refreshSupervisorAvailableUpdates(this.hass);
|
||||
fireEvent(this, "ha-refresh-supervisor");
|
||||
return;
|
||||
}
|
||||
showAlertDialog(this, {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.updates.update_not_loaded.title"
|
||||
"ui.panel.config.updates.check_unavailable.title"
|
||||
),
|
||||
text: this.hass.localize(
|
||||
"ui.panel.config.updates.update_not_loaded.description"
|
||||
"ui.panel.config.updates.check_unavailable.description"
|
||||
),
|
||||
warning: true,
|
||||
});
|
||||
@@ -299,10 +293,6 @@ class HaConfigDashboard extends LitElement {
|
||||
.new {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.keep-together {
|
||||
display: inline-block;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
@@ -1,48 +1,21 @@
|
||||
import "@material/mwc-button/mwc-button";
|
||||
import { mdiPackageVariant } from "@mdi/js";
|
||||
import "@polymer/paper-item/paper-icon-item";
|
||||
import "@polymer/paper-item/paper-item-body";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import "../../../components/ha-alert";
|
||||
import "../../../components/ha-icon-next";
|
||||
import "../../../components/ha-logo-svg";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import { UpdateDescription } from "../../../data/update";
|
||||
import { showUpdateDialog } from "../../../dialogs/update-dialog/show-ha-update-dialog";
|
||||
import { SupervisorAvailableUpdates } from "../../../data/supervisor/root";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { brandsUrl } from "../../../util/brands-url";
|
||||
import "../../../components/ha-icon-next";
|
||||
|
||||
const sortUpdates = memoizeOne((a: UpdateDescription, b: UpdateDescription) => {
|
||||
if (a.domain === "hassio" && b.domain === "hassio") {
|
||||
if (a.identifier === "core") {
|
||||
return -1;
|
||||
}
|
||||
if (b.identifier === "core") {
|
||||
return 1;
|
||||
}
|
||||
if (a.identifier === "supervisor") {
|
||||
return -1;
|
||||
}
|
||||
if (b.identifier === "supervisor") {
|
||||
return 1;
|
||||
}
|
||||
if (a.identifier === "os") {
|
||||
return -1;
|
||||
}
|
||||
if (b.identifier === "os") {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (a.domain === "hassio") {
|
||||
return -1;
|
||||
}
|
||||
if (b.domain === "hassio") {
|
||||
return 1;
|
||||
}
|
||||
return a.name.toUpperCase() < b.name.toUpperCase() ? -1 : 1;
|
||||
});
|
||||
export const SUPERVISOR_UPDATE_NAMES = {
|
||||
core: "Home Assistant Core",
|
||||
os: "Home Assistant Operating System",
|
||||
supervisor: "Home Assistant Supervisor",
|
||||
};
|
||||
|
||||
@customElement("ha-config-updates")
|
||||
class HaConfigUpdates extends LitElement {
|
||||
@@ -51,62 +24,62 @@ class HaConfigUpdates extends LitElement {
|
||||
@property({ type: Boolean }) public narrow!: boolean;
|
||||
|
||||
@property({ attribute: false })
|
||||
public updates?: UpdateDescription[] | null;
|
||||
public supervisorUpdates?: SupervisorAvailableUpdates[] | null;
|
||||
|
||||
@state() private _showAll = false;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.updates?.length) {
|
||||
if (!this.supervisorUpdates?.length) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
// Make sure the first updates shown are for the Supervisor
|
||||
const sortedUpdates = this.updates.sort((a, b) => sortUpdates(a, b));
|
||||
|
||||
const updates =
|
||||
this._showAll || sortedUpdates.length <= 3
|
||||
? sortedUpdates
|
||||
: sortedUpdates.slice(0, 2);
|
||||
this._showAll || this.supervisorUpdates.length <= 3
|
||||
? this.supervisorUpdates
|
||||
: this.supervisorUpdates.slice(0, 2);
|
||||
|
||||
return html`
|
||||
<div class="title">
|
||||
${this.hass.localize("ui.panel.config.updates.title", {
|
||||
count: sortedUpdates.length,
|
||||
count: this.supervisorUpdates.length,
|
||||
})}
|
||||
</div>
|
||||
${updates.map(
|
||||
(update) => html`
|
||||
<paper-icon-item @click=${this._showUpdate} .update=${update}>
|
||||
<span slot="item-icon" class="icon">
|
||||
<img
|
||||
src=${update.icon_url ||
|
||||
brandsUrl({
|
||||
domain: update.domain,
|
||||
type: "icon",
|
||||
useFallback: true,
|
||||
darkOptimized: this.hass.themes?.darkMode,
|
||||
})}
|
||||
/>
|
||||
</span>
|
||||
<paper-item-body two-line>
|
||||
${update.name}
|
||||
<div secondary>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.updates.version_available",
|
||||
{
|
||||
version_available: update.available_version,
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
</paper-item-body>
|
||||
</paper-icon-item>
|
||||
<a href="/hassio${update.panel_path}">
|
||||
<paper-icon-item>
|
||||
<span slot="item-icon" class="icon">
|
||||
${update.update_type === "addon"
|
||||
? update.icon
|
||||
? html`<img src="/api/hassio${update.icon}" />`
|
||||
: html`<ha-svg-icon
|
||||
.path=${mdiPackageVariant}
|
||||
></ha-svg-icon>`
|
||||
: html`<ha-logo-svg></ha-logo-svg>`}
|
||||
</span>
|
||||
<paper-item-body two-line>
|
||||
${update.update_type === "addon"
|
||||
? update.name
|
||||
: SUPERVISOR_UPDATE_NAMES[update.update_type!]}
|
||||
<div secondary>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.updates.version_available",
|
||||
{
|
||||
version_available: update.version_latest,
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
</paper-item-body>
|
||||
${!this.narrow ? html`<ha-icon-next></ha-icon-next>` : ""}
|
||||
</paper-icon-item>
|
||||
</a>
|
||||
`
|
||||
)}
|
||||
${!this._showAll && this.updates.length >= 4
|
||||
${!this._showAll && this.supervisorUpdates.length >= 4
|
||||
? html`
|
||||
<button class="show-more" @click=${this._showAllClicked}>
|
||||
${this.hass.localize("ui.panel.config.updates.more_updates", {
|
||||
count: this.updates!.length - updates.length,
|
||||
count: this.supervisorUpdates!.length - updates.length,
|
||||
})}
|
||||
</button>
|
||||
`
|
||||
@@ -118,14 +91,6 @@ class HaConfigUpdates extends LitElement {
|
||||
this._showAll = true;
|
||||
}
|
||||
|
||||
private _showUpdate(ev) {
|
||||
const update = ev.currentTarget.update as UpdateDescription;
|
||||
showUpdateDialog(this, {
|
||||
update,
|
||||
refreshCallback: () => fireEvent(this, "ha-refresh-updates"),
|
||||
});
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup[] {
|
||||
return [
|
||||
css`
|
||||
@@ -174,9 +139,6 @@ class HaConfigUpdates extends LitElement {
|
||||
outline: none;
|
||||
text-decoration: underline;
|
||||
}
|
||||
paper-icon-item {
|
||||
cursor: pointer;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
@@ -438,13 +438,6 @@ export class HaConfigDeviceDashboard extends LitElement {
|
||||
)}
|
||||
.path=${mdiFilterVariant}
|
||||
></ha-icon-button>
|
||||
${this.narrow && activeFilters?.length
|
||||
? html`<mwc-list-item @click=${this._clearFilter}
|
||||
>${this.hass.localize("ui.components.data-table.filtering_by")}
|
||||
${activeFilters.join(", ")}
|
||||
<span class="clear">Clear</span></mwc-list-item
|
||||
>`
|
||||
: ""}
|
||||
<ha-check-list-item
|
||||
left
|
||||
@request-selected=${this._showDisabledChanged}
|
||||
@@ -530,11 +523,6 @@ export class HaConfigDeviceDashboard extends LitElement {
|
||||
ha-button-menu {
|
||||
margin-left: 8px;
|
||||
}
|
||||
.clear {
|
||||
color: var(--primary-color);
|
||||
padding-left: 8px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
`,
|
||||
haStyle,
|
||||
];
|
||||
|
@@ -86,7 +86,6 @@ export class EnergyDeviceSettings extends LitElement {
|
||||
: device.stat_consumption}</span
|
||||
>
|
||||
<ha-icon-button
|
||||
.label=${this.hass.localize("ui.common.delete")}
|
||||
@click=${this._deleteDevice}
|
||||
.device=${device}
|
||||
.path=${mdiDelete}
|
||||
|
@@ -585,15 +585,6 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
||||
)}
|
||||
.path=${mdiFilterVariant}
|
||||
></ha-icon-button>
|
||||
${this.narrow && activeFilters?.length
|
||||
? html`<mwc-list-item @click=${this._clearFilter}
|
||||
>${this.hass.localize(
|
||||
"ui.components.data-table.filtering_by"
|
||||
)}
|
||||
${activeFilters.join(", ")}
|
||||
<span class="clear">Clear</span></mwc-list-item
|
||||
>`
|
||||
: ""}
|
||||
<ha-check-list-item
|
||||
@request-selected=${this._showDisabledChanged}
|
||||
.selected=${this._showDisabled}
|
||||
@@ -905,11 +896,6 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
||||
ha-button-menu {
|
||||
margin-left: 8px;
|
||||
}
|
||||
.clear {
|
||||
color: var(--primary-color);
|
||||
padding-left: 8px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
@@ -27,18 +27,20 @@ import { customElement, property, state } from "lit/decorators";
|
||||
import { isComponentLoaded } from "../../common/config/is_component_loaded";
|
||||
import { listenMediaQuery } from "../../common/dom/media_query";
|
||||
import { CloudStatus, fetchCloudStatus } from "../../data/cloud";
|
||||
import { fetchUpdateInfo, UpdateDescription } from "../../data/update";
|
||||
import {
|
||||
fetchSupervisorAvailableUpdates,
|
||||
SupervisorAvailableUpdates,
|
||||
} from "../../data/supervisor/root";
|
||||
import "../../layouts/hass-loading-screen";
|
||||
import { HassRouterPage, RouterOptions } from "../../layouts/hass-router-page";
|
||||
import { PageNavigation } from "../../layouts/hass-tabs-subpage";
|
||||
import { HomeAssistant, Route } from "../../types";
|
||||
import { showToast } from "../../util/toast";
|
||||
|
||||
declare global {
|
||||
// for fire event
|
||||
interface HASSDomEvents {
|
||||
"ha-refresh-cloud-status": undefined;
|
||||
"ha-refresh-updates": undefined;
|
||||
"ha-refresh-supervisor": undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -405,7 +407,7 @@ class HaPanelConfig extends HassRouterPage {
|
||||
|
||||
@state() private _cloudStatus?: CloudStatus;
|
||||
|
||||
@state() private _updates?: UpdateDescription[] | null;
|
||||
@state() private _supervisorUpdates?: SupervisorAvailableUpdates[] | null;
|
||||
|
||||
private _listeners: Array<() => void> = [];
|
||||
|
||||
@@ -441,18 +443,18 @@ class HaPanelConfig extends HassRouterPage {
|
||||
}
|
||||
});
|
||||
}
|
||||
if (isComponentLoaded(this.hass, "update")) {
|
||||
this._loadUpdates();
|
||||
this.addEventListener("ha-refresh-updates", () => {
|
||||
this._loadUpdates();
|
||||
if (isComponentLoaded(this.hass, "hassio")) {
|
||||
this._loadSupervisorUpdates();
|
||||
this.addEventListener("ha-refresh-supervisor", () => {
|
||||
this._loadSupervisorUpdates();
|
||||
});
|
||||
this.addEventListener("connection-status", (ev) => {
|
||||
if (ev.detail === "connected") {
|
||||
this._loadUpdates();
|
||||
this._loadSupervisorUpdates();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this._updates = null;
|
||||
this._supervisorUpdates = null;
|
||||
}
|
||||
this.addEventListener("ha-refresh-cloud-status", () =>
|
||||
this._updateCloudStatus()
|
||||
@@ -484,7 +486,7 @@ class HaPanelConfig extends HassRouterPage {
|
||||
isWide,
|
||||
narrow: this.narrow,
|
||||
cloudStatus: this._cloudStatus,
|
||||
updates: this._updates,
|
||||
supervisorUpdates: this._supervisorUpdates,
|
||||
});
|
||||
} else {
|
||||
el.route = this.routeTail;
|
||||
@@ -493,7 +495,7 @@ class HaPanelConfig extends HassRouterPage {
|
||||
el.isWide = isWide;
|
||||
el.narrow = this.narrow;
|
||||
el.cloudStatus = this._cloudStatus;
|
||||
el.updates = this._updates;
|
||||
el.supervisorUpdates = this._supervisorUpdates;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -512,33 +514,13 @@ class HaPanelConfig extends HassRouterPage {
|
||||
}
|
||||
}
|
||||
|
||||
private async _loadUpdates(): Promise<void> {
|
||||
const _showToast = this._updates !== undefined;
|
||||
|
||||
if (_showToast) {
|
||||
showToast(this, {
|
||||
message: this.hass.localize("ui.panel.config.updates.checking_updates"),
|
||||
});
|
||||
}
|
||||
|
||||
private async _loadSupervisorUpdates(): Promise<void> {
|
||||
try {
|
||||
this._updates = await fetchUpdateInfo(this.hass);
|
||||
this._supervisorUpdates = await fetchSupervisorAvailableUpdates(
|
||||
this.hass
|
||||
);
|
||||
} catch (err) {
|
||||
this._updates = null;
|
||||
}
|
||||
|
||||
if (_showToast) {
|
||||
if (this._updates?.length) {
|
||||
showToast(this, {
|
||||
message: this.hass.localize(
|
||||
"ui.panel.config.updates.updates_refreshed"
|
||||
),
|
||||
});
|
||||
} else {
|
||||
showToast(this, {
|
||||
message: this.hass.localize("ui.panel.config.updates.no_new_updates"),
|
||||
});
|
||||
}
|
||||
this._supervisorUpdates = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -15,7 +15,7 @@ import { ifDefined } from "lit/directives/if-defined";
|
||||
import memoizeOne from "memoize-one";
|
||||
import type { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||
import { navigate } from "../../../common/navigate";
|
||||
import "../../../components/search-input";
|
||||
import "../../../common/search/search-input";
|
||||
import { caseInsensitiveStringCompare } from "../../../common/string/compare";
|
||||
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import { extractSearchParam } from "../../../common/url/search-params";
|
||||
|
@@ -213,11 +213,11 @@ export class HaIntegrationCard extends LitElement {
|
||||
} else {
|
||||
stateTextExtra = html`
|
||||
<br />
|
||||
<a href=${`/config/logs/?filter=${item.domain}`}>
|
||||
${this.hass.localize(
|
||||
<a href="/config/logs"
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.check_the_logs"
|
||||
)}
|
||||
</a>
|
||||
)}</a
|
||||
>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
@@ -8,7 +8,7 @@ import {
|
||||
Node,
|
||||
} from "vis-network/peer/esm/vis-network";
|
||||
import { navigate } from "../../../../../common/navigate";
|
||||
import "../../../../../components/search-input";
|
||||
import "../../../../../common/search/search-input";
|
||||
import "../../../../../components/device/ha-device-picker";
|
||||
import "../../../../../components/ha-button-menu";
|
||||
import "../../../../../components/ha-checkbox";
|
||||
@@ -144,6 +144,8 @@ export class ZHANetworkVisualizationPage extends LitElement {
|
||||
<div slot="header">
|
||||
<search-input
|
||||
.hass=${this.hass}
|
||||
no-label-float
|
||||
no-underline
|
||||
class="header"
|
||||
@value-changed=${this._handleSearchChange}
|
||||
.filter=${this._filter}
|
||||
@@ -159,6 +161,8 @@ export class ZHANetworkVisualizationPage extends LitElement {
|
||||
${!this.narrow
|
||||
? html`<search-input
|
||||
.hass=${this.hass}
|
||||
no-label-float
|
||||
no-underline
|
||||
@value-changed=${this._handleSearchChange}
|
||||
.filter=${this._filter}
|
||||
.label=${this.hass.localize(
|
||||
|
@@ -347,7 +347,7 @@ class ZWaveJSConfigDashboard extends LitElement {
|
||||
} else {
|
||||
stateTextExtra = html`
|
||||
<br />
|
||||
<a href="/config/logs?filter=zwave_js"
|
||||
<a href="/config/logs"
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.check_the_logs"
|
||||
)}</a
|
||||
|
@@ -4,7 +4,7 @@ import "../../../layouts/hass-tabs-subpage";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
import { HomeAssistant, Route } from "../../../types";
|
||||
import { configSections } from "../ha-panel-config";
|
||||
import "../../../components/search-input";
|
||||
import "../../../common/search/search-input";
|
||||
import { extractSearchParam } from "../../../common/url/search-params";
|
||||
import "./error-log-card";
|
||||
import "./system-log-card";
|
||||
@@ -22,7 +22,7 @@ export class HaConfigLogs extends LitElement {
|
||||
|
||||
@property() public route!: Route;
|
||||
|
||||
@state() private _filter = extractSearchParam("filter") || "";
|
||||
@state() private _filter = extractSearchParam("filter") ?? "";
|
||||
|
||||
@query("system-log-card", true) private systemLog?: SystemLogCard;
|
||||
|
||||
@@ -43,6 +43,8 @@ export class HaConfigLogs extends LitElement {
|
||||
<div slot="header">
|
||||
<search-input
|
||||
class="header"
|
||||
no-label-float
|
||||
no-underline
|
||||
@value-changed=${this._filterChanged}
|
||||
.hass=${this.hass}
|
||||
.filter=${this._filter}
|
||||
@@ -53,6 +55,9 @@ export class HaConfigLogs extends LitElement {
|
||||
: html`
|
||||
<div class="search">
|
||||
<search-input
|
||||
autofocus
|
||||
no-label-float
|
||||
no-underline
|
||||
@value-changed=${this._filterChanged}
|
||||
.hass=${this.hass}
|
||||
.filter=${this._filter}
|
||||
|
@@ -18,7 +18,6 @@ import "../../../components/ha-code-editor";
|
||||
import "../../../components/ha-icon-button";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import "../../../components/ha-checkbox";
|
||||
import "../../../components/search-input";
|
||||
import "../../../components/ha-expansion-panel";
|
||||
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
|
||||
import { EventsMixin } from "../../../mixins/events-mixin";
|
||||
@@ -86,8 +85,7 @@ class HaPanelDevState extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.filters search-input {
|
||||
display: block;
|
||||
.filters ha-textfield {
|
||||
--mdc-text-field-fill-color: transparent;
|
||||
}
|
||||
|
||||
@@ -254,27 +252,28 @@ class HaPanelDevState extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
||||
</tr>
|
||||
<tr class="filters">
|
||||
<th>
|
||||
<search-input
|
||||
<ha-textfield
|
||||
label="[[localize('ui.panel.developer-tools.tabs.states.filter_entities')]]"
|
||||
type="search"
|
||||
value="[[_entityFilter]]"
|
||||
on-value-changed="_entityFilterChanged"
|
||||
></search-input>
|
||||
on-input="_entityFilterChanged"
|
||||
></ha-textfield>
|
||||
</th>
|
||||
<th>
|
||||
<search-input
|
||||
<ha-textfield
|
||||
label="[[localize('ui.panel.developer-tools.tabs.states.filter_states')]]"
|
||||
type="search"
|
||||
value="[[_stateFilter]]"
|
||||
on-value-changed="_stateFilterChanged"
|
||||
></search-input>
|
||||
on-input="_stateFilterChanged"
|
||||
></ha-textfield>
|
||||
</th>
|
||||
<th hidden$="[[!computeShowAttributes(narrow, _showAttributes)]]">
|
||||
<search-input
|
||||
<ha-textfield
|
||||
label="[[localize('ui.panel.developer-tools.tabs.states.filter_attributes')]]"
|
||||
type="search"
|
||||
value="[[_attributeFilter]]"
|
||||
on-value-changed="_attributeFilterChanged"
|
||||
></search-input>
|
||||
on-input="_attributeFilterChanged"
|
||||
></ha-textfield>
|
||||
</th>
|
||||
</tr>
|
||||
<tr hidden$="[[!computeShowEntitiesPlaceholder(_entities)]]">
|
||||
@@ -441,15 +440,15 @@ class HaPanelDevState extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
||||
}
|
||||
|
||||
_entityFilterChanged(ev) {
|
||||
this._entityFilter = ev.detail.value;
|
||||
this._entityFilter = ev.target.value;
|
||||
}
|
||||
|
||||
_stateFilterChanged(ev) {
|
||||
this._stateFilter = ev.detail.value;
|
||||
this._stateFilter = ev.target.value;
|
||||
}
|
||||
|
||||
_attributeFilterChanged(ev) {
|
||||
this._attributeFilter = ev.detail.value;
|
||||
this._attributeFilter = ev.target.value;
|
||||
}
|
||||
|
||||
_getHistoryURL(entityId, inputDate) {
|
||||
|
@@ -289,12 +289,6 @@ class HaPanelDevTemplate extends LitElement {
|
||||
.rendered.error {
|
||||
color: var(--error-color);
|
||||
}
|
||||
|
||||
@media all and (max-width: 870px) {
|
||||
.render-pane {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
@@ -32,8 +32,6 @@ class HuiEnergyCarbonGaugeCard
|
||||
|
||||
@state() private _data?: EnergyData;
|
||||
|
||||
protected hassSubscribeRequiredHostProps = ["_config"];
|
||||
|
||||
public getCardSize(): number {
|
||||
return 4;
|
||||
}
|
||||
|
@@ -49,8 +49,6 @@ export class HuiEnergyDevicesGraphCard
|
||||
|
||||
@query("ha-chart-base") private _chart?: HaChartBase;
|
||||
|
||||
protected hassSubscribeRequiredHostProps = ["_config"];
|
||||
|
||||
public hassSubscribe(): UnsubscribeFunc[] {
|
||||
return [
|
||||
getEnergyDataCollection(this.hass, {
|
||||
|
@@ -43,8 +43,6 @@ class HuiEnergyDistrubutionCard
|
||||
|
||||
@state() private _data?: EnergyData;
|
||||
|
||||
protected hassSubscribeRequiredHostProps = ["_config"];
|
||||
|
||||
public setConfig(config: EnergyDistributionCardConfig): void {
|
||||
this._config = config;
|
||||
}
|
||||
|
@@ -62,8 +62,6 @@ export class HuiEnergyGasGraphCard
|
||||
|
||||
@state() private _unit?: string;
|
||||
|
||||
protected hassSubscribeRequiredHostProps = ["_config"];
|
||||
|
||||
public hassSubscribe(): UnsubscribeFunc[] {
|
||||
return [
|
||||
getEnergyDataCollection(this.hass, {
|
||||
|
@@ -35,8 +35,6 @@ class HuiEnergyGridGaugeCard
|
||||
|
||||
@state() private _data?: EnergyData;
|
||||
|
||||
protected hassSubscribeRequiredHostProps = ["_config"];
|
||||
|
||||
public hassSubscribe(): UnsubscribeFunc[] {
|
||||
return [
|
||||
getEnergyDataCollection(this.hass!, {
|
||||
|
@@ -30,8 +30,6 @@ class HuiEnergySolarGaugeCard
|
||||
|
||||
@state() private _data?: EnergyData;
|
||||
|
||||
protected hassSubscribeRequiredHostProps = ["_config"];
|
||||
|
||||
public hassSubscribe(): UnsubscribeFunc[] {
|
||||
return [
|
||||
getEnergyDataCollection(this.hass!, {
|
||||
|
@@ -61,8 +61,6 @@ export class HuiEnergySolarGraphCard
|
||||
|
||||
@state() private _end = endOfToday();
|
||||
|
||||
protected hassSubscribeRequiredHostProps = ["_config"];
|
||||
|
||||
public hassSubscribe(): UnsubscribeFunc[] {
|
||||
return [
|
||||
getEnergyDataCollection(this.hass, {
|
||||
|
@@ -45,8 +45,6 @@ export class HuiEnergySourcesTableCard
|
||||
|
||||
@state() private _data?: EnergyData;
|
||||
|
||||
protected hassSubscribeRequiredHostProps = ["_config"];
|
||||
|
||||
public hassSubscribe(): UnsubscribeFunc[] {
|
||||
return [
|
||||
getEnergyDataCollection(this.hass, {
|
||||
|
@@ -50,8 +50,6 @@ export class HuiEnergyUsageGraphCard
|
||||
|
||||
@state() private _end = endOfToday();
|
||||
|
||||
protected hassSubscribeRequiredHostProps = ["_config"];
|
||||
|
||||
public hassSubscribe(): UnsubscribeFunc[] {
|
||||
return [
|
||||
getEnergyDataCollection(this.hass, {
|
||||
|
@@ -15,7 +15,7 @@ import { styleMap } from "lit/directives/style-map";
|
||||
import { until } from "lit/directives/until";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import "../../../../components/search-input";
|
||||
import "../../../../common/search/search-input";
|
||||
import "../../../../components/ha-circular-progress";
|
||||
import { UNAVAILABLE_STATES } from "../../../../data/entity";
|
||||
import type {
|
||||
|
@@ -90,9 +90,11 @@ export class HuiAlarmPanelCardEditor
|
||||
|
||||
private _computeLabelCallback = (schema: HaFormSchema) => {
|
||||
if (schema.name === "entity") {
|
||||
return this.hass!.localize(
|
||||
return `${this.hass!.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.entity"
|
||||
);
|
||||
)} (${this.hass!.localize(
|
||||
"ui.panel.lovelace.editor.card.config.required"
|
||||
)})`;
|
||||
}
|
||||
|
||||
if (schema.name === "name") {
|
||||
|
@@ -107,9 +107,11 @@ export class HuiEntityCardEditor
|
||||
|
||||
private _computeLabelCallback = (schema: HaFormSchema) => {
|
||||
if (schema.name === "entity") {
|
||||
return this.hass!.localize(
|
||||
return `${this.hass!.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.entity"
|
||||
);
|
||||
)} (${this.hass!.localize(
|
||||
"ui.panel.lovelace.editor.card.config.required"
|
||||
)})`;
|
||||
}
|
||||
|
||||
return this.hass!.localize(
|
||||
|
@@ -1,18 +1,25 @@
|
||||
import "../../../../components/ha-form/ha-form";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { html, LitElement, TemplateResult } from "lit";
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { assert } from "superstruct";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
||||
import { computeDomain } from "../../../../common/entity/compute_domain";
|
||||
import { domainIcon } from "../../../../common/entity/domain_icon";
|
||||
import type { LocalizeFunc } from "../../../../common/translations/localize";
|
||||
import type { HaFormSchema } from "../../../../components/ha-form/types";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import type { EntitiesCardEntityConfig } from "../../cards/types";
|
||||
import type { LovelaceRowEditor } from "../../types";
|
||||
import "../../../../components/ha-formfield";
|
||||
import "../../../../components/ha-icon-picker";
|
||||
import "../../../../components/ha-select";
|
||||
import "../../../../components/ha-switch";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { EntitiesCardEntityConfig } from "../../cards/types";
|
||||
import "../../components/hui-action-editor";
|
||||
import "../../components/hui-entity-editor";
|
||||
import "../../components/hui-theme-select-editor";
|
||||
import { LovelaceRowEditor } from "../../types";
|
||||
import { entitiesConfigStruct } from "../structs/entities-struct";
|
||||
import { EditorTarget, EntitiesEditorEvent } from "../types";
|
||||
import { configElementStyle } from "./config-elements-style";
|
||||
|
||||
const SecondaryInfoValues: { [key: string]: { domains?: string[] } } = {
|
||||
"entity-id": {},
|
||||
@@ -38,105 +45,127 @@ export class HuiGenericEntityRowEditor
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
private _schema = memoizeOne(
|
||||
(
|
||||
entity: string,
|
||||
icon: string | undefined,
|
||||
entityState: HassEntity,
|
||||
localize: LocalizeFunc
|
||||
): HaFormSchema[] => {
|
||||
const domain = computeDomain(entity);
|
||||
get _entity(): string {
|
||||
return this._config!.entity || "";
|
||||
}
|
||||
|
||||
return [
|
||||
{ name: "entity", required: true, selector: { entity: {} } },
|
||||
{
|
||||
type: "grid",
|
||||
name: "",
|
||||
schema: [
|
||||
{ name: "name", selector: { text: {} } },
|
||||
{
|
||||
name: "icon",
|
||||
selector: {
|
||||
icon: {
|
||||
placeholder: icon || entityState?.attributes.icon,
|
||||
fallbackPath:
|
||||
!icon && !entityState?.attributes.icon && entityState
|
||||
? domainIcon(domain, entityState)
|
||||
: undefined,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "secondary_info",
|
||||
selector: {
|
||||
select: {
|
||||
options: Object.keys(SecondaryInfoValues)
|
||||
.filter(
|
||||
(info) =>
|
||||
!("domains" in SecondaryInfoValues[info]) ||
|
||||
("domains" in SecondaryInfoValues[info] &&
|
||||
SecondaryInfoValues[info].domains!.includes(domain))
|
||||
)
|
||||
.map((info) => ({
|
||||
value: info,
|
||||
label: localize(
|
||||
`ui.panel.lovelace.editor.card.entities.secondary_info_values.${info}`
|
||||
),
|
||||
})),
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
);
|
||||
get _name(): string {
|
||||
return this._config!.name || "";
|
||||
}
|
||||
|
||||
get _icon(): string {
|
||||
return this._config!.icon || "";
|
||||
}
|
||||
|
||||
get _secondary_info(): string {
|
||||
return this._config!.secondary_info || "";
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass || !this._config) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
const entityState = this.hass.states[this._config.entity];
|
||||
|
||||
const schema = this._schema(
|
||||
this._config.entity,
|
||||
this._config.icon,
|
||||
entityState,
|
||||
this.hass.localize
|
||||
);
|
||||
const domain = computeDomain(this._entity);
|
||||
const entityState = this.hass.states[this._entity];
|
||||
|
||||
return html`
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${this._config}
|
||||
.schema=${schema}
|
||||
.computeLabel=${this._computeLabelCallback}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-form>
|
||||
<div class="card-config">
|
||||
<ha-entity-picker
|
||||
allow-custom-entity
|
||||
.hass=${this.hass}
|
||||
.value=${this._config.entity}
|
||||
.configValue=${"entity"}
|
||||
@change=${this._valueChanged}
|
||||
></ha-entity-picker>
|
||||
<div class="side-by-side">
|
||||
<paper-input
|
||||
.label=${this.hass!.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.name"
|
||||
)}
|
||||
.value=${this._config.name}
|
||||
.configValue=${"name"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></paper-input>
|
||||
<ha-icon-picker
|
||||
.label=${this.hass!.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.icon"
|
||||
)}
|
||||
.value=${this._config.icon}
|
||||
.configValue=${"icon"}
|
||||
.placeholder=${entityState?.attributes.icon}
|
||||
.fallbackPath=${!this._icon &&
|
||||
!entityState?.attributes.icon &&
|
||||
entityState
|
||||
? domainIcon(computeDomain(entityState.entity_id), entityState)
|
||||
: undefined}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-icon-picker>
|
||||
</div>
|
||||
<ha-select
|
||||
label="Secondary Info"
|
||||
.configValue=${"secondary_info"}
|
||||
@selected=${this._valueChanged}
|
||||
@closed=${stopPropagation}
|
||||
.value=${this._config.secondary_info || "none"}
|
||||
naturalMenuWidth
|
||||
fixedMenuPosition
|
||||
>
|
||||
<mwc-list-item value=""
|
||||
>${this.hass!.localize(
|
||||
"ui.panel.lovelace.editor.card.entities.secondary_info_values.none"
|
||||
)}</mwc-list-item
|
||||
>
|
||||
${Object.keys(SecondaryInfoValues).map((info) => {
|
||||
if (
|
||||
!("domains" in SecondaryInfoValues[info]) ||
|
||||
("domains" in SecondaryInfoValues[info] &&
|
||||
SecondaryInfoValues[info].domains!.includes(domain))
|
||||
) {
|
||||
return html`
|
||||
<mwc-list-item .value=${info}>
|
||||
${this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.entities.secondary_info_values.${info}`
|
||||
)}
|
||||
</mwc-list-item>
|
||||
`;
|
||||
}
|
||||
return "";
|
||||
})}
|
||||
</ha-select>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent): void {
|
||||
fireEvent(this, "config-changed", { config: ev.detail.value });
|
||||
}
|
||||
private _valueChanged(ev: EntitiesEditorEvent): void {
|
||||
if (!this._config || !this.hass) {
|
||||
return;
|
||||
}
|
||||
const target = ev.target! as EditorTarget;
|
||||
const value = target.value || ev.detail?.item?.value;
|
||||
|
||||
private _computeLabelCallback = (schema: HaFormSchema) => {
|
||||
if (schema.name === "entity") {
|
||||
return this.hass!.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.entity"
|
||||
);
|
||||
if (this[`_${target.configValue}`] === value) {
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.generic.${schema.name}`
|
||||
) ||
|
||||
this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.entity-row.${schema.name}`
|
||||
)
|
||||
);
|
||||
};
|
||||
if (target.configValue) {
|
||||
if (value === "" || !value) {
|
||||
this._config = { ...this._config };
|
||||
delete this._config[target.configValue!];
|
||||
} else {
|
||||
this._config = {
|
||||
...this._config,
|
||||
[target.configValue!]: value,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fireEvent(this, "config-changed", { config: this._config });
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return configElementStyle;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
@@ -1,13 +1,16 @@
|
||||
import "../../../../components/ha-form/ha-form";
|
||||
import { html, LitElement, TemplateResult } from "lit";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { assert, assign, object, optional, string } from "superstruct";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import type { HaFormSchema } from "../../../../components/ha-form/types";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import type { HumidifierCardConfig } from "../../cards/types";
|
||||
import type { LovelaceCardEditor } from "../../types";
|
||||
import "../../../../components/entity/ha-entity-picker";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { HumidifierCardConfig } from "../../cards/types";
|
||||
import "../../components/hui-theme-select-editor";
|
||||
import { LovelaceCardEditor } from "../../types";
|
||||
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
||||
import { EditorTarget, EntitiesEditorEvent } from "../types";
|
||||
import { configElementStyle } from "./config-elements-style";
|
||||
|
||||
const cardConfigStruct = assign(
|
||||
baseLovelaceCardConfig,
|
||||
@@ -18,21 +21,7 @@ const cardConfigStruct = assign(
|
||||
})
|
||||
);
|
||||
|
||||
const SCHEMA: HaFormSchema[] = [
|
||||
{
|
||||
name: "entity",
|
||||
required: true,
|
||||
selector: { entity: { domain: "humidifer" } },
|
||||
},
|
||||
{
|
||||
type: "grid",
|
||||
name: "",
|
||||
schema: [
|
||||
{ name: "name", selector: { text: {} } },
|
||||
{ name: "theme", selector: { theme: {} } },
|
||||
],
|
||||
},
|
||||
];
|
||||
const includeDomains = ["humidifier"];
|
||||
|
||||
@customElement("hui-humidifier-card-editor")
|
||||
export class HuiHumidifierCardEditor
|
||||
@@ -48,37 +37,81 @@ export class HuiHumidifierCardEditor
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
get _entity(): string {
|
||||
return this._config!.entity || "";
|
||||
}
|
||||
|
||||
get _name(): string {
|
||||
return this._config!.name || "";
|
||||
}
|
||||
|
||||
get _theme(): string {
|
||||
return this._config!.theme || "";
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass || !this._config) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${this._config}
|
||||
.schema=${SCHEMA}
|
||||
.computeLabel=${this._computeLabelCallback}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-form>
|
||||
<div class="card-config">
|
||||
<ha-entity-picker
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.entity"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.required"
|
||||
)})"
|
||||
.hass=${this.hass}
|
||||
.value=${this._entity}
|
||||
.configValue=${"entity"}
|
||||
.includeDomains=${includeDomains}
|
||||
@change=${this._valueChanged}
|
||||
allow-custom-entity
|
||||
></ha-entity-picker>
|
||||
<paper-input
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.name"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.optional"
|
||||
)})"
|
||||
.value=${this._name}
|
||||
.configValue=${"name"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></paper-input>
|
||||
<hui-theme-select-editor
|
||||
.hass=${this.hass}
|
||||
.value=${this._theme}
|
||||
.configValue=${"theme"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></hui-theme-select-editor>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent): void {
|
||||
fireEvent(this, "config-changed", { config: ev.detail.value });
|
||||
private _valueChanged(ev: EntitiesEditorEvent): void {
|
||||
if (!this._config || !this.hass) {
|
||||
return;
|
||||
}
|
||||
const target = ev.target! as EditorTarget;
|
||||
|
||||
if (this[`_${target.configValue}`] === target.value) {
|
||||
return;
|
||||
}
|
||||
if (target.configValue) {
|
||||
if (target.value === "") {
|
||||
this._config = { ...this._config };
|
||||
delete this._config[target.configValue!];
|
||||
} else {
|
||||
this._config = { ...this._config, [target.configValue!]: target.value };
|
||||
}
|
||||
}
|
||||
fireEvent(this, "config-changed", { config: this._config });
|
||||
}
|
||||
|
||||
private _computeLabelCallback = (schema: HaFormSchema) => {
|
||||
if (schema.name === "entity") {
|
||||
return this.hass!.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.entity"
|
||||
);
|
||||
}
|
||||
|
||||
return this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.generic.${schema.name}`
|
||||
);
|
||||
};
|
||||
static get styles(): CSSResultGroup {
|
||||
return configElementStyle;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
@@ -1,13 +1,14 @@
|
||||
import "../../../../components/ha-form/ha-form";
|
||||
import { html, LitElement, TemplateResult } from "lit";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { assert, assign, object, optional, string } from "superstruct";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import type { HaFormSchema } from "../../../../components/ha-form/types";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import type { IframeCardConfig } from "../../cards/types";
|
||||
import type { LovelaceCardEditor } from "../../types";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { IframeCardConfig } from "../../cards/types";
|
||||
import { LovelaceCardEditor } from "../../types";
|
||||
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
||||
import { EditorTarget, EntitiesEditorEvent } from "../types";
|
||||
import { configElementStyle } from "./config-elements-style";
|
||||
|
||||
const cardConfigStruct = assign(
|
||||
baseLovelaceCardConfig,
|
||||
@@ -18,18 +19,6 @@ const cardConfigStruct = assign(
|
||||
})
|
||||
);
|
||||
|
||||
const SCHEMA: HaFormSchema[] = [
|
||||
{ name: "title", selector: { text: {} } },
|
||||
{
|
||||
name: "",
|
||||
type: "grid",
|
||||
schema: [
|
||||
{ name: "url", required: true, selector: { text: {} } },
|
||||
{ name: "aspect_ratio", selector: { text: {} } },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("hui-iframe-card-editor")
|
||||
export class HuiIframeCardEditor
|
||||
extends LitElement
|
||||
@@ -44,28 +33,85 @@ export class HuiIframeCardEditor
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
get _title(): string {
|
||||
return this._config!.title || "";
|
||||
}
|
||||
|
||||
get _url(): string {
|
||||
return this._config!.url || "";
|
||||
}
|
||||
|
||||
get _aspect_ratio(): string {
|
||||
return this._config!.aspect_ratio || "";
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass || !this._config) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${this._config}
|
||||
.schema=${SCHEMA}
|
||||
.computeLabel=${this._computeLabelCallback}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-form>
|
||||
<div class="card-config">
|
||||
<paper-input
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.url"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.required"
|
||||
)})"
|
||||
.value=${this._url}
|
||||
.configValue=${"url"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></paper-input>
|
||||
<div class="side-by-side">
|
||||
<paper-input
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.title"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.optional"
|
||||
)})"
|
||||
.value=${this._title}
|
||||
.configValue=${"title"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></paper-input>
|
||||
<paper-input
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.aspect_ratio"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.optional"
|
||||
)})"
|
||||
.value=${this._aspect_ratio}
|
||||
.configValue=${"aspect_ratio"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></paper-input>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent): void {
|
||||
fireEvent(this, "config-changed", { config: ev.detail.value });
|
||||
private _valueChanged(ev: EntitiesEditorEvent): void {
|
||||
if (!this._config || !this.hass) {
|
||||
return;
|
||||
}
|
||||
const target = ev.target! as EditorTarget;
|
||||
const value = target.value;
|
||||
|
||||
if (this[`_${target.configValue}`] === value) {
|
||||
return;
|
||||
}
|
||||
if (target.configValue) {
|
||||
if (value === "") {
|
||||
this._config = { ...this._config };
|
||||
delete this._config[target.configValue!];
|
||||
} else {
|
||||
this._config = { ...this._config, [target.configValue!]: value };
|
||||
}
|
||||
}
|
||||
fireEvent(this, "config-changed", { config: this._config });
|
||||
}
|
||||
|
||||
private _computeLabelCallback = (schema: HaFormSchema) =>
|
||||
this.hass!.localize(`ui.panel.lovelace.editor.card.generic.${schema.name}`);
|
||||
static get styles(): CSSResultGroup {
|
||||
return configElementStyle;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
@@ -1,21 +1,22 @@
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { assert, object, optional, string, assign } from "superstruct";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import type { ActionConfig } from "../../../../data/lovelace";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import type { LightCardConfig } from "../../cards/types";
|
||||
import "../../../../components/ha-icon-picker";
|
||||
import { ActionConfig } from "../../../../data/lovelace";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { LightCardConfig } from "../../cards/types";
|
||||
import "../../components/hui-action-editor";
|
||||
import type { LovelaceCardEditor } from "../../types";
|
||||
import "../../components/hui-entity-editor";
|
||||
import "../../components/hui-theme-select-editor";
|
||||
import { LovelaceCardEditor } from "../../types";
|
||||
import { actionConfigStruct } from "../structs/action-struct";
|
||||
import type { EditorTarget } from "../types";
|
||||
import { EditorTarget } from "../types";
|
||||
import { configElementStyle } from "./config-elements-style";
|
||||
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
||||
import { domainIcon } from "../../../../common/entity/domain_icon";
|
||||
import { computeDomain } from "../../../../common/entity/compute_domain";
|
||||
import type { HaFormSchema } from "../../../../components/ha-form/types";
|
||||
|
||||
const cardConfigStruct = assign(
|
||||
baseLovelaceCardConfig,
|
||||
@@ -29,6 +30,8 @@ const cardConfigStruct = assign(
|
||||
})
|
||||
);
|
||||
|
||||
const includeDomains = ["light"];
|
||||
|
||||
@customElement("hui-light-card-editor")
|
||||
export class HuiLightCardEditor
|
||||
extends LitElement
|
||||
@@ -43,39 +46,21 @@ export class HuiLightCardEditor
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
private _schema = memoizeOne(
|
||||
(
|
||||
entity: string,
|
||||
icon: string | undefined,
|
||||
entityState: HassEntity
|
||||
): HaFormSchema[] => [
|
||||
{
|
||||
name: "entity",
|
||||
required: true,
|
||||
selector: { entity: { domain: "light" } },
|
||||
},
|
||||
{
|
||||
type: "grid",
|
||||
name: "",
|
||||
schema: [
|
||||
{ name: "name", selector: { text: {} } },
|
||||
{
|
||||
name: "icon",
|
||||
selector: {
|
||||
icon: {
|
||||
placeholder: icon || entityState?.attributes.icon,
|
||||
fallbackPath:
|
||||
!icon && !entityState?.attributes.icon && entityState
|
||||
? domainIcon(computeDomain(entity), entityState)
|
||||
: undefined,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{ name: "theme", selector: { theme: {} } },
|
||||
]
|
||||
);
|
||||
get _name(): string {
|
||||
return this._config!.name || "";
|
||||
}
|
||||
|
||||
get _theme(): string {
|
||||
return this._config!.theme || "";
|
||||
}
|
||||
|
||||
get _entity(): string {
|
||||
return this._config!.entity || "";
|
||||
}
|
||||
|
||||
get _icon(): string {
|
||||
return this._config!.icon || "";
|
||||
}
|
||||
|
||||
get _hold_action(): ActionConfig {
|
||||
return this._config!.hold_action || { action: "more-info" };
|
||||
@@ -99,22 +84,59 @@ export class HuiLightCardEditor
|
||||
"none",
|
||||
];
|
||||
|
||||
const entityState = this.hass.states[this._config.entity];
|
||||
const schema = this._schema(
|
||||
this._config.entity,
|
||||
this._config.icon,
|
||||
entityState
|
||||
);
|
||||
const entityState = this.hass.states[this._entity];
|
||||
|
||||
return html`
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${this._config}
|
||||
.schema=${schema}
|
||||
.computeLabel=${this._computeLabelCallback}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-form>
|
||||
<div class="card-config">
|
||||
<ha-entity-picker
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.entity"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.required"
|
||||
)})"
|
||||
.hass=${this.hass}
|
||||
.value=${this._entity}
|
||||
.configValue=${"entity"}
|
||||
.includeDomains=${includeDomains}
|
||||
@value-changed=${this._valueChanged}
|
||||
allow-custom-entity
|
||||
></ha-entity-picker>
|
||||
<div class="side-by-side">
|
||||
<paper-input
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.name"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.optional"
|
||||
)})"
|
||||
.value=${this._name}
|
||||
.configValue=${"name"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></paper-input>
|
||||
<ha-icon-picker
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.icon"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.optional"
|
||||
)})"
|
||||
.value=${this._icon}
|
||||
.placeholder=${this._icon || entityState?.attributes.icon}
|
||||
.fallbackPath=${!this._icon &&
|
||||
!entityState?.attributes.icon &&
|
||||
entityState
|
||||
? domainIcon(computeDomain(entityState.entity_id), entityState)
|
||||
: undefined}
|
||||
.configValue=${"icon"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-icon-picker>
|
||||
</div>
|
||||
|
||||
<hui-theme-select-editor
|
||||
.hass=${this.hass}
|
||||
.value=${this._theme}
|
||||
.configValue=${"theme"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></hui-theme-select-editor>
|
||||
|
||||
<hui-action-editor
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.hold_action"
|
||||
@@ -125,7 +147,7 @@ export class HuiLightCardEditor
|
||||
.config=${this._hold_action}
|
||||
.actions=${actions}
|
||||
.configValue=${"hold_action"}
|
||||
@value-changed=${this._actionChanged}
|
||||
@value-changed=${this._valueChanged}
|
||||
></hui-action-editor>
|
||||
|
||||
<hui-action-editor
|
||||
@@ -138,13 +160,13 @@ export class HuiLightCardEditor
|
||||
.config=${this._double_tap_action}
|
||||
.actions=${actions}
|
||||
.configValue=${"double_tap_action"}
|
||||
@value-changed=${this._actionChanged}
|
||||
@value-changed=${this._valueChanged}
|
||||
></hui-action-editor>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _actionChanged(ev: CustomEvent): void {
|
||||
private _valueChanged(ev: CustomEvent): void {
|
||||
if (!this._config || !this.hass) {
|
||||
return;
|
||||
}
|
||||
@@ -168,33 +190,9 @@ export class HuiLightCardEditor
|
||||
fireEvent(this, "config-changed", { config: this._config });
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent): void {
|
||||
fireEvent(this, "config-changed", { config: ev.detail.value });
|
||||
static get styles(): CSSResultGroup {
|
||||
return configElementStyle;
|
||||
}
|
||||
|
||||
private _computeLabelCallback = (schema: HaFormSchema) => {
|
||||
if (schema.name === "entity") {
|
||||
return this.hass!.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.entity"
|
||||
);
|
||||
}
|
||||
|
||||
return this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.generic.${schema.name}`
|
||||
);
|
||||
};
|
||||
|
||||
static styles: CSSResultGroup = [
|
||||
configElementStyle,
|
||||
css`
|
||||
ha-form,
|
||||
hui-action-editor {
|
||||
display: block;
|
||||
margin-bottom: 24px;
|
||||
overflow: auto;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import "../../../../components/ha-form/ha-form";
|
||||
import { html, LitElement, TemplateResult } from "lit";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import {
|
||||
array,
|
||||
@@ -12,11 +12,15 @@ import {
|
||||
} from "superstruct";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import "../../../../components/entity/ha-entities-picker";
|
||||
import type { HaFormSchema } from "../../../../components/ha-form/types";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import type { LogbookCardConfig } from "../../cards/types";
|
||||
import type { LovelaceCardEditor } from "../../types";
|
||||
import "../../../../components/entity/ha-entity-picker";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { LogbookCardConfig } from "../../cards/types";
|
||||
import "../../components/hui-entity-editor";
|
||||
import "../../components/hui-theme-select-editor";
|
||||
import { LovelaceCardEditor } from "../../types";
|
||||
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
||||
import { EditorTarget } from "../types";
|
||||
import { configElementStyle } from "./config-elements-style";
|
||||
|
||||
const cardConfigStruct = assign(
|
||||
baseLovelaceCardConfig,
|
||||
@@ -28,18 +32,6 @@ const cardConfigStruct = assign(
|
||||
})
|
||||
);
|
||||
|
||||
const SCHEMA: HaFormSchema[] = [
|
||||
{ name: "title", selector: { text: {} } },
|
||||
{
|
||||
name: "",
|
||||
type: "grid",
|
||||
schema: [
|
||||
{ name: "theme", selector: { theme: {} } },
|
||||
{ name: "hours_to_show", selector: { number: { mode: "box", min: 1 } } },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("hui-logbook-card-editor")
|
||||
export class HuiLogbookCardEditor
|
||||
extends LitElement
|
||||
@@ -49,58 +41,118 @@ export class HuiLogbookCardEditor
|
||||
|
||||
@state() private _config?: LogbookCardConfig;
|
||||
|
||||
@state() private _configEntities?: string[];
|
||||
|
||||
public setConfig(config: LogbookCardConfig): void {
|
||||
assert(config, cardConfigStruct);
|
||||
this._config = config;
|
||||
this._configEntities = config.entities;
|
||||
}
|
||||
|
||||
get _title(): string {
|
||||
return this._config!.title || "";
|
||||
}
|
||||
|
||||
get _entities(): string[] {
|
||||
return this._config!.entities || [];
|
||||
}
|
||||
|
||||
get _hours_to_show(): number {
|
||||
return this._config!.hours_to_show || 24;
|
||||
}
|
||||
|
||||
get _theme(): string {
|
||||
return this._config!.theme || "";
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass || !this._config) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${this._config}
|
||||
.schema=${SCHEMA}
|
||||
.computeLabel=${this._computeLabelCallback}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-form>
|
||||
<h3>
|
||||
${`${this.hass!.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.entities"
|
||||
)} (${this.hass!.localize(
|
||||
"ui.panel.lovelace.editor.card.config.required"
|
||||
)})`}
|
||||
</h3>
|
||||
<ha-entities-picker
|
||||
.hass=${this.hass}
|
||||
.value=${this._entities}
|
||||
@value-changed=${this._entitiesChanged}
|
||||
>
|
||||
</ha-entities-picker>
|
||||
<div class="card-config">
|
||||
<paper-input
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.title"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.optional"
|
||||
)})"
|
||||
.value=${this._title}
|
||||
.configValue=${"title"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></paper-input>
|
||||
<div class="side-by-side">
|
||||
<hui-theme-select-editor
|
||||
.hass=${this.hass}
|
||||
.value=${this._theme}
|
||||
.configValue=${"theme"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></hui-theme-select-editor>
|
||||
<paper-input
|
||||
type="number"
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.hours_to_show"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.optional"
|
||||
)})"
|
||||
.value=${this._hours_to_show}
|
||||
min="1"
|
||||
.configValue=${"hours_to_show"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></paper-input>
|
||||
</div>
|
||||
<h3>
|
||||
${`${this.hass!.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.entities"
|
||||
)} (${this.hass!.localize(
|
||||
"ui.panel.lovelace.editor.card.config.required"
|
||||
)})`}
|
||||
</h3>
|
||||
<ha-entities-picker
|
||||
.hass=${this.hass}
|
||||
.value=${this._configEntities}
|
||||
@value-changed=${this._valueChanged}
|
||||
>
|
||||
</ha-entities-picker>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _entitiesChanged(ev: CustomEvent): void {
|
||||
this._config = { ...this._config!, entities: ev.detail.value };
|
||||
private _valueChanged(ev: CustomEvent): void {
|
||||
if (!this._config || !this.hass) {
|
||||
return;
|
||||
}
|
||||
const target = ev.target! as EditorTarget;
|
||||
|
||||
if (this[`_${target.configValue}`] === target.value) {
|
||||
return;
|
||||
}
|
||||
if (ev.detail && ev.detail.value && Array.isArray(ev.detail.value)) {
|
||||
this._config = { ...this._config, entities: ev.detail.value };
|
||||
} else if (target.configValue) {
|
||||
if (target.value === "") {
|
||||
this._config = { ...this._config };
|
||||
delete this._config[target.configValue!];
|
||||
} else {
|
||||
let value: any = target.value;
|
||||
|
||||
if (target.type === "number") {
|
||||
value = Number(value);
|
||||
}
|
||||
|
||||
this._config = {
|
||||
...this._config,
|
||||
[target.configValue!]: value,
|
||||
};
|
||||
}
|
||||
}
|
||||
fireEvent(this, "config-changed", { config: this._config });
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent): void {
|
||||
fireEvent(this, "config-changed", { config: ev.detail.value });
|
||||
static get styles(): CSSResultGroup {
|
||||
return configElementStyle;
|
||||
}
|
||||
|
||||
private _computeLabelCallback = (schema: HaFormSchema) =>
|
||||
this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.generic.${schema.name}`
|
||||
) ||
|
||||
this.hass!.localize(`ui.panel.lovelace.editor.card.logbook.${schema.name}`);
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
@@ -1,4 +1,3 @@
|
||||
import "../../../../components/ha-form/ha-form";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
@@ -13,6 +12,7 @@ import {
|
||||
assign,
|
||||
} from "superstruct";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { computeRTLDirection } from "../../../../common/util/compute_rtl";
|
||||
import "../../../../components/ha-formfield";
|
||||
import "../../../../components/ha-switch";
|
||||
import { PolymerChangedEvent } from "../../../../polymer-types";
|
||||
@@ -27,7 +27,6 @@ import { entitiesConfigStruct } from "../structs/entities-struct";
|
||||
import { EditorTarget, EntitiesEditorEvent } from "../types";
|
||||
import { configElementStyle } from "./config-elements-style";
|
||||
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
||||
import { HaFormSchema } from "../../../../components/ha-form/types";
|
||||
|
||||
const cardConfigStruct = assign(
|
||||
baseLovelaceCardConfig,
|
||||
@@ -42,20 +41,6 @@ const cardConfigStruct = assign(
|
||||
})
|
||||
);
|
||||
|
||||
const SCHEMA: HaFormSchema[] = [
|
||||
{ name: "title", selector: { text: {} } },
|
||||
{
|
||||
name: "",
|
||||
type: "grid",
|
||||
schema: [
|
||||
{ name: "aspect_ratio", selector: { text: {} } },
|
||||
{ name: "default_zoom", selector: { number: { mode: "box", min: 0 } } },
|
||||
{ name: "dark_mode", selector: { boolean: {} } },
|
||||
{ name: "hours_to_show", selector: { number: { mode: "box", min: 1 } } },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("hui-map-card-editor")
|
||||
export class HuiMapCardEditor extends LitElement implements LovelaceCardEditor {
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
@@ -72,24 +57,99 @@ export class HuiMapCardEditor extends LitElement implements LovelaceCardEditor {
|
||||
: [];
|
||||
}
|
||||
|
||||
get _title(): string {
|
||||
return this._config!.title || "";
|
||||
}
|
||||
|
||||
get _aspect_ratio(): string {
|
||||
return this._config!.aspect_ratio || "";
|
||||
}
|
||||
|
||||
get _default_zoom(): number {
|
||||
return this._config!.default_zoom || 0;
|
||||
}
|
||||
|
||||
get _geo_location_sources(): string[] {
|
||||
return this._config!.geo_location_sources || [];
|
||||
}
|
||||
|
||||
get _hours_to_show(): number {
|
||||
return this._config!.hours_to_show || 0;
|
||||
}
|
||||
|
||||
get _dark_mode(): boolean {
|
||||
return this._config!.dark_mode || false;
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass || !this._config) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${this._config}
|
||||
.schema=${SCHEMA}
|
||||
.computeLabel=${this._computeLabelCallback}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-form>
|
||||
<div class="card-config">
|
||||
<paper-input
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.title"
|
||||
)}
|
||||
(${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.optional"
|
||||
)})"
|
||||
.value=${this._title}
|
||||
.configValue=${"title"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></paper-input>
|
||||
<div class="side-by-side">
|
||||
<paper-input
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.aspect_ratio"
|
||||
)}
|
||||
(${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.optional"
|
||||
)})"
|
||||
.value=${this._aspect_ratio}
|
||||
.configValue=${"aspect_ratio"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></paper-input>
|
||||
<paper-input
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.map.default_zoom"
|
||||
)}
|
||||
(${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.optional"
|
||||
)})"
|
||||
type="number"
|
||||
.value=${this._default_zoom}
|
||||
.configValue=${"default_zoom"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></paper-input>
|
||||
</div>
|
||||
<div class="side-by-side">
|
||||
<ha-formfield
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.map.dark_mode"
|
||||
)}
|
||||
.dir=${computeRTLDirection(this.hass)}
|
||||
>
|
||||
<ha-switch
|
||||
.checked=${this._dark_mode}
|
||||
.configValue=${"dark_mode"}
|
||||
@change=${this._valueChanged}
|
||||
></ha-switch
|
||||
></ha-formfield>
|
||||
<paper-input
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.map.hours_to_show"
|
||||
)}
|
||||
(${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.optional"
|
||||
)})"
|
||||
type="number"
|
||||
.value=${this._hours_to_show}
|
||||
.configValue=${"hours_to_show"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></paper-input>
|
||||
</div>
|
||||
<hui-entity-editor
|
||||
.hass=${this.hass}
|
||||
.entities=${this._configEntities}
|
||||
@@ -107,7 +167,8 @@ export class HuiMapCardEditor extends LitElement implements LovelaceCardEditor {
|
||||
)}
|
||||
.hass=${this.hass}
|
||||
.value=${this._geo_location_sources}
|
||||
@value-changed=${this._geoSourcesChanged}
|
||||
.configValue=${"geo_location_sources"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></hui-input-list-editor>
|
||||
</div>
|
||||
</div>
|
||||
@@ -115,15 +176,18 @@ export class HuiMapCardEditor extends LitElement implements LovelaceCardEditor {
|
||||
}
|
||||
|
||||
private _entitiesValueChanged(ev: EntitiesEditorEvent): void {
|
||||
if (!this._config || !this.hass) {
|
||||
return;
|
||||
}
|
||||
if (ev.detail && ev.detail.entities) {
|
||||
this._config = { ...this._config!, entities: ev.detail.entities };
|
||||
this._config = { ...this._config, entities: ev.detail.entities };
|
||||
|
||||
this._configEntities = processEditorEntities(this._config.entities);
|
||||
fireEvent(this, "config-changed", { config: this._config! });
|
||||
fireEvent(this, "config-changed", { config: this._config });
|
||||
}
|
||||
}
|
||||
|
||||
private _geoSourcesChanged(ev: PolymerChangedEvent<any>): void {
|
||||
private _valueChanged(ev: PolymerChangedEvent<any>): void {
|
||||
if (!this._config || !this.hass) {
|
||||
return;
|
||||
}
|
||||
@@ -132,34 +196,26 @@ export class HuiMapCardEditor extends LitElement implements LovelaceCardEditor {
|
||||
return;
|
||||
}
|
||||
|
||||
const value = ev.detail.value;
|
||||
let value = target.checked ?? ev.detail.value;
|
||||
|
||||
if (this._geo_location_sources === value) {
|
||||
if (value && target.type === "number") {
|
||||
value = Number(value);
|
||||
}
|
||||
if (this[`_${target.configValue}`] === value) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (value === "") {
|
||||
this._config = { ...this._config };
|
||||
delete this._config.geo_location_sources;
|
||||
} else {
|
||||
delete this._config[target.configValue!];
|
||||
} else if (target.configValue) {
|
||||
this._config = {
|
||||
...this._config,
|
||||
geo_location_sources: value,
|
||||
[target.configValue]: value,
|
||||
};
|
||||
}
|
||||
fireEvent(this, "config-changed", { config: this._config });
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent): void {
|
||||
fireEvent(this, "config-changed", { config: ev.detail.value });
|
||||
}
|
||||
|
||||
private _computeLabelCallback = (schema: HaFormSchema) =>
|
||||
this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.generic.${schema.name}`
|
||||
) ||
|
||||
this.hass!.localize(`ui.panel.lovelace.editor.card.map.${schema.name}`);
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
configElementStyle,
|
||||
|
@@ -1,13 +1,16 @@
|
||||
import "../../../../components/ha-form/ha-form";
|
||||
import { html, LitElement, TemplateResult } from "lit";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
import "@polymer/paper-input/paper-textarea";
|
||||
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { assert, assign, object, optional, string } from "superstruct";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import type { HaFormSchema } from "../../../../components/ha-form/types";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import type { MarkdownCardConfig } from "../../cards/types";
|
||||
import type { LovelaceCardEditor } from "../../types";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { MarkdownCardConfig } from "../../cards/types";
|
||||
import "../../components/hui-theme-select-editor";
|
||||
import { LovelaceCardEditor } from "../../types";
|
||||
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
||||
import { EditorTarget, EntitiesEditorEvent } from "../types";
|
||||
import { configElementStyle } from "./config-elements-style";
|
||||
|
||||
const cardConfigStruct = assign(
|
||||
baseLovelaceCardConfig,
|
||||
@@ -18,12 +21,6 @@ const cardConfigStruct = assign(
|
||||
})
|
||||
);
|
||||
|
||||
const SCHEMA: HaFormSchema[] = [
|
||||
{ name: "title", selector: { text: {} } },
|
||||
{ name: "content", required: true, selector: { text: { multiline: true } } },
|
||||
{ name: "theme", selector: { theme: {} } },
|
||||
];
|
||||
|
||||
@customElement("hui-markdown-card-editor")
|
||||
export class HuiMarkdownCardEditor
|
||||
extends LitElement
|
||||
@@ -38,33 +35,90 @@ export class HuiMarkdownCardEditor
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
get _title(): string {
|
||||
return this._config!.title || "";
|
||||
}
|
||||
|
||||
get _content(): string {
|
||||
return this._config!.content || "";
|
||||
}
|
||||
|
||||
get _theme(): string {
|
||||
return this._config!.theme || "";
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass || !this._config) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${this._config}
|
||||
.schema=${SCHEMA}
|
||||
.computeLabel=${this._computeLabelCallback}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-form>
|
||||
<div class="card-config">
|
||||
<paper-input
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.title"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.optional"
|
||||
)})"
|
||||
.value=${this._title}
|
||||
.configValue=${"title"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></paper-input>
|
||||
<paper-textarea
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.markdown.content"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.required"
|
||||
)})"
|
||||
.value=${this._content}
|
||||
.configValue=${"content"}
|
||||
@keydown=${this._ignoreKeydown}
|
||||
@value-changed=${this._valueChanged}
|
||||
autocapitalize="none"
|
||||
autocomplete="off"
|
||||
spellcheck="false"
|
||||
></paper-textarea>
|
||||
<hui-theme-select-editor
|
||||
.hass=${this.hass}
|
||||
.value=${this._theme}
|
||||
.configValue=${"theme"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></hui-theme-select-editor>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent): void {
|
||||
fireEvent(this, "config-changed", { config: ev.detail.value });
|
||||
private _ignoreKeydown(ev: KeyboardEvent) {
|
||||
// Stop keyboard events from the paper-textarea from propagating to avoid accidentally closing the dialog when the user presses Enter.
|
||||
ev.stopPropagation();
|
||||
}
|
||||
|
||||
private _computeLabelCallback = (schema: HaFormSchema) =>
|
||||
this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.generic.${schema.name}`
|
||||
) ||
|
||||
this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.markdown.${schema.name}`
|
||||
);
|
||||
private _valueChanged(ev: EntitiesEditorEvent): void {
|
||||
if (!this._config || !this.hass) {
|
||||
return;
|
||||
}
|
||||
const target = ev.target! as EditorTarget;
|
||||
|
||||
if (this[`_${target.configValue}`] === target.value) {
|
||||
return;
|
||||
}
|
||||
if (target.configValue) {
|
||||
if (target.value === "" && target.configValue !== "content") {
|
||||
this._config = { ...this._config };
|
||||
delete this._config[target.configValue!];
|
||||
} else {
|
||||
this._config = {
|
||||
...this._config,
|
||||
[target.configValue!]: target.value,
|
||||
};
|
||||
}
|
||||
}
|
||||
fireEvent(this, "config-changed", { config: this._config });
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return configElementStyle;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import "@polymer/paper-input/paper-input";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { assert, object, optional, string, assign } from "superstruct";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
@@ -63,7 +63,7 @@ export class HuiPictureCardEditor
|
||||
|
||||
return html`
|
||||
<div class="card-config">
|
||||
<ha-textfield
|
||||
<paper-input
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.image"
|
||||
)} (${this.hass.localize(
|
||||
@@ -71,8 +71,8 @@ export class HuiPictureCardEditor
|
||||
)})"
|
||||
.value=${this._image}
|
||||
.configValue=${"image"}
|
||||
@input=${this._valueChanged}
|
||||
></ha-textfield>
|
||||
@value-changed=${this._valueChanged}
|
||||
></paper-input>
|
||||
<hui-theme-select-editor
|
||||
.hass=${this.hass}
|
||||
.value=${this._theme}
|
||||
@@ -134,15 +134,7 @@ export class HuiPictureCardEditor
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
configElementStyle,
|
||||
css`
|
||||
ha-textfield {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
return configElementStyle;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,16 +1,24 @@
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { assert, assign, boolean, object, optional, string } from "superstruct";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import type { HaFormSchema } from "../../../../components/ha-form/types";
|
||||
import type { ActionConfig } from "../../../../data/lovelace";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import type { PictureEntityCardConfig } from "../../cards/types";
|
||||
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
||||
import { computeRTLDirection } from "../../../../common/util/compute_rtl";
|
||||
import "../../../../components/ha-formfield";
|
||||
import "../../../../components/ha-select";
|
||||
import "../../../../components/ha-switch";
|
||||
import { ActionConfig } from "../../../../data/lovelace";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { PictureEntityCardConfig } from "../../cards/types";
|
||||
import "../../components/hui-action-editor";
|
||||
import type { LovelaceCardEditor } from "../../types";
|
||||
import "../../components/hui-entity-editor";
|
||||
import "../../components/hui-theme-select-editor";
|
||||
import { LovelaceCardEditor } from "../../types";
|
||||
import { actionConfigStruct } from "../structs/action-struct";
|
||||
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
||||
import type { EditorTarget } from "../types";
|
||||
import { EditorTarget } from "../types";
|
||||
import { configElementStyle } from "./config-elements-style";
|
||||
|
||||
const cardConfigStruct = assign(
|
||||
@@ -30,38 +38,7 @@ const cardConfigStruct = assign(
|
||||
})
|
||||
);
|
||||
|
||||
const SCHEMA: HaFormSchema[] = [
|
||||
{ name: "entity", required: true, selector: { entity: {} } },
|
||||
{ name: "name", selector: { text: {} } },
|
||||
{ name: "image", selector: { text: {} } },
|
||||
{ name: "camera_image", selector: { entity: { domain: "camera" } } },
|
||||
{
|
||||
name: "",
|
||||
type: "grid",
|
||||
schema: [
|
||||
{
|
||||
name: "camera_view",
|
||||
selector: { select: { options: ["auto", "live"] } },
|
||||
},
|
||||
{ name: "aspect_ratio", selector: { text: {} } },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "",
|
||||
type: "grid",
|
||||
schema: [
|
||||
{
|
||||
name: "show_name",
|
||||
selector: { boolean: {} },
|
||||
},
|
||||
{
|
||||
name: "show_state",
|
||||
selector: { boolean: {} },
|
||||
},
|
||||
],
|
||||
},
|
||||
{ name: "theme", selector: { theme: {} } },
|
||||
];
|
||||
const includeDomains = ["camera"];
|
||||
|
||||
@customElement("hui-picture-entity-card-editor")
|
||||
export class HuiPictureEntityCardEditor
|
||||
@@ -77,6 +54,30 @@ export class HuiPictureEntityCardEditor
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
get _entity(): string {
|
||||
return this._config!.entity || "";
|
||||
}
|
||||
|
||||
get _name(): string {
|
||||
return this._config!.name || "";
|
||||
}
|
||||
|
||||
get _image(): string {
|
||||
return this._config!.image || "";
|
||||
}
|
||||
|
||||
get _camera_image(): string {
|
||||
return this._config!.camera_image || "";
|
||||
}
|
||||
|
||||
get _camera_view(): string {
|
||||
return this._config!.camera_view || "auto";
|
||||
}
|
||||
|
||||
get _aspect_ratio(): string {
|
||||
return this._config!.aspect_ratio || "";
|
||||
}
|
||||
|
||||
get _tap_action(): ActionConfig {
|
||||
return this._config!.tap_action || { action: "more-info" };
|
||||
}
|
||||
@@ -85,29 +86,140 @@ export class HuiPictureEntityCardEditor
|
||||
return this._config!.hold_action;
|
||||
}
|
||||
|
||||
get _show_name(): boolean {
|
||||
return this._config!.show_name ?? true;
|
||||
}
|
||||
|
||||
get _show_state(): boolean {
|
||||
return this._config!.show_state ?? true;
|
||||
}
|
||||
|
||||
get _theme(): string {
|
||||
return this._config!.theme || "";
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass || !this._config) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
const actions = ["more-info", "toggle", "navigate", "call-service", "none"];
|
||||
|
||||
const data = {
|
||||
show_state: true,
|
||||
show_name: true,
|
||||
camera_view: "auto",
|
||||
...this._config,
|
||||
};
|
||||
const views = ["auto", "live"];
|
||||
const dir = computeRTLDirection(this.hass!);
|
||||
|
||||
return html`
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${data}
|
||||
.schema=${SCHEMA}
|
||||
.computeLabel=${this._computeLabelCallback}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-form>
|
||||
<div class="card-config">
|
||||
<ha-entity-picker
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.entity"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.required"
|
||||
)})"
|
||||
.hass=${this.hass}
|
||||
.value=${this._entity}
|
||||
.configValue=${"entity"}
|
||||
@value-changed=${this._valueChanged}
|
||||
allow-custom-entity
|
||||
></ha-entity-picker>
|
||||
<paper-input
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.name"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.optional"
|
||||
)})"
|
||||
.value=${this._name}
|
||||
.configValue=${"name"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></paper-input>
|
||||
<paper-input
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.image"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.optional"
|
||||
)})"
|
||||
.value=${this._image}
|
||||
.configValue=${"image"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></paper-input>
|
||||
<ha-entity-picker
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.camera_image"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.optional"
|
||||
)})"
|
||||
.hass=${this.hass}
|
||||
.value=${this._camera_image}
|
||||
.configValue=${"camera_image"}
|
||||
@value-changed=${this._valueChanged}
|
||||
.includeDomains=${includeDomains}
|
||||
allow-custom-entity
|
||||
></ha-entity-picker>
|
||||
<div class="side-by-side">
|
||||
<ha-select
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.camera_view"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.optional"
|
||||
)})"
|
||||
.configValue=${"camera_view"}
|
||||
@selected=${this._valueChanged}
|
||||
@closed=${stopPropagation}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
.value=${views.indexOf(this._camera_view)}
|
||||
>
|
||||
${views.map(
|
||||
(view) =>
|
||||
html`<mwc-list-item .value=${view}>${view}</mwc-list-item> `
|
||||
)}
|
||||
</ha-select>
|
||||
<paper-input
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.aspect_ratio"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.optional"
|
||||
)})"
|
||||
.value=${this._aspect_ratio}
|
||||
.configValue=${"aspect_ratio"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></paper-input>
|
||||
</div>
|
||||
<div class="side-by-side">
|
||||
<div>
|
||||
<ha-formfield
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.show_name"
|
||||
)}
|
||||
.dir=${dir}
|
||||
>
|
||||
<ha-switch
|
||||
.checked=${this._config!.show_name !== false}
|
||||
.configValue=${"show_name"}
|
||||
@change=${this._change}
|
||||
></ha-switch
|
||||
></ha-formfield>
|
||||
</div>
|
||||
<div>
|
||||
<ha-formfield
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.show_state"
|
||||
)}
|
||||
.dir=${dir}
|
||||
>
|
||||
<ha-switch
|
||||
.checked=${this._config!.show_state !== false}
|
||||
.configValue=${"show_state"}
|
||||
@change=${this._change}
|
||||
></ha-switch
|
||||
></ha-formfield>
|
||||
</div>
|
||||
</div>
|
||||
<hui-theme-select-editor
|
||||
.hass=${this.hass}
|
||||
.value=${this._theme}
|
||||
.configValue=${"theme"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></hui-theme-select-editor>
|
||||
<div class="side-by-side">
|
||||
<hui-action-editor
|
||||
.label="${this.hass.localize(
|
||||
@@ -119,7 +231,7 @@ export class HuiPictureEntityCardEditor
|
||||
.config=${this._tap_action}
|
||||
.actions=${actions}
|
||||
.configValue=${"tap_action"}
|
||||
@value-changed=${this._changed}
|
||||
@value-changed=${this._valueChanged}
|
||||
></hui-action-editor>
|
||||
<hui-action-editor
|
||||
.label="${this.hass.localize(
|
||||
@@ -131,18 +243,32 @@ export class HuiPictureEntityCardEditor
|
||||
.config=${this._hold_action}
|
||||
.actions=${actions}
|
||||
.configValue=${"hold_action"}
|
||||
@value-changed=${this._changed}
|
||||
@value-changed=${this._valueChanged}
|
||||
></hui-action-editor>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent): void {
|
||||
fireEvent(this, "config-changed", { config: ev.detail.value });
|
||||
private _change(ev: Event) {
|
||||
if (!this._config || !this.hass) {
|
||||
return;
|
||||
}
|
||||
const target = ev.target! as EditorTarget;
|
||||
const value = target.checked;
|
||||
|
||||
if (this[`_${target.configValue}`] === value) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._config = {
|
||||
...this._config,
|
||||
[target.configValue!]: value,
|
||||
};
|
||||
fireEvent(this, "config-changed", { config: this._config });
|
||||
}
|
||||
|
||||
private _changed(ev: CustomEvent): void {
|
||||
private _valueChanged(ev: CustomEvent): void {
|
||||
if (!this._config || !this.hass) {
|
||||
return;
|
||||
}
|
||||
@@ -153,36 +279,23 @@ export class HuiPictureEntityCardEditor
|
||||
return;
|
||||
}
|
||||
|
||||
if (value !== false && !value) {
|
||||
this._config = { ...this._config };
|
||||
delete this._config[target.configValue!];
|
||||
} else {
|
||||
this._config = {
|
||||
...this._config,
|
||||
[target.configValue!]: value,
|
||||
};
|
||||
if (target.configValue) {
|
||||
if (value !== false && !value) {
|
||||
this._config = { ...this._config };
|
||||
delete this._config[target.configValue!];
|
||||
} else {
|
||||
this._config = {
|
||||
...this._config,
|
||||
[target.configValue!]: value,
|
||||
};
|
||||
}
|
||||
}
|
||||
fireEvent(this, "config-changed", { config: this._config });
|
||||
}
|
||||
|
||||
private _computeLabelCallback = (schema: HaFormSchema) => {
|
||||
if (schema.name === "entity") {
|
||||
return this.hass!.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.entity"
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.generic.${schema.name}`
|
||||
) ||
|
||||
this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.picture-entity.${schema.name}`
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
static styles: CSSResultGroup = configElementStyle;
|
||||
static get styles(): CSSResultGroup {
|
||||
return configElementStyle;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
@@ -1,21 +1,25 @@
|
||||
import "../../components/hui-action-editor";
|
||||
import "../../../../components/ha-form/ha-form";
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { array, assert, assign, object, optional, string } from "superstruct";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import type { HaFormSchema } from "../../../../components/ha-form/types";
|
||||
import type { ActionConfig } from "../../../../data/lovelace";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import type { PictureGlanceCardConfig } from "../../cards/types";
|
||||
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
||||
import "../../../../components/entity/ha-entity-picker";
|
||||
import "../../../../components/ha-select";
|
||||
import { ActionConfig } from "../../../../data/lovelace";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { PictureGlanceCardConfig } from "../../cards/types";
|
||||
import "../../components/hui-action-editor";
|
||||
import "../../components/hui-entity-editor";
|
||||
import type { EntityConfig } from "../../entity-rows/types";
|
||||
import type { LovelaceCardEditor } from "../../types";
|
||||
import "../../components/hui-theme-select-editor";
|
||||
import { EntityConfig } from "../../entity-rows/types";
|
||||
import { LovelaceCardEditor } from "../../types";
|
||||
import { processEditorEntities } from "../process-editor-entities";
|
||||
import { actionConfigStruct } from "../structs/action-struct";
|
||||
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
||||
import { entitiesConfigStruct } from "../structs/entities-struct";
|
||||
import type { EditorTarget } from "../types";
|
||||
import { EditorTarget } from "../types";
|
||||
import { configElementStyle } from "./config-elements-style";
|
||||
|
||||
const cardConfigStruct = assign(
|
||||
@@ -34,26 +38,7 @@ const cardConfigStruct = assign(
|
||||
})
|
||||
);
|
||||
|
||||
const actions = ["more-info", "toggle", "navigate", "call-service", "none"];
|
||||
|
||||
const SCHEMA: HaFormSchema[] = [
|
||||
{ name: "title", selector: { text: {} } },
|
||||
{ name: "image", selector: { text: {} } },
|
||||
{ name: "camera_image", selector: { entity: { domain: "camera" } } },
|
||||
{
|
||||
name: "",
|
||||
type: "grid",
|
||||
schema: [
|
||||
{
|
||||
name: "camera_view",
|
||||
selector: { select: { options: ["auto", "live"] } },
|
||||
},
|
||||
{ name: "aspect_ratio", selector: { text: {} } },
|
||||
],
|
||||
},
|
||||
{ name: "entity", selector: { entity: {} } },
|
||||
{ name: "theme", selector: { theme: {} } },
|
||||
];
|
||||
const includeDomains = ["camera"];
|
||||
|
||||
@customElement("hui-picture-glance-card-editor")
|
||||
export class HuiPictureGlanceCardEditor
|
||||
@@ -72,6 +57,39 @@ export class HuiPictureGlanceCardEditor
|
||||
this._configEntities = processEditorEntities(config.entities);
|
||||
}
|
||||
|
||||
get _entity(): string {
|
||||
return this._config!.entity || "";
|
||||
}
|
||||
|
||||
get _title(): string {
|
||||
return this._config!.title || "";
|
||||
}
|
||||
|
||||
get _image(): string {
|
||||
return (
|
||||
this._config!.image ||
|
||||
(this._camera_image
|
||||
? ""
|
||||
: "https://www.home-assistant.io/images/merchandise/shirt-frontpage.png")
|
||||
);
|
||||
}
|
||||
|
||||
get _camera_image(): string {
|
||||
return this._config!.camera_image || "";
|
||||
}
|
||||
|
||||
get _camera_view(): string {
|
||||
return this._config!.camera_view || "auto";
|
||||
}
|
||||
|
||||
get _state_image(): Record<string, unknown> {
|
||||
return this._config!.state_image || {};
|
||||
}
|
||||
|
||||
get _aspect_ratio(): string {
|
||||
return this._config!.aspect_ratio || "";
|
||||
}
|
||||
|
||||
get _tap_action(): ActionConfig {
|
||||
return this._config!.tap_action || { action: "toggle" };
|
||||
}
|
||||
@@ -80,27 +98,102 @@ export class HuiPictureGlanceCardEditor
|
||||
return this._config!.hold_action || { action: "more-info" };
|
||||
}
|
||||
|
||||
get _theme(): string {
|
||||
return this._config!.theme || "";
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass || !this._config) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
const data = { camera_view: "auto", ...this._config };
|
||||
const actions = ["more-info", "toggle", "navigate", "call-service", "none"];
|
||||
const views = ["auto", "live"];
|
||||
|
||||
return html`
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${data}
|
||||
.schema=${SCHEMA}
|
||||
.computeLabel=${this._computeLabelCallback}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-form>
|
||||
<div class="card-config">
|
||||
<paper-input
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.title"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.optional"
|
||||
)})"
|
||||
.value=${this._title}
|
||||
.configValue=${"title"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></paper-input>
|
||||
<paper-input
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.image"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.optional"
|
||||
)})"
|
||||
.value=${this._image}
|
||||
.configValue=${"image"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></paper-input>
|
||||
<ha-entity-picker
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.camera_image"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.optional"
|
||||
)})"
|
||||
.hass=${this.hass}
|
||||
.value=${this._camera_image}
|
||||
.configValue=${"camera_image"}
|
||||
@value-changed=${this._valueChanged}
|
||||
allow-custom-entity
|
||||
.includeDomains=${includeDomains}
|
||||
></ha-entity-picker>
|
||||
<div class="side-by-side">
|
||||
<ha-select
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.camera_view"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.optional"
|
||||
)})"
|
||||
.configValue=${"camera_view"}
|
||||
@selected=${this._valueChanged}
|
||||
@closed=${stopPropagation}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
.value=${this._camera_view}
|
||||
>
|
||||
${views.map(
|
||||
(view) =>
|
||||
html`<mwc-list-item .value=${view}>${view}</mwc-list-item> `
|
||||
)}
|
||||
</ha-select>
|
||||
<paper-input
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.aspect_ratio"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.optional"
|
||||
)})"
|
||||
.value=${this._aspect_ratio}
|
||||
.configValue=${"aspect_ratio"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></paper-input>
|
||||
</div>
|
||||
<ha-entity-picker
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.picture-glance.state_entity"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.optional"
|
||||
)})"
|
||||
.hass=${this.hass}
|
||||
.value=${this._entity}
|
||||
.configValue=${"entity"}
|
||||
@value-changed=${this._valueChanged}
|
||||
allow-custom-entity
|
||||
></ha-entity-picker>
|
||||
<div class="side-by-side">
|
||||
<hui-action-editor
|
||||
.label=${this.hass.localize(
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.tap_action"
|
||||
)}
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.optional"
|
||||
)})"
|
||||
.hass=${this.hass}
|
||||
.config=${this._tap_action}
|
||||
.actions=${actions}
|
||||
@@ -108,9 +201,11 @@ export class HuiPictureGlanceCardEditor
|
||||
@value-changed=${this._valueChanged}
|
||||
></hui-action-editor>
|
||||
<hui-action-editor
|
||||
.label=${this.hass.localize(
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.hold_action"
|
||||
)}
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.optional"
|
||||
)})"
|
||||
.hass=${this.hass}
|
||||
.config=${this._hold_action}
|
||||
.actions=${actions}
|
||||
@@ -121,17 +216,19 @@ export class HuiPictureGlanceCardEditor
|
||||
<hui-entity-editor
|
||||
.hass=${this.hass}
|
||||
.entities=${this._configEntities}
|
||||
@entities-changed=${this._changed}
|
||||
@entities-changed=${this._valueChanged}
|
||||
></hui-entity-editor>
|
||||
<hui-theme-select-editor
|
||||
.hass=${this.hass}
|
||||
.value=${this._theme}
|
||||
.configValue=${"theme"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></hui-theme-select-editor>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent): void {
|
||||
fireEvent(this, "config-changed", { config: ev.detail.value });
|
||||
}
|
||||
|
||||
private _changed(ev: CustomEvent): void {
|
||||
if (!this._config || !this.hass) {
|
||||
return;
|
||||
}
|
||||
@@ -160,24 +257,9 @@ export class HuiPictureGlanceCardEditor
|
||||
fireEvent(this, "config-changed", { config: this._config });
|
||||
}
|
||||
|
||||
private _computeLabelCallback = (schema: HaFormSchema) => {
|
||||
if (schema.name === "entity") {
|
||||
return this.hass!.localize(
|
||||
"ui.panel.lovelace.editor.card.picture-glance.state_entity"
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.generic.${schema.name}`
|
||||
) ||
|
||||
this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.picture-glance.${schema.name}`
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
static styles: CSSResultGroup = configElementStyle;
|
||||
static get styles(): CSSResultGroup {
|
||||
return configElementStyle;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
@@ -1,13 +1,16 @@
|
||||
import "../../../../components/ha-form/ha-form";
|
||||
import { html, LitElement, TemplateResult } from "lit";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { assert, assign, object, optional, string } from "superstruct";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import type { HaFormSchema } from "../../../../components/ha-form/types";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import type { PlantStatusCardConfig } from "../../cards/types";
|
||||
import type { LovelaceCardEditor } from "../../types";
|
||||
import "../../../../components/entity/ha-entity-picker";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { PlantStatusCardConfig } from "../../cards/types";
|
||||
import "../../components/hui-theme-select-editor";
|
||||
import { LovelaceCardEditor } from "../../types";
|
||||
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
||||
import { EditorTarget, EntitiesEditorEvent } from "../types";
|
||||
import { configElementStyle } from "./config-elements-style";
|
||||
|
||||
const cardConfigStruct = assign(
|
||||
baseLovelaceCardConfig,
|
||||
@@ -18,11 +21,7 @@ const cardConfigStruct = assign(
|
||||
})
|
||||
);
|
||||
|
||||
const SCHEMA: HaFormSchema[] = [
|
||||
{ name: "entity", required: true, selector: { entity: { domain: "plant" } } },
|
||||
{ name: "name", selector: { text: {} } },
|
||||
{ name: "theme", selector: { theme: {} } },
|
||||
];
|
||||
const includeDomains = ["plant"];
|
||||
|
||||
@customElement("hui-plant-status-card-editor")
|
||||
export class HuiPlantStatusCardEditor
|
||||
@@ -38,37 +37,83 @@ export class HuiPlantStatusCardEditor
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
get _entity(): string {
|
||||
return this._config!.entity || "";
|
||||
}
|
||||
|
||||
get _name(): string {
|
||||
return this._config!.name || "";
|
||||
}
|
||||
|
||||
get _theme(): string {
|
||||
return this._config!.theme || "";
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass || !this._config) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${this._config}
|
||||
.schema=${SCHEMA}
|
||||
.computeLabel=${this._computeLabelCallback}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-form>
|
||||
<div class="card-config">
|
||||
<ha-entity-picker
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.entity"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.required"
|
||||
)})"
|
||||
.hass=${this.hass}
|
||||
.value=${this._entity}
|
||||
.configValue=${"entity"}
|
||||
.includeDomains=${includeDomains}
|
||||
@change=${this._valueChanged}
|
||||
allow-custom-entity
|
||||
></ha-entity-picker>
|
||||
<paper-input
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.name"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.optional"
|
||||
)})"
|
||||
.value=${this._name}
|
||||
.configValue=${"name"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></paper-input>
|
||||
<hui-theme-select-editor
|
||||
.hass=${this.hass}
|
||||
.value=${this._theme}
|
||||
.configValue=${"theme"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></hui-theme-select-editor>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent): void {
|
||||
fireEvent(this, "config-changed", { config: ev.detail.value });
|
||||
private _valueChanged(ev: EntitiesEditorEvent): void {
|
||||
if (!this._config || !this.hass) {
|
||||
return;
|
||||
}
|
||||
const target = ev.target! as EditorTarget;
|
||||
if (this[`_${target.configValue}`] === target.value) {
|
||||
return;
|
||||
}
|
||||
if (target.configValue) {
|
||||
if (target.value === "") {
|
||||
this._config = { ...this._config };
|
||||
delete this._config[target.configValue!];
|
||||
} else {
|
||||
this._config = {
|
||||
...this._config,
|
||||
[target.configValue!]: target.value,
|
||||
};
|
||||
}
|
||||
}
|
||||
fireEvent(this, "config-changed", { config: this._config });
|
||||
}
|
||||
|
||||
private _computeLabelCallback = (schema: HaFormSchema) => {
|
||||
if (schema.name === "entity") {
|
||||
return this.hass!.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.entity"
|
||||
);
|
||||
}
|
||||
|
||||
return this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.generic.${schema.name}`
|
||||
);
|
||||
};
|
||||
static get styles(): CSSResultGroup {
|
||||
return configElementStyle;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
@@ -1,8 +1,7 @@
|
||||
import "../../../../components/ha-form/ha-form";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import {
|
||||
assert,
|
||||
assign,
|
||||
@@ -14,13 +13,20 @@ import {
|
||||
union,
|
||||
} from "superstruct";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
||||
import { computeDomain } from "../../../../common/entity/compute_domain";
|
||||
import { domainIcon } from "../../../../common/entity/domain_icon";
|
||||
import type { HaFormSchema } from "../../../../components/ha-form/types";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import type { SensorCardConfig } from "../../cards/types";
|
||||
import type { LovelaceCardEditor } from "../../types";
|
||||
import "../../../../components/entity/ha-entity-picker";
|
||||
import "../../../../components/ha-formfield";
|
||||
import "../../../../components/ha-icon-picker";
|
||||
import "../../../../components/ha-select";
|
||||
import "../../../../components/ha-switch";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { SensorCardConfig } from "../../cards/types";
|
||||
import "../../components/hui-theme-select-editor";
|
||||
import { LovelaceCardEditor } from "../../types";
|
||||
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
||||
import { EditorTarget, EntitiesEditorEvent } from "../types";
|
||||
import { configElementStyle } from "./config-elements-style";
|
||||
|
||||
const cardConfigStruct = assign(
|
||||
@@ -37,6 +43,8 @@ const cardConfigStruct = assign(
|
||||
})
|
||||
);
|
||||
|
||||
const includeDomains = ["counter", "input_number", "number", "sensor"];
|
||||
|
||||
@customElement("hui-sensor-card-editor")
|
||||
export class HuiSensorCardEditor
|
||||
extends LitElement
|
||||
@@ -51,121 +59,200 @@ export class HuiSensorCardEditor
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
private _schema = memoizeOne(
|
||||
(
|
||||
entity: string,
|
||||
icon: string | undefined,
|
||||
entityState: HassEntity
|
||||
): HaFormSchema[] => [
|
||||
{
|
||||
name: "entity",
|
||||
selector: {
|
||||
entity: { domain: ["counter", "input_number", "number", "sensor"] },
|
||||
},
|
||||
},
|
||||
{ name: "name", selector: { text: {} } },
|
||||
{
|
||||
type: "grid",
|
||||
name: "",
|
||||
schema: [
|
||||
{
|
||||
name: "icon",
|
||||
selector: {
|
||||
icon: {
|
||||
placeholder: icon || entityState?.attributes.icon,
|
||||
fallbackPath:
|
||||
!icon && !entityState?.attributes.icon && entityState
|
||||
? domainIcon(computeDomain(entity), entityState)
|
||||
: undefined,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "graph",
|
||||
selector: {
|
||||
select: {
|
||||
options: [
|
||||
{
|
||||
value: "none",
|
||||
label: "None",
|
||||
},
|
||||
{
|
||||
value: "line",
|
||||
label: "Line",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{ name: "unit", selector: { text: {} } },
|
||||
{ name: "detail", selector: { boolean: {} } },
|
||||
{ name: "theme", selector: { theme: {} } },
|
||||
{
|
||||
name: "hours_to_show",
|
||||
selector: { number: { min: 1, mode: "box" } },
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
);
|
||||
get _entity(): string {
|
||||
return this._config!.entity || "";
|
||||
}
|
||||
|
||||
get _name(): string {
|
||||
return this._config!.name || "";
|
||||
}
|
||||
|
||||
get _icon(): string {
|
||||
return this._config!.icon || "";
|
||||
}
|
||||
|
||||
get _graph(): string {
|
||||
return this._config!.graph || "none";
|
||||
}
|
||||
|
||||
get _unit(): string {
|
||||
return this._config!.unit || "";
|
||||
}
|
||||
|
||||
get _detail(): number {
|
||||
return this._config!.detail ?? 1;
|
||||
}
|
||||
|
||||
get _theme(): string {
|
||||
return this._config!.theme || "";
|
||||
}
|
||||
|
||||
get _hours_to_show(): number | string {
|
||||
return this._config!.hours_to_show || "24";
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass || !this._config) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
const entityState = this.hass.states[this._config.entity];
|
||||
const graphs = ["line", "none"];
|
||||
|
||||
const schema = this._schema(
|
||||
this._config.entity,
|
||||
this._config.icon,
|
||||
entityState
|
||||
);
|
||||
|
||||
const data = {
|
||||
hours_to_show: 24,
|
||||
graph: "none",
|
||||
...this._config,
|
||||
detail: this._config!.detail === 2,
|
||||
};
|
||||
const entityState = this.hass.states[this._entity];
|
||||
|
||||
return html`
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${data}
|
||||
.schema=${schema}
|
||||
.computeLabel=${this._computeLabelCallback}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-form>
|
||||
<div class="card-config">
|
||||
<ha-entity-picker
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.entity"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.required"
|
||||
)})"
|
||||
.hass=${this.hass}
|
||||
.value=${this._entity}
|
||||
.configValue=${"entity"}
|
||||
.includeDomains=${includeDomains}
|
||||
@change=${this._valueChanged}
|
||||
allow-custom-entity
|
||||
></ha-entity-picker>
|
||||
<paper-input
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.name"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.optional"
|
||||
)})"
|
||||
.value=${this._name}
|
||||
.configValue=${"name"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></paper-input>
|
||||
<div class="side-by-side">
|
||||
<ha-icon-picker
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.icon"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.optional"
|
||||
)})"
|
||||
.value=${this._icon}
|
||||
.placeholder=${this._icon || entityState?.attributes.icon}
|
||||
.fallbackPath=${!this._icon &&
|
||||
!entityState?.attributes.icon &&
|
||||
entityState
|
||||
? domainIcon(computeDomain(entityState.entity_id), entityState)
|
||||
: undefined}
|
||||
.configValue=${"icon"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-icon-picker>
|
||||
<ha-select
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.sensor.graph_type"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.optional"
|
||||
)})"
|
||||
.configValue=${"graph"}
|
||||
@selected=${this._valueChanged}
|
||||
@closed=${stopPropagation}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
.value=${this._graph}
|
||||
>
|
||||
${graphs.map(
|
||||
(graph) =>
|
||||
html`<mwc-list-item .value=${graph}>${graph}</mwc-list-item>`
|
||||
)}
|
||||
</ha-select>
|
||||
</div>
|
||||
<div class="side-by-side">
|
||||
<paper-input
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.unit"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.optional"
|
||||
)})"
|
||||
.value=${this._unit}
|
||||
.configValue=${"unit"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></paper-input>
|
||||
<ha-formfield
|
||||
label=${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.sensor.show_more_detail"
|
||||
)}
|
||||
>
|
||||
<ha-switch
|
||||
.checked=${this._detail === 2}
|
||||
.configValue=${"detail"}
|
||||
@change=${this._change}
|
||||
></ha-switch>
|
||||
</ha-formfield>
|
||||
</div>
|
||||
<div class="side-by-side">
|
||||
<hui-theme-select-editor
|
||||
.hass=${this.hass}
|
||||
.value=${this._theme}
|
||||
.configValue=${"theme"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></hui-theme-select-editor>
|
||||
<paper-input
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.hours_to_show"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.optional"
|
||||
)})"
|
||||
type="number"
|
||||
.value=${this._hours_to_show}
|
||||
min="1"
|
||||
.configValue=${"hours_to_show"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></paper-input>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent): void {
|
||||
const config = ev.detail.value;
|
||||
config.detail = config.detail ? 2 : 1;
|
||||
fireEvent(this, "config-changed", { config });
|
||||
private _change(ev: Event) {
|
||||
if (!this._config || !this.hass) {
|
||||
return;
|
||||
}
|
||||
|
||||
const value = (ev.target! as EditorTarget).checked ? 2 : 1;
|
||||
|
||||
if (this._detail === value) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._config = {
|
||||
...this._config,
|
||||
detail: value,
|
||||
};
|
||||
|
||||
fireEvent(this, "config-changed", { config: this._config });
|
||||
}
|
||||
|
||||
private _computeLabelCallback = (schema: HaFormSchema) => {
|
||||
if (schema.name === "entity") {
|
||||
return this.hass!.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.entity"
|
||||
);
|
||||
private _valueChanged(ev: EntitiesEditorEvent): void {
|
||||
if (!this._config || !this.hass) {
|
||||
return;
|
||||
}
|
||||
const target = ev.target! as EditorTarget;
|
||||
|
||||
if (schema.name === "detail") {
|
||||
return this.hass!.localize(
|
||||
"ui.panel.lovelace.editor.card.sensor.show_more_detail"
|
||||
);
|
||||
if (this[`_${target.configValue}`] === target.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.generic.${schema.name}`
|
||||
) ||
|
||||
this.hass!.localize(`ui.panel.lovelace.editor.card.sensor.${schema.name}`)
|
||||
);
|
||||
};
|
||||
if (target.configValue) {
|
||||
if (
|
||||
target.value === "" ||
|
||||
(target.type === "number" && isNaN(Number(target.value)))
|
||||
) {
|
||||
this._config = { ...this._config };
|
||||
delete this._config[target.configValue!];
|
||||
} else {
|
||||
let value: any = target.value;
|
||||
if (target.type === "number") {
|
||||
value = Number(value);
|
||||
}
|
||||
this._config = { ...this._config, [target.configValue!]: value };
|
||||
}
|
||||
}
|
||||
fireEvent(this, "config-changed", { config: this._config });
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return configElementStyle;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import "../../../../components/ha-textfield";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { assert, assign, object, optional, string } from "superstruct";
|
||||
@@ -57,7 +57,7 @@ export class HuiShoppingListEditor
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
<ha-textfield
|
||||
<paper-input
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.title"
|
||||
)} (${this.hass.localize(
|
||||
@@ -65,8 +65,8 @@ export class HuiShoppingListEditor
|
||||
)})"
|
||||
.value=${this._title}
|
||||
.configValue=${"title"}
|
||||
@input=${this._valueChanged}
|
||||
></ha-textfield>
|
||||
@value-changed=${this._valueChanged}
|
||||
></paper-input>
|
||||
<hui-theme-select-editor
|
||||
.hass=${this.hass}
|
||||
.value=${this._theme}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import "../../../../components/ha-form/ha-form";
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import {
|
||||
array,
|
||||
assert,
|
||||
@@ -14,15 +14,22 @@ import {
|
||||
union,
|
||||
} from "superstruct";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import type { LocalizeFunc } from "../../../../common/translations/localize";
|
||||
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
||||
import "../../../../components/entity/ha-statistics-picker";
|
||||
import type { HaFormSchema } from "../../../../components/ha-form/types";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import type { StatisticsGraphCardConfig } from "../../cards/types";
|
||||
import "../../../../components/ha-checkbox";
|
||||
import "../../../../components/ha-formfield";
|
||||
import "../../../../components/ha-radio";
|
||||
import type { HaRadio } from "../../../../components/ha-radio";
|
||||
import "../../../../components/ha-select";
|
||||
import { StatisticType } from "../../../../data/history";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { StatisticsGraphCardConfig } from "../../cards/types";
|
||||
import { processConfigEntities } from "../../common/process-config-entities";
|
||||
import type { LovelaceCardEditor } from "../../types";
|
||||
import { LovelaceCardEditor } from "../../types";
|
||||
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
||||
import { entitiesConfigStruct } from "../structs/entities-struct";
|
||||
import { EditorTarget } from "../types";
|
||||
import { configElementStyle } from "./config-elements-style";
|
||||
|
||||
const statTypeStruct = union([
|
||||
literal("sum"),
|
||||
@@ -71,82 +78,133 @@ export class HuiStatisticsGraphCardEditor
|
||||
: [];
|
||||
}
|
||||
|
||||
private _schema = memoizeOne((localize: LocalizeFunc) => [
|
||||
{ name: "title", selector: { text: {} } },
|
||||
{
|
||||
name: "",
|
||||
type: "grid",
|
||||
schema: [
|
||||
{
|
||||
name: "period",
|
||||
required: true,
|
||||
selector: {
|
||||
select: {
|
||||
options: periods.map((period) => ({
|
||||
value: period,
|
||||
label: localize(
|
||||
`ui.panel.lovelace.editor.card.statistics-graph.periods.${period}`
|
||||
),
|
||||
})),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "days_to_show",
|
||||
required: true,
|
||||
selector: { number: { min: 1, mode: "box" } },
|
||||
},
|
||||
{
|
||||
name: "stat_types",
|
||||
required: true,
|
||||
type: "multi_select",
|
||||
options: [
|
||||
["mean", "Mean"],
|
||||
["min", "Min"],
|
||||
["max", "Max"],
|
||||
["sum", "Sum"],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "chart_type",
|
||||
required: true,
|
||||
type: "select",
|
||||
options: [
|
||||
["line", "Line"],
|
||||
["bar", "Bar"],
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
get _title(): string {
|
||||
return this._config!.title || "";
|
||||
}
|
||||
|
||||
get _days_to_show(): number {
|
||||
return this._config!.days_to_show || 30;
|
||||
}
|
||||
|
||||
get _period(): string {
|
||||
return this._config!.period || "hour";
|
||||
}
|
||||
|
||||
get _chart_type(): StatisticsGraphCardConfig["chart_type"] {
|
||||
return this._config!.chart_type || "line";
|
||||
}
|
||||
|
||||
get _stat_types(): StatisticType[] {
|
||||
return this._config!.stat_types
|
||||
? Array.isArray(this._config!.stat_types)
|
||||
? this._config!.stat_types
|
||||
: [this._config!.stat_types]
|
||||
: ["mean", "min", "max", "sum"];
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass || !this._config) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
const schema = this._schema(this.hass.localize);
|
||||
const stat_types = this._config!.stat_types
|
||||
? Array.isArray(this._config!.stat_types)
|
||||
? this._config!.stat_types
|
||||
: [this._config!.stat_types]
|
||||
: ["mean", "min", "max", "sum"];
|
||||
const data = {
|
||||
chart_type: "line",
|
||||
period: "hour",
|
||||
days_to_show: 30,
|
||||
...this._config,
|
||||
stat_types,
|
||||
};
|
||||
|
||||
return html`
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${data}
|
||||
.schema=${schema}
|
||||
.computeLabel=${this._computeLabelCallback}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-form>
|
||||
<div class="card-config">
|
||||
<paper-input
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.title"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.optional"
|
||||
)})"
|
||||
.value=${this._title}
|
||||
.configValue=${"title"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></paper-input>
|
||||
<div class="side-by-side">
|
||||
<ha-select
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.statistics-graph.period"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.optional"
|
||||
)})"
|
||||
.configValue=${"period"}
|
||||
@selected=${this._periodSelected}
|
||||
@closed=${stopPropagation}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
.value=${this._period}
|
||||
>
|
||||
${periods.map(
|
||||
(period) =>
|
||||
html`<mwc-list-item .value=${period}>
|
||||
${this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.statistics-graph.periods.${period}`
|
||||
)}
|
||||
</mwc-list-item>`
|
||||
)}
|
||||
</ha-select>
|
||||
<paper-input
|
||||
type="number"
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.days_to_show"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.optional"
|
||||
)})"
|
||||
.value=${this._days_to_show}
|
||||
min="1"
|
||||
.configValue=${"days_to_show"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></paper-input>
|
||||
</div>
|
||||
<p>Show stat types:</p>
|
||||
<div class="side-by-side">
|
||||
<ha-formfield label="Sum">
|
||||
<ha-checkbox
|
||||
.checked=${this._stat_types.includes("sum")}
|
||||
name="sum"
|
||||
@change=${this._statTypesChanged}
|
||||
></ha-checkbox>
|
||||
</ha-formfield>
|
||||
<ha-formfield label="Mean">
|
||||
<ha-checkbox
|
||||
.checked=${this._stat_types.includes("mean")}
|
||||
name="mean"
|
||||
@change=${this._statTypesChanged}
|
||||
></ha-checkbox>
|
||||
</ha-formfield>
|
||||
<ha-formfield label="Min">
|
||||
<ha-checkbox
|
||||
.checked=${this._stat_types.includes("min")}
|
||||
name="min"
|
||||
@change=${this._statTypesChanged}
|
||||
></ha-checkbox>
|
||||
</ha-formfield>
|
||||
<ha-formfield label="Max">
|
||||
<ha-checkbox
|
||||
.checked=${this._stat_types.includes("max")}
|
||||
name="max"
|
||||
@change=${this._statTypesChanged}
|
||||
></ha-checkbox>
|
||||
</ha-formfield>
|
||||
</div>
|
||||
<div class="side-by-side">
|
||||
<p>Chart type:</p>
|
||||
<ha-formfield label="Line">
|
||||
<ha-radio
|
||||
.checked=${this._chart_type === "line"}
|
||||
value="line"
|
||||
name="chart_type"
|
||||
@change=${this._chartTypeChanged}
|
||||
></ha-radio>
|
||||
</ha-formfield>
|
||||
<ha-formfield label="Bar">
|
||||
<ha-radio
|
||||
.checked=${this._chart_type === "bar"}
|
||||
value="bar"
|
||||
name="chart_type"
|
||||
@change=${this._chartTypeChanged}
|
||||
></ha-radio>
|
||||
</ha-formfield>
|
||||
</div>
|
||||
<ha-statistics-picker
|
||||
.hass=${this.hass}
|
||||
.pickStatisticLabel=${`Add a statistic`}
|
||||
@@ -159,23 +217,82 @@ export class HuiStatisticsGraphCardEditor
|
||||
`;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent): void {
|
||||
fireEvent(this, "config-changed", { config: ev.detail.value });
|
||||
private _chartTypeChanged(ev: CustomEvent) {
|
||||
const input = ev.currentTarget as HaRadio;
|
||||
fireEvent(this, "config-changed", {
|
||||
config: { ...this._config!, chart_type: input.value },
|
||||
});
|
||||
}
|
||||
|
||||
private _computeLabelCallback = (schema: HaFormSchema) =>
|
||||
this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.generic.${schema.name}`
|
||||
) ||
|
||||
this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.statistics-graph.${schema.name}`
|
||||
);
|
||||
|
||||
static styles: CSSResultGroup = css`
|
||||
ha-statistics-picker {
|
||||
width: 100%;
|
||||
private _statTypesChanged(ev) {
|
||||
const name = ev.currentTarget.name;
|
||||
const checked = ev.currentTarget.checked;
|
||||
if (checked) {
|
||||
fireEvent(this, "config-changed", {
|
||||
config: { ...this._config!, stat_types: [...this._stat_types, name] },
|
||||
});
|
||||
return;
|
||||
}
|
||||
`;
|
||||
const statTypes = [...this._stat_types];
|
||||
fireEvent(this, "config-changed", {
|
||||
config: {
|
||||
...this._config!,
|
||||
stat_types: statTypes.filter((stat) => stat !== name),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private _periodSelected(ev) {
|
||||
const newPeriod = ev.target.value
|
||||
.period as StatisticsGraphCardConfig["period"];
|
||||
if (newPeriod === this._period) {
|
||||
return;
|
||||
}
|
||||
fireEvent(this, "config-changed", {
|
||||
config: { ...this._config!, period: newPeriod },
|
||||
});
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent): void {
|
||||
if (!this._config || !this.hass) {
|
||||
return;
|
||||
}
|
||||
|
||||
const target = ev.target! as EditorTarget;
|
||||
|
||||
const newValue = ev.detail?.value || target.value;
|
||||
|
||||
if (this[`_${target.configValue}`] === newValue) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (newValue === "") {
|
||||
this._config = { ...this._config };
|
||||
delete this._config[target.configValue!];
|
||||
} else {
|
||||
let value: any = newValue;
|
||||
if (target.type === "number") {
|
||||
value = Number(value);
|
||||
}
|
||||
this._config = {
|
||||
...this._config,
|
||||
[target.configValue!]: value,
|
||||
};
|
||||
}
|
||||
|
||||
fireEvent(this, "config-changed", { config: this._config });
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
configElementStyle,
|
||||
css`
|
||||
ha-statistics-picker {
|
||||
width: 100%;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
@@ -66,9 +66,11 @@ export class HuiThermostatCardEditor
|
||||
|
||||
private _computeLabelCallback = (schema: HaFormSchema) => {
|
||||
if (schema.name === "entity") {
|
||||
return this.hass!.localize(
|
||||
return `${this.hass!.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.entity"
|
||||
);
|
||||
)} (${this.hass!.localize(
|
||||
"ui.panel.lovelace.editor.card.config.required"
|
||||
)})`;
|
||||
}
|
||||
|
||||
return this.hass!.localize(
|
||||
|
@@ -1,18 +1,22 @@
|
||||
import "../../../../components/ha-form/ha-form";
|
||||
import { html, LitElement, TemplateResult } from "lit";
|
||||
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { assert, boolean, object, optional, string, assign } from "superstruct";
|
||||
import { memoize } from "@fullcalendar/common";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import type { WeatherForecastCardConfig } from "../../cards/types";
|
||||
import type { LovelaceCardEditor } from "../../types";
|
||||
import { computeRTLDirection } from "../../../../common/util/compute_rtl";
|
||||
import "../../../../components/entity/ha-entity-attribute-picker";
|
||||
import "../../../../components/entity/ha-entity-picker";
|
||||
import "../../../../components/ha-formfield";
|
||||
import "../../../../components/ha-radio";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { WeatherForecastCardConfig } from "../../cards/types";
|
||||
import "../../components/hui-theme-select-editor";
|
||||
import { LovelaceCardEditor } from "../../types";
|
||||
import { actionConfigStruct } from "../structs/action-struct";
|
||||
import { EditorTarget, EntitiesEditorEvent } from "../types";
|
||||
import { configElementStyle } from "./config-elements-style";
|
||||
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
||||
import { UNAVAILABLE } from "../../../../data/entity";
|
||||
import type { WeatherEntity } from "../../../../data/weather";
|
||||
import type { LocalizeFunc } from "../../../../common/translations/localize";
|
||||
import type { HaFormSchema } from "../../../../components/ha-form/types";
|
||||
import { WeatherEntity } from "../../../../data/weather";
|
||||
|
||||
const cardConfigStruct = assign(
|
||||
baseLovelaceCardConfig,
|
||||
@@ -29,6 +33,8 @@ const cardConfigStruct = assign(
|
||||
})
|
||||
);
|
||||
|
||||
const includeDomains = ["weather"];
|
||||
|
||||
@customElement("hui-weather-forecast-card-editor")
|
||||
export class HuiWeatherForecastCardEditor
|
||||
extends LitElement
|
||||
@@ -55,6 +61,30 @@ export class HuiWeatherForecastCardEditor
|
||||
}
|
||||
}
|
||||
|
||||
get _entity(): string {
|
||||
return this._config!.entity || "";
|
||||
}
|
||||
|
||||
get _name(): string {
|
||||
return this._config!.name || "";
|
||||
}
|
||||
|
||||
get _theme(): string {
|
||||
return this._config!.theme || "";
|
||||
}
|
||||
|
||||
get _show_current(): boolean {
|
||||
return this._config!.show_current ?? true;
|
||||
}
|
||||
|
||||
get _show_forecast(): boolean {
|
||||
return this._config!.show_forecast ?? this._has_forecast === true;
|
||||
}
|
||||
|
||||
get _secondary_info_attribute(): string {
|
||||
return this._config!.secondary_info_attribute || "";
|
||||
}
|
||||
|
||||
get _has_forecast(): boolean | undefined {
|
||||
if (this.hass && this._config) {
|
||||
const stateObj = this.hass.states[this._config.entity] as WeatherEntity;
|
||||
@@ -65,134 +95,146 @@ export class HuiWeatherForecastCardEditor
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private _schema = memoize(
|
||||
(
|
||||
entity: string,
|
||||
localize: LocalizeFunc,
|
||||
hasForecast?: boolean
|
||||
): HaFormSchema[] => {
|
||||
const schema: HaFormSchema[] = [
|
||||
{
|
||||
name: "entity",
|
||||
required: true,
|
||||
selector: { entity: { domain: "weather" } },
|
||||
},
|
||||
{ name: "name", selector: { text: {} } },
|
||||
{
|
||||
name: "",
|
||||
type: "grid",
|
||||
schema: [
|
||||
{
|
||||
name: "secondary_info_attribute",
|
||||
selector: { attribute: { entity_id: entity } },
|
||||
},
|
||||
{ name: "theme", selector: { theme: {} } },
|
||||
],
|
||||
},
|
||||
];
|
||||
if (hasForecast) {
|
||||
schema.push({
|
||||
name: "forecast",
|
||||
selector: {
|
||||
select: {
|
||||
options: [
|
||||
{
|
||||
value: "show_both",
|
||||
label: localize(
|
||||
"ui.panel.lovelace.editor.card.weather-forecast.show_both"
|
||||
),
|
||||
},
|
||||
{
|
||||
value: "show_current",
|
||||
label: localize(
|
||||
"ui.panel.lovelace.editor.card.weather-forecast.show_only_current"
|
||||
),
|
||||
},
|
||||
{
|
||||
value: "show_forecast",
|
||||
label: localize(
|
||||
"ui.panel.lovelace.editor.card.weather-forecast.show_only_forecast"
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
return schema;
|
||||
}
|
||||
);
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass || !this._config) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
const schema = this._schema(
|
||||
this._config.entity,
|
||||
this.hass.localize,
|
||||
this._has_forecast
|
||||
);
|
||||
|
||||
const data: WeatherForecastCardConfig = {
|
||||
show_current: true,
|
||||
show_forecast: this._has_forecast,
|
||||
...this._config,
|
||||
};
|
||||
|
||||
data.forecast =
|
||||
data.show_current && data.show_forecast
|
||||
? "show_both"
|
||||
: data.show_current
|
||||
? "show_current"
|
||||
: "show_forecast";
|
||||
|
||||
return html`
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${data}
|
||||
.schema=${schema}
|
||||
.computeLabel=${this._computeLabelCallback}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-form>
|
||||
<div class="card-config">
|
||||
<ha-entity-picker
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.entity"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.required"
|
||||
)})"
|
||||
.hass=${this.hass}
|
||||
.value=${this._entity}
|
||||
.configValue=${"entity"}
|
||||
.includeDomains=${includeDomains}
|
||||
@change=${this._valueChanged}
|
||||
allow-custom-entity
|
||||
></ha-entity-picker>
|
||||
<div class="side-by-side">
|
||||
<paper-input
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.name"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.optional"
|
||||
)})"
|
||||
.value=${this._name}
|
||||
.configValue=${"name"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></paper-input>
|
||||
<hui-theme-select-editor
|
||||
.hass=${this.hass}
|
||||
.value=${this._theme}
|
||||
.configValue=${"theme"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></hui-theme-select-editor>
|
||||
<ha-entity-attribute-picker
|
||||
.hass=${this.hass}
|
||||
.entityId=${this._entity}
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.secondary_info_attribute"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.optional"
|
||||
)})"
|
||||
.value=${this._secondary_info_attribute}
|
||||
.configValue=${"secondary_info_attribute"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-entity-attribute-picker>
|
||||
</div>
|
||||
<div class="side-by-side">
|
||||
<ha-formfield
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.weather-forecast.show_both"
|
||||
)}
|
||||
.dir=${computeRTLDirection(this.hass)}
|
||||
>
|
||||
<ha-radio
|
||||
.disabled=${this._has_forecast === false}
|
||||
.checked=${this._has_forecast === true &&
|
||||
this._show_current &&
|
||||
this._show_forecast}
|
||||
.configValue=${"show_both"}
|
||||
@change=${this._valueChanged}
|
||||
></ha-radio
|
||||
></ha-formfield>
|
||||
<ha-formfield
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.weather-forecast.show_only_current"
|
||||
)}
|
||||
.dir=${computeRTLDirection(this.hass)}
|
||||
>
|
||||
<ha-radio
|
||||
.disabled=${this._has_forecast === false}
|
||||
.checked=${this._has_forecast === false ||
|
||||
(this._show_current && !this._show_forecast)}
|
||||
.configValue=${"show_current"}
|
||||
@change=${this._valueChanged}
|
||||
></ha-radio
|
||||
></ha-formfield>
|
||||
<ha-formfield
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.weather-forecast.show_only_forecast"
|
||||
)}
|
||||
.dir=${computeRTLDirection(this.hass)}
|
||||
>
|
||||
<ha-radio
|
||||
.disabled=${this._has_forecast === false}
|
||||
.checked=${this._has_forecast === true &&
|
||||
!this._show_current &&
|
||||
this._show_forecast}
|
||||
.configValue=${"show_forecast"}
|
||||
@change=${this._valueChanged}
|
||||
></ha-radio
|
||||
></ha-formfield>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent): void {
|
||||
const config = ev.detail.value;
|
||||
if (config.forecast === "show_both") {
|
||||
config.show_current = true;
|
||||
config.show_forecast = true;
|
||||
} else if (config.forecast === "show_current") {
|
||||
config.show_current = true;
|
||||
config.show_forecast = false;
|
||||
} else {
|
||||
config.show_current = false;
|
||||
config.show_forecast = true;
|
||||
private _valueChanged(ev: EntitiesEditorEvent): void {
|
||||
if (!this._config || !this.hass) {
|
||||
return;
|
||||
}
|
||||
const target = ev.currentTarget! as EditorTarget;
|
||||
|
||||
delete config.forecast;
|
||||
fireEvent(this, "config-changed", { config });
|
||||
if (this[`_${target.configValue}`] === target.value) {
|
||||
return;
|
||||
}
|
||||
if (target.configValue) {
|
||||
if (target.configValue.startsWith("show_")) {
|
||||
this._config = { ...this._config };
|
||||
if (target.configValue === "show_both") {
|
||||
/* delete since true is default */
|
||||
delete this._config.show_current;
|
||||
delete this._config.show_forecast;
|
||||
} else if (target.configValue === "show_current") {
|
||||
delete this._config.show_current;
|
||||
this._config.show_forecast = false;
|
||||
} else if (target.configValue === "show_forecast") {
|
||||
delete this._config.show_forecast;
|
||||
this._config.show_current = false;
|
||||
}
|
||||
} else if (target.value === "") {
|
||||
this._config = { ...this._config };
|
||||
delete this._config[target.configValue!];
|
||||
} else {
|
||||
this._config = {
|
||||
...this._config,
|
||||
[target.configValue!]:
|
||||
target.checked !== undefined ? target.checked : target.value,
|
||||
};
|
||||
}
|
||||
}
|
||||
fireEvent(this, "config-changed", { config: this._config });
|
||||
}
|
||||
|
||||
private _computeLabelCallback = (schema: HaFormSchema) => {
|
||||
if (schema.name === "entity") {
|
||||
return `${this.hass!.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.entity"
|
||||
)} (${this.hass!.localize(
|
||||
"ui.panel.lovelace.editor.card.config.required"
|
||||
)})`;
|
||||
}
|
||||
|
||||
return (
|
||||
this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.generic.${schema.name}`
|
||||
) ||
|
||||
this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.weather_forecast.${schema.name}`
|
||||
)
|
||||
);
|
||||
};
|
||||
static get styles(): CSSResultGroup {
|
||||
return configElementStyle;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
@@ -16,7 +16,7 @@ export class HuiDialogEditLovelace extends LitElement {
|
||||
|
||||
@state() private _lovelace?: Lovelace;
|
||||
|
||||
@state() private _config?: LovelaceConfig;
|
||||
private _config?: LovelaceConfig;
|
||||
|
||||
private _saving = false;
|
||||
|
||||
|
@@ -1,9 +1,10 @@
|
||||
import "../../../../components/ha-textfield";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { LovelaceConfig } from "../../../../data/lovelace";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { configElementStyle } from "../config-elements/config-elements-style";
|
||||
import { EditorTarget } from "../types";
|
||||
|
||||
declare global {
|
||||
@@ -29,14 +30,16 @@ export class HuiLovelaceEditor extends LitElement {
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<ha-textfield
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.edit_lovelace.title"
|
||||
)}
|
||||
.value=${this._title}
|
||||
.configValue=${"title"}
|
||||
@change=${this._valueChanged}
|
||||
></ha-textfield>
|
||||
<div class="card-config">
|
||||
<paper-input
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.edit_lovelace.title"
|
||||
)}
|
||||
.value=${this._title}
|
||||
.configValue=${"title"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></paper-input>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -63,11 +66,9 @@ export class HuiLovelaceEditor extends LitElement {
|
||||
fireEvent(this, "lovelace-config-changed", { config: newConfig });
|
||||
}
|
||||
|
||||
static styles: CSSResultGroup = css`
|
||||
ha-textfield {
|
||||
display: block;
|
||||
}
|
||||
`;
|
||||
static get styles(): CSSResultGroup {
|
||||
return configElementStyle;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
@@ -1,17 +1,24 @@
|
||||
import "../../../../components/ha-form/ha-form";
|
||||
import { html, LitElement, TemplateResult } from "lit";
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
||||
import { slugify } from "../../../../common/string/slugify";
|
||||
import type { HaFormSchema } from "../../../../components/ha-form/types";
|
||||
import type { LovelaceViewConfig } from "../../../../data/lovelace";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import "../../../../components/ha-formfield";
|
||||
import "../../../../components/ha-icon-picker";
|
||||
import "../../../../components/ha-select";
|
||||
import "../../../../components/ha-switch";
|
||||
import { LovelaceViewConfig } from "../../../../data/lovelace";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import "../../components/hui-theme-select-editor";
|
||||
import {
|
||||
DEFAULT_VIEW_LAYOUT,
|
||||
PANEL_VIEW_LAYOUT,
|
||||
SIDEBAR_VIEW_LAYOUT,
|
||||
} from "../../views/const";
|
||||
import { configElementStyle } from "../config-elements/config-elements-style";
|
||||
import { EditorTarget } from "../types";
|
||||
|
||||
declare global {
|
||||
interface HASSDomEvents {
|
||||
@@ -31,35 +38,32 @@ export class HuiViewEditor extends LitElement {
|
||||
|
||||
private _suggestedPath = false;
|
||||
|
||||
private _schema = memoizeOne((localize): HaFormSchema[] => [
|
||||
{ name: "title", selector: { text: {} } },
|
||||
{
|
||||
name: "icon",
|
||||
selector: {
|
||||
icon: {},
|
||||
},
|
||||
},
|
||||
{ name: "path", selector: { text: {} } },
|
||||
{ name: "theme", selector: { theme: {} } },
|
||||
{
|
||||
name: "type",
|
||||
selector: {
|
||||
select: {
|
||||
options: [
|
||||
DEFAULT_VIEW_LAYOUT,
|
||||
SIDEBAR_VIEW_LAYOUT,
|
||||
PANEL_VIEW_LAYOUT,
|
||||
].map((type) => ({
|
||||
value: type,
|
||||
label: localize(`ui.panel.lovelace.editor.edit_view.types.${type}`),
|
||||
})),
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
get _path(): string {
|
||||
if (!this._config) {
|
||||
return "";
|
||||
}
|
||||
return this._config.path || "";
|
||||
}
|
||||
|
||||
set config(config: LovelaceViewConfig) {
|
||||
this._config = config;
|
||||
get _title(): string {
|
||||
if (!this._config) {
|
||||
return "";
|
||||
}
|
||||
return this._config.title || "";
|
||||
}
|
||||
|
||||
get _icon(): string {
|
||||
if (!this._config) {
|
||||
return "";
|
||||
}
|
||||
return this._config.icon || "";
|
||||
}
|
||||
|
||||
get _theme(): string {
|
||||
if (!this._config) {
|
||||
return "";
|
||||
}
|
||||
return this._config.theme || "Backend-selected";
|
||||
}
|
||||
|
||||
get _type(): string {
|
||||
@@ -71,59 +75,142 @@ export class HuiViewEditor extends LitElement {
|
||||
: this._config.type || DEFAULT_VIEW_LAYOUT;
|
||||
}
|
||||
|
||||
set config(config: LovelaceViewConfig) {
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
const schema = this._schema(this.hass.localize);
|
||||
const data = {
|
||||
theme: "Backend-selected",
|
||||
...this._config,
|
||||
type: this._type,
|
||||
};
|
||||
|
||||
return html`
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${data}
|
||||
.schema=${schema}
|
||||
.computeLabel=${this._computeLabelCallback}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-form>
|
||||
<div class="card-config">
|
||||
<paper-input
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.title"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.optional"
|
||||
)})"
|
||||
.value=${this._title}
|
||||
.configValue=${"title"}
|
||||
@value-changed=${this._valueChanged}
|
||||
@blur=${this._handleTitleBlur}
|
||||
></paper-input>
|
||||
<ha-icon-picker
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.icon"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.optional"
|
||||
)})"
|
||||
.value=${this._icon}
|
||||
.placeholder=${this._icon}
|
||||
.configValue=${"icon"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-icon-picker>
|
||||
<paper-input
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.url"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.optional"
|
||||
)})"
|
||||
.value=${this._path}
|
||||
.configValue=${"path"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></paper-input>
|
||||
<hui-theme-select-editor
|
||||
.hass=${this.hass}
|
||||
.value=${this._theme}
|
||||
.configValue=${"theme"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></hui-theme-select-editor>
|
||||
<ha-select
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.edit_view.type"
|
||||
)}
|
||||
.value=${this._type}
|
||||
@selected=${this._typeChanged}
|
||||
@closed=${stopPropagation}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
>
|
||||
${[DEFAULT_VIEW_LAYOUT, SIDEBAR_VIEW_LAYOUT, PANEL_VIEW_LAYOUT].map(
|
||||
(type) => html`<mwc-list-item .value=${type}>
|
||||
${this.hass.localize(
|
||||
`ui.panel.lovelace.editor.edit_view.types.${type}`
|
||||
)}
|
||||
</mwc-list-item>`
|
||||
)}
|
||||
</ha-select>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent): void {
|
||||
const config = ev.detail.value;
|
||||
private _valueChanged(ev: Event): void {
|
||||
const target = ev.currentTarget! as EditorTarget;
|
||||
|
||||
if (config.type === "masonry") {
|
||||
delete config.type;
|
||||
if (this[`_${target.configValue}`] === target.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
let newConfig;
|
||||
|
||||
if (target.configValue) {
|
||||
newConfig = {
|
||||
...this._config,
|
||||
[target.configValue!]:
|
||||
target.checked !== undefined ? target.checked : target.value,
|
||||
};
|
||||
}
|
||||
|
||||
fireEvent(this, "view-config-changed", { config: newConfig });
|
||||
}
|
||||
|
||||
private _typeChanged(ev): void {
|
||||
const selected = ev.target.value;
|
||||
if (selected === "") {
|
||||
return;
|
||||
}
|
||||
const newConfig = {
|
||||
...this._config,
|
||||
};
|
||||
delete newConfig.panel;
|
||||
if (selected === "masonry") {
|
||||
delete newConfig.type;
|
||||
} else {
|
||||
newConfig.type = selected;
|
||||
}
|
||||
fireEvent(this, "view-config-changed", { config: newConfig });
|
||||
}
|
||||
|
||||
private _handleTitleBlur(ev) {
|
||||
if (
|
||||
this.isNew &&
|
||||
!this._suggestedPath &&
|
||||
config.title &&
|
||||
(!this._config.path ||
|
||||
config.path === slugify(this._config.title || "", "-"))
|
||||
!this.isNew ||
|
||||
this._suggestedPath ||
|
||||
this._config.path ||
|
||||
!ev.currentTarget.value
|
||||
) {
|
||||
config.path = slugify(config.title, "-");
|
||||
return;
|
||||
}
|
||||
|
||||
const config = {
|
||||
...this._config,
|
||||
path: slugify(ev.currentTarget.value, "-"),
|
||||
};
|
||||
fireEvent(this, "view-config-changed", { config });
|
||||
}
|
||||
|
||||
private _computeLabelCallback = (schema: HaFormSchema) => {
|
||||
if (schema.name === "path") {
|
||||
return this.hass!.localize(`ui.panel.lovelace.editor.card.generic.url`);
|
||||
}
|
||||
return (
|
||||
this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.generic.${schema.name}`
|
||||
) || this.hass.localize("ui.panel.lovelace.editor.edit_view.type")
|
||||
);
|
||||
};
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
configElementStyle,
|
||||
css`
|
||||
.panel {
|
||||
color: var(--secondary-text-color);
|
||||
display: block;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
@@ -9,17 +9,13 @@ import {
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import "../../../components/ha-date-input";
|
||||
import { UNAVAILABLE_STATES, UNKNOWN } from "../../../data/entity";
|
||||
import {
|
||||
setInputDateTimeValue,
|
||||
stateToIsoDateString,
|
||||
} from "../../../data/input_datetime";
|
||||
import { setInputDateTimeValue } from "../../../data/input_datetime";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import { hasConfigOrEntityChanged } from "../common/has-changed";
|
||||
import "../components/hui-generic-entity-row";
|
||||
import { createEntityNotFoundWarning } from "../components/hui-warning";
|
||||
import type { EntityConfig, LovelaceRow } from "./types";
|
||||
import "../../../components/ha-time-input";
|
||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||
|
||||
@customElement("hui-input-datetime-entity-row")
|
||||
class HuiInputDatetimeEntityRow extends LitElement implements LovelaceRow {
|
||||
@@ -53,22 +49,14 @@ class HuiInputDatetimeEntityRow extends LitElement implements LovelaceRow {
|
||||
`;
|
||||
}
|
||||
|
||||
const name = this._config.name || computeStateName(stateObj);
|
||||
|
||||
return html`
|
||||
<hui-generic-entity-row
|
||||
.hass=${this.hass}
|
||||
.config=${this._config}
|
||||
.hideName=${stateObj.attributes.has_date &&
|
||||
stateObj.attributes.has_time}
|
||||
>
|
||||
<hui-generic-entity-row .hass=${this.hass} .config=${this._config}>
|
||||
${stateObj.attributes.has_date
|
||||
? html`
|
||||
<ha-date-input
|
||||
.label=${stateObj.attributes.has_time ? name : undefined}
|
||||
.locale=${this.hass.locale}
|
||||
.disabled=${UNAVAILABLE_STATES.includes(stateObj.state)}
|
||||
.value=${stateToIsoDateString(stateObj)}
|
||||
.value=${`${stateObj.attributes.year}-${stateObj.attributes.month}-${stateObj.attributes.day}`}
|
||||
@value-changed=${this._dateChanged}
|
||||
>
|
||||
</ha-date-input>
|
||||
|
@@ -1,5 +1,12 @@
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import { css, html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import {
|
||||
css,
|
||||
CSSResultGroup,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
TemplateResult,
|
||||
} from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { stopPropagation } from "../../../common/dom/stop_propagation";
|
||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||
@@ -80,15 +87,17 @@ class HuiInputSelectEntityRow extends LitElement implements LovelaceRow {
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
hui-generic-entity-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
ha-select {
|
||||
width: 100%;
|
||||
}
|
||||
`;
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
hui-generic-entity-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
ha-select {
|
||||
width: 100%;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
private _selectedChanged(ev): void {
|
||||
const stateObj = this.hass!.states[this._config!.entity];
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { css, html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { UNAVAILABLE, UNAVAILABLE_STATES } from "../../../data/entity";
|
||||
import { setValue } from "../../../data/input_text";
|
||||
@@ -8,7 +8,6 @@ import "../components/hui-generic-entity-row";
|
||||
import { createEntityNotFoundWarning } from "../components/hui-warning";
|
||||
import { EntityConfig, LovelaceRow } from "./types";
|
||||
import "../../../components/ha-textfield";
|
||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||
|
||||
@customElement("hui-input-text-entity-row")
|
||||
class HuiInputTextEntityRow extends LitElement implements LovelaceRow {
|
||||
@@ -43,13 +42,8 @@ class HuiInputTextEntityRow extends LitElement implements LovelaceRow {
|
||||
}
|
||||
|
||||
return html`
|
||||
<hui-generic-entity-row
|
||||
.hass=${this.hass}
|
||||
.config=${this._config}
|
||||
hideName
|
||||
>
|
||||
<hui-generic-entity-row .hass=${this.hass} .config=${this._config}>
|
||||
<ha-textfield
|
||||
.label=${this._config.name || computeStateName(stateObj)}
|
||||
.disabled=${stateObj.state === UNAVAILABLE}
|
||||
.value=${stateObj.state}
|
||||
.minlength=${stateObj.attributes.min}
|
||||
@@ -81,16 +75,6 @@ class HuiInputTextEntityRow extends LitElement implements LovelaceRow {
|
||||
|
||||
ev.target.blur();
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
hui-generic-entity-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
ha-textfield {
|
||||
width: 100%;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
@@ -23,18 +23,6 @@ class HuiTimerEntityRow extends LitElement {
|
||||
throw new Error("Invalid configuration");
|
||||
}
|
||||
this._config = config;
|
||||
|
||||
if (!this.hass) {
|
||||
return;
|
||||
}
|
||||
|
||||
const stateObj = this.hass!.states[this._config.entity];
|
||||
|
||||
if (stateObj) {
|
||||
this._startInterval(stateObj);
|
||||
} else {
|
||||
this._clearInterval();
|
||||
}
|
||||
}
|
||||
|
||||
public disconnectedCallback(): void {
|
||||
@@ -87,19 +75,18 @@ class HuiTimerEntityRow extends LitElement {
|
||||
protected updated(changedProps: PropertyValues) {
|
||||
super.updated(changedProps);
|
||||
|
||||
if (!this._config || !changedProps.has("hass")) {
|
||||
return;
|
||||
}
|
||||
const stateObj = this.hass!.states[this._config!.entity];
|
||||
const oldHass = changedProps.get("hass") as this["hass"];
|
||||
const oldStateObj = oldHass
|
||||
? oldHass.states[this._config!.entity]
|
||||
: undefined;
|
||||
if (changedProps.has("hass")) {
|
||||
const stateObj = this.hass!.states[this._config!.entity];
|
||||
const oldHass = changedProps.get("hass") as this["hass"];
|
||||
const oldStateObj = oldHass
|
||||
? oldHass.states[this._config!.entity]
|
||||
: undefined;
|
||||
|
||||
if (oldStateObj !== stateObj) {
|
||||
this._startInterval(stateObj);
|
||||
} else if (!stateObj) {
|
||||
this._clearInterval();
|
||||
if (oldStateObj !== stateObj) {
|
||||
this._startInterval(stateObj);
|
||||
} else if (!stateObj) {
|
||||
this._clearInterval();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -69,10 +69,7 @@ export class SideBarView extends LitElement implements LovelaceViewElement {
|
||||
this._createCards();
|
||||
}
|
||||
|
||||
if (
|
||||
!changedProperties.has("lovelace") &&
|
||||
!changedProperties.has("_config")
|
||||
) {
|
||||
if (!changedProperties.has("lovelace")) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@@ -9,8 +9,6 @@ import {
|
||||
import { ResolvedMediaSource } from "../../data/media_source";
|
||||
import { HomeAssistant } from "../../types";
|
||||
|
||||
export const ERR_UNSUPPORTED_MEDIA = "Unsupported Media";
|
||||
|
||||
export class BrowserMediaPlayer {
|
||||
private player: HTMLAudioElement;
|
||||
|
||||
@@ -27,10 +25,6 @@ export class BrowserMediaPlayer {
|
||||
private onChange: () => void
|
||||
) {
|
||||
const player = new Audio(this.resolved.url);
|
||||
if (player.canPlayType(resolved.mime_type) === "") {
|
||||
throw new Error(ERR_UNSUPPORTED_MEDIA);
|
||||
}
|
||||
player.autoplay = true;
|
||||
player.volume = volume;
|
||||
player.addEventListener("play", this._handleChange);
|
||||
player.addEventListener("playing", () => {
|
||||
@@ -39,7 +33,15 @@ export class BrowserMediaPlayer {
|
||||
});
|
||||
player.addEventListener("pause", this._handleChange);
|
||||
player.addEventListener("ended", this._handleChange);
|
||||
player.addEventListener("canplaythrough", this._handleChange);
|
||||
player.addEventListener("canplaythrough", () => {
|
||||
if (this._removed) {
|
||||
return;
|
||||
}
|
||||
if (this.buffering) {
|
||||
player.play();
|
||||
}
|
||||
this.onChange();
|
||||
});
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
|
@@ -49,13 +49,9 @@ import {
|
||||
SUPPORT_VOLUME_SET,
|
||||
} from "../../data/media-player";
|
||||
import { ResolvedMediaSource } from "../../data/media_source";
|
||||
import { showAlertDialog } from "../../dialogs/generic/show-dialog-box";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import "../lovelace/components/hui-marquee";
|
||||
import {
|
||||
BrowserMediaPlayer,
|
||||
ERR_UNSUPPORTED_MEDIA,
|
||||
} from "./browser-media-player";
|
||||
import { BrowserMediaPlayer } from "./browser-media-player";
|
||||
|
||||
declare global {
|
||||
interface HASSDomEvents {
|
||||
@@ -129,25 +125,13 @@ export class BarMediaPlayer extends LitElement {
|
||||
throw Error("Only browser supported");
|
||||
}
|
||||
this._tearDownBrowserPlayer();
|
||||
try {
|
||||
this._browserPlayer = new BrowserMediaPlayer(
|
||||
this.hass,
|
||||
item,
|
||||
resolved,
|
||||
this._browserPlayerVolume,
|
||||
() => this.requestUpdate("_browserPlayer")
|
||||
);
|
||||
} catch (err: any) {
|
||||
if (err.message === ERR_UNSUPPORTED_MEDIA) {
|
||||
showAlertDialog(this, {
|
||||
text: this.hass.localize(
|
||||
"ui.components.media-browser.media_not_supported"
|
||||
),
|
||||
});
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
this._browserPlayer = new BrowserMediaPlayer(
|
||||
this.hass,
|
||||
item,
|
||||
resolved,
|
||||
this._browserPlayerVolume,
|
||||
() => this.requestUpdate("_browserPlayer")
|
||||
);
|
||||
this._newMediaExpected = false;
|
||||
}
|
||||
|
||||
@@ -280,9 +264,7 @@ export class BarMediaPlayer extends LitElement {
|
||||
return html`
|
||||
<div class="choose-player ${isBrowser ? "browser" : ""}">
|
||||
${
|
||||
!this.narrow &&
|
||||
stateObj &&
|
||||
supportsFeature(stateObj, SUPPORT_VOLUME_SET)
|
||||
stateObj && supportsFeature(stateObj, SUPPORT_VOLUME_SET)
|
||||
? html`
|
||||
<ha-button-menu corner="BOTTOM_START" y="0" x="76">
|
||||
<ha-icon-button
|
||||
@@ -619,7 +601,6 @@ export class BarMediaPlayer extends LitElement {
|
||||
|
||||
img {
|
||||
max-height: 100px;
|
||||
max-width: 100px;
|
||||
}
|
||||
|
||||
.app img {
|
||||
@@ -632,8 +613,8 @@ export class BarMediaPlayer extends LitElement {
|
||||
}
|
||||
|
||||
:host([narrow]) {
|
||||
min-height: 56px;
|
||||
max-height: 56px;
|
||||
min-height: 80px;
|
||||
max-height: 80px;
|
||||
}
|
||||
|
||||
:host([narrow]) .controls-progress {
|
||||
@@ -641,19 +622,13 @@ export class BarMediaPlayer extends LitElement {
|
||||
min-width: 48px;
|
||||
}
|
||||
|
||||
:host([narrow]) .media-info {
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
:host([narrow]) .controls {
|
||||
display: flex;
|
||||
padding-bottom: 0;
|
||||
--mdc-icon-size: 30px;
|
||||
}
|
||||
|
||||
:host([narrow]) .choose-player {
|
||||
padding-left: 0;
|
||||
padding-right: 8px;
|
||||
min-width: 48px;
|
||||
flex: unset;
|
||||
justify-content: center;
|
||||
@@ -661,16 +636,16 @@ export class BarMediaPlayer extends LitElement {
|
||||
|
||||
:host([narrow]) .choose-player.browser {
|
||||
justify-content: flex-end;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:host([narrow]) img {
|
||||
max-height: 56px;
|
||||
max-width: 56px;
|
||||
max-height: 80px;
|
||||
}
|
||||
|
||||
:host([narrow]) .blank-image {
|
||||
height: 56px;
|
||||
width: 56px;
|
||||
height: 80px;
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
:host([narrow]) mwc-linear-progress {
|
||||
|
@@ -27,10 +27,7 @@ import {
|
||||
MediaPickedEvent,
|
||||
MediaPlayerItem,
|
||||
} from "../../data/media-player";
|
||||
import {
|
||||
ResolvedMediaSource,
|
||||
resolveMediaSource,
|
||||
} from "../../data/media_source";
|
||||
import { resolveMediaSource } from "../../data/media_source";
|
||||
import "../../layouts/ha-app-layout";
|
||||
import { haStyle } from "../../resources/styles";
|
||||
import type { HomeAssistant, Route } from "../../types";
|
||||
@@ -227,19 +224,11 @@ class PanelMediaBrowser extends LitElement {
|
||||
}
|
||||
|
||||
this._player.showResolvingNewMediaPicked();
|
||||
let resolvedUrl: ResolvedMediaSource;
|
||||
try {
|
||||
resolvedUrl = await resolveMediaSource(this.hass, item.media_content_id);
|
||||
} catch (err: any) {
|
||||
showAlertDialog(this, {
|
||||
title: this.hass.localize(
|
||||
"ui.components.media-browser.media_browsing_error"
|
||||
),
|
||||
text: err.message,
|
||||
});
|
||||
this._player.hideResolvingNewMediaPicked();
|
||||
return;
|
||||
}
|
||||
|
||||
const resolvedUrl = await resolveMediaSource(
|
||||
this.hass,
|
||||
item.media_content_id
|
||||
);
|
||||
|
||||
if (resolvedUrl.mime_type.startsWith("audio/")) {
|
||||
this._player.playItem(item, resolvedUrl);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user