Repository navigation
Expand file tree
/
Copy pathpreview.html
More file actions
174 lines (165 loc) · 7.64 KB
/
Copy pathpreview.html
File metadata and controls
174 lines (165 loc) · 7.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Pool Timer Card — Preview claro / oscuro</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Segoe UI', sans-serif;
display: flex;
flex-wrap: wrap;
min-height: 100vh;
}
/* Each panel reproduces the CSS variables Home Assistant actually exposes.
Taken from a live HA 2026.8 instance -- note that the light theme does
NOT define --ha-card-background at all, which is exactly the condition
that used to make the card fall back to its dark literal. */
.panel {
flex: 1 1 460px;
padding: 24px 20px 40px;
display: flex;
flex-direction: column;
align-items: center;
gap: 16px;
}
.panel h2 {
font-size: 13px;
letter-spacing: 1px;
text-transform: uppercase;
opacity: 0.6;
font-weight: 600;
}
.panel .wrap { width: 100%; max-width: 420px; }
.panel--light {
/* --ha-card-background deliberately absent */
--card-background-color: #fff;
--primary-text-color: #141414;
--secondary-text-color: #5e5e5e;
--primary-background-color: #fafafa;
--divider-color: #0000001f;
background: #fafafa;
color: #141414;
}
.panel--dark {
--ha-card-background: #1C1C1E;
--card-background-color: #1C1C1E;
--primary-text-color: #E1E1E1;
--secondary-text-color: #9B9B9B;
--primary-background-color: #111111;
--divider-color: #ffffff1f;
background: #111111;
color: #E1E1E1;
}
ha-card { display: block; }
</style>
</head>
<body>
<section class="panel panel--light">
<h2>Tema claro</h2>
<div class="wrap"><pool-timer-card id="cardLight"></pool-timer-card></div>
</section>
<section class="panel panel--dark">
<h2>Tema oscuro</h2>
<div class="wrap"><pool-timer-card id="cardDark"></pool-timer-card></div>
</section>
<script>
// Load the card with a cache-busting query. `python -m http.server` sends no
// cache headers, so the browser happily serves a stale pool-timer-card.js and
// every check in this harness silently passes against old code.
(function () {
const s = document.createElement('script');
s.src = 'pool-timer-card.js?t=' + Date.now();
s.onload = () => window.__initPreview();
document.head.appendChild(s);
})();
</script>
<script>
const CONFIG = {
entity: 'switch.pool_pump',
name: 'Pool Timer',
schedule_entity: 'input_text.pool_timer_schedule',
mode_entity: 'input_select.pool_timer_mode',
state_entity: 'input_text.pool_timer_state',
preset_entity: 'input_select.pool_timer_preset',
schedule: [
{ start: '08:00', end: '13:00' },
{ start: '16:00', end: '20:00' },
],
// Kept so the auto-setup migration seed has something to read. Once
// `preset_entity` exists these are NOT the live source any more.
presets: [
{ name: 'Verano', schedule: [{ start: '10:00', end: '14:00' }, { start: '16:00', end: '19:30' }] },
{ name: 'Invierno', schedule: [{ start: '11:00', end: '11:30' }] },
{ name: 'Verano corto', schedule: [{ start: '10:00', end: '13:00' }, { start: '16:00', end: '18:30' }] },
],
corner_actions: [
{ name: 'Calentador', icon: '🔥', position: 'tl', service: 'switch', entity_id: 'switch.pool_heater', action: 'toggle' },
{ name: 'Luz', icon: '💡', position: 'tr', service: 'switch', entity_id: 'switch.pool_light', action: 'toggle' },
],
};
function makeHass(darkMode) {
const hass = {
language: 'es',
themes: { darkMode, theme: 'default', default_theme: 'default' },
states: {
'switch.pool_pump': { state: 'off', entity_id: 'switch.pool_pump' },
'switch.pool_heater': { state: 'off', entity_id: 'switch.pool_heater' },
'switch.pool_light': { state: 'on', entity_id: 'switch.pool_light' },
// `max` must be >= 48 or the card raises a "schedule can't be saved" notice
'input_text.pool_timer_schedule': { state: '', entity_id: 'input_text.pool_timer_schedule',
attributes: { max: 255 } },
'input_text.pool_timer_state': { state: '', entity_id: 'input_text.pool_timer_state',
attributes: { max: 255 } },
'input_select.pool_timer_mode': { state: 'Auto', entity_id: 'input_select.pool_timer_mode',
attributes: { options: ['Auto', 'Perm', 'OFF'] } },
// --- Server-side presets ---------------------------------------
// The options ARE the preset list. 'Custom' is not a preset: it is the
// absence of a match, and the blueprint ignores it.
'input_select.pool_timer_preset': { state: 'Verano corto',
entity_id: 'input_select.pool_timer_preset',
attributes: { friendly_name: 'Pool Timer Preset',
// 'Fiesta' exists ONLY here, not in the YAML presets:
// it is the unambiguous proof of which source is in use.
options: ['Custom', 'Verano', 'Invierno', 'Verano corto', 'Fiesta'] } },
// The link is the friendly name, never the entity_id. The entity ids here
// are deliberately NOT the slug of the name, to catch any code that tries
// to derive one from the other.
'input_text.ptp_a1b2': { state: '000000000000000000001111111100001111111000000000',
entity_id: 'input_text.ptp_a1b2',
attributes: { friendly_name: 'Pool Timer Preset Verano', max: 255 } },
'input_text.ptp_c3d4': { state: '000000000000000000000011000000000000000000000000',
entity_id: 'input_text.ptp_c3d4',
attributes: { friendly_name: 'Pool Timer Preset Invierno', max: 255 } },
'input_text.ptp_e5f6': { state: '000000000000000000000000000000000000111111111111',
entity_id: 'input_text.ptp_e5f6',
attributes: { friendly_name: 'Pool Timer Preset Fiesta', max: 255 } },
// 'Verano corto' has NO helper on purpose: that is the "missing helper"
// scenario its option must render disabled.
},
user: { is_admin: true },
callService: (domain, service, data) => {
const call = { domain, service, data };
(window.__llamadas = window.__llamadas || []).push(call);
console.log(`[HA Stub ${darkMode ? 'dark' : 'light'}] ${domain}.${service}`, data);
return Promise.resolve();
},
callWS: (msg) => {
(window.__ws = window.__ws || []).push(msg);
console.log('[HA Stub callWS]', msg);
return Promise.resolve([]);
},
};
return hass;
}
window.__initPreview = function () {
for (const [id, dark] of [['cardLight', false], ['cardDark', true]]) {
const card = document.getElementById(id);
card.setConfig(JSON.parse(JSON.stringify(CONFIG)));
card.hass = makeHass(dark);
}
};
</script>
</body>
</html>