<div {{ stimulus_controller('@pwa/battery') }}>
<div class="battery-dashboard">
<div class="dashboard-header">
<h1>Battery Monitor</h1>
<div id="last-updated">Never updated</div>
</div>
<div class="stats-grid">
<div class="stat-card">
<div class="stat-label">Battery Level</div>
<div id="stat-level" class="stat-value">--%</div>
<div id="stat-level-bar" class="stat-bar">
<div class="stat-bar-fill"></div>
</div>
</div>
<div class="stat-card">
<div class="stat-label">Status</div>
<div id="stat-status" class="stat-value">--</div>
</div>
<div class="stat-card">
<div class="stat-label">Time to Full</div>
<div id="stat-charging-time" class="stat-value">--</div>
</div>
<div class="stat-card">
<div class="stat-label">Time Remaining</div>
<div id="stat-discharging-time" class="stat-value">--</div>
</div>
</div>
<div class="chart-container">
<h3>Battery Level History</h3>
<canvas id="battery-chart" width="600" height="200"></canvas>
</div>
</div>
</div>
<script>
const batteryHistory = [];
const MAX_HISTORY_POINTS = 30;
document.addEventListener('pwa--battery:updated', (event) => {
const { charging, level, chargingTime, dischargingTime } = event.detail;
const percentage = Math.round(level * 100);
// Update stats
document.getElementById('stat-level').textContent = `${percentage}%`;
document.getElementById('stat-status').textContent = charging ? 'Charging ⚡' : 'Discharging';
// Update level bar
const barFill = document.querySelector('#stat-level-bar .stat-bar-fill');
barFill.style.width = `${percentage}%`;
if (level <= 0.15) {
barFill.style.background = '#ef4444';
} else if (level <= 0.30) {
barFill.style.background = '#f59e0b';
} else {
barFill.style.background = '#10b981';
}
// Update times
document.getElementById('stat-charging-time').textContent =
formatTime(chargingTime);
document.getElementById('stat-discharging-time').textContent =
formatTime(dischargingTime);
// Update last updated time
document.getElementById('last-updated').textContent =
`Last updated: ${new Date().toLocaleTimeString()}`;
// Add to history
batteryHistory.push({
timestamp: Date.now(),
level: percentage
});
if (batteryHistory.length > MAX_HISTORY_POINTS) {
batteryHistory.shift();
}
// Update chart
drawChart();
});
function formatTime(seconds) {
if (seconds === null || !isFinite(seconds)) {
return '∞';
}
if (seconds === 0) {
return 'Full';
}
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
if (hours > 0) {
return `${hours}h ${minutes}m`;
}
return `${minutes}m`;
}
function drawChart() {
const canvas = document.getElementById('battery-chart');
const ctx = canvas.getContext('2d');
// Clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
if (batteryHistory.length < 2) return;
// Draw grid
ctx.strokeStyle = '#e5e7eb';
ctx.lineWidth = 1;
for (let i = 0; i <= 4; i++) {
const y = (canvas.height * i) / 4;
ctx.beginPath();
ctx.moveTo(0, y);
ctx.lineTo(canvas.width, y);
ctx.stroke();
// Labels
ctx.fillStyle = '#9ca3af';
ctx.font = '12px sans-serif';
ctx.fillText(`${100 - (i * 25)}%`, 5, y - 5);
}
// Draw line
ctx.strokeStyle = '#3b82f6';
ctx.lineWidth = 2;
ctx.beginPath();
batteryHistory.forEach((point, index) => {
const x = (canvas.width * index) / (batteryHistory.length - 1);
const y = canvas.height - (canvas.height * point.level / 100);
if (index === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
});
ctx.stroke();
// Draw points
ctx.fillStyle = '#3b82f6';
batteryHistory.forEach((point, index) => {
const x = (canvas.width * index) / (batteryHistory.length - 1);
const y = canvas.height - (canvas.height * point.level / 100);
ctx.beginPath();
ctx.arc(x, y, 3, 0, 2 * Math.PI);
ctx.fill();
});
}
</script>
<style>
.battery-dashboard {
max-width: 1000px;
margin: 0 auto;
padding: 20px;
}
.dashboard-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
}
#last-updated {
color: #6b7280;
font-size: 14px;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.stat-card {
padding: 20px;
background: white;
border: 1px solid #e5e7eb;
border-radius: 8px;
}
.stat-label {
font-size: 14px;
color: #6b7280;
margin-bottom: 8px;
}
.stat-value {
font-size: 28px;
font-weight: bold;
color: #1f2937;
}
.stat-bar {
margin-top: 10px;
height: 8px;
background: #e5e7eb;
border-radius: 4px;
overflow: hidden;
}
.stat-bar-fill {
height: 100%;
background: #10b981;
transition: width 0.3s ease, background 0.3s ease;
}
.chart-container {
padding: 20px;
background: white;
border: 1px solid #e5e7eb;
border-radius: 8px;
}
.chart-container h3 {
margin: 0 0 15px 0;
}
#battery-chart {
width: 100%;
height: auto;
}
</style>