From 58645301ec111bfeb2a618f5f169cc734042dc5f Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Thu, 11 Mar 2021 19:55:47 -0800 Subject: [PATCH] add acpi command parser --- jc/cli.py | 3 +- jc/parsers/acpi.py | 191 +++++++++++++++++++++++++ tests/fixtures/generic/acpi-V.out | 16 +++ tests/fixtures/generic/acpi-V2.out | 12 ++ tests/fixtures/generic/acpi-V3.out | 13 ++ tests/fixtures/ubuntu-18.04/acpi-V.out | 2 + 6 files changed, 236 insertions(+), 1 deletion(-) create mode 100644 jc/parsers/acpi.py create mode 100644 tests/fixtures/generic/acpi-V.out create mode 100644 tests/fixtures/generic/acpi-V2.out create mode 100644 tests/fixtures/generic/acpi-V3.out create mode 100644 tests/fixtures/ubuntu-18.04/acpi-V.out diff --git a/jc/cli.py b/jc/cli.py index a9b11723..8874bffc 100644 --- a/jc/cli.py +++ b/jc/cli.py @@ -21,7 +21,7 @@ import jc.appdirs as appdirs class info(): - version = '1.14.4' + version = '1.14.5' description = 'JSON CLI output utility' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' @@ -30,6 +30,7 @@ class info(): __version__ = info.version parsers = [ + 'acpi', 'airport', 'airport-s', 'arp', diff --git a/jc/parsers/acpi.py b/jc/parsers/acpi.py new file mode 100644 index 00000000..fc41ca57 --- /dev/null +++ b/jc/parsers/acpi.py @@ -0,0 +1,191 @@ +"""jc - JSON CLI output utility `acpi` command output parser + +Usage (cli): + + $ acpi -V | jc --acpi + + or + + $ jc acpi -V + +Usage (module): + + import jc.parsers.acpi + result = jc.parsers.acpi.parse(acpi_command_output) + +Compatibility: + + 'linux' + +Examples: + + $ acpi -V | jc --acpi -p + [] + + $ acpi -V | jc --acpi -p -r + [] +""" +import jc.utils + + +class info(): + version = '1.0' + description = 'acpi command parser' + author = 'Kelly Brazil' + author_email = 'kellyjonbrazil@gmail.com' + + # compatible options: linux, darwin, cygwin, win32, aix, freebsd + compatible = ['linux'] + magic_commands = ['acpi'] + + +__version__ = info.version + + +def process(proc_data): + """ + Final processing to conform to the schema. + + Parameters: + + proc_data: (List of Dictionaries) raw structured data to process + + Returns: + + List of Dictionaries. Structured data with the following schema: + + [ + { + "type": string, + "id": integer, + "state": string, + "charge_percent": integer, + "until_charged": string, + "charge_remaining" string, + "design_capacity_mah": integer, + "last_full_capacity": integer, + "last_full_capacity_percent": integer, + "on-line": boolean, + "mode": string, + "temperature": float, + "temperature_unit": string, + "trip_points": [ + { + "id": integer, + "switches_to_mode": string, + "temperature": float, + "temperature_unit": string + } + ], + "messages": [ + string + ] + } + ] + """ + + # rebuild output for added semantic information + return proc_data + + +def parse(data, raw=False, quiet=False): + """ + Main text parsing function + + Parameters: + + data: (string) text data to parse + raw: (boolean) output preprocessed JSON if True + quiet: (boolean) suppress warning messages if True + + Returns: + + List of Dictionaries. Raw or processed structured data. + """ + if not quiet: + jc.utils.compatibility(__name__, info.compatible) + + raw_output = [] + output_line = {} + line_state = '' + last_line_state = '' + + if jc.utils.has_data(data): + + for line in filter(None, data.splitlines()): + if line.startswith('Battery'): + line_state = 'battery' + if line_state != last_line_state: + if output_line: + raw_output.append(output_line) + last_line_state = line_state + + output_line = {} + + if line.startswith('Adapter'): + line_state = 'adapter' + if line_state != last_line_state: + if output_line: + raw_output.append(output_line) + last_line_state = line_state + + output_line = {} + + if line.startswith('Thermal'): + line_state = 'thermal' + if line_state != last_line_state: + if output_line: + raw_output.append(output_line) + last_line_state = line_state + + output_line = {} + + if line.startswith('Cooling'): + line_state = 'cooling' + if line_state != last_line_state: + if output_line: + raw_output.append(output_line) + last_line_state = line_state + + output_line = {} + + if line_state == 'battery': + output_line['type'] = 'battery' + output_line['id'] = line.split()[1][:-1] + if 'Charging' in line: + output_line['state'] = line.split()[2][:-1] + output_line['charge_percent'] = line.split()[3][:-2] + if 'rate information unavailable' not in line: + output_line['until_charged'] = line.split()[4] + + if 'design capacity' in line: + output_line['design_capacity_mah'] = line.split()[4] + output_line['last_full_capacity'] = line.split()[9] + output_line['last_full_capacity_percent'] = line.split()[-1][:-1] + + if 'Discharging' in line: + output_line['state'] = line.split()[2][:-1] + output_line['charge_percent'] = line.split()[3][:-2] + if 'rate information unavailable' not in line: + output_line['charge_remaining'] = line.split()[4] + + continue + + if line_state == 'adapter': + continue + + if line_state == 'thermal': + continue + + if line_state == 'cooling': + continue + + last_line_state = line_state + + if output_line: + raw_output.append(output_line) + + if raw: + return raw_output + else: + return process(raw_output) diff --git a/tests/fixtures/generic/acpi-V.out b/tests/fixtures/generic/acpi-V.out new file mode 100644 index 00000000..35c7916e --- /dev/null +++ b/tests/fixtures/generic/acpi-V.out @@ -0,0 +1,16 @@ +Battery 0: Charging, 71%, 00:29:20 until charged +Battery 0: design capacity 2110 mAh, last full capacity 2271 mAh = 100% +Battery 1: Discharging, 98%, 01:43:14 remaining +Battery 1: design capacity 4400 mAh, last full capacity 3013 mAh = 68% +Battery 2: Discharging, 0%, rate information unavailable +Adapter 0: on-line +Adapter 1: off-line +Thermal 0: ok, 46.0 degrees C +Thermal 0: trip point 0 switches to mode critical at temperature 127.0 degrees C +Thermal 0: trip point 1 switches to mode hot at temperature 127.0 degrees C +Cooling 0: Processor 0 of 10 +Cooling 1: Processor 0 of 10 +Cooling 2: x86_pkg_temp no state information available +Cooling 3: Processor 0 of 10 +Cooling 4: intel_powerclamp no state information available +Cooling 5: Processor 0 of 10 diff --git a/tests/fixtures/generic/acpi-V2.out b/tests/fixtures/generic/acpi-V2.out new file mode 100644 index 00000000..9af0dda3 --- /dev/null +++ b/tests/fixtures/generic/acpi-V2.out @@ -0,0 +1,12 @@ +Battery 0: Charging, 71%, 00:29:20 until charged +Battery 0: design capacity 2110 mAh, last full capacity 2271 mAh = 100% +Adapter 0: on-line +Thermal 0: ok, 46.0 degrees C +Thermal 0: trip point 0 switches to mode critical at temperature 127.0 degrees C +Thermal 0: trip point 1 switches to mode hot at temperature 127.0 degrees C +Cooling 0: Processor 0 of 10 +Cooling 1: Processor 0 of 10 +Cooling 2: x86_pkg_temp no state information available +Cooling 3: Processor 0 of 10 +Cooling 4: intel_powerclamp no state information available +Cooling 5: Processor 0 of 10 diff --git a/tests/fixtures/generic/acpi-V3.out b/tests/fixtures/generic/acpi-V3.out new file mode 100644 index 00000000..94de9309 --- /dev/null +++ b/tests/fixtures/generic/acpi-V3.out @@ -0,0 +1,13 @@ +Battery 0: Discharging, 98%, 01:43:14 remaining +Battery 0: design capacity 4400 mAh, last full capacity 3013 mAh = 68% +Battery 1: Discharging, 0%, rate information unavailable +Adapter 0: off-line +Thermal 0: ok, 60.5 degrees C +Thermal 0: trip point 0 switches to mode critical at temperature 84.0 degrees C +Cooling 0: Processor 0 of 3 +Cooling 1: intel_powerclamp no state information available +Cooling 2: Processor 0 of 3 +Cooling 3: x86_pkg_temp no state information available +Cooling 4: Processor 0 of 3 +Cooling 5: LCD 0 of 15 +Cooling 6: Processor 0 of 3 diff --git a/tests/fixtures/ubuntu-18.04/acpi-V.out b/tests/fixtures/ubuntu-18.04/acpi-V.out new file mode 100644 index 00000000..964dbddf --- /dev/null +++ b/tests/fixtures/ubuntu-18.04/acpi-V.out @@ -0,0 +1,2 @@ +Adapter 0: on-line +Cooling 0: Processor 0 of 7