mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-25 18:26:35 +00:00
Support ingress custom panels (#3085)
* Support ingress custom panels * Fix types * Add disabled placeholder to network card
This commit is contained in:
parent
5dbd5c7395
commit
bbae3291e1
@ -44,6 +44,7 @@ class HassioAddonNetwork extends EventsMixin(PolymerElement) {
|
|||||||
<td>[[item.container]]</td>
|
<td>[[item.container]]</td>
|
||||||
<td>
|
<td>
|
||||||
<paper-input
|
<paper-input
|
||||||
|
placeholder="disabled"
|
||||||
value="{{item.host}}"
|
value="{{item.host}}"
|
||||||
no-label-float=""
|
no-label-float=""
|
||||||
></paper-input>
|
></paper-input>
|
||||||
|
@ -17,6 +17,9 @@ import {
|
|||||||
HassioSupervisorInfo,
|
HassioSupervisorInfo,
|
||||||
HassioHostInfo,
|
HassioHostInfo,
|
||||||
HassioHomeAssistantInfo,
|
HassioHomeAssistantInfo,
|
||||||
|
fetchHassioAddonInfo,
|
||||||
|
createHassioSession,
|
||||||
|
HassioPanelInfo,
|
||||||
} from "../../src/data/hassio";
|
} from "../../src/data/hassio";
|
||||||
import { makeDialogManager } from "../../src/dialogs/make-dialog-manager";
|
import { makeDialogManager } from "../../src/dialogs/make-dialog-manager";
|
||||||
import { ProvideHassLitMixin } from "../../src/mixins/provide-hass-lit-mixin";
|
import { ProvideHassLitMixin } from "../../src/mixins/provide-hass-lit-mixin";
|
||||||
@ -32,6 +35,7 @@ customElements.get("paper-icon-button").prototype._keyBindings = {};
|
|||||||
@customElement("hassio-main")
|
@customElement("hassio-main")
|
||||||
class HassioMain extends ProvideHassLitMixin(HassRouterPage) {
|
class HassioMain extends ProvideHassLitMixin(HassRouterPage) {
|
||||||
@property() public hass!: HomeAssistant;
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public panel!: HassioPanelInfo;
|
||||||
|
|
||||||
protected routerOptions: RouterOptions = {
|
protected routerOptions: RouterOptions = {
|
||||||
// Hass.io has a page with tabs, so we route all non-matching routes to it.
|
// Hass.io has a page with tabs, so we route all non-matching routes to it.
|
||||||
@ -117,6 +121,11 @@ class HassioMain extends ProvideHassLitMixin(HassRouterPage) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async _fetchData() {
|
private async _fetchData() {
|
||||||
|
if (this.panel.config && this.panel.config.ingress) {
|
||||||
|
await this._redirectIngress(this.panel.config.ingress);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const [supervisorInfo, hostInfo, hassInfo] = await Promise.all([
|
const [supervisorInfo, hostInfo, hassInfo] = await Promise.all([
|
||||||
fetchHassioSupervisorInfo(this.hass),
|
fetchHassioSupervisorInfo(this.hass),
|
||||||
fetchHassioHostInfo(this.hass),
|
fetchHassioHostInfo(this.hass),
|
||||||
@ -127,6 +136,28 @@ class HassioMain extends ProvideHassLitMixin(HassRouterPage) {
|
|||||||
this._hassInfo = hassInfo;
|
this._hassInfo = hassInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async _redirectIngress(addonSlug: string) {
|
||||||
|
try {
|
||||||
|
const [addon] = await Promise.all([
|
||||||
|
fetchHassioAddonInfo(this.hass, addonSlug).catch(() => {
|
||||||
|
throw new Error("Failed to fetch add-on info");
|
||||||
|
}),
|
||||||
|
createHassioSession(this.hass).catch(() => {
|
||||||
|
throw new Error("Failed to create an ingress session");
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
if (!addon.ingress_url) {
|
||||||
|
throw new Error("Add-on does not support Ingress");
|
||||||
|
}
|
||||||
|
location.assign(addon.ingress_url);
|
||||||
|
// await a promise that doesn't resolve, so we show the loading screen
|
||||||
|
// while we load the next page.
|
||||||
|
await new Promise(() => undefined);
|
||||||
|
} catch (err) {
|
||||||
|
alert(`Unable to open ingress connection `);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private _apiCalled(ev) {
|
private _apiCalled(ev) {
|
||||||
if (!ev.detail.success) {
|
if (!ev.detail.success) {
|
||||||
return;
|
return;
|
||||||
|
@ -15,7 +15,7 @@ import "./ha-icon";
|
|||||||
|
|
||||||
import "../components/user/ha-user-badge";
|
import "../components/user/ha-user-badge";
|
||||||
import isComponentLoaded from "../common/config/is_component_loaded";
|
import isComponentLoaded from "../common/config/is_component_loaded";
|
||||||
import { HomeAssistant, Panel } from "../types";
|
import { HomeAssistant, PanelInfo } from "../types";
|
||||||
import { fireEvent } from "../common/dom/fire_event";
|
import { fireEvent } from "../common/dom/fire_event";
|
||||||
import { DEFAULT_PANEL } from "../common/const";
|
import { DEFAULT_PANEL } from "../common/const";
|
||||||
|
|
||||||
@ -32,7 +32,9 @@ const computePanels = (hass: HomeAssistant) => {
|
|||||||
logbook: 2,
|
logbook: 2,
|
||||||
history: 3,
|
history: 3,
|
||||||
};
|
};
|
||||||
const result: Panel[] = Object.values(panels).filter((panel) => panel.title);
|
const result: PanelInfo[] = Object.values(panels).filter(
|
||||||
|
(panel) => panel.title
|
||||||
|
);
|
||||||
|
|
||||||
result.sort((a, b) => {
|
result.sort((a, b) => {
|
||||||
const aBuiltIn = a.component_name in sortValue;
|
const aBuiltIn = a.component_name in sortValue;
|
||||||
|
@ -1,4 +1,11 @@
|
|||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant, PanelInfo } from "../types";
|
||||||
|
|
||||||
|
export type HassioPanelInfo = PanelInfo<
|
||||||
|
| undefined
|
||||||
|
| {
|
||||||
|
ingress?: string;
|
||||||
|
}
|
||||||
|
>;
|
||||||
|
|
||||||
interface HassioResponse<T> {
|
interface HassioResponse<T> {
|
||||||
data: T;
|
data: T;
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { PanelInfo } from "../types";
|
||||||
|
|
||||||
export interface CustomPanelConfig {
|
export interface CustomPanelConfig {
|
||||||
name: string;
|
name: string;
|
||||||
embed_iframe: boolean;
|
embed_iframe: boolean;
|
||||||
@ -6,3 +8,7 @@ export interface CustomPanelConfig {
|
|||||||
module_url?: string;
|
module_url?: string;
|
||||||
html_url?: string;
|
html_url?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type CustomPanelInfo<T = {}> = PanelInfo<
|
||||||
|
T & { _panel_custom: CustomPanelConfig }
|
||||||
|
>;
|
||||||
|
@ -4,8 +4,7 @@ import { createCustomPanelElement } from "../util/custom-panel/create-custom-pan
|
|||||||
import { setCustomPanelProperties } from "../util/custom-panel/set-custom-panel-properties";
|
import { setCustomPanelProperties } from "../util/custom-panel/set-custom-panel-properties";
|
||||||
import { fireEvent } from "../common/dom/fire_event";
|
import { fireEvent } from "../common/dom/fire_event";
|
||||||
import { PolymerElement } from "@polymer/polymer";
|
import { PolymerElement } from "@polymer/polymer";
|
||||||
import { Panel } from "../types";
|
import { CustomPanelInfo } from "../data/panel_custom";
|
||||||
import { CustomPanelConfig } from "../data/panel_custom";
|
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
@ -39,12 +38,12 @@ function setProperties(properties) {
|
|||||||
setCustomPanelProperties(panelEl, properties);
|
setCustomPanelProperties(panelEl, properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
function initialize(panel: Panel, properties: {}) {
|
function initialize(panel: CustomPanelInfo, properties: {}) {
|
||||||
const style = document.createElement("style");
|
const style = document.createElement("style");
|
||||||
style.innerHTML = "body{margin:0}";
|
style.innerHTML = "body{margin:0}";
|
||||||
document.head.appendChild(style);
|
document.head.appendChild(style);
|
||||||
|
|
||||||
const config = panel.config!._panel_custom as CustomPanelConfig;
|
const config = panel.config._panel_custom;
|
||||||
let start: Promise<unknown> = Promise.resolve();
|
let start: Promise<unknown> = Promise.resolve();
|
||||||
|
|
||||||
if (!webComponentsSupported) {
|
if (!webComponentsSupported) {
|
||||||
|
@ -2,8 +2,8 @@ import { property, PropertyValues, UpdatingElement } from "lit-element";
|
|||||||
import { loadCustomPanel } from "../../util/custom-panel/load-custom-panel";
|
import { loadCustomPanel } from "../../util/custom-panel/load-custom-panel";
|
||||||
import { createCustomPanelElement } from "../../util/custom-panel/create-custom-panel-element";
|
import { createCustomPanelElement } from "../../util/custom-panel/create-custom-panel-element";
|
||||||
import { setCustomPanelProperties } from "../../util/custom-panel/set-custom-panel-properties";
|
import { setCustomPanelProperties } from "../../util/custom-panel/set-custom-panel-properties";
|
||||||
import { HomeAssistant, Route, Panel } from "../../types";
|
import { HomeAssistant, Route } from "../../types";
|
||||||
import { CustomPanelConfig } from "../../data/panel_custom";
|
import { CustomPanelInfo } from "../../data/panel_custom";
|
||||||
import { navigate } from "../../common/navigate";
|
import { navigate } from "../../common/navigate";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
@ -16,7 +16,7 @@ export class HaPanelCustom extends UpdatingElement {
|
|||||||
@property() public hass!: HomeAssistant;
|
@property() public hass!: HomeAssistant;
|
||||||
@property() public narrow!: boolean;
|
@property() public narrow!: boolean;
|
||||||
@property() public route!: Route;
|
@property() public route!: Route;
|
||||||
@property() public panel!: Panel;
|
@property() public panel!: CustomPanelInfo;
|
||||||
private _setProperties?: (props: {}) => void | undefined;
|
private _setProperties?: (props: {}) => void | undefined;
|
||||||
|
|
||||||
// Since navigate fires events on `window`, we need to expose this as a function
|
// Since navigate fires events on `window`, we need to expose this as a function
|
||||||
@ -66,8 +66,8 @@ export class HaPanelCustom extends UpdatingElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createPanel(panel: Panel) {
|
private _createPanel(panel: CustomPanelInfo) {
|
||||||
const config = panel.config!._panel_custom as CustomPanelConfig;
|
const config = panel.config!._panel_custom;
|
||||||
|
|
||||||
const tempA = document.createElement("a");
|
const tempA = document.createElement("a");
|
||||||
tempA.href = config.html_url || config.js_url || config.module_url || "";
|
tempA.href = config.html_url || config.js_url || config.module_url || "";
|
||||||
|
16
src/types.ts
16
src/types.ts
@ -78,16 +78,16 @@ export interface Themes {
|
|||||||
themes: { [key: string]: Theme };
|
themes: { [key: string]: Theme };
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Panel {
|
export interface PanelInfo<T = {} | null> {
|
||||||
component_name: string;
|
component_name: string;
|
||||||
config: { [key: string]: any } | null;
|
config: T;
|
||||||
icon: string | null;
|
icon: string | null;
|
||||||
title: string | null;
|
title: string | null;
|
||||||
url_path: string;
|
url_path: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Panels {
|
export interface Panels {
|
||||||
[name: string]: Panel;
|
[name: string]: PanelInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Translation {
|
export interface Translation {
|
||||||
@ -212,14 +212,6 @@ export type CameraEntity = HassEntityBase & {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface PanelInfo<T = unknown> {
|
|
||||||
component_name: string;
|
|
||||||
icon?: string;
|
|
||||||
title?: string;
|
|
||||||
url_path: string;
|
|
||||||
config: T;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Route {
|
export interface Route {
|
||||||
prefix: string;
|
prefix: string;
|
||||||
path: string;
|
path: string;
|
||||||
@ -229,7 +221,7 @@ export interface PanelElement extends HTMLElement {
|
|||||||
hass?: HomeAssistant;
|
hass?: HomeAssistant;
|
||||||
narrow?: boolean;
|
narrow?: boolean;
|
||||||
route?: Route | null;
|
route?: Route | null;
|
||||||
panel?: Panel;
|
panel?: PanelInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LocalizeMixin {
|
export interface LocalizeMixin {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user