1 /** 2 * battery-d - simple library for reading battery info on linux laptops. 3 * License: MIT 4 * Copyright: Copyright © 2017, Azbuka 5 * Authors: Azbuka 6 */ 7 module battery.d; 8 9 import core.time : Duration, dur; 10 11 /** 12 * Current battery status 13 */ 14 enum BatteryStatus : ubyte { 15 /// Discharging 16 DISCHARGING, 17 /// Charging 18 CHARGING, 19 /// Full (100%) 20 FULL 21 } 22 23 /** 24 * Gets list of all aviable batteries in system 25 * Examples: 26 * --- 27 * import std.array; 28 * import battery.d; 29 * getBatteryList().array; // ["BAT0"], laptops in general have only 1 battery 30 * getBatteryList().front; // "BAT1" 31 * --- 32 * Returns: range of battery names 33 */ 34 auto getBatteryList() { 35 import std.file : dirEntries, SpanMode; 36 import std.algorithm : map, filter, startsWith; 37 import std.path : baseName; 38 return "/sys/class/power_supply".dirEntries(SpanMode.shallow) 39 .map!(a => a.name.baseName) 40 .filter!(a => a.startsWith("BAT")); 41 } 42 43 /** 44 * Battery exception. Thrown on errors. 45 */ 46 class BatteryException : Exception { 47 pure nothrow @nogc @safe this(string msg, 48 string file = __FILE__, 49 size_t line = __LINE__, 50 Throwable next = null) { 51 super(msg, file, line, next); 52 } 53 } 54 55 /** 56 * Main battery class 57 */ 58 class Battery { 59 private { 60 string bname; 61 string[string] rawdata; 62 float lvl; 63 Duration untilfull; 64 Duration remaining; 65 BatteryStatus stat; 66 } 67 68 /** 69 * Constructor. 70 * Params: 71 * battery_name = name of battery (example: BAT0) 72 * Throws: BatteryException if there is no such battery 73 */ 74 this(string battery_name) { 75 import std.file : exists; 76 if(!("/sys/class/power_supply/" ~ battery_name).exists) 77 throw new BatteryException("There is no such battery"); 78 this.bname = battery_name; 79 this.update; 80 } 81 82 /** 83 * Cunstructor. Uses first battery, returned by `getBatteryList()` 84 * Throws: battery exception, if no batteries found 85 */ 86 this() { 87 auto a = getBatteryList; 88 if(a.empty) 89 throw new BatteryException("Battery not found"); 90 this(a.front); 91 } 92 93 /** 94 * Updates battery info 95 */ 96 void update() { 97 import std.stdio : File; 98 import std.array : split, replaceFirst; 99 import std.conv : to; 100 import core.exception : RangeError; 101 102 auto f = File("/sys/class/power_supply/" ~ this.bname ~ "/uevent"); 103 foreach(line; f.byLine) { 104 auto s = line.split("="); 105 this.rawdata[s[0] 106 .replaceFirst("POWER_SUPPLY_", "") 107 .to!string] = s[1].to!string; 108 } 109 110 size_t rate, full, curr; 111 try { 112 rate = this.rawdata["CURRENT_NOW"].to!size_t; 113 full = this.rawdata["CHARGE_FULL"].to!size_t; 114 curr = this.rawdata["CHARGE_NOW"].to!size_t; 115 } catch (RangeError e) { 116 rate = this.rawdata["POWER_NOW"].to!size_t; 117 full = this.rawdata["ENERGY_FULL"].to!size_t; 118 curr = this.rawdata["ENERGY_NOW"].to!size_t; 119 } 120 121 this.lvl = (float(curr) / full) * 100; 122 123 if(rate) { 124 switch(this.rawdata["STATUS"]) { 125 case "Discharging": 126 this.remaining = ((float(curr) / rate) * 3600) 127 .to!long 128 .dur!"seconds"; 129 this.untilfull = Duration.zero; 130 this.stat = BatteryStatus.DISCHARGING; 131 break; 132 case "Charging": 133 this.remaining = Duration.zero; 134 this.untilfull = ((float(full - curr) / rate) * 3600) 135 .to!long 136 .dur!"seconds"; 137 this.stat = BatteryStatus.CHARGING; 138 break; 139 case "Full": 140 this.stat = BatteryStatus.FULL; 141 goto default; 142 default: 143 this.remaining = Duration.zero; 144 this.untilfull = Duration.zero; 145 break; 146 } 147 } else { 148 this.remaining = Duration.zero; 149 this.untilfull = Duration.zero; 150 } 151 } 152 153 /** 154 * Current battery level. 0-100% 155 * Returns: current battery level in % 156 */ 157 float level() { 158 return this.lvl; 159 } 160 161 /** 162 * Time until battery is full. 163 * `Duration.zero` if battery full or discharging. 164 * Returns: time until battery is full 165 */ 166 Duration timeUntilFull() { 167 return this.untilfull; 168 } 169 170 /** 171 * Time remaining. 172 * `Duration.zero` if battery full or charging. 173 * Returns: time remaining 174 */ 175 Duration timeRemaining() { 176 return this.remaining; 177 } 178 179 /** 180 * Current battery status. 181 * Returns: battery status 182 */ 183 BatteryStatus status() { 184 return this.stat; 185 } 186 187 /** 188 * Raw battery data. 189 * Returns: raw battery data 190 */ 191 string[string] raw() { 192 return this.rawdata; 193 } 194 195 /** 196 * Battery name. 197 * Returns: battery name 198 */ 199 string name() { 200 return this.bname; 201 } 202 }