1"""jc - JSON CLI output utility `acpi` command output parser 2 3Usage (cli): 4 5 $ acpi -V | jc --acpi 6 7 or 8 9 $ jc acpi -V 10 11Usage (module): 12 13 import jc.parsers.acpi 14 result = jc.parsers.acpi.parse(acpi_command_output) 15 16Schema: 17 18 [ 19 { 20 "type": string, 21 "id": integer, 22 "state": string, 23 "charge_percent": integer, 24 "until_charged": string, 25 "until_charged_hours": integer, 26 "until_charged_minuts": integer, 27 "until_charged_seconds": integer, 28 "until_charged_total_seconds": integer, 29 "charge_remaining": string, 30 "charge_remaining_hours": integer, 31 "charge_remaining_minutes": integer, 32 "charge_remaining_seconds": integer, 33 "charge_remaining_total_seconds": integer, 34 "design_capacity_mah": integer, 35 "last_full_capacity": integer, 36 "last_full_capacity_percent": integer, 37 "on-line": boolean, 38 "mode": string, 39 "temperature": float, 40 "temperature_unit": string, 41 "trip_points": [ 42 { 43 "id": integer, 44 "switches_to_mode": string, 45 "temperature": float, 46 "temperature_unit": string 47 } 48 ], 49 "messages": [ 50 string 51 ] 52 } 53 ] 54 55Examples: 56 57 $ acpi -V | jc --acpi -p 58 [ 59 { 60 "type": "Battery", 61 "id": 0, 62 "state": "Charging", 63 "charge_percent": 71, 64 "until_charged": "00:29:20", 65 "design_capacity_mah": 2110, 66 "last_full_capacity": 2271, 67 "last_full_capacity_percent": 100, 68 "until_charged_hours": 0, 69 "until_charged_minutes": 29, 70 "until_charged_seconds": 20, 71 "until_charged_total_seconds": 1760 72 }, 73 { 74 "type": "Adapter", 75 "id": 0, 76 "on-line": true 77 }, 78 { 79 "type": "Thermal", 80 "id": 0, 81 "mode": "ok", 82 "temperature": 46.0, 83 "temperature_unit": "C", 84 "trip_points": [ 85 { 86 "id": 0, 87 "switches_to_mode": "critical", 88 "temperature": 127.0, 89 "temperature_unit": "C" 90 }, 91 { 92 "id": 1, 93 "switches_to_mode": "hot", 94 "temperature": 127.0, 95 "temperature_unit": "C" 96 } 97 ] 98 }, 99 { 100 "type": "Cooling", 101 "id": 0, 102 "messages": [ 103 "Processor 0 of 10" 104 ] 105 }, 106 { 107 "type": "Cooling", 108 "id": 1, 109 "messages": [ 110 "Processor 0 of 10" 111 ] 112 }, 113 { 114 "type": "Cooling", 115 "id": 2, 116 "messages": [ 117 "x86_pkg_temp no state information available" 118 ] 119 }, 120 { 121 "type": "Cooling", 122 "id": 3, 123 "messages": [ 124 "Processor 0 of 10" 125 ] 126 }, 127 { 128 "type": "Cooling", 129 "id": 4, 130 "messages": [ 131 "intel_powerclamp no state information available" 132 ] 133 }, 134 { 135 "type": "Cooling", 136 "id": 5, 137 "messages": [ 138 "Processor 0 of 10" 139 ] 140 } 141 ] 142 143 $ acpi -V | jc --acpi -p -r 144 [ 145 { 146 "type": "Battery", 147 "id": "0", 148 "state": "Charging", 149 "charge_percent": "71", 150 "until_charged": "00:29:20", 151 "design_capacity_mah": "2110", 152 "last_full_capacity": "2271", 153 "last_full_capacity_percent": "100" 154 }, 155 { 156 "type": "Adapter", 157 "id": "0", 158 "on-line": true 159 }, 160 { 161 "type": "Thermal", 162 "id": "0", 163 "mode": "ok", 164 "temperature": "46.0", 165 "temperature_unit": "C", 166 "trip_points": [ 167 { 168 "id": "0", 169 "switches_to_mode": "critical", 170 "temperature": "127.0", 171 "temperature_unit": "C" 172 }, 173 { 174 "id": "1", 175 "switches_to_mode": "hot", 176 "temperature": "127.0", 177 "temperature_unit": "C" 178 } 179 ] 180 }, 181 { 182 "type": "Cooling", 183 "id": "0", 184 "messages": [ 185 "Processor 0 of 10" 186 ] 187 }, 188 { 189 "type": "Cooling", 190 "id": "1", 191 "messages": [ 192 "Processor 0 of 10" 193 ] 194 }, 195 { 196 "type": "Cooling", 197 "id": "2", 198 "messages": [ 199 "x86_pkg_temp no state information available" 200 ] 201 }, 202 { 203 "type": "Cooling", 204 "id": "3", 205 "messages": [ 206 "Processor 0 of 10" 207 ] 208 }, 209 { 210 "type": "Cooling", 211 "id": "4", 212 "messages": [ 213 "intel_powerclamp no state information available" 214 ] 215 }, 216 { 217 "type": "Cooling", 218 "id": "5", 219 "messages": [ 220 "Processor 0 of 10" 221 ] 222 } 223 ] 224""" 225import jc.utils 226 227 228class info(): 229 """Provides parser metadata (version, author, etc.)""" 230 version = '1.3' 231 description = '`acpi` command parser' 232 author = 'Kelly Brazil' 233 author_email = 'kellyjonbrazil@gmail.com' 234 235 # compatible options: linux, darwin, cygwin, win32, aix, freebsd 236 compatible = ['linux'] 237 magic_commands = ['acpi'] 238 239 240__version__ = info.version 241 242 243def _process(proc_data): 244 """ 245 Final processing to conform to the schema. 246 247 Parameters: 248 249 proc_data: (List of Dictionaries) raw structured data to process 250 251 Returns: 252 253 List of Dictionaries. Structured data to conform to the schema. 254 """ 255 int_list = ['id', 'charge_percent', 'design_capacity_mah', 'last_full_capacity', 'last_full_capacity_percent'] 256 float_list = ['temperature'] 257 258 for entry in proc_data: 259 for key in entry: 260 if key in int_list: 261 entry[key] = jc.utils.convert_to_int(entry[key]) 262 if key in float_list: 263 entry[key] = jc.utils.convert_to_float(entry[key]) 264 265 if 'trip_points' in entry: 266 for tp in entry['trip_points']: 267 for key in tp: 268 if key in int_list: 269 tp[key] = jc.utils.convert_to_int(tp[key]) 270 if key in float_list: 271 tp[key] = jc.utils.convert_to_float(tp[key]) 272 273 for entry in proc_data: 274 if 'until_charged' in entry: 275 entry['until_charged_hours'] = int(entry['until_charged'].split(':')[0]) 276 entry['until_charged_minutes'] = int(entry['until_charged'].split(':')[1]) 277 entry['until_charged_seconds'] = int(entry['until_charged'].split(':')[2]) 278 entry['until_charged_total_seconds'] = (entry['until_charged_hours'] * 3600) + \ 279 (entry['until_charged_minutes'] * 60) + entry['until_charged_seconds'] 280 281 if 'charge_remaining' in entry: 282 entry['charge_remaining_hours'] = int(entry['charge_remaining'].split(':')[0]) 283 entry['charge_remaining_minutes'] = int(entry['charge_remaining'].split(':')[1]) 284 entry['charge_remaining_seconds'] = int(entry['charge_remaining'].split(':')[2]) 285 entry['charge_remaining_total_seconds'] = (entry['charge_remaining_hours'] * 3600) + \ 286 (entry['charge_remaining_minutes'] * 60) + entry['charge_remaining_seconds'] 287 288 return proc_data 289 290 291def parse(data, raw=False, quiet=False): 292 """ 293 Main text parsing function 294 295 Parameters: 296 297 data: (string) text data to parse 298 raw: (boolean) output preprocessed JSON if True 299 quiet: (boolean) suppress warning messages if True 300 301 Returns: 302 303 List of Dictionaries. Raw or processed structured data. 304 """ 305 jc.utils.compatibility(__name__, info.compatible, quiet) 306 jc.utils.input_type_check(data) 307 308 raw_output = [] 309 output_line = {} 310 line_state = '' 311 last_line_state = '' 312 obj_type = '' 313 obj_id = '' 314 trip_points_list = [] 315 trip_points_dict = {} 316 messages_list = [] 317 318 if jc.utils.has_data(data): 319 320 for line in filter(None, data.splitlines()): 321 obj_type = line.split()[0] 322 obj_id = line.split()[1][:-1] 323 line_state = obj_type + obj_id 324 325 if line_state != last_line_state: 326 if output_line: 327 raw_output.append(output_line) 328 329 output_line = {} 330 trip_points_list = [] 331 messages_list = [] 332 333 if obj_type == 'Battery': 334 output_line['type'] = obj_type 335 output_line['id'] = obj_id 336 if 'Charging' in line or 'Discharging' in line or 'Full' in line: 337 output_line['state'] = line.split()[2][:-1] 338 output_line['charge_percent'] = line.split()[3].rstrip('%,') 339 if 'rate information unavailable' not in line: 340 if 'Charging' in line: 341 output_line['until_charged'] = line.split()[4] 342 if 'Discharging' in line: 343 output_line['charge_remaining'] = line.split()[4] 344 345 if 'design capacity' in line: 346 output_line['design_capacity_mah'] = line.split()[4] 347 output_line['last_full_capacity'] = line.split()[9] 348 output_line['last_full_capacity_percent'] = line.split()[-1][:-1] 349 350 if obj_type == 'Adapter': 351 output_line['type'] = obj_type 352 output_line['id'] = obj_id 353 if 'on-line' in line: 354 output_line['on-line'] = True 355 else: 356 output_line['on-line'] = False 357 358 if obj_type == 'Thermal': 359 output_line['type'] = obj_type 360 output_line['id'] = obj_id 361 if 'trip point' not in line: 362 output_line['mode'] = line.split()[2][:-1] 363 output_line['temperature'] = line.split()[3] 364 output_line['temperature_unit'] = line.split()[-1] 365 else: 366 trip_points_dict = { 367 "id": line.split()[4], 368 "switches_to_mode": line.split()[8], 369 "temperature": line.split()[11], 370 "temperature_unit": line.split()[-1] 371 } 372 trip_points_list.append(trip_points_dict) 373 output_line['trip_points'] = trip_points_list 374 375 if obj_type == 'Cooling': 376 output_line['type'] = obj_type 377 output_line['id'] = obj_id 378 messages_list.append(line.split(maxsplit=2)[2]) 379 output_line['messages'] = messages_list 380 381 last_line_state = line_state 382 383 if output_line: 384 raw_output.append(output_line) 385 386 if raw: 387 return raw_output 388 else: 389 return _process(raw_output) 390