@@ -194,28 +194,151 @@ jobs:
194194 mkdir -p /tmp/html-112025-viewer
195195 python3 << 'EOF'
196196 import re
197+ import json
198+ import time
197199
198200 ino_file = 'TankAlarm-112025-Viewer-BluesOpta/TankAlarm-112025-Viewer-BluesOpta.ino'
199201 output_dir = '/tmp/html-112025-viewer'
200202
201203 with open(ino_file, 'r', encoding='utf-8', errors='ignore') as f:
202204 content = f.read()
203205
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)
206+ # Find the VIEWER_DASHBOARD_HTML constant - need to handle macro splits
207+ # The HTML is split into multiple R"HTML()HTML" sections with STR() macros in between
208+ start_pattern = r'static const char VIEWER_DASHBOARD_HTML\[\] PROGMEM = R"HTML\('
209+ start_match = re.search(start_pattern, content)
207210
208- if match:
209- html_content = match.group(1)
211+ if start_match:
212+ # Find the end - look for the final )HTML"; that ends the declaration
213+ start_pos = start_match.end()
214+ # Find the position after the HTML definition ends
215+ end_pattern = r'\)HTML";'
216+ remaining = content[start_pos:]
217+
218+ # Collect all parts, handling the macro splits
219+ html_parts = []
220+ current_pos = 0
221+
222+ while True:
223+ # Find next )HTML"
224+ end_match = re.search(r'\)HTML"', remaining[current_pos:])
225+ if not end_match:
226+ break
227+
228+ # Extract the HTML part
229+ html_parts.append(remaining[current_pos:current_pos + end_match.start()])
230+
231+ # Check if this is the final terminator
232+ check_pos = current_pos + end_match.end()
233+ if check_pos < len(remaining) and remaining[check_pos:check_pos+1] == ';':
234+ # This is the final terminator
235+ break
236+
237+ # Look for the next R"HTML(
238+ next_start = re.search(r'R"HTML\(', remaining[check_pos:])
239+ if next_start:
240+ # Extract the macro value in between
241+ macro_section = remaining[check_pos:check_pos + next_start.start()]
242+ # Replace STR(WEB_REFRESH_SECONDS) with 30
243+ if 'STR(WEB_REFRESH_SECONDS)' in macro_section:
244+ html_parts.append('30')
245+ current_pos = check_pos + next_start.end()
246+ else:
247+ break
248+
249+ html_content = ''.join(html_parts)
250+
251+ # Create sample data for the viewer
252+ current_time = int(time.time())
253+ sample_data = {
254+ "viewerName": "Demo Tank Alarm Viewer",
255+ "viewerUid": "dev:viewer001",
256+ "sourceServerName": "Demo Server",
257+ "sourceServerUid": "dev:server001",
258+ "generatedEpoch": current_time - 300, # 5 minutes ago
259+ "lastFetchEpoch": current_time - 120, # 2 minutes ago
260+ "nextFetchEpoch": current_time + 21480, # ~6 hours from now
261+ "refreshSeconds": 21600, # 6 hours
262+ "baseHour": 6,
263+ "tanks": [
264+ {
265+ "client": "dev:client001",
266+ "site": "North Facility",
267+ "label": "Primary",
268+ "tank": 1,
269+ "levelInches": 78.5,
270+ "percent": 85.3,
271+ "alarm": False,
272+ "lastUpdate": current_time - 180
273+ },
274+ {
275+ "client": "dev:client001",
276+ "site": "North Facility",
277+ "label": "Secondary",
278+ "tank": 2,
279+ "levelInches": 45.2,
280+ "percent": 52.1,
281+ "alarm": False,
282+ "lastUpdate": current_time - 180
283+ },
284+ {
285+ "client": "dev:client002",
286+ "site": "South Facility",
287+ "label": "Storage",
288+ "tank": 1,
289+ "levelInches": 18.3,
290+ "percent": 22.5,
291+ "alarm": True,
292+ "alarmType": "LOW",
293+ "lastUpdate": current_time - 240
294+ },
295+ {
296+ "client": "dev:client003",
297+ "site": "East Facility",
298+ "label": "Main",
299+ "tank": 1,
300+ "levelInches": 92.1,
301+ "percent": 95.8,
302+ "alarm": False,
303+ "lastUpdate": current_time - 150
304+ },
305+ {
306+ "client": "dev:client004",
307+ "site": "West Facility",
308+ "label": "Backup",
309+ "tank": 1,
310+ "levelInches": 12.7,
311+ "percent": 15.2,
312+ "alarm": True,
313+ "alarmType": "CRITICAL",
314+ "lastUpdate": current_time - 90
315+ }
316+ ]
317+ }
318+
319+ # Inject the sample data into the HTML by replacing the fetchTanks call
320+ # Find the fetchTanks() call at the end of the script and replace it with data injection
321+ sample_data_json = json.dumps(sample_data)
322+
323+ # Replace the async fetchTanks call with synchronous data application
324+ html_content = html_content.replace(
325+ 'fetchTanks();',
326+ f'applyTankData({sample_data_json}, null);'
327+ )
210328
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)
329+ # Also remove the setInterval call to prevent attempts to fetch from non-existent API
330+ html_content = re.sub(
331+ r'setInterval\(\(\) => fetchTanks\(state\.selected\), REFRESH_SECONDS \* 1000\);',
332+ '// Auto-refresh disabled for static preview',
333+ html_content
334+ )
213335
214336 output_file = output_dir + '/dashboard.html'
215337 with open(output_file, 'w', encoding='utf-8') as out:
216338 out.write(html_content)
217339
218340 print(f'Extracted viewer dashboard HTML: {len(html_content)} bytes')
341+ print(f'Injected sample data with {len(sample_data["tanks"])} tanks')
219342 EOF
220343
221344 - name : Take screenshots with Playwright
0 commit comments