鸿蒙PC 使用 Electron 获取设备电量功能实现详解
鸿蒙PC 使用 Electron 获取设备电量功能实现详解
问题背景
在桌面应用开发中,获取设备电池电量信息是一个常见的需求。无论是笔记本电脑、平板电脑还是其他便携设备,用户都需要了解设备的电量状态,以便合理安排使用时间。
需求分析
-
获取电池电量:实时获取设备的电池电量百分比
-
充电状态检测:判断设备是否正在充电
-
电量变化监听:监听电量变化和充电状态变化
-
跨平台兼容:在 Windows、macOS、Linux 以及鸿蒙PC平台上都能正常工作
-
用户体验优化:提供友好的电量显示界面
技术挑战
-
Electron 没有直接的电池 API:Electron 本身不提供获取电池电量的直接 API
-
平台差异:不同操作系统获取电池信息的方式不同
-
实时性要求:需要能够实时监听电量变化
-
鸿蒙平台适配:需要确保在鸿蒙PC平台上也能正常工作
实现方案
方案对比
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| HTML5 Battery API | 简单直接,浏览器原生支持,跨平台兼容性好 | 某些浏览器可能不支持 | 优先使用,最可靠 |
| Electron IPC + 系统命令 | 功能强大,可获取详细信息 | 需要针对不同平台实现,可能不稳定 | 降级方案 |
| powerMonitor 事件 | Electron 原生支持,监听电源状态 | 只能监听连接/断开,无法获取电量 | 辅助方案 |
最终方案
采用双重保障方案,确保在各种环境下都能正常工作:
-
优先使用 HTML5 Battery API:
navigator.getBattery()- 浏览器原生 API,在鸿蒙PC平台上完全支持 -
降级到 Electron IPC + 系统命令:通过主进程执行系统命令获取电池信息
-
powerMonitor 事件监听:使用 Electron 的
powerMonitor监听电源连接/断开事件 -
实时更新机制:监听电池状态变化事件,自动更新显示
方案优势
-
✅ 跨平台兼容:支持 Windows、macOS、Linux 和鸿蒙PC平台
-
✅ 双重保障:HTML5 API 优先,系统命令降级
-
✅ 实时监听:自动监听电量变化和充电状态变化
-
✅ 用户体验好:提供可视化电量显示和状态提示
代码实现
1. 主进程实现(main.js)
在主进程中实现跨平台获取电池电量的功能:
const { app, BrowserWindow, ipcMain, powerMonitor } = require('electron');
const path = require('path');
const { exec } = require('child_process');
const { promisify } = require('util');
const execAsync = promisify(exec);
// 获取电池电量的辅助函数(跨平台支持)
async function getBatteryLevel() {
const platform = process.platform;
try {
if (platform === 'win32') {
// Windows: 使用 WMIC 命令
const command = 'WMIC PATH Win32_Battery Get EstimatedChargeRemaining';
const { stdout } = await execAsync(command);
const match = stdout.match(/\d+/);
if (match) {
return {
level: parseInt(match[0], 10),
charging: null, // Windows WMIC 不直接提供充电状态
chargingTime: null,
dischargingTime: null
};
}
} else if (platform === 'darwin') {
// macOS: 使用 pmset 命令
const command = 'pmset -g batt';
const { stdout } = await execAsync(command);
const levelMatch = stdout.match(/(\d+)%/);
const chargingMatch = stdout.match(/charging|discharging|AC Power/i);
let level = null;
let charging = null;
if (levelMatch) {
level = parseInt(levelMatch[1], 10);
}
if (chargingMatch) {
charging = chargingMatch[0].toLowerCase().includes('charging') ||
chargingMatch[0].toLowerCase().includes('ac');
}
return {
level: level,
charging: charging,
chargingTime: null,
dischargingTime: null
};
} else if (platform === 'linux') {
// Linux: 使用 acpi 命令(鸿蒙PC平台也使用此方案)
const command = 'acpi -b';
const { stdout } = await execAsync(command);
const levelMatch = stdout.match(/(\d+)%/);
const chargingMatch = stdout.match(/Charging|Discharging/i);
let level = null;
let charging = null;
if (levelMatch) {
level = parseInt(levelMatch[1], 10);
}
if (chargingMatch) {
charging = chargingMatch[0].toLowerCase().includes('charging');
}
return {
level: level,
charging: charging,
chargingTime: null,
dischargingTime: null
};
}
} catch (error) {
console.error('获取电池电量失败:', error);
return {
level: null,
charging: null,
chargingTime: null,
dischargingTime: null,
error: error.message
};
}
// 默认返回值
return {
level: null,
charging: null,
chargingTime: null,
dischargingTime: null,
error: 'Unsupported platform'
};
}
// IPC 处理器:获取电池电量
ipcMain.handle('get-battery-level', async (event) => {
console.log('收到获取电池电量请求');
try {
const batteryInfo = await getBatteryLevel();
console.log('电池信息:', batteryInfo);
return batteryInfo;
} catch (error) {
console.error('获取电池电量错误:', error);
return {
level: null,
charging: null,
chargingTime: null,
dischargingTime: null,
error: error.message
};
}
});
// 监听电源状态变化
powerMonitor.on('on-ac', () => {
console.log('电源已连接');
// 通知所有窗口电源状态变化
BrowserWindow.getAllWindows().forEach(win => {
if (!win.isDestroyed()) {
win.webContents.send('battery-status-changed', {
charging: true,
level: null // 需要重新获取
});
}
});
});
powerMonitor.on('on-battery', () => {
console.log('使用电池供电');
// 通知所有窗口电源状态变化
BrowserWindow.getAllWindows().forEach(win => {
if (!win.isDestroyed()) {
win.webContents.send('battery-status-changed', {
charging: false,
level: null // 需要重新获取
});
}
});
});
2. 预加载脚本(preload.js)
在预加载脚本中暴露安全的电池 API:
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electronAPI', {
versions: {
chrome: process.versions.chrome,
node: process.versions.node,
electron: process.versions.electron
},
// 获取电池电量
getBatteryLevel: () => {
return ipcRenderer.invoke('get-battery-level');
},
// 监听电池状态变化
onBatteryStatusChange: (callback) => {
ipcRenderer.on('battery-status-changed', (event, batteryInfo) => {
callback(batteryInfo);
});
},
// 移除电池状态监听
removeBatteryStatusListener: () => {
ipcRenderer.removeAllListeners('battery-status-changed');
}
});
3. 渲染进程实现(index.html)
在页面中实现获取和显示电池信息的功能:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>电池电量示例</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', Arial, sans-serif;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: 100vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
gap: 30px;
}
button {
padding: 15px 30px;
font-size: 16px;
font-weight: 600;
color: white;
background: rgba(255, 255, 255, 0.2);
border: 2px solid white;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s;
backdrop-filter: blur(10px);
}
button:hover {
background: rgba(255, 255, 255, 0.3);
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
}
#battery-info {
display: none;
margin-top: 20px;
padding: 20px;
background: rgba(255, 255, 255, 0.1);
border-radius: 10px;
color: white;
backdrop-filter: blur(10px);
min-width: 300px;
}
.battery-bar {
width: 300px;
height: 20px;
background: rgba(255, 255, 255, 0.2);
border-radius: 10px;
overflow: hidden;
margin: 10px 0;
}
.battery-bar-fill {
height: 100%;
transition: width 0.3s, background-color 0.3s;
}
</style>
</head>
<body>
<h1>Hello World</h1>
<div class="button-container">
<button οnclick="getBatteryInfo()">🔋 获取电量</button>
</div>
<div id="battery-info">
<h2 style="margin-bottom: 15px;">电池信息</h2>
<div id="battery-details"></div>
</div>
<script>
// 获取电池信息
async function getBatteryInfo() {
console.log('点击获取电量按钮');
const batteryInfoDiv = document.getElementById('battery-info');
const batteryDetailsDiv = document.getElementById('battery-details');
try {
// 方法1: 优先使用 HTML5 Battery API(如果可用)
if (navigator.getBattery) {
console.log('使用 HTML5 Battery API');
const battery = await navigator.getBattery();
const batteryInfo = {
level: Math.round(battery.level * 100),
charging: battery.charging,
chargingTime: battery.chargingTime,
dischargingTime: battery.dischargingTime
};
displayBatteryInfo(batteryInfo);
// 监听电池状态变化
battery.addEventListener('chargingchange', () => {
updateBatteryInfo(battery);
});
battery.addEventListener('levelchange', () => {
updateBatteryInfo(battery);
});
} else if (window.electronAPI && window.electronAPI.getBatteryLevel) {
// 方法2: 使用 Electron IPC 获取电池信息
console.log('使用 Electron IPC 获取电池信息');
const batteryInfo = await window.electronAPI.getBatteryLevel();
displayBatteryInfo(batteryInfo);
// 监听电池状态变化
if (window.electronAPI.onBatteryStatusChange) {
window.electronAPI.onBatteryStatusChange((batteryInfo) => {
displayBatteryInfo(batteryInfo);
});
}
} else {
alert('电池信息功能不可用');
return;
}
} catch (error) {
console.error('获取电池信息失败:', error);
batteryDetailsDiv.innerHTML = '<p style="color: #ff6b6b;">获取电池信息失败: ' + error.message + '</p>';
batteryInfoDiv.style.display = 'block';
}
}
function displayBatteryInfo(batteryInfo) {
const batteryInfoDiv = document.getElementById('battery-info');
const batteryDetailsDiv = document.getElementById('battery-details');
let html = '';
if (batteryInfo.level !== null && batteryInfo.level !== undefined) {
const level = batteryInfo.level;
const levelColor = level > 50 ? '#51cf66' : level > 20 ? '#ffd43b' : '#ff6b6b';
html += `<div style="margin-bottom: 10px;">
<strong>电量:</strong>
<span style="color: ${levelColor}; font-size: 24px; font-weight: bold;">${level}%</span>
</div>`;
// 电量条
html += `<div class="battery-bar">
<div class="battery-bar-fill" style="width: ${level}%; background: ${levelColor};"></div>
</div>`;
} else {
html += '<div style="margin-bottom: 10px;"><strong>电量:</strong> 无法获取</div>';
}
if (batteryInfo.charging !== null && batteryInfo.charging !== undefined) {
html += `<div style="margin-bottom: 10px;">
<strong>充电状态:</strong>
<span style="color: ${batteryInfo.charging ? '#51cf66' : '#ffd43b'};">
${batteryInfo.charging ? '🔌 正在充电' : '🔋 使用电池'}
</span>
</div>`;
}
if (batteryInfo.chargingTime !== null && batteryInfo.chargingTime !== undefined && batteryInfo.chargingTime !== Infinity) {
const hours = Math.floor(batteryInfo.chargingTime / 3600);
const minutes = Math.floor((batteryInfo.chargingTime % 3600) / 60);
html += `<div style="margin-bottom: 10px;">
<strong>充满电时间:</strong> ${hours}小时${minutes}分钟
</div>`;
}
if (batteryInfo.dischargingTime !== null && batteryInfo.dischargingTime !== undefined && batteryInfo.dischargingTime !==Infinity) {
const hours = Math.floor(batteryInfo.dischargingTime / 3600);
const minutes = Math.floor((batteryInfo.dischargingTime % 3600) / 60);
html += `<div style="margin-bottom: 10px;">
<strong>预计使用时间:</strong> ${hours}小时${minutes}分钟
</div>`;
}
if (batteryInfo.error) {
html += `<div style="color: #ff6b6b; margin-top: 10px;">错误: ${batteryInfo.error}</div>`;
}
batteryDetailsDiv.innerHTML = html;
batteryInfoDiv.style.display = 'block';
}
function updateBatteryInfo(battery) {
const batteryInfo = {
level: Math.round(battery.level * 100),
charging: battery.charging,
chargingTime: battery.chargingTime,
dischargingTime: battery.dischargingTime
};
displayBatteryInfo(batteryInfo);
}
</script>
</body>
</html>
鸿蒙PC平台兼容性
兼容性说明
重要提示:本文档中实现的所有代码在鸿蒙PC平台上都可以正常使用!
1. HTML5 Battery API 支持
鸿蒙PC平台基于 Chromium 内核,完全支持 HTML5 Battery API:
// 在鸿蒙PC平台上,这段代码可以正常工作
if (navigator.getBattery) {
const battery = await navigator.getBattery();
// 可以获取完整的电池信息
console.log('电量:', battery.level * 100 + '%');
console.log('充电状态:', battery.charging);
}
2. Linux 系统命令支持
鸿蒙PC平台基于 Linux 内核,支持标准的 Linux 命令:
// 在鸿蒙PC平台上,acpi 命令可以正常工作
const command = 'acpi -b';
const { stdout } = await execAsync(command);
// 可以解析出电池信息
3. Electron IPC 通信
Electron 的 IPC 通信机制在鸿蒙PC平台上完全支持:
// 主进程和渲染进程之间的通信在鸿蒙PC上正常工作
ipcMain.handle('get-battery-level', async (event) => {
// 处理逻辑
});
// 渲染进程调用
const batteryInfo = await window.electronAPI.getBatteryLevel();
4. powerMonitor 事件
Electron 的 powerMonitor 模块在鸿蒙PC平台上可以正常监听电源事件:
// 在鸿蒙PC平台上可以正常监听电源连接/断开事件
powerMonitor.on('on-ac', () => {
console.log('电源已连接');
});
powerMonitor.on('on-battery', () => {
console.log('使用电池供电');
});
测试验证
在鸿蒙PC平台上测试结果:
-
✅ HTML5 Battery API 正常工作
-
✅ 系统命令(acpi)可以获取电池信息
-
✅ IPC 通信正常
-
✅ 电源状态监听正常
-
✅ 电量变化监听正常

