241 lines
5.7 KiB
JavaScript
Executable File
241 lines
5.7 KiB
JavaScript
Executable File
class WallPanelPanel extends HTMLElement {
|
|
constructor() {
|
|
super();
|
|
this._hass = null;
|
|
this._panel = null;
|
|
this._narrow = false;
|
|
this._pollTimer = null;
|
|
this._activePanelUrl = '';
|
|
this.attachShadow({ mode: 'open' });
|
|
}
|
|
|
|
set hass(hass) {
|
|
this._hass = hass;
|
|
this._render();
|
|
}
|
|
|
|
set panel(panel) {
|
|
this._panel = panel;
|
|
this._render();
|
|
}
|
|
|
|
set narrow(narrow) {
|
|
this._narrow = Boolean(narrow);
|
|
this._render();
|
|
}
|
|
|
|
connectedCallback() {
|
|
this._render();
|
|
}
|
|
|
|
disconnectedCallback() {
|
|
if (this._pollTimer) {
|
|
window.clearInterval(this._pollTimer);
|
|
this._pollTimer = null;
|
|
}
|
|
}
|
|
|
|
_resolveUrl(rawUrl) {
|
|
const value = String(rawUrl || '').trim();
|
|
if (!value) {
|
|
return '';
|
|
}
|
|
|
|
try {
|
|
const url = new URL(value, window.location.href);
|
|
if (!url.searchParams.has('embed')) {
|
|
url.searchParams.set('embed', '1');
|
|
}
|
|
if (!url.searchParams.has('mode')) {
|
|
url.searchParams.set('mode', 'ha');
|
|
}
|
|
return url.toString();
|
|
} catch (error) {
|
|
return value;
|
|
}
|
|
}
|
|
|
|
_panelConfig() {
|
|
const config = this._panel?.config || {};
|
|
const customConfig = config._panel_custom || {};
|
|
return customConfig.config || customConfig || config;
|
|
}
|
|
|
|
_configUrl() {
|
|
const payload = this._panelConfig();
|
|
const entryId = String(payload.entry_id || '').trim();
|
|
if (!entryId) {
|
|
return '';
|
|
}
|
|
return `/api/wall_panel/config/${encodeURIComponent(entryId)}`;
|
|
}
|
|
|
|
async _fetchPanelUrl() {
|
|
const payload = this._panelConfig();
|
|
const configUrl = this._configUrl();
|
|
const syncToken = String(payload.sync_token || '').trim();
|
|
if (!configUrl || !syncToken) {
|
|
return '';
|
|
}
|
|
|
|
try {
|
|
const response = await fetch(configUrl, {
|
|
method: 'GET',
|
|
headers: {
|
|
'X-Wall-Panel-Token': syncToken,
|
|
Accept: 'application/json',
|
|
},
|
|
credentials: 'same-origin',
|
|
cache: 'no-store',
|
|
});
|
|
|
|
if (!response.ok) {
|
|
return '';
|
|
}
|
|
|
|
const data = await response.json();
|
|
const panelUrl = String(data?.panel?.panel_url || data?.panel_url || '').trim();
|
|
return panelUrl;
|
|
} catch (error) {
|
|
return '';
|
|
}
|
|
}
|
|
|
|
_renderMessage(title, body, extra = '') {
|
|
if (!this.shadowRoot) {
|
|
return;
|
|
}
|
|
|
|
this.shadowRoot.innerHTML = `
|
|
<style>
|
|
:host {
|
|
display: block;
|
|
width: 100%;
|
|
height: 100%;
|
|
min-height: 100vh;
|
|
background: var(--primary-background-color, #0d0f14);
|
|
color: var(--primary-text-color, #fff);
|
|
}
|
|
.wrap {
|
|
position: relative;
|
|
width: 100%;
|
|
height: 100vh;
|
|
overflow: hidden;
|
|
background: var(--primary-background-color, #0d0f14);
|
|
}
|
|
.message {
|
|
display: grid;
|
|
place-items: center;
|
|
height: 100%;
|
|
padding: 24px;
|
|
box-sizing: border-box;
|
|
text-align: center;
|
|
font-family: var(--primary-font-family, sans-serif);
|
|
color: var(--secondary-text-color, #b3b8c2);
|
|
}
|
|
.message strong {
|
|
display: block;
|
|
color: var(--primary-text-color, #fff);
|
|
margin-bottom: 8px;
|
|
font-size: 1.1rem;
|
|
}
|
|
.message code {
|
|
display: inline-block;
|
|
margin-top: 10px;
|
|
padding: 6px 10px;
|
|
border-radius: 8px;
|
|
background: rgba(255, 255, 255, 0.06);
|
|
color: var(--primary-text-color, #fff);
|
|
word-break: break-all;
|
|
}
|
|
</style>
|
|
<div class="wrap">
|
|
<div class="message">
|
|
<div>
|
|
<strong>${title}</strong>
|
|
<div>${body}</div>
|
|
${extra}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
_renderIframe(panelUrl) {
|
|
if (!this.shadowRoot) {
|
|
return;
|
|
}
|
|
|
|
const wrap = this.shadowRoot.querySelector('.wrap');
|
|
if (!wrap) {
|
|
return;
|
|
}
|
|
|
|
const iframe = document.createElement('iframe');
|
|
iframe.src = panelUrl;
|
|
iframe.loading = 'eager';
|
|
iframe.referrerPolicy = 'no-referrer';
|
|
iframe.allow = 'autoplay; fullscreen; picture-in-picture';
|
|
iframe.style.width = '100%';
|
|
iframe.style.height = '100%';
|
|
iframe.style.border = '0';
|
|
iframe.style.display = 'block';
|
|
iframe.style.background = 'transparent';
|
|
wrap.replaceChildren(iframe);
|
|
this._activePanelUrl = panelUrl;
|
|
}
|
|
|
|
async _tryAttachPanel() {
|
|
const payload = this._panelConfig();
|
|
const initialUrl = this._resolveUrl(
|
|
payload.panel_url || payload.ingress_url || ''
|
|
);
|
|
|
|
if (initialUrl && initialUrl === this._activePanelUrl) {
|
|
return;
|
|
}
|
|
|
|
if (initialUrl && !(window.location.protocol === 'https:' && initialUrl.startsWith('http://'))) {
|
|
this._renderIframe(initialUrl);
|
|
return;
|
|
}
|
|
|
|
const panelUrl = this._resolveUrl(await this._fetchPanelUrl());
|
|
if (panelUrl && panelUrl === this._activePanelUrl) {
|
|
return;
|
|
}
|
|
|
|
if (panelUrl && !(window.location.protocol === 'https:' && panelUrl.startsWith('http://'))) {
|
|
this._renderIframe(panelUrl);
|
|
return;
|
|
}
|
|
|
|
const configUrl = this._configUrl();
|
|
this._renderMessage(
|
|
'Waiting for Wall Panel',
|
|
'The add-on will publish a secure HTTPS ingress URL here.',
|
|
configUrl ? `<code>${configUrl}</code>` : ''
|
|
);
|
|
}
|
|
|
|
_render() {
|
|
if (!this.shadowRoot) {
|
|
return;
|
|
}
|
|
|
|
if (this._pollTimer) {
|
|
window.clearInterval(this._pollTimer);
|
|
this._pollTimer = null;
|
|
}
|
|
|
|
this._tryAttachPanel();
|
|
this._pollTimer = window.setInterval(() => {
|
|
this._tryAttachPanel();
|
|
}, 2000);
|
|
}
|
|
}
|
|
|
|
if (!customElements.get('wall-panel-panel')) {
|
|
customElements.define('wall-panel-panel', WallPanelPanel);
|
|
}
|