(same style as for a labeled slider) and directly put
diff --git a/src/dialogs/more-info/controls/more-info-light.ts b/src/dialogs/more-info/controls/more-info-light.ts
index 29fb2644d8..be85eb7921 100644
--- a/src/dialogs/more-info/controls/more-info-light.ts
+++ b/src/dialogs/more-info/controls/more-info-light.ts
@@ -20,13 +20,13 @@ import "../../../components/ha-labeled-slider";
import "../../../components/ha-select";
import {
getLightCurrentModeRgbColor,
- LightColorModes,
+ LightColorMode,
LightEntity,
+ LightEntityFeature,
lightIsInColorMode,
lightSupportsColor,
lightSupportsColorMode,
- lightSupportsDimming,
- SUPPORT_EFFECT,
+ lightSupportsBrightness,
} from "../../../data/light";
import type { HomeAssistant } from "../../../types";
@@ -56,7 +56,7 @@ class MoreInfoLight extends LitElement {
@state() private _colorPickerColor?: [number, number, number];
- @state() private _mode?: "color" | LightColorModes;
+ @state() private _mode?: "color" | LightColorMode;
protected render(): TemplateResult {
if (!this.hass || !this.stateObj) {
@@ -65,29 +65,29 @@ class MoreInfoLight extends LitElement {
const supportsTemp = lightSupportsColorMode(
this.stateObj,
- LightColorModes.COLOR_TEMP
+ LightColorMode.COLOR_TEMP
);
const supportsWhite = lightSupportsColorMode(
this.stateObj,
- LightColorModes.WHITE
+ LightColorMode.WHITE
);
const supportsRgbww = lightSupportsColorMode(
this.stateObj,
- LightColorModes.RGBWW
+ LightColorMode.RGBWW
);
const supportsRgbw =
!supportsRgbww &&
- lightSupportsColorMode(this.stateObj, LightColorModes.RGBW);
+ lightSupportsColorMode(this.stateObj, LightColorMode.RGBW);
const supportsColor =
supportsRgbww || supportsRgbw || lightSupportsColor(this.stateObj);
return html`
- ${lightSupportsDimming(this.stateObj)
+ ${lightSupportsBrightness(this.stateObj)
? html`
@@ -260,31 +260,31 @@ class MoreInfoLight extends LitElement {
let brightnessAdjust = 100;
this._brightnessAdjusted = undefined;
if (
- stateObj.attributes.color_mode === LightColorModes.RGB &&
- !lightSupportsColorMode(stateObj, LightColorModes.RGBWW) &&
- !lightSupportsColorMode(stateObj, LightColorModes.RGBW)
+ stateObj.attributes.color_mode === LightColorMode.RGB &&
+ !lightSupportsColorMode(stateObj, LightColorMode.RGBWW) &&
+ !lightSupportsColorMode(stateObj, LightColorMode.RGBW)
) {
- const maxVal = Math.max(...stateObj.attributes.rgb_color);
+ const maxVal = Math.max(...stateObj.attributes.rgb_color!);
if (maxVal < 255) {
this._brightnessAdjusted = maxVal;
brightnessAdjust = (this._brightnessAdjusted / 255) * 100;
}
}
this._brightnessSliderValue = Math.round(
- (stateObj.attributes.brightness * brightnessAdjust) / 255
+ ((stateObj.attributes.brightness || 0) * brightnessAdjust) / 255
);
this._ctSliderValue = stateObj.attributes.color_temp;
this._wvSliderValue =
- stateObj.attributes.color_mode === LightColorModes.RGBW
- ? Math.round((stateObj.attributes.rgbw_color[3] * 100) / 255)
+ stateObj.attributes.color_mode === LightColorMode.RGBW
+ ? Math.round((stateObj.attributes.rgbw_color![3] * 100) / 255)
: undefined;
this._cwSliderValue =
- stateObj.attributes.color_mode === LightColorModes.RGBWW
- ? Math.round((stateObj.attributes.rgbww_color[3] * 100) / 255)
+ stateObj.attributes.color_mode === LightColorMode.RGBWW
+ ? Math.round((stateObj.attributes.rgbww_color![3] * 100) / 255)
: undefined;
this._wwSliderValue =
- stateObj.attributes.color_mode === LightColorModes.RGBWW
- ? Math.round((stateObj.attributes.rgbww_color[4] * 100) / 255)
+ stateObj.attributes.color_mode === LightColorMode.RGBWW
+ ? Math.round((stateObj.attributes.rgbww_color![4] * 100) / 255)
: undefined;
const currentRgbColor = getLightCurrentModeRgbColor(stateObj);
@@ -307,10 +307,10 @@ class MoreInfoLight extends LitElement {
(supportsTemp: boolean, supportsWhite: boolean) => {
const modes = [{ label: "Color", value: "color" }];
if (supportsTemp) {
- modes.push({ label: "Temperature", value: LightColorModes.COLOR_TEMP });
+ modes.push({ label: "Temperature", value: LightColorMode.COLOR_TEMP });
}
if (supportsWhite) {
- modes.push({ label: "White", value: LightColorModes.WHITE });
+ modes.push({ label: "White", value: LightColorMode.WHITE });
}
return modes;
}
@@ -342,7 +342,7 @@ class MoreInfoLight extends LitElement {
this._brightnessSliderValue = bri;
- if (this._mode === LightColorModes.WHITE) {
+ if (this._mode === LightColorMode.WHITE) {
this.hass.callService("light", "turn_on", {
entity_id: this.stateObj!.entity_id,
white: Math.min(255, Math.round((bri * 255) / 100)),
@@ -486,7 +486,7 @@ class MoreInfoLight extends LitElement {
}
private _setRgbWColor(rgbColor: [number, number, number]) {
- if (lightSupportsColorMode(this.stateObj!, LightColorModes.RGBWW)) {
+ if (lightSupportsColorMode(this.stateObj!, LightColorMode.RGBWW)) {
const rgbww_color: [number, number, number, number, number] = this
.stateObj!.attributes.rgbww_color
? [...this.stateObj!.attributes.rgbww_color]
@@ -495,7 +495,7 @@ class MoreInfoLight extends LitElement {
entity_id: this.stateObj!.entity_id,
rgbww_color: rgbColor.concat(rgbww_color.slice(3)),
});
- } else if (lightSupportsColorMode(this.stateObj!, LightColorModes.RGBW)) {
+ } else if (lightSupportsColorMode(this.stateObj!, LightColorMode.RGBW)) {
const rgbw_color: [number, number, number, number] = this.stateObj!
.attributes.rgbw_color
? [...this.stateObj!.attributes.rgbw_color]
@@ -524,8 +524,8 @@ class MoreInfoLight extends LitElement {
];
if (
- lightSupportsColorMode(this.stateObj!, LightColorModes.RGBWW) ||
- lightSupportsColorMode(this.stateObj!, LightColorModes.RGBW)
+ lightSupportsColorMode(this.stateObj!, LightColorMode.RGBWW) ||
+ lightSupportsColorMode(this.stateObj!, LightColorMode.RGBW)
) {
this._setRgbWColor(
this._colorBrightnessSliderValue
@@ -535,7 +535,7 @@ class MoreInfoLight extends LitElement {
)
: [ev.detail.rgb.r, ev.detail.rgb.g, ev.detail.rgb.b]
);
- } else if (lightSupportsColorMode(this.stateObj!, LightColorModes.RGB)) {
+ } else if (lightSupportsColorMode(this.stateObj!, LightColorMode.RGB)) {
const rgb_color: [number, number, number] = [
ev.detail.rgb.r,
ev.detail.rgb.g,
diff --git a/src/dialogs/more-info/ha-more-info-dialog.ts b/src/dialogs/more-info/ha-more-info-dialog.ts
index f07a3c3863..d47a02fbad 100644
--- a/src/dialogs/more-info/ha-more-info-dialog.ts
+++ b/src/dialogs/more-info/ha-more-info-dialog.ts
@@ -90,7 +90,7 @@ export class MoreInfoDialog extends LitElement {
const stateObj = this.hass.states[entityId];
const domain = computeDomain(entityId);
- const name = stateObj ? computeStateName(stateObj) : entityId;
+ const name = (stateObj && computeStateName(stateObj)) || entityId;
const tabs = this._getTabs(entityId, this.hass.user!.is_admin);
return html`
diff --git a/src/external_app/external_app_entrypoint.ts b/src/external_app/external_app_entrypoint.ts
index 46d5a39dee..0f059a11ae 100644
--- a/src/external_app/external_app_entrypoint.ts
+++ b/src/external_app/external_app_entrypoint.ts
@@ -7,7 +7,7 @@ This is the entry point for providing external app stuff from app entrypoint.
import { fireEvent } from "../common/dom/fire_event";
import { HomeAssistantMain } from "../layouts/home-assistant-main";
-import type { EMExternalMessageCommands } from "./external_messaging";
+import type { EMIncomingMessageCommands } from "./external_messaging";
export const attachExternalToApp = (hassMainEl: HomeAssistantMain) => {
window.addEventListener("haptic", (ev) =>
@@ -24,7 +24,7 @@ export const attachExternalToApp = (hassMainEl: HomeAssistantMain) => {
const handleExternalMessage = (
hassMainEl: HomeAssistantMain,
- msg: EMExternalMessageCommands
+ msg: EMIncomingMessageCommands
): boolean => {
const bus = hassMainEl.hass.auth.external!;
diff --git a/src/external_app/external_messaging.ts b/src/external_app/external_messaging.ts
index fdab1ab2eb..2bf8fa34fb 100644
--- a/src/external_app/external_messaging.ts
+++ b/src/external_app/external_messaging.ts
@@ -8,7 +8,6 @@ interface CommandInFlight {
export interface EMMessage {
id?: number;
type: string;
- payload?: unknown;
}
interface EMError {
@@ -30,34 +29,120 @@ interface EMMessageResultError {
error: EMError;
}
-interface EMExternalMessageRestart {
+interface EMOutgoingMessageConfigGet extends EMMessage {
+ type: "config/get";
+}
+
+interface EMOutgoingMessageMatterCommission extends EMMessage {
+ type: "matter/commission";
+}
+
+type EMOutgoingMessageWithAnswer = {
+ "config/get": {
+ request: EMOutgoingMessageConfigGet;
+ response: ExternalConfig;
+ };
+ "matter/commission": {
+ request: EMOutgoingMessageMatterCommission;
+ response: {
+ code: string;
+ };
+ };
+};
+
+interface EMOutgoingMessageExoplayerPlayHLS extends EMMessage {
+ type: "exoplayer/play_hls";
+ payload: {
+ url: string;
+ muted: boolean;
+ };
+}
+interface EMOutgoingMessageExoplayerResize extends EMMessage {
+ type: "exoplayer/resize";
+ payload: {
+ left: number;
+ top: number;
+ right: number;
+ bottom: number;
+ };
+}
+
+interface EMOutgoingMessageExoplayerStop extends EMMessage {
+ type: "exoplayer/stop";
+}
+
+interface EMOutgoingMessageThemeUpdate extends EMMessage {
+ type: "theme-update";
+}
+
+interface EMOutgoingMessageHaptic extends EMMessage {
+ type: "haptic";
+ payload: { hapticType: string };
+}
+
+interface EMOutgoingMessageConnectionStatus extends EMMessage {
+ type: "connection-status";
+ payload: { event: string };
+}
+
+interface EMOutgoingMessageAppConfiguration extends EMMessage {
+ type: "config_screen/show";
+}
+
+interface EMOutgoingMessageTagWrite extends EMMessage {
+ type: "tag/write";
+ payload: {
+ name: string | null;
+ tag: string;
+ };
+}
+
+interface EMOutgoingMessageSidebarShow extends EMMessage {
+ type: "sidebar/show";
+}
+
+type EMOutgoingMessageWithoutAnswer =
+ | EMOutgoingMessageHaptic
+ | EMOutgoingMessageConnectionStatus
+ | EMOutgoingMessageAppConfiguration
+ | EMOutgoingMessageTagWrite
+ | EMOutgoingMessageSidebarShow
+ | EMOutgoingMessageExoplayerPlayHLS
+ | EMOutgoingMessageExoplayerResize
+ | EMOutgoingMessageExoplayerStop
+ | EMOutgoingMessageThemeUpdate
+ | EMMessageResultSuccess
+ | EMMessageResultError;
+
+interface EMIncomingMessageRestart {
id: number;
type: "command";
command: "restart";
}
-interface EMExternMessageShowNotifications {
+interface EMIncomingMessageShowNotifications {
id: number;
type: "command";
command: "notifications/show";
}
-export type EMExternalMessageCommands =
- | EMExternalMessageRestart
- | EMExternMessageShowNotifications;
+export type EMIncomingMessageCommands =
+ | EMIncomingMessageRestart
+ | EMIncomingMessageShowNotifications;
-type ExternalMessage =
+type EMIncomingMessage =
| EMMessageResultSuccess
| EMMessageResultError
- | EMExternalMessageCommands;
+ | EMIncomingMessageCommands;
-type ExternalMessageHandler = (msg: EMExternalMessageCommands) => boolean;
+type EMIncomingMessageHandler = (msg: EMIncomingMessageCommands) => boolean;
export interface ExternalConfig {
hasSettingsScreen: boolean;
hasSidebar: boolean;
canWriteTag: boolean;
hasExoPlayer: boolean;
+ canCommissionMatter: boolean;
}
export class ExternalMessaging {
@@ -67,7 +152,7 @@ export class ExternalMessaging {
public msgId = 0;
- private _commandHandler?: ExternalMessageHandler;
+ private _commandHandler?: EMIncomingMessageHandler;
public async attach() {
window[CALLBACK_EXTERNAL_BUS] = (msg) => this.receiveMessage(msg);
@@ -77,12 +162,12 @@ export class ExternalMessaging {
payload: { event: ev.detail },
})
);
- this.config = await this.sendMessage
({
+ this.config = await this.sendMessage<"config/get">({
type: "config/get",
});
}
- public addCommandHandler(handler: ExternalMessageHandler) {
+ public addCommandHandler(handler: EMIncomingMessageHandler) {
this._commandHandler = handler;
}
@@ -90,31 +175,33 @@ export class ExternalMessaging {
* Send message to external app that expects a response.
* @param msg message to send
*/
- public sendMessage(msg: EMMessage): Promise {
+ public sendMessage(
+ msg: EMOutgoingMessageWithAnswer[T]["request"]
+ ): Promise {
const msgId = ++this.msgId;
msg.id = msgId;
- this.fireMessage(msg);
+ this._sendExternal(msg);
- return new Promise((resolve, reject) => {
- this.commands[msgId] = { resolve, reject };
- });
+ return new Promise(
+ (resolve, reject) => {
+ this.commands[msgId] = { resolve, reject };
+ }
+ );
}
/**
* Send message to external app without expecting a response.
* @param msg message to send
*/
- public fireMessage(
- msg: EMMessage | EMMessageResultSuccess | EMMessageResultError
- ) {
+ public fireMessage(msg: EMOutgoingMessageWithoutAnswer) {
if (!msg.id) {
msg.id = ++this.msgId;
}
this._sendExternal(msg);
}
- public receiveMessage(msg: ExternalMessage) {
+ public receiveMessage(msg: EMIncomingMessage) {
if (__DEV__) {
// eslint-disable-next-line no-console
console.log("Receiving message from external app", msg);
diff --git a/src/panels/config/application_credentials/dialog-add-application-credential.ts b/src/panels/config/application_credentials/dialog-add-application-credential.ts
index e01ded0e87..f1fc6ecf87 100644
--- a/src/panels/config/application_credentials/dialog-add-application-credential.ts
+++ b/src/panels/config/application_credentials/dialog-add-application-credential.ts
@@ -1,8 +1,9 @@
import "@material/mwc-button";
import "@material/mwc-list/mwc-list-item";
+import { mdiOpenInNew } from "@mdi/js";
+import { ComboBoxLitRenderer } from "@vaadin/combo-box/lit";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
-import { ComboBoxLitRenderer } from "@vaadin/combo-box/lit";
import { fireEvent } from "../../../common/dom/fire_event";
import "../../../components/ha-circular-progress";
import "../../../components/ha-combo-box";
@@ -10,14 +11,15 @@ import { createCloseHeading } from "../../../components/ha-dialog";
import "../../../components/ha-markdown";
import "../../../components/ha-textfield";
import {
- fetchApplicationCredentialsConfig,
- createApplicationCredential,
- ApplicationCredentialsConfig,
ApplicationCredential,
+ ApplicationCredentialsConfig,
+ createApplicationCredential,
+ fetchApplicationCredentialsConfig,
} from "../../../data/application_credential";
import { domainToName } from "../../../data/integration";
import { haStyleDialog } from "../../../resources/styles";
import { HomeAssistant } from "../../../types";
+import { documentationUrl } from "../../../util/documentation-url";
import { AddApplicationCredentialDialogParams } from "./show-dialog-add-application-credential";
interface Domain {
@@ -98,6 +100,25 @@ export class DialogAddApplicationCredential extends LitElement {
>
${this._loading
@@ -163,15 +192,18 @@ export class DialogAddApplicationCredential extends LitElement {
`}
@@ -213,7 +245,7 @@ export class DialogAddApplicationCredential extends LitElement {
this.closeDialog();
}
- private async _createApplicationCredential(ev) {
+ private async _addApplicationCredential(ev) {
ev.preventDefault();
if (!this._domain || !this._clientId || !this._clientSecret) {
return;
@@ -260,6 +292,12 @@ export class DialogAddApplicationCredential extends LitElement {
display: block;
margin-bottom: 24px;
}
+ a {
+ text-decoration: none;
+ }
+ a ha-svg-icon {
+ --mdc-icon-size: 16px;
+ }
`,
];
}
diff --git a/src/panels/config/automation/action/ha-automation-action-row.ts b/src/panels/config/automation/action/ha-automation-action-row.ts
index c742eefc01..0d89543050 100644
--- a/src/panels/config/automation/action/ha-automation-action-row.ts
+++ b/src/panels/config/automation/action/ha-automation-action-row.ts
@@ -404,11 +404,15 @@ export default class HaAutomationActionRow extends LitElement {
private _onDelete() {
showConfirmationDialog(this, {
+ title: this.hass.localize(
+ "ui.panel.config.automation.editor.actions.delete_confirm_title"
+ ),
text: this.hass.localize(
- "ui.panel.config.automation.editor.actions.delete_confirm"
+ "ui.panel.config.automation.editor.actions.delete_confirm_text"
),
dismissText: this.hass.localize("ui.common.cancel"),
confirmText: this.hass.localize("ui.common.delete"),
+ destructive: true,
confirm: () => {
fireEvent(this, "value-changed", { value: null });
},
diff --git a/src/panels/config/automation/condition/ha-automation-condition-row.ts b/src/panels/config/automation/condition/ha-automation-condition-row.ts
index dcc93b8015..386e158d01 100644
--- a/src/panels/config/automation/condition/ha-automation-condition-row.ts
+++ b/src/panels/config/automation/condition/ha-automation-condition-row.ts
@@ -314,11 +314,15 @@ export default class HaAutomationConditionRow extends LitElement {
private _onDelete() {
showConfirmationDialog(this, {
+ title: this.hass.localize(
+ "ui.panel.config.automation.editor.conditions.delete_confirm_title"
+ ),
text: this.hass.localize(
- "ui.panel.config.automation.editor.conditions.delete_confirm"
+ "ui.panel.config.automation.editor.conditions.delete_confirm_text"
),
dismissText: this.hass.localize("ui.common.cancel"),
confirmText: this.hass.localize("ui.common.delete"),
+ destructive: true,
confirm: () => {
fireEvent(this, "value-changed", { value: null });
},
diff --git a/src/panels/config/automation/ha-automation-editor.ts b/src/panels/config/automation/ha-automation-editor.ts
index 14f34c7f6b..e7c7236fd3 100644
--- a/src/panels/config/automation/ha-automation-editor.ts
+++ b/src/panels/config/automation/ha-automation-editor.ts
@@ -31,6 +31,7 @@ import { classMap } from "lit/directives/class-map";
import { fireEvent } from "../../../common/dom/fire_event";
import { navigate } from "../../../common/navigate";
import { copyToClipboard } from "../../../common/util/copy-clipboard";
+import { afterNextRender } from "../../../common/util/render-status";
import "../../../components/ha-button-menu";
import "../../../components/ha-card";
import "../../../components/ha-fab";
@@ -540,11 +541,15 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
private async confirmUnsavedChanged(): Promise