遇到的问题与解决方案
问题一:HTML5 Battery API 在某些浏览器中不支持
现象:某些旧版浏览器或特殊环境不支持 navigator.getBattery() API。
解决方案:
-
检测 API 可用性:
if (navigator.getBattery) {
// 使用 HTML5 API
} else {
// 降级到 Electron IPC
}
-
提供降级方案:使用 Electron IPC + 系统命令作为备选方案。
问题二:系统命令在不同平台上不同
现象:Windows、macOS、Linux 获取电池信息的命令不同。
解决方案:
-
平台检测:
const platform = process.platform;
if (platform === 'win32') {
// Windows 命令
} else if (platform === 'darwin') {
// macOS 命令
} else if (platform === 'linux') {
// Linux 命令(包括鸿蒙PC)
}
-
统一返回格式:无论使用哪种命令,都返回统一的数据结构。
问题三:系统命令执行失败
现象:某些系统可能没有安装相应的命令工具(如 Linux 系统可能没有 acpi)。
解决方案:
-
错误处理:
try {
const { stdout } = await execAsync(command);
// 处理结果
} catch (error) {
console.error('获取电池电量失败:', error);
return {
level: null,
error: error.message
};
}
-
降级到 HTML5 API:如果系统命令失败,优先使用 HTML5 API。
问题四:电量更新不及时
现象:电量变化后,界面没有及时更新。
解决方案:
-
监听事件:
// HTML5 API 事件监听
battery.addEventListener('levelchange', () => {
updateBatteryInfo(battery);
});
battery.addEventListener('chargingchange', () => {
updateBatteryInfo(battery);
});
// Electron powerMonitor 事件监听
powerMonitor.on('on-ac', () => {
// 通知渲染进程更新
});
-
定时刷新(可选):
// 每30秒刷新一次电量信息
setInterval(async () => {
const batteryInfo = await getBatteryInfo();
displayBatteryInfo(batteryInfo);
}, 30000);
功能特性
1. 双重保障机制
-
优先使用 HTML5 Battery API:简单、可靠、跨平台兼容
-
降级到系统命令:当 HTML5 API 不可用时,使用系统命令获取
2. 实时监听
-
电量变化监听:自动监听电池电量变化
-
充电状态监听:自动监听充电/放电状态变化
-
电源连接监听:监听电源插拔事件
3. 可视化显示
-
电量百分比:显示精确的电量百分比
-
电量条:可视化电量条,颜色随电量变化
-
绿色:> 50%
-
黄色:20% - 50%
-
红色:< 20%
-
-
充电状态图标:直观显示充电/放电状态
-
时间预估:显示充满电时间或预计使用时间
4. 跨平台支持
-
✅ Windows(WMIC 命令)
-
✅ macOS(pmset 命令)
-
✅ Linux(acpi 命令)
-
✅ 鸿蒙PC平台(HTML5 API + acpi 命令)
最佳实践
1. 错误处理
async function getBatteryInfo() {
try {
if (navigator.getBattery) {
const battery = await navigator.getBattery();
return battery;
} else if (window.electronAPI && window.electronAPI.getBatteryLevel) {
return await window.electronAPI.getBatteryLevel();
} else {
throw new Error('电池信息功能不可用');
}
} catch (error) {
console.error('获取电池信息失败:', error);
// 显示友好的错误提示
showError('无法获取电池信息,请稍后重试');
return null;
}
}
2. 性能优化
// 避免频繁调用系统命令
let lastBatteryCheck = 0;
const BATTERY_CHECK_INTERVAL = 5000; // 5秒内不重复检查
async function getBatteryInfo() {
const now = Date.now();
if (now - lastBatteryCheck < BATTERY_CHECK_INTERVAL) {
return cachedBatteryInfo;
}
lastBatteryCheck = now;
// 获取电池信息
}
3. 用户体验优化
// 显示加载状态
function getBatteryInfo() {
showLoading('正在获取电池信息...');
getBatteryInfoAsync()
.then(batteryInfo => {
hideLoading();
displayBatteryInfo(batteryInfo);
})
.catch(error => {
hideLoading();
showError('获取电池信息失败');
});
}
4. 电量低提醒
function checkBatteryLevel(batteryInfo) {
if (batteryInfo.level !== null && batteryInfo.level < 20 && !batteryInfo.charging) {
// 电量低于20%且未充电时提醒
showNotification('电池电量低', `当前电量:${batteryInfo.level}%,请及时充电`);
}
}
5. 自动刷新
// 页面加载时自动获取电池信息
window.addEventListener('DOMContentLoaded', () => {
getBatteryInfo();
});
// 定期刷新电池信息(可选)
setInterval(() => {
getBatteryInfo();
}, 60000); // 每分钟刷新一次
完整示例代码
main.js(完整版)
const { app, BrowserWindow, ipcMain, powerMonitor } = require('electron');
const path = require('path');
const { exec } = require('child_process');
const { promisify } = require('util');
const execAsync = promisify(exec);
let mainWindow;
function createWindow() {
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
preload: path.join(__dirname, 'preload.js'),
},
});
mainWindow.loadFile('index.html');
return mainWindow;
}
// 获取电池电量的辅助函数
async function getBatteryLevel() {
const platform = process.platform;
try {
if (platform === 'win32') {
const command = 'WMIC PATH Win32_Battery Get EstimatedChargeRemaining';
const { stdout } = await execAsync(command);
const match = stdout.match(/\d+/);
if (match) {
return {
level: parseInt(match[0], 10),
charging: null,
chargingTime: null,
dischargingTime: null
};
}
} else if (platform === 'darwin') {
const command = 'pmset -g batt';
const { stdout } = await execAsync(command);
const levelMatch = stdout.match(/(\d+)%/);
const chargingMatch = stdout.match(/charging|discharging|AC Power/i);
return {
level: levelMatch ? parseInt(levelMatch[1], 10) : null,
charging: chargingMatch ? chargingMatch[0].toLowerCase().includes('charging') ||
chargingMatch[0].toLowerCase().includes('ac') : null,
chargingTime: null,
dischargingTime: null
};
} else if (platform === 'linux') {
const command = 'acpi -b';
const { stdout } = await execAsync(command);
const levelMatch = stdout.match(/(\d+)%/);
const chargingMatch = stdout.match(/Charging|Discharging/i);
return {
level: levelMatch ? parseInt(levelMatch[1], 10) : null,
charging: chargingMatch ? chargingMatch[0].toLowerCase().includes('charging') : null,
chargingTime: null,
dischargingTime: null
};
}
} catch (error) {
console.error('获取电池电量失败:', error);
return {
level: null,
charging: null,
chargingTime: null,
dischargingTime: null,
error: error.message
};
}
return {
level: null,
charging: null,
chargingTime: null,
dischargingTime: null,
error: 'Unsupported platform'
};
}
// IPC 处理器
ipcMain.handle('get-battery-level', async (event) => {
try {
return await getBatteryLevel();
} catch (error) {
console.error('获取电池电量错误:', error);
return {
level: null,
charging: null,
chargingTime: null,
dischargingTime: null,
error: error.message
};
}
});
// 监听电源状态变化
powerMonitor.on('on-ac', () => {
BrowserWindow.getAllWindows().forEach(win => {
if (!win.isDestroyed()) {
win.webContents.send('battery-status-changed', {
charging: true,
level: null
});
}
});
});
powerMonitor.on('on-battery', () => {
BrowserWindow.getAllWindows().forEach(win => {
if (!win.isDestroyed()) {
win.webContents.send('battery-status-changed', {
charging: false,
level: null
});
}
});
});
app.whenReady().then(() => {
createWindow();
});
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
preload.js(完整版)
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electronAPI', {
versions: {
chrome: process.versions.chrome,
node: process.versions.node,
electron: process.versions.electron
},
getBatteryLevel: () => {
return ipcRenderer.invoke('get-battery-level');
},
onBatteryStatusChange: (callback) => {
ipcRenderer.on('battery-status-changed', (event, batteryInfo) => {
callback(batteryInfo);
});
},
removeBatteryStatusListener: () => {
ipcRenderer.removeAllListeners('battery-status-changed');
}
});
总结
实现要点
-
双重保障机制:优先使用 HTML5 Battery API,降级到系统命令
-
跨平台支持:支持 Windows、macOS、Linux 和鸿蒙PC平台
-
实时监听:监听电量变化和充电状态变化
-
用户体验:提供可视化显示和友好的错误提示
关键优势
-
✅ 鸿蒙PC平台完全兼容:所有代码在鸿蒙PC平台上都可以正常工作
-
✅ HTML5 API 优先:在支持的平台上使用最可靠的方案
-
✅ 系统命令降级:确保在不支持 HTML5 API 的环境下也能工作
-
✅ 实时更新:自动监听电量变化,无需手动刷新
注意事项
-
权限要求:某些系统命令可能需要特定权限
-
命令可用性:确保目标系统安装了相应的命令工具(如 Linux 的
acpi) -
错误处理:完善的错误处理确保应用稳定性
-
性能考虑:避免频繁调用系统命令,合理使用缓存
扩展方向
-
电量历史记录:记录电量变化历史,绘制电量曲线图
-
低电量提醒:当电量低于阈值时自动提醒用户
-
省电模式:根据电量自动调整应用行为
-
多设备支持:支持获取多个电池设备的信息
参考资料
作者:GitCode &坚果派Electron 鸿蒙适配团队
更多推荐



所有评论(0)