Merge PR #1639: Migrate docker_monitored_containers to modern Group integration

Migrate docker_monitored_containers to modern Group integration
pull/1640/head
Carlo Costanzo 9 hours ago committed by GitHub
commit 8df520c930
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

2
.gitignore vendored

@ -70,6 +70,7 @@ hacs
alexa_media
custom_components
config/www/community
config/.cache/brands/integrations/
community
image
tts
@ -80,3 +81,4 @@ panel-notes
docker_14
docker_69
.codex_tmp/

@ -194,11 +194,10 @@
card_param: cards
filter:
include:
- group: group.docker_monitored_containers
- group: switch.docker_monitored_containers
options:
type: custom:button-card
template: bearstone_infra_container_row
icon: mdi:docker
sort:
method: name

@ -449,7 +449,7 @@
card_param: cards
filter:
include:
- group: group.docker_monitored_containers
- group: switch.docker_monitored_containers
options:
type: custom:button-card
template: bearstone_infra_container_row
@ -636,4 +636,3 @@
name: Download
- entity: sensor.speedtest_upload
name: Upload

@ -614,7 +614,7 @@ bearstone_infra_container_row:
}
const switchEntity = key ? `switch.${key}_container` : '';
const switchEntityAlt = key ? `switch.${key}_container_2` : '';
const monitored = states['group.docker_monitored_containers']?.attributes?.entity_id || [];
const monitored = states['switch.docker_monitored_containers']?.attributes?.entity_id || [];
const restartCandidates = key ? [
`button.${key}_restart_container`,
`button.${key}_restart_container_2`,

@ -59,6 +59,7 @@ Live collection of plug-and-play Home Assistant packages. Each YAML file in this
| [vacation_mode.yaml](vacation_mode.yaml) | Auto-enable vacation mode after 24 hours away or no bed use, track sitter analytics/secure-house checks, and deliver Chromecast-first vacation briefings with a garage Alexa welcome. | `input_boolean.vacation_mode`, `input_boolean.house_sitter_present`, `sensor.vacation_house_sitter_*`, `group.garage_doors`, `lock.front_door`, `script.notify_engine`, `script.joanna_send_telegram` |
| [maintenance_log.yaml](maintenance_log.yaml) | Joanna maintenance webhook ingest for water softener salt with idempotent event handling, Activity feed logging, and recorder-backed helper history for long-term graphing. | `automation.maintenance_log_joanna_webhook_ingest`, `input_number.water_softener_salt_total_added_lb`, `counter.water_softener_salt_event_count`, `sensor.water_softener_salt_days_since_last_add` |
| [powerwall.yaml](powerwall.yaml) | Track Tesla Powerwall grid status and shed loads automatically when off-grid (alerts include Activity feed + Repairs). | `binary_sensor.powerwall_grid_status`, `sensor.powerwall_*`, `repairs.create` |
| [tesla_model_y.yaml](tesla_model_y.yaml) | Remind the garage and parents to plug in the Model Y after low-battery arrivals and after 8 PM when it is home but not charging. | `sensor.spaceship_battery_level`, `switch.spaceship_charge`, `notify.alexa_media_garage`, `script.notify_engine` |
| [vacuum.yaml](vacuum.yaml) | Dreame vacuum orchestration with room tracking, push alerts, Activity feed, Repairs issues on errors, and Alexa one-off room-clean switches. | `input_select.l10s_vacuum_phase`, `sensor.l10s_vacuum_error`, `repairs.create` |
| [hass_agent_homepc.yaml](hass_agent_homepc.yaml) | Mirrors PC lock/unlock state from HASS.Agent to the office lamp for instant desk presence cues. | `sensor.carlo_homepc_carlo_homepc_sessionstate`, `switch.office_lamp_switch` |
| [sleepiq.yaml](sleepiq.yaml) | Sleep Number presence/snore automations; Overview Health consumes direct SleepIQ integration entities for scores, vitals, pressure, and bed controls. | `sensor.sleepnumber_carlo_carlo_sleep_score`, `sensor.sleepnumber_carlo_stacey_sleep_score`, `number.sleepnumber_carlo_carlo_firmness`, `select.sleepnumber_carlo_foundation_preset_right` |

@ -383,6 +383,15 @@ automation:
action:
- variables:
message: "{{ trigger.json.message | default('Joanna: empty reply') }}"
telegram_message: "{{ trigger.json.telegram_message | default(message, true) }}"
telegram_parse_mode: >-
{% set raw = trigger.json.telegram_parse_mode | default('plain_text', true) | string | lower | trim %}
{% if raw in ['html', 'plain_text'] %}
{{ raw }}
{% else %}
plain_text
{% endif %}
telegram_disable_preview: "{{ trigger.json.disable_web_page_preview | default(true, true) }}"
level: "{{ trigger.json.level | default('active') | lower }}"
inline_keyboard_payload: >-
{% set kb = trigger.json.inline_keyboard if trigger.json.inline_keyboard is defined else none %}
@ -408,14 +417,16 @@ automation:
- service: telegram_bot.send_message
data:
chat_id: !secret telegram_allowed_chat_id_carlo
message: "{{ message }}"
parse_mode: plain_text
disable_web_page_preview: true
message: "{{ telegram_message }}"
parse_mode: "{{ telegram_parse_mode }}"
disable_web_page_preview: "{{ telegram_disable_preview }}"
inline_keyboard: "{{ inline_keyboard_payload }}"
default:
- service: script.joanna_send_telegram
data:
message: "{{ message }}"
message: "{{ telegram_message }}"
parse_mode: "{{ telegram_parse_mode }}"
disable_web_page_preview: "{{ telegram_disable_preview }}"
- service: script.send_to_logbook
data:
topic: JOANNA

@ -4,9 +4,9 @@
# Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig
# -------------------------------------------------------------------
# Docker Infrastructure - Host patching and container alerts
# Related Issue: 1632, 1584
# APT webhook results (docker_10/14/17/69) and container down repairs.
# -------------------------------------------------------------------
# Related Issue: 1584
# Notes: Hosts run weekly Wed 12:00 APT job and POST JSON to webhooks.
# Notes: Reboots are handled directly on each host by apt_weekly.sh.
# Notes: Reboot staggering: docker_14 first, docker_69 second, docker_10 third.
@ -66,9 +66,10 @@ input_text:
name: "docker_69 APT last result"
max: 255
group:
docker_monitored_containers:
switch:
- platform: group
name: Docker Monitored Containers
unique_id: docker_monitored_containers
entities:
- switch.cloudflared_kch_container
- switch.cloudflared_wp_container
@ -206,14 +207,14 @@ template:
unique_id: docker_monitored_container_count
icon: mdi:format-list-numbered
state: >-
{{ state_attr('group.docker_monitored_containers', 'entity_id') | default([], true) | count }}
{{ state_attr('switch.docker_monitored_containers', 'entity_id') | default([], true) | count }}
- name: "Docker Monitored Unavailable Count"
unique_id: docker_monitored_unavailable_count
icon: mdi:lan-disconnect
state: >-
{% set ns = namespace(keys=[], unavailable=0) %}
{% set monitored = state_attr('group.docker_monitored_containers', 'entity_id') | default([], true) %}
{% set monitored = state_attr('switch.docker_monitored_containers', 'entity_id') | default([], true) %}
{% for switch_entity in monitored %}
{% set key = switch_entity | replace('switch.', '') | regex_replace('_container(?:_2)?$', '') %}
{% if key not in ns.keys %}
@ -257,7 +258,7 @@ template:
icon: mdi:docker
state: >-
{% set ns = namespace(keys=[], down=[]) %}
{% set monitored = state_attr('group.docker_monitored_containers', 'entity_id') | default([], true) | list %}
{% set monitored = state_attr('switch.docker_monitored_containers', 'entity_id') | default([], true) | list %}
{% set telemetry_degraded = is_state('binary_sensor.docker_container_telemetry_degraded', 'on') %}
{% for switch_entity in monitored %}
{% set key = switch_entity | replace('switch.', '') | regex_replace('_container(?:_2)?$', '') %}
@ -301,7 +302,7 @@ template:
attributes:
down_containers: >-
{% set ns = namespace(keys=[], down=[]) %}
{% set monitored = state_attr('group.docker_monitored_containers', 'entity_id') | default([], true) | list %}
{% set monitored = state_attr('switch.docker_monitored_containers', 'entity_id') | default([], true) | list %}
{% set telemetry_degraded = is_state('binary_sensor.docker_container_telemetry_degraded', 'on') %}
{% for switch_entity in monitored %}
{% set key = switch_entity | replace('switch.', '') | regex_replace('_container(?:_2)?$', '') %}
@ -437,7 +438,7 @@ script:
status_entity_alt: "binary_sensor.{{ container_key }}_status_2"
state_entity: "sensor.{{ container_key }}_state"
state_entity_alt: "sensor.{{ container_key }}_state_2"
monitored_switches: "{{ state_attr('group.docker_monitored_containers', 'entity_id') | default([], true) }}"
monitored_switches: "{{ state_attr('switch.docker_monitored_containers', 'entity_id') | default([], true) }}"
tracked_container: "{{ switch_entity in monitored_switches or switch_entity_alt in monitored_switches }}"
effective_entity: >-
{% if expand(status_entity) | count > 0 %}
@ -798,7 +799,7 @@ automation:
entity_id: "{{ trigger.event.data.entity_id | default('') }}"
old_state: "{{ (trigger.event.data.old_state.state if trigger.event.data.old_state is not none else '') | lower }}"
new_state: "{{ (trigger.event.data.new_state.state if trigger.event.data.new_state is not none else '') | lower }}"
monitored_switches: "{{ state_attr('group.docker_monitored_containers', 'entity_id') | default([], true) | list }}"
monitored_switches: "{{ state_attr('switch.docker_monitored_containers', 'entity_id') | default([], true) | list }}"
is_monitored_container_event: >-
{% set ent = entity_id %}
{% if ent.startswith('switch.') and (ent.endswith('_container') or ent.endswith('_container_2')) %}
@ -888,7 +889,7 @@ automation:
minutes: "/55"
action:
- variables:
monitored_switches: "{{ state_attr('group.docker_monitored_containers', 'entity_id') | default([], true) | list }}"
monitored_switches: "{{ state_attr('switch.docker_monitored_containers', 'entity_id') | default([], true) | list }}"
- repeat:
for_each: "{{ monitored_switches }}"
sequence:

@ -330,13 +330,8 @@ automation:
trigger:
- platform: time_pattern
minutes: '/45'
- platform: state
entity_id:
- cover.large_garage_door
- cover.small_garage_door
to: 'open'
for: "00:01:00"
minutes: '/30'
- platform: state
entity_id: group.family
to: not_home
@ -392,46 +387,3 @@ automation:
- service: script.speech_engine
data:
value1: "Check the garage doors. The Small garage is {{ states('cover.small_garage_door')}} and the large garage is {{ states('cover.large_garage_door')}} [Always mention the specific garage door that is currently open and remind us to close it for the night]"
# - alias: 'Garage Camera on Alexa Shows'
# id: 4373df2a-77f2-4e19-be7c-46c7b27ca583
# mode: single
# trigger:
# - platform: state
# entity_id:
# - cover.large_garage_door
# - cover.small_garage_door
# from: 'closed'
# to: 'open'
# for: "00:00:15"
# - platform: state
# entity_id: binary_sensor.mcu1_gpio12 #interior Garage Doors
# from: 'off'
# to: 'on'
# for: "00:00:05"
# - platform: state
# entity_id:
# - person.carlo
# - person.stacey
# - person.paige
# - person.justin
# to: 'not_home'
# from: 'home'
# action:
# - service: media_player.play_media
# target:
# entity_id: media_player.kitchen
# data:
# media_content_id: 'show garage camera from home assistant'
# media_content_type: custom
# - delay: '00:20:00'
# - service: media_player.play_media
# target:
# entity_id: media_player.kitchen
# data:
# media_content_id: 'hide garage camera'
# media_content_type: custom

@ -7,7 +7,7 @@
# Trigger with input_button.llmvision_garbage_check, input_button.llmvision_front_door_package_check, or front door person activity to update vision-backed package sensors.
# -------------------------------------------------------------------
# Notes: LLMVision analyzes camera.garagecam; expects strict "on"/"off" output.
# Notes: Front-door package detection runs 3 minutes after `sensor.frontdoorbell_person_active_count_2` goes above 0.
# Notes: Front-door package detection waits 3 minutes after person activity or 20 seconds after the front door relocks, then analyzes a short live stream for better package accuracy.
# Docs: https://llmvision.gitbook.io/getting-started/usage/image-analyzer
######################################################################
@ -129,30 +129,79 @@ automation:
- platform: numeric_state
entity_id: sensor.frontdoorbell_person_active_count_2
above: 0
for: "00:03:00"
id: person_count
- platform: state
entity_id: lock.front_door
from: unlocked
to: locked
id: front_door_locked
- platform: state
entity_id: input_button.llmvision_front_door_package_check
id: manual_check
variables:
trigger_source: >-
{% if trigger.id == 'manual_check' %}
manual button
{% elif trigger.id == 'front_door_locked' %}
front door relock
{% else %}
front door person activity
{% endif %}
prompt_text: >-
Examine the front door camera image for delivery packages. Focus only on boxes, padded envelopes,
or shopping bags left on the porch or by the front door. If one or more packages are clearly visible,
respond exactly: on. If no packages are clearly visible, respond exactly: off. No other words.
Examine these front door camera frames for delivery packages. Focus on the porch and doorstep area
near the wall and doormat in the lower-right part of the image. Treat cardboard boxes, padded mailers,
poly bags, and shopping bags as packages. Ignore the street, cars, landscaping, and anything not resting
on the porch or doorstep. If any package is clearly visible, respond exactly: on. If no package is
clearly visible, respond exactly: off. No other words.
action:
- service: llmvision.data_analyzer
- choose:
- conditions:
- condition: template
value_template: "{{ trigger.id == 'person_count' }}"
sequence:
- delay: "00:03:00"
- conditions:
- condition: template
value_template: "{{ trigger.id == 'front_door_locked' }}"
sequence:
- delay: "00:00:20"
- service: llmvision.stream_analyzer
response_variable: llmvision_result
data:
provider: !secret llmvision_provider_entry
model: gpt-4.1-nano
model: gpt-4.1-mini
message: "{{ prompt_text }}"
sensor_entity: input_boolean.front_door_packages_present
image_entity:
- camera.frontdoorbell
duration: 6
max_frames: 5
include_filename: false
target_width: 1280
target_width: 1920
max_tokens: 16
expose_images: true
- variables:
normalized_response: "{{ llmvision_result.response_text | default('') | trim | lower }}"
- choose:
- conditions:
- condition: template
value_template: >-
{{ normalized_response in ['on', 'yes', 'true']
or normalized_response.startswith('on')
or normalized_response.startswith('yes') }}
sequence:
- service: input_boolean.turn_on
target:
entity_id: input_boolean.front_door_packages_present
- conditions:
- condition: template
value_template: >-
{{ normalized_response in ['off', 'no', 'false']
or normalized_response.startswith('off')
or normalized_response.startswith('no') }}
sequence:
- service: input_boolean.turn_off
target:
entity_id: input_boolean.front_door_packages_present
- service: input_text.set_value
target:
entity_id: input_text.llmvision_front_door_last_response
@ -181,7 +230,7 @@ automation:
data:
topic: PACKAGES
message: >-
Front door package vision check ran via {{ 'manual button' if trigger.id == 'manual_check' else 'front door person activity' }}
Front door package vision check ran via {{ trigger_source }}
and returned {{ llmvision_result.response_text | default(states('input_boolean.front_door_packages_present')) | lower }}.
- choose:
- conditions:

@ -7,7 +7,7 @@
# Script wrappers for Telegram messaging using UI-configured integration.
# -------------------------------------------------------------------
# Notes: Do not add `telegram_bot:` YAML here; integration is UI-only.
# Notes: Joanna transport sends as plain_text to avoid Telegram parse-entity failures.
# Notes: Joanna transport defaults to plain_text, but can opt into HTML when the appliance provides a vetted rich message.
# Notes: Keep Skills logic in docker_17/codex_appliance; this package is delivery/transport only.
######################################################################
@ -20,9 +20,23 @@ script:
message:
description: Message body to send.
example: Joanna is online.
parse_mode:
description: Telegram parse mode (`plain_text` or `html`).
example: html
disable_web_page_preview:
description: Whether Telegram should suppress web page previews.
example: true
sequence:
- variables:
chunk_size: 3400
requested_parse_mode: >-
{% set raw = parse_mode | default('plain_text', true) | string | lower | trim %}
{% if raw in ['html', 'plain_text'] %}
{{ raw }}
{% else %}
plain_text
{% endif %}
preview_disabled: "{{ disable_web_page_preview | default(true, true) }}"
normalized_message: >-
{% set raw = message | default('', true) | string %}
{{ raw | replace('\r\n', '\n') | replace('\r', '\n') | trim }}
@ -60,8 +74,8 @@ script:
data:
chat_id: !secret telegram_allowed_chat_id_carlo
message: "{{ chunk_message }}"
parse_mode: plain_text
disable_web_page_preview: true
parse_mode: "{{ requested_parse_mode }}"
disable_web_page_preview: "{{ preview_disabled }}"
response_variable: telegram_send_response
- choose:
- conditions:
@ -77,4 +91,4 @@ script:
chat_id: !secret telegram_allowed_chat_id_carlo
message: "{{ fallback_message if fallback_message | length > 0 else 'Joanna: message delivery fallback (content omitted)' }}"
parse_mode: plain_text
disable_web_page_preview: true
disable_web_page_preview: "{{ preview_disabled }}"

@ -0,0 +1,99 @@
######################################################################
# @CCOSTAN - Follow Me on X
# For more info visit https://www.vcloudinfo.com/click-here
# Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig
# -------------------------------------------------------------------
# Tesla Model Y - Arrival and nightly plug-in reminders
# Garage speech + parents push reminders when the Tesla comes home low.
# -------------------------------------------------------------------
# Related Issue: 1279
# Notes: Expects Tesla Fleet UI entities `sensor.spaceship_battery_level`
# and `switch.spaceship_charge`.
# Notes: Uses `device_tracker.spaceship_location` for arrival/home state.
# Notes: The nightly parents reminder starts at 8 PM, waits until the
# car is home, then loops every 30 minutes via a custom event until it
# is charging or no longer home.
######################################################################
automation:
- alias: "Tesla Model Y - Garage Plug In Reminder"
id: 6a873341-c823-4d5d-9d42-fc2df52b93db
mode: restart
trigger:
- platform: state
entity_id: device_tracker.spaceship_location
to: 'home'
condition:
- condition: template
value_template: >-
{{ trigger.from_state is not none and
trigger.from_state.state not in ['home', 'unknown', 'unavailable'] }}
- condition: template
value_template: >-
{% set battery = states('sensor.spaceship_battery_level') %}
{{ battery not in ['unknown', 'unavailable', 'none', ''] and
(battery | float(100)) < 50 and
is_state('switch.spaceship_charge', 'off') }}
action:
- wait_for_trigger:
- platform: state
entity_id: cover.large_garage_door
from: 'closed'
to: 'open'
for: '00:03:20'
timeout: '00:10:00'
continue_on_timeout: false
- condition: template
value_template: >-
{% set battery = states('sensor.spaceship_battery_level') %}
{{ battery not in ['unknown', 'unavailable', 'none', ''] and
(battery | float(100)) < 50 and
is_state('switch.spaceship_charge', 'off') }}
- service: notify.alexa_media_garage
data:
message: >-
Reminder: the Tesla battery is at
{{ states('sensor.spaceship_battery_level') | float(0) | round(0) }} percent.
Please plug in the Model Y.
data:
type: announce
- service: script.send_to_logbook
data:
topic: "TESLA"
message: >-
Garage reminder announced to plug in the Model Y at
{{ states('sensor.spaceship_battery_level') | float(0) | round(0) }} percent.
- alias: "Tesla Model Y - Nightly Plug In Reminder"
id: 8b243b63-f5c3-4436-b596-0ec00a2108ab
mode: single
trigger:
- platform: time
at: '20:00:00'
- platform: event
event_type: event_tesla_model_y_nightly_loop
action:
- wait_template: "{{ is_state('device_tracker.spaceship_location', 'home') }}"
- condition: template
value_template: >-
{% set battery = states('sensor.spaceship_battery_level') %}
{{ is_state('device_tracker.spaceship_location', 'home') and
battery not in ['unknown', 'unavailable', 'none', ''] and
(battery | float(100)) < 50 and
is_state('switch.spaceship_charge', 'off') }}
- service: script.notify_engine
data:
title: "Tesla Plug In Reminder"
value1: >-
The Model Y is home, below 50 percent, and not plugged in.
Current battery:
{{ states('sensor.spaceship_battery_level') | float(0) | round(0) }} percent.
who: 'parents'
group: 'Tesla_Model_Y'
- service: script.send_to_logbook
data:
topic: "TESLA"
message: >-
Nightly plug-in reminder sent because the Model Y is home below 50 percent and not charging.
- delay: '00:30:00'
- event: event_tesla_model_y_nightly_loop

@ -131,6 +131,12 @@
{%- endif -%}
{%- endmacro -%}
{%- macro front_door_packages() -%}
{% if is_state('binary_sensor.front_door_packages_present', 'on') -%}
There appears to be a package waiting at the front door.
{%- endif -%}
{%- endmacro -%}
{%- macro medicine() -%}
{% if is_state('input_boolean.medicine', 'off') -%}
It looks like Carlo has not taken his medicine yet. Please make sure Carlo takes his medicine now.
@ -293,6 +299,8 @@
{{ window_check() }}
{% endif %}
{{ front_door_packages() }}
{{ NewDevice | default }}
{% if call_garbage_day == 1 %}

@ -0,0 +1,129 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Open OneNote</title>
<style>
:root { color-scheme: light; }
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
background: linear-gradient(180deg, #f5f7fb 0%, #e8eef8 100%);
color: #122033;
}
main {
max-width: 32rem;
margin: 0 auto;
min-height: 100vh;
padding: 2rem 1.25rem;
display: flex;
flex-direction: column;
justify-content: center;
gap: 1rem;
}
.card {
background: rgba(255, 255, 255, 0.94);
border: 1px solid rgba(18, 32, 51, 0.08);
border-radius: 18px;
box-shadow: 0 18px 40px rgba(18, 32, 51, 0.12);
padding: 1.25rem;
}
h1 {
margin: 0 0 0.5rem;
font-size: 1.35rem;
line-height: 1.2;
}
p {
margin: 0;
line-height: 1.5;
}
.title {
margin-top: 0.75rem;
font-size: 0.95rem;
color: #41556f;
word-break: break-word;
}
.actions {
display: flex;
flex-direction: column;
gap: 0.75rem;
margin-top: 1rem;
}
.button {
display: block;
text-decoration: none;
text-align: center;
padding: 0.9rem 1rem;
border-radius: 12px;
font-weight: 600;
}
.button.primary {
background: #166534;
color: #fff;
}
.button.secondary {
background: #fff;
color: #122033;
border: 1px solid rgba(18, 32, 51, 0.14);
}
.hint {
font-size: 0.88rem;
color: #5c708a;
}
</style>
</head>
<body>
<main>
<section class="card">
<h1>Opening OneNote</h1>
<p>Joanna is handing this note to the OneNote app now.</p>
<p id="note-title" class="title"></p>
<div class="actions">
<a id="app-link" class="button primary" href="#">Open in OneNote App</a>
<a id="web-link" class="button secondary" href="https://www.onenote.com/">Open Web Copy</a>
</div>
<p class="hint">If the app does not open automatically in a second or two, tap the OneNote button.</p>
</section>
</main>
<script>
(function () {
var params = new URLSearchParams(window.location.search);
var clientUrl = String(params.get('client') || '').trim();
var webUrl = String(params.get('web') || '').trim() || 'https://www.onenote.com/';
var title = String(params.get('title') || '').trim();
var appLink = document.getElementById('app-link');
var webLink = document.getElementById('web-link');
var titleNode = document.getElementById('note-title');
if (title) {
titleNode.textContent = title;
} else {
titleNode.textContent = 'OneNote note';
}
webLink.href = webUrl;
if (!/^onenote:/i.test(clientUrl)) {
appLink.style.display = 'none';
window.location.replace(webUrl);
return;
}
appLink.href = clientUrl;
var fallbackTimer = window.setTimeout(function () {
window.location.replace(webUrl);
}, 1400);
var cancelFallback = function () {
window.clearTimeout(fallbackTimer);
};
window.addEventListener('pagehide', cancelFallback, { once: true });
document.addEventListener('visibilitychange', function () {
if (document.visibilityState === 'hidden') {
cancelFallback();
}
}, { once: true });
window.location.href = clientUrl;
}());
</script>
</body>
</html>
Loading…
Cancel
Save

Powered by TurnKey Linux.