Allow trigger reconnect from external bus (#10819)
This commit is contained in:
parent
149f381bc3
commit
39774c0e02
|
@ -43,6 +43,7 @@ import {
|
||||||
PersistentNotification,
|
PersistentNotification,
|
||||||
subscribeNotifications,
|
subscribeNotifications,
|
||||||
} from "../data/persistent_notification";
|
} from "../data/persistent_notification";
|
||||||
|
import { getExternalConfig } from "../external_app/external_config";
|
||||||
import { actionHandler } from "../panels/lovelace/common/directives/action-handler-directive";
|
import { actionHandler } from "../panels/lovelace/common/directives/action-handler-directive";
|
||||||
import { haStyleScrollbar } from "../resources/styles";
|
import { haStyleScrollbar } from "../resources/styles";
|
||||||
import type { HomeAssistant, PanelInfo, Route } from "../types";
|
import type { HomeAssistant, PanelInfo, Route } from "../types";
|
||||||
|
@ -266,6 +267,11 @@ class HaSidebar extends LitElement {
|
||||||
subscribeNotifications(this.hass.connection, (notifications) => {
|
subscribeNotifications(this.hass.connection, (notifications) => {
|
||||||
this._notifications = notifications;
|
this._notifications = notifications;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Temporary workaround for a bug in Android. Can be removed in Home Assistant 2022.2
|
||||||
|
if (this.hass.auth.external) {
|
||||||
|
getExternalConfig(this.hass.auth.external);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected updated(changedProps) {
|
protected updated(changedProps) {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* Auth class that connects to a native app for authentication.
|
* Auth class that connects to a native app for authentication.
|
||||||
*/
|
*/
|
||||||
import { Auth } from "home-assistant-js-websocket";
|
import { Auth } from "home-assistant-js-websocket";
|
||||||
import { ExternalMessaging, InternalMessage } from "./external_messaging";
|
import { ExternalMessaging, EMMessage } from "./external_messaging";
|
||||||
|
|
||||||
const CALLBACK_SET_TOKEN = "externalAuthSetToken";
|
const CALLBACK_SET_TOKEN = "externalAuthSetToken";
|
||||||
const CALLBACK_REVOKE_TOKEN = "externalAuthRevokeToken";
|
const CALLBACK_REVOKE_TOKEN = "externalAuthRevokeToken";
|
||||||
|
@ -36,7 +36,7 @@ declare global {
|
||||||
postMessage(payload: BasePayload);
|
postMessage(payload: BasePayload);
|
||||||
};
|
};
|
||||||
externalBus: {
|
externalBus: {
|
||||||
postMessage(payload: InternalMessage);
|
postMessage(payload: EMMessage);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { Connection } from "home-assistant-js-websocket";
|
||||||
import {
|
import {
|
||||||
externalForwardConnectionEvents,
|
externalForwardConnectionEvents,
|
||||||
externalForwardHaptics,
|
externalForwardHaptics,
|
||||||
|
@ -7,39 +8,50 @@ const CALLBACK_EXTERNAL_BUS = "externalBus";
|
||||||
|
|
||||||
interface CommandInFlight {
|
interface CommandInFlight {
|
||||||
resolve: (data: any) => void;
|
resolve: (data: any) => void;
|
||||||
reject: (err: ExternalError) => void;
|
reject: (err: EMError) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface InternalMessage {
|
export interface EMMessage {
|
||||||
id?: number;
|
id?: number;
|
||||||
type: string;
|
type: string;
|
||||||
payload?: unknown;
|
payload?: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ExternalError {
|
interface EMError {
|
||||||
code: string;
|
code: string;
|
||||||
message: string;
|
message: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ExternalMessageResult {
|
interface EMMessageResultSuccess {
|
||||||
id: number;
|
id: number;
|
||||||
type: "result";
|
type: "result";
|
||||||
success: true;
|
success: true;
|
||||||
result: unknown;
|
result: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ExternalMessageResultError {
|
interface EMMessageResultError {
|
||||||
id: number;
|
id: number;
|
||||||
type: "result";
|
type: "result";
|
||||||
success: false;
|
success: false;
|
||||||
error: ExternalError;
|
error: EMError;
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExternalMessage = ExternalMessageResult | ExternalMessageResultError;
|
interface EMExternalMessageRestart {
|
||||||
|
id: number;
|
||||||
|
type: "command";
|
||||||
|
command: "restart";
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExternalMessage =
|
||||||
|
| EMMessageResultSuccess
|
||||||
|
| EMMessageResultError
|
||||||
|
| EMExternalMessageRestart;
|
||||||
|
|
||||||
export class ExternalMessaging {
|
export class ExternalMessaging {
|
||||||
public commands: { [msgId: number]: CommandInFlight } = {};
|
public commands: { [msgId: number]: CommandInFlight } = {};
|
||||||
|
|
||||||
|
public connection?: Connection;
|
||||||
|
|
||||||
public cache: Record<string, any> = {};
|
public cache: Record<string, any> = {};
|
||||||
|
|
||||||
public msgId = 0;
|
public msgId = 0;
|
||||||
|
@ -54,7 +66,7 @@ export class ExternalMessaging {
|
||||||
* Send message to external app that expects a response.
|
* Send message to external app that expects a response.
|
||||||
* @param msg message to send
|
* @param msg message to send
|
||||||
*/
|
*/
|
||||||
public sendMessage<T>(msg: InternalMessage): Promise<T> {
|
public sendMessage<T>(msg: EMMessage): Promise<T> {
|
||||||
const msgId = ++this.msgId;
|
const msgId = ++this.msgId;
|
||||||
msg.id = msgId;
|
msg.id = msgId;
|
||||||
|
|
||||||
|
@ -69,7 +81,9 @@ export class ExternalMessaging {
|
||||||
* Send message to external app without expecting a response.
|
* Send message to external app without expecting a response.
|
||||||
* @param msg message to send
|
* @param msg message to send
|
||||||
*/
|
*/
|
||||||
public fireMessage(msg: InternalMessage) {
|
public fireMessage(
|
||||||
|
msg: EMMessage | EMMessageResultSuccess | EMMessageResultError
|
||||||
|
) {
|
||||||
if (!msg.id) {
|
if (!msg.id) {
|
||||||
msg.id = ++this.msgId;
|
msg.id = ++this.msgId;
|
||||||
}
|
}
|
||||||
|
@ -82,6 +96,43 @@ export class ExternalMessaging {
|
||||||
console.log("Receiving message from external app", msg);
|
console.log("Receiving message from external app", msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (msg.type === "command") {
|
||||||
|
if (!this.connection) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.warn("Received command without having connection set", msg);
|
||||||
|
this.fireMessage({
|
||||||
|
id: msg.id,
|
||||||
|
type: "result",
|
||||||
|
success: false,
|
||||||
|
error: {
|
||||||
|
code: "commands_not_init",
|
||||||
|
message: `Commands connection not set`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else if (msg.command === "restart") {
|
||||||
|
this.connection.socket.close();
|
||||||
|
this.fireMessage({
|
||||||
|
id: msg.id,
|
||||||
|
type: "result",
|
||||||
|
success: true,
|
||||||
|
result: null,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.warn("Received unknown command", msg.command, msg);
|
||||||
|
this.fireMessage({
|
||||||
|
id: msg.id,
|
||||||
|
type: "result",
|
||||||
|
success: false,
|
||||||
|
error: {
|
||||||
|
code: "unknown_command",
|
||||||
|
message: `Unknown command ${msg.command}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const pendingCmd = this.commands[msg.id];
|
const pendingCmd = this.commands[msg.id];
|
||||||
|
|
||||||
if (!pendingCmd) {
|
if (!pendingCmd) {
|
||||||
|
@ -99,7 +150,7 @@ export class ExternalMessaging {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _sendExternal(msg: InternalMessage) {
|
protected _sendExternal(msg: EMMessage) {
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log("Sending message to external app", msg);
|
console.log("Sending message to external app", msg);
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { Constructor } from "../types";
|
||||||
|
import { HassBaseEl } from "./hass-base-mixin";
|
||||||
|
|
||||||
|
export const ExternalMixin = <T extends Constructor<HassBaseEl>>(
|
||||||
|
superClass: T
|
||||||
|
) =>
|
||||||
|
class extends superClass {
|
||||||
|
protected hassConnected() {
|
||||||
|
super.hassConnected();
|
||||||
|
|
||||||
|
if (this.hass!.auth.external) {
|
||||||
|
this.hass!.auth.external.connection = this.hass!.connection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
|
@ -6,6 +6,7 @@ import DisconnectToastMixin from "./disconnect-toast-mixin";
|
||||||
import { hapticMixin } from "./haptic-mixin";
|
import { hapticMixin } from "./haptic-mixin";
|
||||||
import { HassBaseEl } from "./hass-base-mixin";
|
import { HassBaseEl } from "./hass-base-mixin";
|
||||||
import { loggingMixin } from "./logging-mixin";
|
import { loggingMixin } from "./logging-mixin";
|
||||||
|
import { ExternalMixin } from "./external-mixin";
|
||||||
import MoreInfoMixin from "./more-info-mixin";
|
import MoreInfoMixin from "./more-info-mixin";
|
||||||
import NotificationMixin from "./notification-mixin";
|
import NotificationMixin from "./notification-mixin";
|
||||||
import { panelTitleMixin } from "./panel-title-mixin";
|
import { panelTitleMixin } from "./panel-title-mixin";
|
||||||
|
@ -31,4 +32,5 @@ export class HassElement extends ext(HassBaseEl, [
|
||||||
hapticMixin,
|
hapticMixin,
|
||||||
panelTitleMixin,
|
panelTitleMixin,
|
||||||
loggingMixin,
|
loggingMixin,
|
||||||
|
ExternalMixin,
|
||||||
]) {}
|
]) {}
|
||||||
|
|
|
@ -131,5 +131,7 @@ export default <T extends Constructor<HassBaseEl>>(superClass: T) =>
|
||||||
(themeMeta.getAttribute("default-content") as string);
|
(themeMeta.getAttribute("default-content") as string);
|
||||||
themeMeta.setAttribute("content", themeColor);
|
themeMeta.setAttribute("content", themeColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.hass!.auth.external?.fireMessage({ type: "theme-update" });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,16 +2,16 @@ import { assert } from "chai";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ExternalMessaging,
|
ExternalMessaging,
|
||||||
InternalMessage,
|
EMMessage,
|
||||||
} from "../../src/external_app/external_messaging";
|
} from "../../src/external_app/external_messaging";
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
global.__DEV__ = true;
|
global.__DEV__ = true;
|
||||||
|
|
||||||
class MockExternalMessaging extends ExternalMessaging {
|
class MockExternalMessaging extends ExternalMessaging {
|
||||||
public mockSent: InternalMessage[] = [];
|
public mockSent: EMMessage[] = [];
|
||||||
|
|
||||||
protected _sendExternal(msg: InternalMessage) {
|
protected _sendExternal(msg: EMMessage) {
|
||||||
this.mockSent.push(msg);
|
this.mockSent.push(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue