Skip to content

Commit 29b23ab

Browse files
committed
Add PowerShell script for Home Assistant config checks in Docker and Supervisor modes
1 parent fe43b17 commit 29b23ab

4 files changed

Lines changed: 169 additions & 42 deletions

File tree

-3.54 KB
Loading

config/packages/vacuum.yaml

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
# - Treat 2+ minutes in a room as "being cleaned" and dequeue immediately (queue = remaining rooms).
1313
# - Phase changes happen only after verified completion at dock (`task_status: completed`).
1414
# - Guarded fallback: if docked with empty queue for 10 minutes but no `completed`, advance with `fallback_advance` log.
15-
# - Use `dreame_vacuum.vacuum_clean_segment` with the integration's segment ids.
15+
# - This installed Dreame build still needs native `dreame_vacuum.vacuum_clean_segment`; do not rely on HA `vacuum.clean_area`.
1616
# - Jinja2 loop scoping: use a `namespace` when building lists (otherwise the queue can appear empty and get cleared).
1717
# - If docked+completed still has queue entries, treat queue as stale and clear it before phase advance.
1818
# - Mop phases use `sweeping_and_mopping` instead of mop-only.
@@ -133,8 +133,6 @@ script:
133133
{{ bath_ids }}
134134
{% endif %}
135135
segments_to_clean: "{{ queue_ints if queue_ints | length > 0 else phase_segments }}"
136-
segment_ids: "{{ segments_to_clean | list }}"
137-
138136
# 0. Reseed the current phase when queue is empty.
139137
- choose:
140138
- conditions:
@@ -178,11 +176,11 @@ script:
178176
- service: script.send_to_logbook
179177
data:
180178
topic: "VACUUM"
181-
message: "Vacuum is already cleaning; queue/phase updated but not issuing a new segment clean action."
179+
message: "Vacuum is already cleaning; queue/phase updated but not issuing a new segment-clean action."
182180
- stop: "Already cleaning."
183181
default: []
184182

185-
# 4. Start cleaning
183+
# 4. Start cleaning with the native Dreame segment service.
186184
- service: select.select_option
187185
target:
188186
entity_id: select.l10s_vacuum_cleaning_mode
@@ -197,8 +195,7 @@ script:
197195
target:
198196
entity_id: vacuum.l10s_vacuum
199197
data:
200-
# Clean the queued room segments directly.
201-
segments: "{{ segment_ids }}"
198+
segments: "{{ segments_to_clean }}"
202199

203200

204201
## 3. Automations

config/www/onenote-launch.html

Lines changed: 85 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -8,82 +8,116 @@
88
:root { color-scheme: light; }
99
body {
1010
margin: 0;
11-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
12-
background: linear-gradient(180deg, #f5f7fb 0%, #e8eef8 100%);
13-
color: #122033;
11+
font-family: "Segoe UI", -apple-system, BlinkMacSystemFont, sans-serif;
12+
background:
13+
radial-gradient(circle at top left, rgba(126, 34, 206, 0.16), transparent 28rem),
14+
radial-gradient(circle at bottom right, rgba(22, 101, 52, 0.14), transparent 26rem),
15+
linear-gradient(180deg, #eef2ff 0%, #f6f8fc 48%, #eef6f2 100%);
16+
color: #18243a;
1417
}
1518
main {
16-
max-width: 32rem;
19+
max-width: 36rem;
1720
margin: 0 auto;
1821
min-height: 100vh;
19-
padding: 2rem 1.25rem;
22+
padding: 2rem 1.25rem 2.5rem;
2023
display: flex;
2124
flex-direction: column;
2225
justify-content: center;
23-
gap: 1rem;
2426
}
2527
.card {
26-
background: rgba(255, 255, 255, 0.94);
27-
border: 1px solid rgba(18, 32, 51, 0.08);
28-
border-radius: 18px;
29-
box-shadow: 0 18px 40px rgba(18, 32, 51, 0.12);
30-
padding: 1.25rem;
28+
background: rgba(255, 255, 255, 0.92);
29+
border: 1px solid rgba(126, 34, 206, 0.14);
30+
border-radius: 24px;
31+
box-shadow: 0 22px 55px rgba(24, 36, 58, 0.14);
32+
padding: 1.5rem;
33+
backdrop-filter: blur(10px);
34+
}
35+
.eyebrow {
36+
display: inline-flex;
37+
align-items: center;
38+
padding: 0.35rem 0.7rem;
39+
border-radius: 999px;
40+
background: rgba(126, 34, 206, 0.1);
41+
color: #6b21a8;
42+
font-size: 0.78rem;
43+
font-weight: 700;
44+
letter-spacing: 0.04em;
45+
text-transform: uppercase;
46+
margin-bottom: 0.9rem;
3147
}
3248
h1 {
3349
margin: 0 0 0.5rem;
34-
font-size: 1.35rem;
35-
line-height: 1.2;
50+
font-size: clamp(1.6rem, 5vw, 2rem);
51+
line-height: 1.1;
3652
}
3753
p {
3854
margin: 0;
39-
line-height: 1.5;
55+
line-height: 1.55;
4056
}
4157
.title {
42-
margin-top: 0.75rem;
43-
font-size: 0.95rem;
44-
color: #41556f;
58+
margin-top: 1rem;
59+
padding: 0.95rem 1rem;
60+
border-radius: 16px;
61+
background: linear-gradient(180deg, rgba(126, 34, 206, 0.06), rgba(255, 255, 255, 0.92));
62+
border: 1px solid rgba(126, 34, 206, 0.12);
63+
font-size: 0.98rem;
64+
color: #4a5670;
4565
word-break: break-word;
4666
}
4767
.actions {
4868
display: flex;
4969
flex-direction: column;
50-
gap: 0.75rem;
51-
margin-top: 1rem;
70+
gap: 0.85rem;
71+
margin-top: 1.15rem;
5272
}
5373
.button {
5474
display: block;
5575
text-decoration: none;
5676
text-align: center;
57-
padding: 0.9rem 1rem;
58-
border-radius: 12px;
59-
font-weight: 600;
77+
padding: 1rem 1.1rem;
78+
border-radius: 16px;
79+
font-weight: 700;
80+
letter-spacing: 0.01em;
81+
transition: transform 120ms ease, box-shadow 120ms ease, opacity 120ms ease;
82+
box-shadow: 0 10px 24px rgba(24, 36, 58, 0.1);
83+
}
84+
.button:hover {
85+
transform: translateY(-1px);
6086
}
6187
.button.primary {
62-
background: #166534;
88+
background: linear-gradient(135deg, #7c3aed 0%, #5b21b6 100%);
6389
color: #fff;
6490
}
6591
.button.secondary {
6692
background: #fff;
67-
color: #122033;
68-
border: 1px solid rgba(18, 32, 51, 0.14);
93+
color: #18243a;
94+
border: 1px solid rgba(24, 36, 58, 0.12);
95+
}
96+
.button.muted {
97+
background: rgba(255, 255, 255, 0.7);
98+
color: #52627d;
99+
border: 1px dashed rgba(24, 36, 58, 0.18);
100+
box-shadow: none;
69101
}
70102
.hint {
71-
font-size: 0.88rem;
72-
color: #5c708a;
103+
margin-top: 1rem;
104+
font-size: 0.9rem;
105+
color: #5f6f87;
73106
}
74107
</style>
75108
</head>
76109
<body>
77110
<main>
78111
<section class="card">
112+
<div class="eyebrow">OneNote launch</div>
79113
<h1>Opening OneNote</h1>
80-
<p>Joanna is handing this note to the OneNote app now.</p>
114+
<p>Joanna found the note and prepared the cleanest handoff path.</p>
81115
<p id="note-title" class="title"></p>
82116
<div class="actions">
83-
<a id="app-link" class="button primary" href="#">Open in OneNote App</a>
117+
<a id="open-link" class="button primary" href="https://www.onenote.com/">Open in OneNote</a>
84118
<a id="web-link" class="button secondary" href="https://www.onenote.com/">Open Web Copy</a>
85119
</div>
86-
<p class="hint">If the app does not open automatically in a second or two, tap the OneNote button.</p>
120+
<p id="hint" class="hint">If the desktop app is available, it will open automatically. Otherwise use the button above.</p>
87121
</section>
88122
</main>
89123
<script>
@@ -92,9 +126,22 @@ <h1>Opening OneNote</h1>
92126
var clientUrl = String(params.get('client') || '').trim();
93127
var webUrl = String(params.get('web') || '').trim() || 'https://www.onenote.com/';
94128
var title = String(params.get('title') || '').trim();
95-
var appLink = document.getElementById('app-link');
129+
var openLink = document.getElementById('open-link');
96130
var webLink = document.getElementById('web-link');
131+
var hintNode = document.getElementById('hint');
97132
var titleNode = document.getElementById('note-title');
133+
var isLaunchableClientUrl = function (value) {
134+
if (!/^onenote:/i.test(value)) {
135+
return false;
136+
}
137+
try {
138+
var inner = new URL(String(value).replace(/^onenote:/i, ''));
139+
var host = String(inner.hostname || '').toLowerCase();
140+
return host === 'd.docs.live.net' || host.endsWith('.sharepoint.com');
141+
} catch (err) {
142+
return false;
143+
}
144+
};
98145

99146
if (title) {
100147
titleNode.textContent = title;
@@ -103,13 +150,16 @@ <h1>Opening OneNote</h1>
103150
}
104151

105152
webLink.href = webUrl;
106-
if (!/^onenote:/i.test(clientUrl)) {
107-
appLink.style.display = 'none';
108-
window.location.replace(webUrl);
153+
if (!isLaunchableClientUrl(clientUrl)) {
154+
openLink.href = webUrl;
155+
openLink.textContent = 'Open in OneNote';
156+
webLink.className = 'button muted';
157+
webLink.textContent = 'Web fallback ready';
158+
hintNode.textContent = 'This note opens best through the browser handoff. Use the main button.';
109159
return;
110160
}
111161

112-
appLink.href = clientUrl;
162+
openLink.href = clientUrl;
113163
var fallbackTimer = window.setTimeout(function () {
114164
window.location.replace(webUrl);
115165
}, 1400);

tools/ha_check_config.ps1

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
param(
2+
[string]$TargetHost = 'docker_10',
3+
[string]$ContainerName = 'home-assistant',
4+
[string]$ConfigPath = '/config',
5+
[ValidateSet('auto', 'container', 'supervised')]
6+
[string]$Mode = 'auto'
7+
)
8+
9+
Set-StrictMode -Version Latest
10+
$ErrorActionPreference = 'Stop'
11+
12+
$ssh = Get-Command ssh -ErrorAction Stop
13+
14+
function Quote-BashArg {
15+
param(
16+
[Parameter(Mandatory = $true)]
17+
[string]$Value
18+
)
19+
20+
return "'" + $Value.Replace("'", "'`"`'`"`'") + "'"
21+
}
22+
23+
$containerNameQ = Quote-BashArg -Value $ContainerName
24+
$configPathQ = Quote-BashArg -Value $ConfigPath
25+
26+
$containerCheck = @"
27+
if ! command -v docker >/dev/null 2>&1; then
28+
echo Docker CLI not found on host. >&2
29+
exit 127
30+
fi
31+
if ! docker ps --format '{{.Names}}' | grep -Fx $containerNameQ >/dev/null 2>&1; then
32+
echo Container $containerNameQ is not running. >&2
33+
exit 1
34+
fi
35+
echo Running Home Assistant config check in container $containerNameQ...
36+
docker exec $containerNameQ python -m homeassistant --script check_config --config $configPathQ
37+
"@
38+
39+
$supervisedCheck = @'
40+
if ! command -v ha >/dev/null 2>&1; then
41+
echo Home Assistant Supervisor CLI not found on host. >&2
42+
exit 127
43+
fi
44+
echo Running Home Assistant config check via Supervisor CLI...
45+
ha core check
46+
'@
47+
48+
switch ($Mode) {
49+
'supervised' { $remoteCommand = $supervisedCheck }
50+
'container' { $remoteCommand = $containerCheck }
51+
'auto' {
52+
$remoteCommand = @"
53+
if command -v ha >/dev/null 2>&1; then
54+
echo Running Home Assistant config check via Supervisor CLI...
55+
ha core check
56+
elif command -v docker >/dev/null 2>&1; then
57+
$containerCheck
58+
else
59+
echo Neither Home Assistant Supervisor CLI nor Docker CLI is available on host. >&2
60+
exit 127
61+
fi
62+
"@
63+
}
64+
}
65+
66+
$remoteCommand = ($remoteCommand -replace "`r`n", "`n").Trim()
67+
68+
$sshArgs = @(
69+
'-o'
70+
'RemoteCommand=none'
71+
'-o'
72+
'RequestTTY=no'
73+
$TargetHost
74+
'bash'
75+
'-lc'
76+
(Quote-BashArg -Value $remoteCommand)
77+
)
78+
79+
& $ssh.Source @sshArgs
80+
exit $LASTEXITCODE

0 commit comments

Comments
 (0)