UNPKG

10.6 kBJavaScriptView Raw
1'use strict';
2// @ts-check;
3// ==================================================================================
4// battery.js
5// ----------------------------------------------------------------------------------
6// Description: System Information - library
7// for Node.js
8// Copyright: (c) 2014 - 2020
9// Author: Sebastian Hildebrandt
10// ----------------------------------------------------------------------------------
11// License: MIT
12// ==================================================================================
13// 6. Battery
14// ----------------------------------------------------------------------------------
15
16const exec = require('child_process').exec;
17const fs = require('fs');
18const util = require('./util');
19
20let _platform = process.platform;
21
22const _linux = (_platform === 'linux');
23const _darwin = (_platform === 'darwin');
24const _windows = (_platform === 'win32');
25const _freebsd = (_platform === 'freebsd');
26const _openbsd = (_platform === 'openbsd');
27const _netbsd = (_platform === 'netbsd');
28const _sunos = (_platform === 'sunos');
29
30module.exports = function (callback) {
31
32 return new Promise((resolve) => {
33 process.nextTick(() => {
34 let result = {
35 hasbattery: false,
36 cyclecount: 0,
37 ischarging: false,
38 designedcapacity: 0,
39 maxcapacity: 0,
40 currentcapacity: 0,
41 voltage: 0,
42 capacityUnit: '',
43 percent: 0,
44 timeremaining: -1,
45 acconnected: true,
46 type: '',
47 model: '',
48 manufacturer: '',
49 serial: ''
50 };
51
52 if (_linux) {
53 let battery_path = '';
54 if (fs.existsSync('/sys/class/power_supply/BAT1/uevent')) {
55 battery_path = '/sys/class/power_supply/BAT1/';
56 } else if (fs.existsSync('/sys/class/power_supply/BAT0/uevent')) {
57 battery_path = '/sys/class/power_supply/BAT0/';
58 }
59 if (battery_path) {
60 fs.readFile(battery_path + 'uevent', function (error, stdout) {
61 if (!error) {
62 let lines = stdout.toString().split('\n');
63
64 result.ischarging = (util.getValue(lines, 'POWER_SUPPLY_STATUS', '=').toLowerCase() === 'charging');
65 result.acconnected = result.ischarging;
66 result.voltage = parseInt('0' + util.getValue(lines, 'POWER_SUPPLY_VOLTAGE_NOW', '='), 10) / 1000000.0;
67 result.capacityUnit = result.voltage ? 'mWh' : 'mAh';
68 result.cyclecount = parseInt('0' + util.getValue(lines, 'POWER_SUPPLY_CYCLE_COUNT', '='), 10);
69 result.maxcapacity = Math.round(parseInt('0' + util.getValue(lines, 'POWER_SUPPLY_CHARGE_FULL', '='), 10) / 1000.0 / (result.voltage || 1));
70 result.designedcapacity = Math.round(parseInt('0' + util.getValue(lines, 'POWER_SUPPLY_CHARGE_FULL_DESIGN', '='), 10) / 1000.0 / (result.voltage || 1)) | result.maxcapacity;
71 result.currentcapacity = Math.round(parseInt('0' + util.getValue(lines, 'POWER_SUPPLY_CHARGE_NOW', '='), 10) / 1000.0 / (result.voltage || 1));
72 if (!result.maxcapacity) {
73 result.maxcapacity = parseInt('0' + util.getValue(lines, 'POWER_SUPPLY_ENERGY_FULL', '='), 10) / 1000.0;
74 result.designcapacity = parseInt('0' + util.getValue(lines, 'POWER_SUPPLY_ENERGY_FULL_DESIGN', '='), 10) / 1000.0 | result.maxcapacity;
75 result.currentcapacity = parseInt('0' + util.getValue(lines, 'POWER_SUPPLY_ENERGY_NOW', '='), 10) / 1000.0;
76 }
77 const percent = util.getValue(lines, 'POWER_SUPPLY_CAPACITY', '=');
78 const energy = parseInt('0' + util.getValue(lines, 'POWER_SUPPLY_ENERGY_NOW', '='), 10);
79 const power = parseInt('0' + util.getValue(lines, 'POWER_SUPPLY_POWER_NOW', '='), 10);
80 const current = parseInt('0' + util.getValue(lines, 'POWER_SUPPLY_CURRENT_NOW', '='), 10);
81
82 result.percent = parseInt('0' + percent, 10);
83 if (result.maxcapacity && result.currentcapacity) {
84 result.hasbattery = true;
85 if (!percent) {
86 result.percent = 100.0 * result.currentcapacity / result.maxcapacity;
87 }
88 }
89 if (result.ischarging) {
90 result.hasbattery = true;
91 }
92 if (energy && power) {
93 result.timeremaining = Math.floor(energy / power * 60);
94 } else if (current && result.currentcapacity) {
95 result.timeremaining = Math.floor(result.currentcapacity / current * 60);
96 }
97 result.type = util.getValue(lines, 'POWER_SUPPLY_TECHNOLOGY', '=');
98 result.model = util.getValue(lines, 'POWER_SUPPLY_MODEL_NAME', '=');
99 result.manufacturer = util.getValue(lines, 'POWER_SUPPLY_MANUFACTURER', '=');
100 result.serial = util.getValue(lines, 'POWER_SUPPLY_SERIAL_NUMBER', '=');
101 if (callback) { callback(result); }
102 resolve(result);
103 } else {
104 if (callback) { callback(result); }
105 resolve(result);
106 }
107 });
108 } else {
109 if (callback) { callback(result); }
110 resolve(result);
111 }
112 }
113 if (_freebsd || _openbsd || _netbsd) {
114 exec('sysctl hw.acpi.battery hw.acpi.acline', function (error, stdout) {
115 let lines = stdout.toString().split('\n');
116 const batteries = parseInt('0' + util.getValue(lines, 'hw.acpi.battery.units'), 10);
117 const percent = parseInt('0' + util.getValue(lines, 'hw.acpi.battery.life'), 10);
118 result.hasbattery = (batteries > 0);
119 result.cyclecount = -1;
120 result.ischarging = util.getValue(lines, 'hw.acpi.acline') !== '1';
121 result.acconnected = result.ischarging;
122 result.maxcapacity = -1;
123 result.currentcapacity = -1;
124 result.capacityUnit = 'unknown';
125 result.percent = batteries ? percent : -1;
126 if (callback) { callback(result); }
127 resolve(result);
128 });
129 }
130
131 if (_darwin) {
132 exec('ioreg -n AppleSmartBattery -r | egrep "CycleCount|IsCharging|DesignCapacity|MaxCapacity|CurrentCapacity|BatterySerialNumber|TimeRemaining|Voltage"; pmset -g batt | grep %', function (error, stdout) {
133 if (stdout) {
134 let lines = stdout.toString().replace(/ +/g, '').replace(/"+/g, '').replace(/-/g, '').split('\n');
135 result.cyclecount = parseInt('0' + util.getValue(lines, 'cyclecount', '='), 10);
136 result.voltage = parseInt('0' + util.getValue(lines, 'voltage', '='), 10) / 1000.0;
137 result.capacityUnit = result.voltage ? 'mWh' : 'mAh';
138 result.maxcapacity = Math.round(parseInt('0' + util.getValue(lines, 'maxcapacity', '='), 10) * (result.voltage || 1));
139 result.currentcapacity = Math.round(parseInt('0' + util.getValue(lines, 'currentcapacity', '='), 10) * (result.voltage || 1));
140 result.designedcapacity = Math.round(parseInt('0' + util.getValue(lines, 'DesignCapacity', '='), 10) * (result.voltage || 1));
141 result.manufacturer = 'Apple';
142 result.serial = util.getValue(lines, 'BatterySerialNumber', '=');
143 let percent = -1;
144 const line = util.getValue(lines, 'internal', 'Battery');
145 let parts = line.split(';');
146 if (parts && parts[0]) {
147 let parts2 = parts[0].split('\t');
148 if (parts2 && parts2[1]) {
149 percent = parseFloat(parts2[1].trim().replace(/%/g, ''));
150 }
151 }
152 if (parts && parts[1]) {
153 result.ischarging = (parts[1].trim() === 'charging');
154 result.acconnected = (parts[1].trim() !== 'discharging');
155 } else {
156 result.ischarging = util.getValue(lines, 'ischarging', '=').toLowerCase() === 'yes';
157 result.acconnected = result.ischarging;
158 }
159 if (result.maxcapacity && result.currentcapacity) {
160 result.hasbattery = true;
161 result.type = 'Li-ion';
162 result.percent = percent !== -1 ? percent : Math.round(100.0 * result.currentcapacity / result.maxcapacity);
163 if (!result.ischarging) {
164 result.timeremaining = parseInt('0' + util.getValue(lines, 'TimeRemaining', '='), 10);
165 }
166 }
167 }
168 if (callback) { callback(result); }
169 resolve(result);
170 });
171 }
172 if (_sunos) {
173 if (callback) { callback(result); }
174 resolve(result);
175 }
176 if (_windows) {
177 try {
178 util.wmic('Path Win32_Battery Get BatteryStatus, DesignCapacity, EstimatedChargeRemaining, DesignVoltage, FullChargeCapacity /value').then((stdout) => {
179 if (stdout) {
180 let lines = stdout.split('\r\n');
181 let status = util.getValue(lines, 'BatteryStatus', '=').trim();
182 // 1 = "Discharging"
183 // 2 = "On A/C"
184 // 3 = "Fully Charged"
185 // 4 = "Low"
186 // 5 = "Critical"
187 // 6 = "Charging"
188 // 7 = "Charging High"
189 // 8 = "Charging Low"
190 // 9 = "Charging Critical"
191 // 10 = "Undefined"
192 // 11 = "Partially Charged"
193 if (status && status != '10') {
194 const statusValue = parseInt(status);
195 result.hasbattery = true;
196 result.maxcapacity = parseInt(util.getValue(lines, 'DesignCapacity', '=') || 0);
197 result.designcapacity = parseInt(util.getValue(lines, 'DesignCapacity', '=') || 0);
198 result.voltage = parseInt(util.getValue(lines, 'DesignVoltage', '=') || 0) / 1000.0;
199 result.capacityUnit = 'mWh';
200 result.percent = parseInt(util.getValue(lines, 'EstimatedChargeRemaining', '=') || 0);
201 result.currentcapacity = parseInt(result.maxcapacity * result.percent / 100);
202 result.ischarging = (statusValue >= 6 && statusValue <= 9) || statusValue === 11 || (!(statusValue === 3) && !(statusValue === 1) && result.percent < 100);
203 result.acconnected = result.ischarging || statusValue === 2;
204 }
205 }
206 if (callback) { callback(result); }
207 resolve(result);
208 });
209 } catch (e) {
210 if (callback) { callback(result); }
211 resolve(result);
212 }
213 }
214 });
215 });
216};