-
This commit is contained in:
parent
ed73223ee1
commit
335b7bee5a
@ -7,7 +7,7 @@ from homeassistant.core import HomeAssistant
|
|||||||
from .const import CONF_CONFIG, DOMAIN
|
from .const import CONF_CONFIG, DOMAIN
|
||||||
from .frontend import async_setup_frontend
|
from .frontend import async_setup_frontend
|
||||||
from .helpers import current_entry_config
|
from .helpers import current_entry_config
|
||||||
from .views import WallPanelConfigView
|
from .views import WallPanelConfigView, WallPanelPanelView
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry) -> bool:
|
async def async_setup_entry(hass: HomeAssistant, entry) -> bool:
|
||||||
@ -26,6 +26,10 @@ async def async_setup_entry(hass: HomeAssistant, entry) -> bool:
|
|||||||
hass.http.register_view(WallPanelConfigView)
|
hass.http.register_view(WallPanelConfigView)
|
||||||
state["_config_view_registered"] = True
|
state["_config_view_registered"] = True
|
||||||
|
|
||||||
|
if not state.get("_panel_view_registered"):
|
||||||
|
hass.http.register_view(WallPanelPanelView)
|
||||||
|
state["_panel_view_registered"] = True
|
||||||
|
|
||||||
entry.async_on_unload(entry.add_update_listener(_async_options_updated))
|
entry.async_on_unload(entry.add_update_listener(_async_options_updated))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|||||||
@ -62,6 +62,10 @@ class WallPanelPanel extends HTMLElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_configUrl() {
|
_configUrl() {
|
||||||
|
return '/api/wall_panel/panel';
|
||||||
|
}
|
||||||
|
|
||||||
|
_legacyConfigUrl() {
|
||||||
const payload = this._panelConfig();
|
const payload = this._panelConfig();
|
||||||
const entryId = String(payload.entry_id || '').trim();
|
const entryId = String(payload.entry_id || '').trim();
|
||||||
if (!entryId) {
|
if (!entryId) {
|
||||||
@ -72,12 +76,13 @@ class WallPanelPanel extends HTMLElement {
|
|||||||
|
|
||||||
async _fetchPanelUrl() {
|
async _fetchPanelUrl() {
|
||||||
const payload = this._panelConfig();
|
const payload = this._panelConfig();
|
||||||
const configUrl = this._configUrl();
|
|
||||||
const syncToken = String(payload.sync_token || '').trim();
|
const syncToken = String(payload.sync_token || '').trim();
|
||||||
if (!configUrl || !syncToken) {
|
if (!syncToken) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const configUrls = [this._configUrl(), this._legacyConfigUrl()].filter(Boolean);
|
||||||
|
for (const configUrl of configUrls) {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(configUrl, {
|
const response = await fetch(configUrl, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
@ -90,15 +95,20 @@ class WallPanelPanel extends HTMLElement {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
return '';
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
const panelUrl = String(data?.panel?.panel_url || data?.panel_url || '').trim();
|
const panelUrl = String(data?.panel?.panel_url || data?.panel_url || '').trim();
|
||||||
|
if (panelUrl) {
|
||||||
return panelUrl;
|
return panelUrl;
|
||||||
} catch (error) {
|
|
||||||
return '';
|
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
_renderMessage(title, body, extra = '') {
|
_renderMessage(title, body, extra = '') {
|
||||||
|
|||||||
@ -30,6 +30,23 @@ def _entry_from_hass(hass: HomeAssistant, entry_id: str):
|
|||||||
return hass.data.get(DOMAIN, {}).get(entry_id, {}).get("entry")
|
return hass.data.get(DOMAIN, {}).get(entry_id, {}).get("entry")
|
||||||
|
|
||||||
|
|
||||||
|
def _default_entry_from_hass(hass: HomeAssistant):
|
||||||
|
entries = hass.data.get(DOMAIN, {})
|
||||||
|
for value in entries.values():
|
||||||
|
entry = value.get("entry") if isinstance(value, dict) else None
|
||||||
|
if entry is not None:
|
||||||
|
return entry
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _current_entry(hass: HomeAssistant, entry_id: str | None = None):
|
||||||
|
if entry_id:
|
||||||
|
entry = _entry_from_hass(hass, entry_id)
|
||||||
|
if entry is not None:
|
||||||
|
return entry
|
||||||
|
return _default_entry_from_hass(hass)
|
||||||
|
|
||||||
|
|
||||||
def _request_token(request: web.Request) -> str:
|
def _request_token(request: web.Request) -> str:
|
||||||
header = request.headers.get("X-Wall-Panel-Token", "").strip()
|
header = request.headers.get("X-Wall-Panel-Token", "").strip()
|
||||||
if header:
|
if header:
|
||||||
@ -180,3 +197,54 @@ class WallPanelConfigView(HomeAssistantView):
|
|||||||
"config": config,
|
"config": config,
|
||||||
"panel": current_entry_panel(entry),
|
"panel": current_entry_panel(entry),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
class WallPanelPanelView(HomeAssistantView):
|
||||||
|
"""Serve the active Wall Panel config without an entry id."""
|
||||||
|
|
||||||
|
url = "/api/wall_panel/panel"
|
||||||
|
name = "api:wall_panel:panel"
|
||||||
|
requires_auth = False
|
||||||
|
|
||||||
|
async def async_get(self, request: web.Request) -> web.Response:
|
||||||
|
hass = request.app["hass"]
|
||||||
|
entry = _default_entry_from_hass(hass)
|
||||||
|
if entry is None:
|
||||||
|
return _response({"ok": False, "error": "Unknown entry"}, 404)
|
||||||
|
if not _authorized(entry, request):
|
||||||
|
return _response({"ok": False, "error": "Unauthorized"}, 401)
|
||||||
|
|
||||||
|
config = current_entry_config(entry)
|
||||||
|
return _response({
|
||||||
|
"ok": True,
|
||||||
|
"config": config,
|
||||||
|
"panel": current_entry_panel(entry),
|
||||||
|
})
|
||||||
|
|
||||||
|
async def async_post(self, request: web.Request) -> web.Response:
|
||||||
|
hass = request.app["hass"]
|
||||||
|
entry = _default_entry_from_hass(hass)
|
||||||
|
if entry is None:
|
||||||
|
return _response({"ok": False, "error": "Unknown entry"}, 404)
|
||||||
|
|
||||||
|
payload = await request.json()
|
||||||
|
if not isinstance(payload, dict):
|
||||||
|
return _response({"ok": False, "error": "Invalid payload"}, 400)
|
||||||
|
|
||||||
|
action = str(payload.get("action", "") or "").strip().lower()
|
||||||
|
if action != "register-panel":
|
||||||
|
return _response({"ok": False, "error": "Unknown action"}, 404)
|
||||||
|
|
||||||
|
action_payload = payload.get("payload")
|
||||||
|
if not isinstance(action_payload, dict):
|
||||||
|
action_payload = payload
|
||||||
|
|
||||||
|
panel_url = str(action_payload.get("panel_url", "") or "").strip()
|
||||||
|
if not panel_url:
|
||||||
|
return _response({"ok": False, "error": "panel_url is required"}, 400)
|
||||||
|
|
||||||
|
_save_entry_panel_url(hass, entry, panel_url)
|
||||||
|
return _response({
|
||||||
|
"ok": True,
|
||||||
|
"panel": current_entry_panel(entry),
|
||||||
|
})
|
||||||
|
|||||||
@ -270,7 +270,17 @@ function app_supervisor_addon_slug(): string
|
|||||||
return $override;
|
return $override;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'wall_panel';
|
return 'self';
|
||||||
|
}
|
||||||
|
|
||||||
|
function app_addon_register_panel_url(): string
|
||||||
|
{
|
||||||
|
$override = trim((string)getenv('WALL_PANEL_REGISTER_URL'));
|
||||||
|
if ($override !== '') {
|
||||||
|
return $override;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'http://supervisor/core/api/wall_panel/register-panel';
|
||||||
}
|
}
|
||||||
|
|
||||||
function app_supervisor_ingress_url(): string
|
function app_supervisor_ingress_url(): string
|
||||||
@ -292,35 +302,47 @@ function app_supervisor_ingress_url(): string
|
|||||||
return trim((string)($response['ingress_url'] ?? ''));
|
return trim((string)($response['ingress_url'] ?? ''));
|
||||||
}
|
}
|
||||||
|
|
||||||
function app_register_ingress_url(array $config): void
|
function app_register_ingress_url(array $config): bool
|
||||||
{
|
{
|
||||||
if (!app_remote_sync_enabled($config)) {
|
if (app_runtime_mode() !== 'addon') {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$ingressUrl = app_supervisor_ingress_url();
|
$ingressUrl = app_supervisor_ingress_url();
|
||||||
if ($ingressUrl === '') {
|
if ($ingressUrl === '') {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$syncUrl = trim((string)($config['home_assistant']['sync_url'] ?? ''));
|
$registerUrl = app_addon_register_panel_url();
|
||||||
$syncToken = trim((string)($config['home_assistant']['sync_token'] ?? ''));
|
if ($registerUrl === '') {
|
||||||
$cacheKey = hash('sha256', $ingressUrl . '|' . $syncUrl . '|' . $syncToken);
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cacheKey = hash('sha256', $ingressUrl . '|' . $registerUrl);
|
||||||
|
|
||||||
$cachePath = app_panel_registration_cache_path();
|
$cachePath = app_panel_registration_cache_path();
|
||||||
$cache = app_load_json_file($cachePath, []);
|
$cache = app_load_json_file($cachePath, []);
|
||||||
if (trim((string)($cache['cache_key'] ?? '')) === $cacheKey) {
|
if (trim((string)($cache['cache_key'] ?? '')) === $cacheKey) {
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$response = app_http_json_request('POST', $syncUrl, [
|
$headers = [];
|
||||||
'X-Wall-Panel-Token: ' . $syncToken,
|
$supervisorToken = trim((string)getenv('SUPERVISOR_TOKEN'));
|
||||||
], [
|
if ($supervisorToken !== '') {
|
||||||
'action' => 'register-panel',
|
$headers[] = 'Authorization: Bearer ' . $supervisorToken;
|
||||||
'payload' => [
|
}
|
||||||
|
|
||||||
|
$response = app_http_json_request(
|
||||||
|
'POST',
|
||||||
|
$registerUrl,
|
||||||
|
$headers,
|
||||||
|
[
|
||||||
'panel_url' => $ingressUrl,
|
'panel_url' => $ingressUrl,
|
||||||
],
|
],
|
||||||
], max(1, (int)($config['home_assistant']['sync_timeout'] ?? 10)), (bool)($config['home_assistant']['sync_verify_ssl'] ?? true), false);
|
max(1, (int)($config['home_assistant']['sync_timeout'] ?? 10)),
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
if (is_array($response) && (bool)($response['ok'] ?? false)) {
|
if (is_array($response) && (bool)($response['ok'] ?? false)) {
|
||||||
app_save_json_file($cachePath, [
|
app_save_json_file($cachePath, [
|
||||||
@ -328,7 +350,10 @@ function app_register_ingress_url(array $config): void
|
|||||||
'ingress_url' => $ingressUrl,
|
'ingress_url' => $ingressUrl,
|
||||||
'registered_at' => time(),
|
'registered_at' => time(),
|
||||||
]);
|
]);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function app_load_remote_config(array $config, bool $refresh = false): array|null
|
function app_load_remote_config(array $config, bool $refresh = false): array|null
|
||||||
|
|||||||
16
run.sh
16
run.sh
@ -5,6 +5,7 @@ DOCROOT="${WALL_PANEL_DOCROOT:-/app}"
|
|||||||
PORT="${WALL_PANEL_PORT:-8099}"
|
PORT="${WALL_PANEL_PORT:-8099}"
|
||||||
CONFIG_PATH="${WALL_PANEL_CONFIG_PATH:-/config/config.json}"
|
CONFIG_PATH="${WALL_PANEL_CONFIG_PATH:-/config/config.json}"
|
||||||
STORAGE_DIR="${WALL_PANEL_STORAGE_DIR:-/config/storage}"
|
STORAGE_DIR="${WALL_PANEL_STORAGE_DIR:-/config/storage}"
|
||||||
|
RUNTIME_MODE="${WALL_PANEL_RUNTIME_MODE:-standalone}"
|
||||||
|
|
||||||
mkdir -p "$(dirname "$CONFIG_PATH")" "$STORAGE_DIR"
|
mkdir -p "$(dirname "$CONFIG_PATH")" "$STORAGE_DIR"
|
||||||
|
|
||||||
@ -14,8 +15,19 @@ fi
|
|||||||
|
|
||||||
export WALL_PANEL_CONFIG_PATH="$CONFIG_PATH"
|
export WALL_PANEL_CONFIG_PATH="$CONFIG_PATH"
|
||||||
export WALL_PANEL_STORAGE_DIR="$STORAGE_DIR"
|
export WALL_PANEL_STORAGE_DIR="$STORAGE_DIR"
|
||||||
export WALL_PANEL_RUNTIME_MODE="addon"
|
export WALL_PANEL_RUNTIME_MODE="$RUNTIME_MODE"
|
||||||
|
|
||||||
php -r 'require "/app/lib/bootstrap.php"; $config = app_load_config(); app_register_ingress_url($config);' >/dev/null 2>&1 || true &
|
if [ "$RUNTIME_MODE" = "addon" ]; then
|
||||||
|
(
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 120 ]; do
|
||||||
|
if php -r 'require "/app/lib/bootstrap.php"; $config = app_load_config(); exit(app_register_ingress_url($config) ? 0 : 1);' >/dev/null 2>&1; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
i=$((i + 1))
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
) >/dev/null 2>&1 || true &
|
||||||
|
fi
|
||||||
|
|
||||||
exec php -S "0.0.0.0:${PORT}" -t "$DOCROOT"
|
exec php -S "0.0.0.0:${PORT}" -t "$DOCROOT"
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"active": false,
|
"active": false,
|
||||||
"sensor_entity_id": "binary_sensor.barn_all_occupancy",
|
"sensor_entity_id": "binary_sensor.doorbell_all_occupancy",
|
||||||
"opened_at": 1774439958,
|
"opened_at": 1774441493,
|
||||||
"expires_at": null
|
"expires_at": null
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
name: Wall Panel
|
name: Wall Panel
|
||||||
description: Wall Panel PHP interface as a Home Assistant add-on
|
description: Wall Panel PHP interface as a Home Assistant add-on
|
||||||
version: "1.0.10"
|
version: "1.0.12"
|
||||||
slug: wall_panel
|
slug: wall_panel
|
||||||
url: https://git.striker72rus.ru/PHP/wallpanell.git
|
url: https://git.striker72rus.ru/PHP/wallpanell.git
|
||||||
init: false
|
init: false
|
||||||
|
|||||||
@ -270,7 +270,7 @@ function app_supervisor_addon_slug(): string
|
|||||||
return $override;
|
return $override;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'wall_panel';
|
return 'self';
|
||||||
}
|
}
|
||||||
|
|
||||||
function app_supervisor_ingress_url(): string
|
function app_supervisor_ingress_url(): string
|
||||||
@ -292,35 +292,57 @@ function app_supervisor_ingress_url(): string
|
|||||||
return trim((string)($response['ingress_url'] ?? ''));
|
return trim((string)($response['ingress_url'] ?? ''));
|
||||||
}
|
}
|
||||||
|
|
||||||
function app_register_ingress_url(array $config): void
|
function app_addon_register_panel_url(): string
|
||||||
{
|
{
|
||||||
if (!app_remote_sync_enabled($config)) {
|
$override = trim((string)getenv('WALL_PANEL_REGISTER_URL'));
|
||||||
return;
|
if ($override !== '') {
|
||||||
|
return $override;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'http://supervisor/core/api/wall_panel/register-panel';
|
||||||
|
}
|
||||||
|
|
||||||
|
function app_register_ingress_url(array $config): bool
|
||||||
|
{
|
||||||
|
if (app_runtime_mode() !== 'addon') {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$ingressUrl = app_supervisor_ingress_url();
|
$ingressUrl = app_supervisor_ingress_url();
|
||||||
if ($ingressUrl === '') {
|
if ($ingressUrl === '') {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$syncUrl = trim((string)($config['home_assistant']['sync_url'] ?? ''));
|
$registerUrl = app_addon_register_panel_url();
|
||||||
$syncToken = trim((string)($config['home_assistant']['sync_token'] ?? ''));
|
if ($registerUrl === '') {
|
||||||
$cacheKey = hash('sha256', $ingressUrl . '|' . $syncUrl . '|' . $syncToken);
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cacheKey = hash('sha256', $ingressUrl . '|' . $registerUrl);
|
||||||
|
|
||||||
$cachePath = app_panel_registration_cache_path();
|
$cachePath = app_panel_registration_cache_path();
|
||||||
$cache = app_load_json_file($cachePath, []);
|
$cache = app_load_json_file($cachePath, []);
|
||||||
if (trim((string)($cache['cache_key'] ?? '')) === $cacheKey) {
|
if (trim((string)($cache['cache_key'] ?? '')) === $cacheKey) {
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$response = app_http_json_request('POST', $syncUrl, [
|
$headers = [];
|
||||||
'X-Wall-Panel-Token: ' . $syncToken,
|
$supervisorToken = trim((string)getenv('SUPERVISOR_TOKEN'));
|
||||||
], [
|
if ($supervisorToken !== '') {
|
||||||
'action' => 'register-panel',
|
$headers[] = 'Authorization: Bearer ' . $supervisorToken;
|
||||||
'payload' => [
|
}
|
||||||
|
|
||||||
|
$response = app_http_json_request(
|
||||||
|
'POST',
|
||||||
|
$registerUrl,
|
||||||
|
$headers,
|
||||||
|
[
|
||||||
'panel_url' => $ingressUrl,
|
'panel_url' => $ingressUrl,
|
||||||
],
|
],
|
||||||
], max(1, (int)($config['home_assistant']['sync_timeout'] ?? 10)), (bool)($config['home_assistant']['sync_verify_ssl'] ?? true), false);
|
max(1, (int)($config['home_assistant']['sync_timeout'] ?? 10)),
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
if (is_array($response) && (bool)($response['ok'] ?? false)) {
|
if (is_array($response) && (bool)($response['ok'] ?? false)) {
|
||||||
app_save_json_file($cachePath, [
|
app_save_json_file($cachePath, [
|
||||||
@ -328,7 +350,10 @@ function app_register_ingress_url(array $config): void
|
|||||||
'ingress_url' => $ingressUrl,
|
'ingress_url' => $ingressUrl,
|
||||||
'registered_at' => time(),
|
'registered_at' => time(),
|
||||||
]);
|
]);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function app_load_remote_config(array $config, bool $refresh = false): array|null
|
function app_load_remote_config(array $config, bool $refresh = false): array|null
|
||||||
|
|||||||
@ -16,6 +16,15 @@ export WALL_PANEL_CONFIG_PATH="$CONFIG_PATH"
|
|||||||
export WALL_PANEL_STORAGE_DIR="$STORAGE_DIR"
|
export WALL_PANEL_STORAGE_DIR="$STORAGE_DIR"
|
||||||
export WALL_PANEL_RUNTIME_MODE="addon"
|
export WALL_PANEL_RUNTIME_MODE="addon"
|
||||||
|
|
||||||
php -r 'require "/app/lib/bootstrap.php"; $config = app_load_config(); app_register_ingress_url($config);' >/dev/null 2>&1 || true &
|
(
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 120 ]; do
|
||||||
|
if php -r 'require "/app/lib/bootstrap.php"; $config = app_load_config(); exit(app_register_ingress_url($config) ? 0 : 1);' >/dev/null 2>&1; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
i=$((i + 1))
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
) >/dev/null 2>&1 || true &
|
||||||
|
|
||||||
exec php -S "0.0.0.0:${PORT}" -t "$DOCROOT"
|
exec php -S "0.0.0.0:${PORT}" -t "$DOCROOT"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user