OpusBike/webapp/index.html

214 lines
9.8 KiB
HTML
Raw Permalink Normal View History

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<title>OpusBike — ProActys</title>
<!-- PWA -->
<link rel="manifest" href="manifest.json">
<meta name="theme-color" content="#F5F7FA">
<!-- Apple PWA meta tags -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<meta name="apple-mobile-web-app-title" content="OpusBike">
<link rel="apple-touch-icon" href="icons/apple-touch-icon.png">
<!-- Favicon -->
<link rel="icon" type="image/svg+xml" href="icons/favicon.svg">
<!-- Styles -->
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<!-- Connect View -->
<div id="connect-view" class="view active">
<div class="connect-container">
<div class="logo-header">
<img src="icons/icon-192.png" alt="OpusBike" class="app-logo">
<h1>OpusBike</h1>
<p class="subtitle">Bosch Smart System Monitor</p>
</div>
<div id="connection-status" class="status-badge disconnected">
<span class="status-dot"></span>
<span id="status-text">Disconnected</span>
</div>
<div id="mode-label" class="mode-role-label"></div>
<div class="connect-actions">
<button id="btn-connect" class="btn btn-primary" onclick="app.connect()">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M6.5 6.5l11 11M17.5 6.5l-11 11M12 2v4M12 18v4M2 12h4M18 12h4"/>
</svg>
Connect via Bluetooth
</button>
<button id="btn-demo" class="btn btn-secondary" onclick="app.startDemo()">
Demo Mode
</button>
</div>
<div id="ble-unsupported" class="info-card hidden">
<p>Web Bluetooth is not available in this browser.</p>
<div id="ios-bluefy-hint" class="bluefy-hint hidden">
<p><strong>iPhone / iPad?</strong></p>
<p>Install <a href="https://apps.apple.com/app/bluefy-web-ble-browser/id1492822055" target="_blank" rel="noopener">Bluefy</a> (free) for direct Bluetooth access.</p>
<a href="https://apps.apple.com/app/bluefy-web-ble-browser/id1492822055" target="_blank" rel="noopener" class="btn-bluefy">
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M17.71 7.71L12 2h-1v7.59L6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 11 14.41V22h1l5.71-5.71-4.3-4.29 4.3-4.29zM13 5.83l1.88 1.88L13 9.59V5.83zm1.88 10.46L13 18.17v-3.76l1.88 1.88z"/></svg>
Get Bluefy (free)
</a>
</div>
<p class="relay-fallback-text">Or view live data via WebSocket relay:</p>
<div class="ws-status">
<span class="status-dot"></span>
<span id="ws-status-text">Connecting to relay...</span>
</div>
</div>
<!-- BLE Troubleshooting -->
<details id="ble-troubleshoot" class="troubleshoot-section">
<summary>Don't see your bike?</summary>
<div class="troubleshoot-content">
<p><strong>Web Bluetooth uses its own pairing flow.</strong> If your bike is paired in iOS Settings, the Web Bluetooth picker won't find it.</p>
<ol>
<li>Go to <strong>Settings &rarr; Bluetooth</strong></li>
<li>Find <strong>"smart system eBike"</strong> (or similar Bosch name)</li>
<li>Tap the <strong>(i)</strong> icon &rarr; <strong>Forget This Device</strong></li>
<li>Come back here and tap <strong>Connect via Bluetooth</strong></li>
<li>The Web Bluetooth picker will appear — select your bike</li>
</ol>
<p class="troubleshoot-note">Make sure the Bosch system is powered on (turn the crank or press the button on the display).</p>
</div>
</details>
<div class="qr-section">
<p class="qr-label">Share with your team</p>
<img src="icons/qr-code.png" alt="QR Code" class="qr-image">
<canvas id="qr-canvas" width="200" height="200"></canvas>
<p class="qr-url">monitor.proactys.swiss/opusbike</p>
</div>
<p class="brand-footer">
<img src="icons/proactys-logo.png" alt="ProActys" />
ProActys GmbH
</p>
</div>
</div>
<!-- Ride View (Dashboard) -->
<div id="ride-view" class="view">
<div class="dashboard">
<div class="dashboard-header">
<div class="header-left">
<h2 id="ride-title">OpusBike</h2>
<div id="dash-connection-status" class="status-badge connected">
<span class="status-dot"></span>
<span id="dash-status-text">Connected</span>
</div>
<span id="demo-badge" class="demo-badge">DEMO</span>
</div>
<img src="icons/proactys-logo.png" alt="ProActys" class="proactys-logo-sm">
<button id="btn-disconnect" class="btn btn-small btn-danger" onclick="app.disconnect()">
Disconnect
</button>
</div>
<!-- Main metrics grid -->
<div class="metrics-grid">
<!-- Speed = hero metric -->
<div class="metric-card metric-speed">
<div class="metric-label">Speed</div>
<div class="metric-value">
<span id="val-speed">0.0</span>
<span class="metric-unit">km/h</span>
</div>
</div>
<div class="metric-card metric-cadence">
<div class="metric-label">Cadence</div>
<div class="metric-value">
<span id="val-cadence">0</span>
<span class="metric-unit">rpm</span>
</div>
</div>
<div class="metric-card metric-humanpower">
<div class="metric-label">Human Power</div>
<div class="metric-value">
<span id="val-humanpower">0</span>
<span class="metric-unit">W</span>
</div>
</div>
<div class="metric-card metric-motorpower">
<div class="metric-label">Motor Power</div>
<div class="metric-value">
<span id="val-motorpower">0</span>
<span class="metric-unit">W</span>
</div>
</div>
<div class="metric-card metric-battery">
<div class="metric-label">Battery</div>
<div class="metric-value">
<span id="val-battery"></span>
<span class="metric-unit">%</span>
</div>
<div class="battery-bar"><div class="battery-fill" id="battery-bar" style="width:0%"></div></div>
</div>
<div class="metric-card metric-time">
<div class="metric-label">Ride Time</div>
<div class="metric-value">
<span id="val-time">00:00:00</span>
</div>
</div>
<div class="metric-card metric-mode">
<div class="metric-label">Mode</div>
<div class="metric-value">
<span id="val-mode">OFF</span>
</div>
<div class="mode-indicator" id="mode-indicator">
<span class="mode-dot" data-mode="OFF">OFF</span>
<span class="mode-dot" data-mode="ECO">ECO</span>
<span class="mode-dot" data-mode="TOUR">TOUR</span>
<span class="mode-dot" data-mode="EMTB">eMTB</span>
<span class="mode-dot" data-mode="TURBO">TURBO</span>
</div>
</div>
</div>
</div>
</div>
<!-- Tab bar -->
<nav id="tab-bar" class="tab-bar hidden">
<button class="tab active" data-view="ride-view" onclick="app.switchTab('ride-view')">
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M3 13h8V3H3v10zm0 8h8v-6H3v6zm10 0h8V11h-8v10zm0-18v6h8V3h-8z"/></svg>
<span>Dashboard</span>
</button>
<button class="tab" data-view="connect-view" onclick="app.switchTab('connect-view')">
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></svg>
<span>Connect</span>
</button>
</nav>
<!-- Scripts -->
<script src="js/boschProtocol.js"></script>
<script src="js/bleService.js"></script>
<script src="js/wsRelay.js"></script>
<script src="js/app.js"></script>
<!-- Service Worker registration -->
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('js/sw.js')
.then(reg => console.log('SW registered:', reg.scope))
.catch(err => console.warn('SW registration failed:', err));
}
</script>
</body>
</html>