mirror of
https://github.com/home-assistant/frontend.git
synced 2026-07-22 02:57:03 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 61693f9c59 | |||
| a25aa656a3 | |||
| 88f244b9df | |||
| ad3389fd4d |
@@ -1,19 +1,23 @@
|
||||
import { consume, type ContextType } from "@lit/context";
|
||||
import { initialState } from "@lit/task";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { AsyncValueTask } from "../../common/controllers/async-value-task";
|
||||
import { consumeEntityState } from "../../common/decorators/consume-context-entry";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import {
|
||||
configContext,
|
||||
connectionContext,
|
||||
entitiesContext,
|
||||
} from "../../data/context";
|
||||
import { entityIcon } from "../../data/icons";
|
||||
import type { IconSelector } from "../../data/selector";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import "../ha-icon-picker";
|
||||
import "../ha-state-icon";
|
||||
|
||||
@customElement("ha-selector-icon")
|
||||
export class HaIconSelector extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public selector!: IconSelector;
|
||||
|
||||
@property() public value?: string;
|
||||
@@ -30,10 +34,21 @@ export class HaIconSelector extends LitElement {
|
||||
icon_entity?: string;
|
||||
};
|
||||
|
||||
private get _stateObj(): HassEntity | undefined {
|
||||
const iconEntity = this.context?.icon_entity;
|
||||
return iconEntity ? this.hass.states[iconEntity] : undefined;
|
||||
}
|
||||
@state()
|
||||
@consumeEntityState({ entityIdPath: ["context", "icon_entity"] })
|
||||
private _stateObj?: HassEntity;
|
||||
|
||||
@state()
|
||||
@consume({ context: entitiesContext, subscribe: true })
|
||||
private _entities?: ContextType<typeof entitiesContext>;
|
||||
|
||||
@state()
|
||||
@consume({ context: configContext, subscribe: true })
|
||||
private _config?: ContextType<typeof configContext>;
|
||||
|
||||
@state()
|
||||
@consume({ context: connectionContext, subscribe: true })
|
||||
private _connection?: ContextType<typeof connectionContext>;
|
||||
|
||||
private _placeholderTask = new AsyncValueTask(this, {
|
||||
task: ([
|
||||
@@ -44,19 +59,31 @@ export class HaIconSelector extends LitElement {
|
||||
connection,
|
||||
stateObj,
|
||||
]) => {
|
||||
if (placeholder || attributeIcon || !stateObj) {
|
||||
if (
|
||||
placeholder ||
|
||||
attributeIcon ||
|
||||
!entities ||
|
||||
!config ||
|
||||
!connection ||
|
||||
!stateObj
|
||||
) {
|
||||
return initialState;
|
||||
}
|
||||
return entityIcon(entities, config, connection, stateObj);
|
||||
return entityIcon(
|
||||
entities,
|
||||
config.config,
|
||||
connection.connection,
|
||||
stateObj
|
||||
);
|
||||
},
|
||||
args: () => {
|
||||
const stateObj = this._stateObj;
|
||||
return [
|
||||
this.selector.icon?.placeholder,
|
||||
stateObj?.attributes.icon,
|
||||
this.hass.entities,
|
||||
this.hass.config,
|
||||
this.hass.connection,
|
||||
this._entities,
|
||||
this._config,
|
||||
this._connection,
|
||||
stateObj,
|
||||
] as const;
|
||||
},
|
||||
@@ -72,7 +99,6 @@ export class HaIconSelector extends LitElement {
|
||||
|
||||
return html`
|
||||
<ha-icon-picker
|
||||
.hass=${this.hass}
|
||||
.label=${this.label}
|
||||
.value=${this.value}
|
||||
.required=${this.required}
|
||||
|
||||
+1
-2
@@ -1,4 +1,3 @@
|
||||
import { memoize } from "@fullcalendar/core/internal";
|
||||
import { setHours, setMinutes } from "date-fns";
|
||||
import type { HassConfig } from "home-assistant-js-websocket";
|
||||
import memoizeOne from "memoize-one";
|
||||
@@ -411,7 +410,7 @@ export type BackupType = "automatic" | "manual" | "app_update";
|
||||
|
||||
const BACKUP_TYPE_ORDER: BackupType[] = ["automatic", "app_update", "manual"];
|
||||
|
||||
export const getBackupTypes = memoize((isHassio: boolean) =>
|
||||
export const getBackupTypes = memoizeOne((isHassio: boolean) =>
|
||||
isHassio
|
||||
? BACKUP_TYPE_ORDER
|
||||
: BACKUP_TYPE_ORDER.filter((type) => type !== "app_update")
|
||||
|
||||
@@ -179,6 +179,16 @@ export class EntityVoiceSettings extends SubscribeMixin(LitElement) {
|
||||
|
||||
const anyExposed = uiExposed || manExposedAlexa || manExposedGoogle;
|
||||
|
||||
const exposedToAlexa =
|
||||
showAssistants.includes("cloud.alexa") &&
|
||||
(alexaManual ? manExposedAlexa : this.exposed["cloud.alexa"]);
|
||||
const exposedToGoogle =
|
||||
showAssistants.includes("cloud.google_assistant") &&
|
||||
(googleManual
|
||||
? manExposedGoogle
|
||||
: this.exposed["cloud.google_assistant"]);
|
||||
const exposedToAssist = this.exposed.conversation;
|
||||
|
||||
return html`
|
||||
<ha-md-list-item>
|
||||
<h3 slot="headline">
|
||||
@@ -275,7 +285,24 @@ export class EntityVoiceSettings extends SubscribeMixin(LitElement) {
|
||||
</h3>
|
||||
|
||||
<p class="description">
|
||||
${this.hass.localize("ui.dialogs.voice-settings.aliases_description")}
|
||||
${[
|
||||
this.hass.localize("ui.dialogs.voice-settings.aliases_description"),
|
||||
exposedToAlexa &&
|
||||
this.hass.localize(
|
||||
"ui.dialogs.voice-settings.aliases_description_alexa"
|
||||
),
|
||||
exposedToGoogle &&
|
||||
this.hass.localize(
|
||||
"ui.dialogs.voice-settings.aliases_description_google"
|
||||
),
|
||||
exposedToAssist &&
|
||||
(exposedToAlexa || exposedToGoogle) &&
|
||||
this.hass.localize(
|
||||
"ui.dialogs.voice-settings.aliases_description_assist"
|
||||
),
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(" ")}
|
||||
</p>
|
||||
|
||||
${
|
||||
|
||||
@@ -137,7 +137,6 @@ export class HuiDialogEditSection
|
||||
case "tab-settings":
|
||||
content = html`
|
||||
<hui-section-settings-editor
|
||||
.hass=${this.hass}
|
||||
.config=${this._config}
|
||||
.viewConfig=${this._viewConfig}
|
||||
@value-changed=${this._configChanged}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { mdiPalette } from "@mdi/js";
|
||||
import { LitElement, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { consumeLocalize } from "../../../../common/decorators/consume-context-entry";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import type { LocalizeFunc } from "../../../../common/translations/localize";
|
||||
import "../../../../components/ha-form/ha-form";
|
||||
@@ -15,7 +16,6 @@ import {
|
||||
type LovelaceSectionRawConfig,
|
||||
} from "../../../../data/lovelace/config/section";
|
||||
import type { LovelaceViewConfig } from "../../../../data/lovelace/config/view";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
|
||||
interface SettingsData {
|
||||
column_span?: number;
|
||||
@@ -27,14 +27,15 @@ interface SettingsData {
|
||||
|
||||
@customElement("hui-section-settings-editor")
|
||||
export class HuiDialogEditSection extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
@consumeLocalize()
|
||||
private _localize!: LocalizeFunc;
|
||||
|
||||
@property({ attribute: false }) public config!: LovelaceSectionRawConfig;
|
||||
|
||||
@property({ attribute: false }) public viewConfig!: LovelaceViewConfig;
|
||||
|
||||
private _schema = memoizeOne(
|
||||
(maxColumns: number, backgroundEnabled: boolean, localize: LocalizeFunc) =>
|
||||
(maxColumns: number, localize: LocalizeFunc) =>
|
||||
[
|
||||
{
|
||||
name: "column_span",
|
||||
@@ -50,48 +51,45 @@ export class HuiDialogEditSection extends LitElement {
|
||||
name: "background_enabled",
|
||||
selector: { boolean: {} },
|
||||
},
|
||||
...(backgroundEnabled
|
||||
? ([
|
||||
{
|
||||
name: "background",
|
||||
type: "expandable",
|
||||
flatten: true,
|
||||
expanded: true,
|
||||
iconPath: mdiPalette,
|
||||
schema: [
|
||||
{
|
||||
name: "background_color",
|
||||
selector: {
|
||||
ui_color: {
|
||||
extra_options: [
|
||||
{
|
||||
value: "default",
|
||||
label: localize(
|
||||
"ui.panel.lovelace.editor.edit_section.settings.background_color_default"
|
||||
),
|
||||
display_color:
|
||||
"var(--ha-section-background-color, var(--secondary-background-color))",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "background",
|
||||
type: "expandable",
|
||||
flatten: true,
|
||||
expanded: true,
|
||||
hidden: { field: "background_enabled", value: false },
|
||||
iconPath: mdiPalette,
|
||||
schema: [
|
||||
{
|
||||
name: "background_color",
|
||||
selector: {
|
||||
ui_color: {
|
||||
extra_options: [
|
||||
{
|
||||
value: "default",
|
||||
label: localize(
|
||||
"ui.panel.lovelace.editor.edit_section.settings.background_color_default"
|
||||
),
|
||||
display_color:
|
||||
"var(--ha-section-background-color, var(--secondary-background-color))",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "background_opacity",
|
||||
selector: {
|
||||
number: {
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1,
|
||||
unit_of_measurement: "%",
|
||||
mode: "slider",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
},
|
||||
] as const satisfies readonly HaFormSchema[])
|
||||
: []),
|
||||
},
|
||||
{
|
||||
name: "background_opacity",
|
||||
selector: {
|
||||
number: {
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1,
|
||||
unit_of_measurement: "%",
|
||||
mode: "slider",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "theme",
|
||||
selector: {
|
||||
@@ -116,13 +114,11 @@ export class HuiDialogEditSection extends LitElement {
|
||||
|
||||
const schema = this._schema(
|
||||
this.viewConfig.max_columns || 4,
|
||||
backgroundEnabled,
|
||||
this.hass.localize
|
||||
this._localize
|
||||
);
|
||||
|
||||
return html`
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${data}
|
||||
.schema=${schema}
|
||||
.computeLabel=${this._computeLabel}
|
||||
@@ -135,14 +131,14 @@ export class HuiDialogEditSection extends LitElement {
|
||||
private _computeLabel = (
|
||||
schema: SchemaUnion<ReturnType<typeof this._schema>>
|
||||
) =>
|
||||
this.hass.localize(
|
||||
this._localize(
|
||||
`ui.panel.lovelace.editor.edit_section.settings.${schema.name}`
|
||||
);
|
||||
|
||||
private _computeHelper = (
|
||||
schema: SchemaUnion<ReturnType<typeof this._schema>>
|
||||
) =>
|
||||
this.hass.localize(
|
||||
this._localize(
|
||||
`ui.panel.lovelace.editor.edit_section.settings.${schema.name}_helper`
|
||||
) || "";
|
||||
|
||||
|
||||
@@ -170,7 +170,6 @@ export class HuiDialogEditView extends DirtyStateProviderMixin<LovelaceViewConfi
|
||||
content = html`
|
||||
<hui-view-editor
|
||||
.isNew=${this._params.viewIndex === undefined}
|
||||
.hass=${this.hass}
|
||||
.config=${this._config}
|
||||
@view-config-changed=${this._viewConfigChanged}
|
||||
></hui-view-editor>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { consumeLocalize } from "../../../../common/decorators/consume-context-entry";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { slugify } from "../../../../common/string/slugify";
|
||||
import type { LocalizeFunc } from "../../../../common/translations/localize";
|
||||
@@ -10,7 +11,6 @@ import type {
|
||||
SchemaUnion,
|
||||
} from "../../../../components/ha-form/types";
|
||||
import type { LovelaceViewConfig } from "../../../../data/lovelace/config/view";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import {
|
||||
MASONRY_VIEW_LAYOUT,
|
||||
SECTIONS_VIEW_LAYOUT,
|
||||
@@ -33,7 +33,8 @@ const INTEGER_REGEX = /^[0-9]+$/;
|
||||
|
||||
@customElement("hui-view-editor")
|
||||
export class HuiViewEditor extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
@consumeLocalize()
|
||||
private _localize!: LocalizeFunc;
|
||||
|
||||
@property({ attribute: false }) public isNew = false;
|
||||
|
||||
@@ -44,7 +45,7 @@ export class HuiViewEditor extends LitElement {
|
||||
private _suggestedPath = false;
|
||||
|
||||
private _schema = memoizeOne(
|
||||
(localize: LocalizeFunc, viewType: string) =>
|
||||
(localize: LocalizeFunc) =>
|
||||
[
|
||||
{
|
||||
name: "type",
|
||||
@@ -87,41 +88,42 @@ export class HuiViewEditor extends LitElement {
|
||||
boolean: {},
|
||||
},
|
||||
},
|
||||
...(viewType === SECTIONS_VIEW_LAYOUT
|
||||
? ([
|
||||
{
|
||||
name: "section_specifics",
|
||||
type: "expandable",
|
||||
flatten: true,
|
||||
expanded: true,
|
||||
schema: [
|
||||
{
|
||||
name: "max_columns",
|
||||
selector: {
|
||||
number: {
|
||||
min: 1,
|
||||
max: 10,
|
||||
mode: "slider",
|
||||
slider_ticks: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "dense_section_placement",
|
||||
selector: {
|
||||
boolean: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "top_margin",
|
||||
selector: {
|
||||
boolean: {},
|
||||
},
|
||||
},
|
||||
],
|
||||
{
|
||||
name: "section_specifics",
|
||||
type: "expandable",
|
||||
flatten: true,
|
||||
expanded: true,
|
||||
hidden: {
|
||||
field: "type",
|
||||
operator: "not_eq",
|
||||
value: SECTIONS_VIEW_LAYOUT,
|
||||
},
|
||||
schema: [
|
||||
{
|
||||
name: "max_columns",
|
||||
selector: {
|
||||
number: {
|
||||
min: 1,
|
||||
max: 10,
|
||||
mode: "slider",
|
||||
slider_ticks: true,
|
||||
},
|
||||
},
|
||||
] as const satisfies HaFormSchema[])
|
||||
: []),
|
||||
},
|
||||
{
|
||||
name: "dense_section_placement",
|
||||
selector: {
|
||||
boolean: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "top_margin",
|
||||
selector: {
|
||||
boolean: {},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
] as const satisfies HaFormSchema[]
|
||||
);
|
||||
|
||||
@@ -134,12 +136,6 @@ export class HuiViewEditor extends LitElement {
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (!this.hass) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const schema = this._schema(this.hass.localize, this._type);
|
||||
|
||||
const data = {
|
||||
...this._config,
|
||||
type: this._type,
|
||||
@@ -155,9 +151,8 @@ export class HuiViewEditor extends LitElement {
|
||||
|
||||
return html`
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${data}
|
||||
.schema=${schema}
|
||||
.schema=${this._schema(this._localize)}
|
||||
.computeLabel=${this._computeLabel}
|
||||
.computeHelper=${this._computeHelper}
|
||||
.computeError=${this._computeError}
|
||||
@@ -207,14 +202,14 @@ export class HuiViewEditor extends LitElement {
|
||||
}
|
||||
|
||||
private _computeError = (error: string) =>
|
||||
this.hass.localize(`ui.panel.lovelace.editor.edit_view.${error}`) || error;
|
||||
this._localize(`ui.panel.lovelace.editor.edit_view.${error}`) || error;
|
||||
|
||||
private _computeLabel = (
|
||||
schema: SchemaUnion<ReturnType<typeof this._schema>>
|
||||
) => {
|
||||
switch (schema.name) {
|
||||
case "path":
|
||||
return this.hass!.localize("ui.panel.lovelace.editor.card.generic.url");
|
||||
return this._localize("ui.panel.lovelace.editor.card.generic.url");
|
||||
case "type":
|
||||
case "show_icon_and_title":
|
||||
case "subview":
|
||||
@@ -222,11 +217,11 @@ export class HuiViewEditor extends LitElement {
|
||||
case "dense_section_placement":
|
||||
case "top_margin":
|
||||
case "section_specifics":
|
||||
return this.hass.localize(
|
||||
return this._localize(
|
||||
`ui.panel.lovelace.editor.edit_view.${schema.name}`
|
||||
);
|
||||
default:
|
||||
return this.hass!.localize(
|
||||
return this._localize(
|
||||
`ui.panel.lovelace.editor.card.generic.${schema.name}`
|
||||
);
|
||||
}
|
||||
@@ -241,7 +236,7 @@ export class HuiViewEditor extends LitElement {
|
||||
case "subview":
|
||||
case "dense_section_placement":
|
||||
case "top_margin":
|
||||
return this.hass.localize(
|
||||
return this._localize(
|
||||
`ui.panel.lovelace.editor.edit_view.${schema.name}_helper`
|
||||
);
|
||||
|
||||
|
||||
@@ -2025,9 +2025,12 @@
|
||||
"voice-settings": {
|
||||
"expose_header": "Expose",
|
||||
"aliases_header": "Aliases",
|
||||
"aliases_description": "Aliases are alternative names to call your entity. Only supported by Assist and Google Assistant.",
|
||||
"aliases_description": "Aliases are the names voice assistants use for this entity.",
|
||||
"aliases_description_assist": "Assist uses all aliases equally.",
|
||||
"aliases_description_alexa": "Amazon Alexa uses only the first alias.",
|
||||
"aliases_description_google": "Google Assistant uses the first alias as the main name and the rest as alternatives.",
|
||||
"aliases_no_unique_id": "Aliases are not supported for entities without a unique ID. See the {faq_link} for more detail.",
|
||||
"entity_name_alias_description": "Default name. Disable it if you want your voice assistants to ignore it and just use aliases.",
|
||||
"entity_name_alias_description": "Default name. When enabled, it is used as the first alias.",
|
||||
"ask_pin": "Ask for PIN",
|
||||
"manual_config": "Managed in configuration.yaml",
|
||||
"unsupported": "Unsupported",
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import { supportsFeature } from "../common/entity/supports-feature";
|
||||
import { cleanupMediaTitle } from "../data/media-player";
|
||||
import {
|
||||
cleanupMediaTitle,
|
||||
MediaPlayerEntityFeature,
|
||||
} from "../data/media-player";
|
||||
import type { HomeAssistant } from "../types";
|
||||
|
||||
export default class MediaPlayerEntity {
|
||||
@@ -75,51 +78,60 @@ export default class MediaPlayerEntity {
|
||||
}
|
||||
|
||||
get supportsPause() {
|
||||
return supportsFeature(this.stateObj, 1);
|
||||
return supportsFeature(this.stateObj, MediaPlayerEntityFeature.PAUSE);
|
||||
}
|
||||
|
||||
get supportsVolumeSet() {
|
||||
return supportsFeature(this.stateObj, 4);
|
||||
return supportsFeature(this.stateObj, MediaPlayerEntityFeature.VOLUME_SET);
|
||||
}
|
||||
|
||||
get supportsVolumeMute() {
|
||||
return supportsFeature(this.stateObj, 8);
|
||||
return supportsFeature(this.stateObj, MediaPlayerEntityFeature.VOLUME_MUTE);
|
||||
}
|
||||
|
||||
get supportsPreviousTrack() {
|
||||
return supportsFeature(this.stateObj, 16);
|
||||
return supportsFeature(
|
||||
this.stateObj,
|
||||
MediaPlayerEntityFeature.PREVIOUS_TRACK
|
||||
);
|
||||
}
|
||||
|
||||
get supportsNextTrack() {
|
||||
return supportsFeature(this.stateObj, 32);
|
||||
return supportsFeature(this.stateObj, MediaPlayerEntityFeature.NEXT_TRACK);
|
||||
}
|
||||
|
||||
get supportsTurnOn() {
|
||||
return supportsFeature(this.stateObj, 128);
|
||||
return supportsFeature(this.stateObj, MediaPlayerEntityFeature.TURN_ON);
|
||||
}
|
||||
|
||||
get supportsTurnOff() {
|
||||
return supportsFeature(this.stateObj, 256);
|
||||
return supportsFeature(this.stateObj, MediaPlayerEntityFeature.TURN_OFF);
|
||||
}
|
||||
|
||||
get supportsPlayMedia() {
|
||||
return supportsFeature(this.stateObj, 512);
|
||||
return supportsFeature(this.stateObj, MediaPlayerEntityFeature.PLAY_MEDIA);
|
||||
}
|
||||
|
||||
get supportsVolumeButtons() {
|
||||
return supportsFeature(this.stateObj, 1024);
|
||||
return supportsFeature(this.stateObj, MediaPlayerEntityFeature.VOLUME_STEP);
|
||||
}
|
||||
|
||||
get supportsSelectSource() {
|
||||
return supportsFeature(this.stateObj, 2048);
|
||||
return supportsFeature(
|
||||
this.stateObj,
|
||||
MediaPlayerEntityFeature.SELECT_SOURCE
|
||||
);
|
||||
}
|
||||
|
||||
get supportsSelectSoundMode() {
|
||||
return supportsFeature(this.stateObj, 65536);
|
||||
return supportsFeature(
|
||||
this.stateObj,
|
||||
MediaPlayerEntityFeature.SELECT_SOUND_MODE
|
||||
);
|
||||
}
|
||||
|
||||
get supportsPlay() {
|
||||
return supportsFeature(this.stateObj, 16384);
|
||||
return supportsFeature(this.stateObj, MediaPlayerEntityFeature.PLAY);
|
||||
}
|
||||
|
||||
get primaryTitle() {
|
||||
|
||||
Reference in New Issue
Block a user