Skip to content

Commit f17667c

Browse files
authored
Merge pull request #88 from SenaxInc/copilot/add-example-data-for-preview
Inject sample data into website previews for Viewer and Server dashboards
2 parents 0aebd8b + 1e72f74 commit f17667c

File tree

1 file changed

+276
-22
lines changed

1 file changed

+276
-22
lines changed

.github/workflows/html-render.yml

Lines changed: 276 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -112,29 +112,62 @@ jobs:
112112
113113
# Add sample data for dynamic content
114114
if page_name == 'dashboard':
115-
# Add sample tank data at the end before closing body tag
115+
# Add sample tank data grouped by sites
116116
sample_data = '''
117+
<h2>North Facility</h2>
117118
<div class="tank-container">
118119
<div class="tank-card">
119-
<div class="tank-header">Site A - Tank #1</div>
120-
<div class="tank-level">Level: 75%</div>
121-
<div class="tank-change positive">Change: +5%</div>
122-
<div class="status-normal">Status: Normal</div>
120+
<div class="tank-header">Tank #1</div>
121+
<div class="tank-level">Level: 85.3%</div>
122+
<div class="tank-change positive">24hr Change: +2.1%</div>
123+
<div class="status status-normal">Status: Normal</div>
124+
<div style="font-size: 12px; color: #666; margin-top: 10px;">Updated: 11/22/2025, 4:55:56 AM</div>
123125
</div>
124126
<div class="tank-card">
125-
<div class="tank-header">Site B - Tank #2</div>
126-
<div class="tank-level">Level: 30%</div>
127-
<div class="tank-change negative">Change: -10%</div>
128-
<div class="status-alarm">Status: LOW ALARM</div>
127+
<div class="tank-header">Tank #2</div>
128+
<div class="tank-level">Level: 52.1%</div>
129+
<div class="tank-change negative">24hr Change: -3.2%</div>
130+
<div class="status status-normal">Status: Normal</div>
131+
<div style="font-size: 12px; color: #666; margin-top: 10px;">Updated: 11/22/2025, 4:55:56 AM</div>
129132
</div>
130133
</div>
131-
<div class="footer">Last updated: 2025-11-12 18:00:00 UTC</div>
132-
</body>
133-
</html>
134+
<h2>South Facility</h2>
135+
<div class="tank-container">
136+
<div class="tank-card">
137+
<div class="tank-header">Tank #1</div>
138+
<div class="tank-level">Level: 22.5%</div>
139+
<div class="tank-change negative">24hr Change: -8.5%</div>
140+
<div class="status status-alarm">Status: LOW</div>
141+
<div style="font-size: 12px; color: #666; margin-top: 10px;">Updated: 11/22/2025, 4:54:56 AM</div>
142+
</div>
143+
</div>
144+
<h2>East Facility</h2>
145+
<div class="tank-container">
146+
<div class="tank-card">
147+
<div class="tank-header">Tank #1</div>
148+
<div class="tank-level">Level: 95.8%</div>
149+
<div class="tank-change positive">24hr Change: +1.2%</div>
150+
<div class="status status-normal">Status: Normal</div>
151+
<div style="font-size: 12px; color: #666; margin-top: 10px;">Updated: 11/22/2025, 4:56:26 AM</div>
152+
</div>
153+
</div>
154+
<h2>West Facility</h2>
155+
<div class="tank-container">
156+
<div class="tank-card">
157+
<div class="tank-header">Tank #1</div>
158+
<div class="tank-level">Level: 15.2%</div>
159+
<div class="tank-change negative">24hr Change: -12.8%</div>
160+
<div class="status status-alarm">Status: CRITICAL</div>
161+
<div style="font-size: 12px; color: #666; margin-top: 10px;">Updated: 11/22/2025, 4:57:26 AM</div>
162+
</div>
163+
</div>
164+
<div class="footer">Last updated: 2025-11-22 18:00:00 UTC</div>
134165
'''
135-
# Insert before closing tags if they don't exist
136-
if '</body>' not in html_content:
137-
html_content += sample_data
166+
# Replace the "No tank reports received yet" message with sample data
167+
html_content = html_content.replace(
168+
"<p style='text-align: center;'>No tank reports received yet.</p>",
169+
sample_data
170+
)
138171
139172
# Make sure we have closing tags
140173
if '</html>' not in html_content:
@@ -155,6 +188,8 @@ jobs:
155188
mkdir -p /tmp/html-112025
156189
python3 << 'EOF'
157190
import re
191+
import json
192+
import time
158193
159194
ino_file = 'TankAlarm-112025-Server-BluesOpta/TankAlarm-112025-Server-BluesOpta.ino'
160195
output_dir = '/tmp/html-112025'
@@ -169,11 +204,107 @@ jobs:
169204
if match:
170205
html_content = match.group(1)
171206
207+
# Create sample data for the server dashboard
208+
current_time = int(time.time())
209+
sample_data = {
210+
"serverUid": "dev:server001",
211+
"server": {
212+
"name": "Demo Tank Alarm Server",
213+
"clientFleet": "demo-tankalarm-clients",
214+
"webRefreshSeconds": 60,
215+
"pinConfigured": True
216+
},
217+
"nextDailyEmailEpoch": current_time + 43200, # 12 hours from now
218+
"lastSyncEpoch": current_time - 180, # 3 minutes ago
219+
"clients": [
220+
{
221+
"uid": "dev:client001",
222+
"site": "North Facility",
223+
"lastUpdate": current_time - 180,
224+
"tanks": [
225+
{
226+
"tank": 1,
227+
"label": "Primary",
228+
"levelInches": 78.5,
229+
"percent": 85.3,
230+
"alarm": False,
231+
"lastUpdate": current_time - 180
232+
},
233+
{
234+
"tank": 2,
235+
"label": "Secondary",
236+
"levelInches": 45.2,
237+
"percent": 52.1,
238+
"alarm": False,
239+
"lastUpdate": current_time - 180
240+
}
241+
]
242+
},
243+
{
244+
"uid": "dev:client002",
245+
"site": "South Facility",
246+
"lastUpdate": current_time - 240,
247+
"tanks": [
248+
{
249+
"tank": 1,
250+
"label": "Storage",
251+
"levelInches": 18.3,
252+
"percent": 22.5,
253+
"alarm": True,
254+
"alarmType": "LOW",
255+
"lastUpdate": current_time - 240
256+
}
257+
]
258+
},
259+
{
260+
"uid": "dev:client003",
261+
"site": "East Facility",
262+
"lastUpdate": current_time - 150,
263+
"tanks": [
264+
{
265+
"tank": 1,
266+
"label": "Main",
267+
"levelInches": 92.1,
268+
"percent": 95.8,
269+
"alarm": False,
270+
"lastUpdate": current_time - 150
271+
}
272+
]
273+
},
274+
{
275+
"uid": "dev:client004",
276+
"site": "West Facility",
277+
"lastUpdate": current_time - 90,
278+
"tanks": [
279+
{
280+
"tank": 1,
281+
"label": "Backup",
282+
"levelInches": 12.7,
283+
"percent": 15.2,
284+
"alarm": True,
285+
"alarmType": "CRITICAL",
286+
"lastUpdate": current_time - 90
287+
}
288+
]
289+
}
290+
]
291+
}
292+
293+
# Inject the sample data
294+
sample_data_json = json.dumps(sample_data)
295+
296+
# Replace the refreshData() call with data injection
297+
html_content = html_content.replace(
298+
'refreshData();',
299+
f'applyServerData({sample_data_json}, null);'
300+
)
301+
172302
output_file = output_dir + '/dashboard.html'
173303
with open(output_file, 'w', encoding='utf-8') as out:
174304
out.write(html_content)
175305
176306
print(f'Extracted dashboard HTML: {len(html_content)} bytes')
307+
print(f'Injected sample data with {len(sample_data["clients"])} clients')
177308
178309
# Find the CONFIG_GENERATOR_HTML constant
179310
config_pattern = r'static const char CONFIG_GENERATOR_HTML\[\] PROGMEM = R"HTML\((.*?)\)HTML"'
@@ -194,28 +325,151 @@ jobs:
194325
mkdir -p /tmp/html-112025-viewer
195326
python3 << 'EOF'
196327
import re
328+
import json
329+
import time
197330
198331
ino_file = 'TankAlarm-112025-Viewer-BluesOpta/TankAlarm-112025-Viewer-BluesOpta.ino'
199332
output_dir = '/tmp/html-112025-viewer'
200333
201334
with open(ino_file, 'r', encoding='utf-8', errors='ignore') as f:
202335
content = f.read()
203336
204-
# Find the VIEWER_DASHBOARD_HTML constant
205-
html_pattern = r'static const char VIEWER_DASHBOARD_HTML\[\] PROGMEM = R"HTML\((.*?)\)HTML"'
206-
match = re.search(html_pattern, content, re.DOTALL)
337+
# Find the VIEWER_DASHBOARD_HTML constant - need to handle macro splits
338+
# The HTML is split into multiple R"HTML()HTML" sections with STR() macros in between
339+
start_pattern = r'static const char VIEWER_DASHBOARD_HTML\[\] PROGMEM = R"HTML\('
340+
start_match = re.search(start_pattern, content)
207341
208-
if match:
209-
html_content = match.group(1)
342+
if start_match:
343+
# Find the end - look for the final )HTML"; that ends the declaration
344+
start_pos = start_match.end()
345+
# Find the position after the HTML definition ends
346+
end_pattern = r'\)HTML";'
347+
remaining = content[start_pos:]
348+
349+
# Collect all parts, handling the macro splits
350+
html_parts = []
351+
current_pos = 0
352+
353+
while True:
354+
# Find next )HTML"
355+
end_match = re.search(r'\)HTML"', remaining[current_pos:])
356+
if not end_match:
357+
break
358+
359+
# Extract the HTML part
360+
html_parts.append(remaining[current_pos:current_pos + end_match.start()])
361+
362+
# Check if this is the final terminator
363+
check_pos = current_pos + end_match.end()
364+
if check_pos < len(remaining) and remaining[check_pos:check_pos+1] == ';':
365+
# This is the final terminator
366+
break
367+
368+
# Look for the next R"HTML(
369+
next_start = re.search(r'R"HTML\(', remaining[check_pos:])
370+
if next_start:
371+
# Extract the macro value in between
372+
macro_section = remaining[check_pos:check_pos + next_start.start()]
373+
# Replace STR(WEB_REFRESH_SECONDS) with 30
374+
if 'STR(WEB_REFRESH_SECONDS)' in macro_section:
375+
html_parts.append('30')
376+
current_pos = check_pos + next_start.end()
377+
else:
378+
break
379+
380+
html_content = ''.join(html_parts)
381+
382+
# Create sample data for the viewer
383+
current_time = int(time.time())
384+
sample_data = {
385+
"viewerName": "Demo Tank Alarm Viewer",
386+
"viewerUid": "dev:viewer001",
387+
"sourceServerName": "Demo Server",
388+
"sourceServerUid": "dev:server001",
389+
"generatedEpoch": current_time - 300, # 5 minutes ago
390+
"lastFetchEpoch": current_time - 120, # 2 minutes ago
391+
"nextFetchEpoch": current_time + 21480, # ~6 hours from now
392+
"refreshSeconds": 21600, # 6 hours
393+
"baseHour": 6,
394+
"tanks": [
395+
{
396+
"client": "dev:client001",
397+
"site": "North Facility",
398+
"label": "Primary",
399+
"tank": 1,
400+
"levelInches": 78.5,
401+
"percent": 85.3,
402+
"alarm": False,
403+
"lastUpdate": current_time - 180
404+
},
405+
{
406+
"client": "dev:client001",
407+
"site": "North Facility",
408+
"label": "Secondary",
409+
"tank": 2,
410+
"levelInches": 45.2,
411+
"percent": 52.1,
412+
"alarm": False,
413+
"lastUpdate": current_time - 180
414+
},
415+
{
416+
"client": "dev:client002",
417+
"site": "South Facility",
418+
"label": "Storage",
419+
"tank": 1,
420+
"levelInches": 18.3,
421+
"percent": 22.5,
422+
"alarm": True,
423+
"alarmType": "LOW",
424+
"lastUpdate": current_time - 240
425+
},
426+
{
427+
"client": "dev:client003",
428+
"site": "East Facility",
429+
"label": "Main",
430+
"tank": 1,
431+
"levelInches": 92.1,
432+
"percent": 95.8,
433+
"alarm": False,
434+
"lastUpdate": current_time - 150
435+
},
436+
{
437+
"client": "dev:client004",
438+
"site": "West Facility",
439+
"label": "Backup",
440+
"tank": 1,
441+
"levelInches": 12.7,
442+
"percent": 15.2,
443+
"alarm": True,
444+
"alarmType": "CRITICAL",
445+
"lastUpdate": current_time - 90
446+
}
447+
]
448+
}
449+
450+
# Inject the sample data into the HTML by replacing the fetchTanks call
451+
# Find the fetchTanks() call at the end of the script and replace it with data injection
452+
sample_data_json = json.dumps(sample_data)
453+
454+
# Replace the async fetchTanks call with synchronous data application
455+
html_content = html_content.replace(
456+
'fetchTanks();',
457+
f'applyTankData({sample_data_json}, null);'
458+
)
210459
211-
# Replace the STR(WEB_REFRESH_SECONDS) macro with a placeholder value
212-
html_content = re.sub(r'\)HTML" STR\(WEB_REFRESH_SECONDS\) R"HTML\(', '30', html_content)
460+
# Also remove the setInterval call to prevent attempts to fetch from non-existent API
461+
html_content = re.sub(
462+
r'setInterval\(\(\) => fetchTanks\(state\.selected\), REFRESH_SECONDS \* 1000\);',
463+
'// Auto-refresh disabled for static preview',
464+
html_content
465+
)
213466
214467
output_file = output_dir + '/dashboard.html'
215468
with open(output_file, 'w', encoding='utf-8') as out:
216469
out.write(html_content)
217470
218471
print(f'Extracted viewer dashboard HTML: {len(html_content)} bytes')
472+
print(f'Injected sample data with {len(sample_data["tanks"])} tanks')
219473
EOF
220474
221475
- name: Take screenshots with Playwright

0 commit comments

Comments
 (0)