From b3601bb7757174782b7e824ac3d1e6d6e8d544f0 Mon Sep 17 00:00:00 2001 From: EdenRafael Date: Thu, 21 Nov 2024 21:55:07 +0200 Subject: [PATCH 01/30] created the amixer first skeleton --- jc/parsers/amixer.py | 146 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 jc/parsers/amixer.py diff --git a/jc/parsers/amixer.py b/jc/parsers/amixer.py new file mode 100644 index 00000000..bc04ca8c --- /dev/null +++ b/jc/parsers/amixer.py @@ -0,0 +1,146 @@ +from typing import List, Dict + +import jc.utils + + +class info(): + """Provides parser metadata (version, author, etc.)""" + version = '1' + description = '`amixer` command parser' + author = 'Eden Refael' + author_email = 'edenraf@hotmail.com' + compatible = ['linux', 'darwin', 'cygwin', 'aix', 'freebsd'] + magic_commands = ['amixer'] + tags = ['command'] + + +__version__ = info.version + +def _process(proc_data: List[Dict]) -> List[Dict]: + """ + Final processing to conform to the schema. + + Parameters: + + proc_data: (List of Dictionaries) raw structured data to process + + Returns: + + List of Dictionaries. Structured data to conform to the schema: + """ + # int_list = {'expires'} + # + # # in BSD style, change name to null if it is a question mark + # for entry in proc_data: + # if 'name' in entry and entry['name'] == '?': + # entry['name'] = None + # + # for key in entry: + # if key in int_list: + # entry[key] = jc.utils.convert_to_int(entry[key]) + # + # return proc_data + + +def parse( + data: str, + raw: bool = False, + quiet: bool = False +) -> List[Dict]: + """ + Main text parsing function + + Parameters: + + data: (string) text data to parse + raw: (boolean) unprocessed output if True + quiet: (boolean) suppress warning messages if True + + Returns: + + List of Dictionaries. Raw or processed structured data. + """ + # jc.utils.compatibility(__name__, info.compatible, quiet) + # jc.utils.input_type_check(data) + # + # raw_output = [] + # cleandata = list(filter(None, data.splitlines())) + # + # if jc.utils.has_data(data): + # + # # remove final Entries row if -v was used + # if cleandata[-1].startswith('Entries:'): + # cleandata.pop(-1) + # + # # detect if freebsd/osx style was used + # if cleandata[0][-1] == ']': + # for line in cleandata: + # splitline = line.split() + # output_line: Dict[str, Any] = { + # 'name': splitline[0], + # 'address': splitline[1].lstrip('(').rstrip(')'), + # 'hwtype': splitline[-1].lstrip('[').rstrip(']'), + # 'hwaddress': splitline[3], + # 'iface': splitline[5] + # } + # + # if 'permanent' in splitline: + # output_line['permanent'] = True + # else: + # output_line['permanent'] = False + # + # if 'expires' in splitline: + # output_line['expires'] = splitline[-3] + # + # raw_output.append(output_line) + # + # # detect if linux style was used + # elif cleandata[0].startswith('Address'): + # + # # fix header row to change Flags Mask to flags_mask + # cleandata[0] = cleandata[0].replace('Flags Mask', 'flags_mask') + # cleandata[0] = cleandata[0].lower() + # + # raw_output = jc.parsers.universal.simple_table_parse(cleandata) + # + # # otherwise, try bsd style + # else: + # for line in cleandata: + # splitline = line.split() + # + # # Ignore AIX bucket information + # if 'bucket:' in splitline[0]: + # continue + # elif 'There' in splitline[0] and 'are' in splitline[1]: + # continue + # + # # AIX uses (incomplete) + # elif '' not in splitline and '(incomplete)' not in splitline: + # output_line = { + # 'name': splitline[0], + # 'address': splitline[1].lstrip('(').rstrip(')'), + # 'hwtype': splitline[4].lstrip('[').rstrip(']'), + # 'hwaddress': splitline[3], + # } + # # Handle permanence and ignore interface in AIX + # if 'permanent' in splitline: + # output_line['permanent'] = True + # elif 'in' not in splitline[6]: # AIX doesn't show interface + # output_line['iface'] = splitline[6] + # + # else: + # output_line = { + # 'name': splitline[0], + # 'address': splitline[1].lstrip('(').rstrip(')'), + # 'hwtype': None, + # 'hwaddress': None, + # } + # # AIX doesn't show interface + # if len(splitline) >= 5: + # output_line['iface'] = splitline[5] + # + # raw_output.append(output_line) + # + # return raw_output if raw else _process(raw_output) + + From 6a02b3d7f4e61bab7cd105c6a22f3cd0c5441660 Mon Sep 17 00:00:00 2001 From: EdenRafael Date: Thu, 21 Nov 2024 22:06:50 +0200 Subject: [PATCH 02/30] push testing and integrate this commit and branch with issue: #591 --- jc/parsers/amixer.py | 1 + 1 file changed, 1 insertion(+) diff --git a/jc/parsers/amixer.py b/jc/parsers/amixer.py index bc04ca8c..d0575261 100644 --- a/jc/parsers/amixer.py +++ b/jc/parsers/amixer.py @@ -59,6 +59,7 @@ def parse( Returns: List of Dictionaries. Raw or processed structured data. + push test """ # jc.utils.compatibility(__name__, info.compatible, quiet) # jc.utils.input_type_check(data) From 27ce3b2c3d495aa6ae1b8a02836194856e658d5e Mon Sep 17 00:00:00 2001 From: EdenRafael Date: Fri, 22 Nov 2024 15:22:16 +0200 Subject: [PATCH 03/30] #591 checks the input data with jc utils --- jc/parsers/amixer.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/jc/parsers/amixer.py b/jc/parsers/amixer.py index d0575261..0ef67203 100644 --- a/jc/parsers/amixer.py +++ b/jc/parsers/amixer.py @@ -9,7 +9,7 @@ class info(): description = '`amixer` command parser' author = 'Eden Refael' author_email = 'edenraf@hotmail.com' - compatible = ['linux', 'darwin', 'cygwin', 'aix', 'freebsd'] + compatible = ['linux'] magic_commands = ['amixer'] tags = ['command'] @@ -48,23 +48,25 @@ def parse( quiet: bool = False ) -> List[Dict]: """ - Main text parsing function + Main text parsing function, The amixer is alsa mixer tool and output, Will work with + Linux OS only. Parameters: - data: (string) text data to parse raw: (boolean) unprocessed output if True quiet: (boolean) suppress warning messages if True Returns: - List of Dictionaries. Raw or processed structured data. push test """ - # jc.utils.compatibility(__name__, info.compatible, quiet) - # jc.utils.input_type_check(data) - # - # raw_output = [] + # checks os compatibility and print a stderr massage if not compatible. quiet True could remove this check. + jc.utils.compatibility(__name__, info.compatible, quiet) + + # check if string + jc.utils.input_type_check(data) + + raw_output = [] # cleandata = list(filter(None, data.splitlines())) # # if jc.utils.has_data(data): From 37dc01f96029737a8998d556b6145d13039bb62e Mon Sep 17 00:00:00 2001 From: Eden Refael Date: Sun, 24 Nov 2024 21:16:48 +0200 Subject: [PATCH 04/30] created the data parser of the sget control of the amixer sget command. --- jc/parsers/amixer.py | 57 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 5 deletions(-) diff --git a/jc/parsers/amixer.py b/jc/parsers/amixer.py index 0ef67203..8bd5dea6 100644 --- a/jc/parsers/amixer.py +++ b/jc/parsers/amixer.py @@ -67,12 +67,59 @@ def parse( jc.utils.input_type_check(data) raw_output = [] - # cleandata = list(filter(None, data.splitlines())) - # + lines = list(filter(None, data.splitlines())) # lines=cleandata + + data = {} + # Strip and check for the first line to extract the control name directly + first_line = lines[0].strip() + if first_line.startswith("Simple mixer control"): + # Extract the control name, removing the single quotes + control_name = first_line.split("'")[1] # Extracts the part inside the single quotes + else: + raise ValueError("Invalid amixer output format: missing control name.") + + # Add control name to the parsed data as a key + data["control_name"] = control_name + + # Parse the rest of the output + for line in lines[1:]: + line = line.strip() + if line.startswith("Capabilities:"): + data["capabilities"] = line.split(":")[1].strip().split() + elif line.startswith("Playback channels:"): + data["playback_channels"] = line.split(":")[1].strip().split(" - ") + elif line.startswith("Limits:"): + limits = line.split(":")[1].strip().split(" - ") + data["limits"] = { + "playback_min": int(limits[0].split()[1]), + "playback_max": int(limits[1]) + } + elif line.startswith("Mono:") or line.startswith("Front Left:") or line.startswith("Front Right:"): + # Identifying whether it's Mono, Front Left, or Front Right + channel_name = line.split(":")[0].strip().lower().replace(" ", "_") + channel_info = line.split(":")[1].strip() + + # Example: "Playback 255 [100%] [0.00dB] [on]" + channel_data = channel_info.split(" ") + playback_value = int(channel_data[0]) + percentage = channel_data[1][1:-1] # Extract percentage e.g. "100%" + db_value = channel_data[2][1:-3] # Extract dB value e.g. "0.00dB" + status = channel_data[3][1:-1] # Extract status "on" or "off" + + # Storing channel data in the dict + data[channel_name] = { + "playback_value": playback_value, + "percentage": percentage, + "dB": db_value, + "status": status + } + + return data + # if jc.utils.has_data(data): - # - # # remove final Entries row if -v was used - # if cleandata[-1].startswith('Entries:'): + + # remove final Entries row if -v was used + # if cleandata[-1].startswith('Entries:'): # cleandata.pop(-1) # # # detect if freebsd/osx style was used From f3261d30c2fc48d57a64e4a2554f1e178c28cdd8 Mon Sep 17 00:00:00 2001 From: Eden Refael Date: Mon, 25 Nov 2024 10:03:06 +0200 Subject: [PATCH 05/30] test commit - just for tests --- jc/parsers/amixer.py | 1 + 1 file changed, 1 insertion(+) diff --git a/jc/parsers/amixer.py b/jc/parsers/amixer.py index 8bd5dea6..9d832a81 100644 --- a/jc/parsers/amixer.py +++ b/jc/parsers/amixer.py @@ -115,6 +115,7 @@ def parse( } return data + # commit test # if jc.utils.has_data(data): From 2475452292980786e923681f6e6c14a410644fdd Mon Sep 17 00:00:00 2001 From: Eden Refael Date: Mon, 25 Nov 2024 10:04:59 +0200 Subject: [PATCH 06/30] another test commit --- jc/parsers/amixer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jc/parsers/amixer.py b/jc/parsers/amixer.py index 9d832a81..1159379b 100644 --- a/jc/parsers/amixer.py +++ b/jc/parsers/amixer.py @@ -115,7 +115,7 @@ def parse( } return data - # commit test + # another test # if jc.utils.has_data(data): From ca62b6a16d71a480f3a552bf1c80165ce281f3e6 Mon Sep 17 00:00:00 2001 From: Eden Refael Date: Mon, 25 Nov 2024 10:05:39 +0200 Subject: [PATCH 07/30] another test commit --- jc/parsers/amixer.py | 1 - 1 file changed, 1 deletion(-) diff --git a/jc/parsers/amixer.py b/jc/parsers/amixer.py index 1159379b..8bd5dea6 100644 --- a/jc/parsers/amixer.py +++ b/jc/parsers/amixer.py @@ -115,7 +115,6 @@ def parse( } return data - # another test # if jc.utils.has_data(data): From 90498052e00944d24a638840bc98c749cac7ff22 Mon Sep 17 00:00:00 2001 From: Eden Refael Date: Thu, 28 Nov 2024 00:41:14 +0200 Subject: [PATCH 08/30] created a dedicated pseudo algorithm for the amixer sget and tried various of strings. --- jc/parsers/amixer.py | 229 ++++++++++++++++++++++++------------------- 1 file changed, 128 insertions(+), 101 deletions(-) diff --git a/jc/parsers/amixer.py b/jc/parsers/amixer.py index 8bd5dea6..629871c6 100644 --- a/jc/parsers/amixer.py +++ b/jc/parsers/amixer.py @@ -40,6 +40,7 @@ def _process(proc_data: List[Dict]) -> List[Dict]: # entry[key] = jc.utils.convert_to_int(entry[key]) # # return proc_data + pass def parse( @@ -66,131 +67,157 @@ def parse( # check if string jc.utils.input_type_check(data) - raw_output = [] - lines = list(filter(None, data.splitlines())) # lines=cleandata - - data = {} - # Strip and check for the first line to extract the control name directly + # split the output lines \r\n or \n + # raw_output = [] + # lines: list = list(filter(None, data.splitlines())) # lines=cleandata + mapping = {} + lines = data.splitlines() first_line = lines[0].strip() + + # Extract the control name from the first line if first_line.startswith("Simple mixer control"): - # Extract the control name, removing the single quotes - control_name = first_line.split("'")[1] # Extracts the part inside the single quotes + control_name = first_line.split("'")[1] else: raise ValueError("Invalid amixer output format: missing control name.") + # map the control name + mapping["control_name"] = control_name - # Add control name to the parsed data as a key - data["control_name"] = control_name - - # Parse the rest of the output + # Process subsequent lines for capabilities, channels, limits, and channel-specific mapping. + # gets the lines from the next line - because we already took care the first line. for line in lines[1:]: + # strip the line (maybe there are white spaces in the begin&end) line = line.strip() + if line.startswith("Capabilities:"): - data["capabilities"] = line.split(":")[1].strip().split() + mapping["capabilities"] = line.split(":")[1].strip().split() elif line.startswith("Playback channels:"): - data["playback_channels"] = line.split(":")[1].strip().split(" - ") + mapping["playback_channels"] = line.split(":")[1].strip().split(" - ") elif line.startswith("Limits:"): limits = line.split(":")[1].strip().split(" - ") - data["limits"] = { - "playback_min": int(limits[0].split()[1]), - "playback_max": int(limits[1]) + mapping["limits"] = { + "playback_min": limits[0].split()[1], + "playback_max": limits[1] } elif line.startswith("Mono:") or line.startswith("Front Left:") or line.startswith("Front Right:"): - # Identifying whether it's Mono, Front Left, or Front Right + # Identify the channel name and parse its information channel_name = line.split(":")[0].strip().lower().replace(" ", "_") channel_info = line.split(":")[1].strip() - # Example: "Playback 255 [100%] [0.00dB] [on]" channel_data = channel_info.split(" ") - playback_value = int(channel_data[0]) - percentage = channel_data[1][1:-1] # Extract percentage e.g. "100%" - db_value = channel_data[2][1:-3] # Extract dB value e.g. "0.00dB" - status = channel_data[3][1:-1] # Extract status "on" or "off" - - # Storing channel data in the dict - data[channel_name] = { + if channel_data[0] == "": + continue + playback_value = channel_data[1] + percentage = channel_data[2].strip("[]") # Extract percentage e.g., "100%" + db_value = channel_data[3].strip("[]") # Extract dB value e.g., "0.00dB" + status = channel_data[4].strip("[]") # Extract status e.g., "on" or "off" + + # Store channel mapping in the dictionary + mapping[channel_name] = { "playback_value": playback_value, "percentage": percentage, "dB": db_value, "status": status } - return data + return mapping - # if jc.utils.has_data(data): +""" +Input Explained/Rules/Pseudo Algorithm: +1. There will always be the first line which tells the user about the control name. +2. There will always be the Capabilities which include many of capabilities - It will be listed and separated by `" "`. +3. After that we'll need to distinct between the Channel - Could be many of channels - It will be listed and separated + by `" "`. + 3a. Capture channels - List of channels + 3b. Playback channels - List of channels +4. Limits - We'll always have the minimum limit and the maximum limit. + +Input Example: + +1.user@kazuar-endpoint:~$ amixer sget Capture +Simple mixer control 'Capture',0 + Capabilities: cvolume cswitch + Capture channels: Front Left - Front Right + Limits: Capture 0 - 63 + Front Left: Capture 63 [100%] [30.00dB] [on] + Front Right: Capture 63 [100%] [30.00dB] [on] + + + + +2.user@kazuar-endpoint:~$ amixer sget Master +Simple mixer control 'Master',0 + Capabilities: pvolume pvolume-joined pswitch pswitch-joined + Playback channels: Mono + Limits: Playback 0 - 87 + Mono: Playback 87 [100%] [0.00dB] [on] + + + + + +3.user@kazuar-endpoint:~$ amixer sget Speaker +Simple mixer control 'Speaker',0 + Capabilities: pvolume pswitch + Playback channels: Front Left - Front Right + Limits: Playback 0 - 87 + Mono: + Front Left: Playback 87 [100%] [0.00dB] [on] + Front Right: Playback 87 [100%] [0.00dB] [on] + + + + +4.user@kazuar-endpoint:~$ amixer sget Headphone +Simple mixer control 'Headphone',0 + Capabilities: pvolume pswitch + Playback channels: Front Left - Front Right + Limits: Playback 0 - 87 + Mono: + Front Left: Playback 0 [0%] [-65.25dB] [off] + Front Right: Playback 0 [0%] [-65.25dB] [off] + + + +""" + +if __name__ == '__main__': + data_sget_master = """Simple mixer control 'Master',0 + Capabilities: pvolume pvolume-joined pswitch pswitch-joined + Playback channels: Mono + Limits: Playback 0 - 87 + Mono: Playback 87 [100%] [0.00dB] [on]""" + + data_sget_capture = """Simple mixer control 'Capture',0 + Capabilities: cvolume cswitch + Capture channels: Front Left - Front Right + Limits: Capture 0 - 63 + Front Left: Capture 63 [100%] [30.00dB] [on] + Front Right: Capture 63 [100%] [30.00dB] [on]""" - # remove final Entries row if -v was used - # if cleandata[-1].startswith('Entries:'): - # cleandata.pop(-1) - # - # # detect if freebsd/osx style was used - # if cleandata[0][-1] == ']': - # for line in cleandata: - # splitline = line.split() - # output_line: Dict[str, Any] = { - # 'name': splitline[0], - # 'address': splitline[1].lstrip('(').rstrip(')'), - # 'hwtype': splitline[-1].lstrip('[').rstrip(']'), - # 'hwaddress': splitline[3], - # 'iface': splitline[5] - # } - # - # if 'permanent' in splitline: - # output_line['permanent'] = True - # else: - # output_line['permanent'] = False - # - # if 'expires' in splitline: - # output_line['expires'] = splitline[-3] - # - # raw_output.append(output_line) - # - # # detect if linux style was used - # elif cleandata[0].startswith('Address'): - # - # # fix header row to change Flags Mask to flags_mask - # cleandata[0] = cleandata[0].replace('Flags Mask', 'flags_mask') - # cleandata[0] = cleandata[0].lower() - # - # raw_output = jc.parsers.universal.simple_table_parse(cleandata) - # - # # otherwise, try bsd style - # else: - # for line in cleandata: - # splitline = line.split() - # - # # Ignore AIX bucket information - # if 'bucket:' in splitline[0]: - # continue - # elif 'There' in splitline[0] and 'are' in splitline[1]: - # continue - # - # # AIX uses (incomplete) - # elif '' not in splitline and '(incomplete)' not in splitline: - # output_line = { - # 'name': splitline[0], - # 'address': splitline[1].lstrip('(').rstrip(')'), - # 'hwtype': splitline[4].lstrip('[').rstrip(']'), - # 'hwaddress': splitline[3], - # } - # # Handle permanence and ignore interface in AIX - # if 'permanent' in splitline: - # output_line['permanent'] = True - # elif 'in' not in splitline[6]: # AIX doesn't show interface - # output_line['iface'] = splitline[6] - # - # else: - # output_line = { - # 'name': splitline[0], - # 'address': splitline[1].lstrip('(').rstrip(')'), - # 'hwtype': None, - # 'hwaddress': None, - # } - # # AIX doesn't show interface - # if len(splitline) >= 5: - # output_line['iface'] = splitline[5] - # - # raw_output.append(output_line) - # - # return raw_output if raw else _process(raw_output) + data_sget_speakers = """Simple mixer control 'Speaker',0 + Capabilities: pvolume pswitch + Playback channels: Front Left - Front Right + Limits: Playback 0 - 87 + Mono: + Front Left: Playback 87 [100%] [0.00dB] [on] + Front Right: Playback 87 [100%] [0.00dB] [on]""" + data_sget_headphones = """Simple mixer control 'Headphone',0 + Capabilities: pvolume pswitch + Playback channels: Front Left - Front Right + Limits: Playback 0 - 87 + Mono: + Front Left: Playback 0 [0%] [-65.25dB] [off] + Front Right: Playback 0 [0%] [-65.25dB] [off]""" + output_data_sget_master = parse(data=data_sget_master) + output_data_sget_speakers = parse(data=data_sget_speakers) + output_data_sget_headphones = parse(data=data_sget_headphones) + output_data_sget_capture = parse(data=data_sget_capture) + di = {'master': output_data_sget_master, + 'speakers': output_data_sget_speakers, + 'headphones': output_data_sget_headphones, + 'capture': output_data_sget_capture} + for key, val in di.items(): + print(f"[info] for key: {key}") + print(f"[info] the output is: {val}") \ No newline at end of file From 1002afca8cae4a0a84234403c5d4b450fb381d09 Mon Sep 17 00:00:00 2001 From: Eden Refael Date: Thu, 28 Nov 2024 00:46:29 +0200 Subject: [PATCH 09/30] orginized the docstring with general explanation about the tool and the amixer tool output and algorithm of the input parsing and input examples. --- jc/parsers/amixer.py | 118 +++++++++++++++++++++---------------------- 1 file changed, 58 insertions(+), 60 deletions(-) diff --git a/jc/parsers/amixer.py b/jc/parsers/amixer.py index 629871c6..e06fd206 100644 --- a/jc/parsers/amixer.py +++ b/jc/parsers/amixer.py @@ -49,14 +49,70 @@ def parse( quiet: bool = False ) -> List[Dict]: """ - Main text parsing function, The amixer is alsa mixer tool and output, Will work with - Linux OS only. + Main text parsing function, The amixer is alsa mixer tool and output, Will work with Linux OS only. + + + The Algorithm for parsing the `amixer sget` command, Input Explained/Rules/Pseudo Algorithm: + 1. There will always be the first line which tells the user about the control name. + 2. There will always be the Capabilities which include many of capabilities - It will be listed and separated by `" "`. + 3. After that we'll need to distinct between the Channel - Could be many of channels - It will be listed and separated + by `" "`. + 3a. Capture channels - List of channels + 3b. Playback channels - List of channels + 4. Limits - We'll always have the minimum limit and the maximum limit. + + + Input Example: + 1.user@kazuar-endpoint:~$ amixer sget Capture + Simple mixer control 'Capture',0 + Capabilities: cvolume cswitch + Capture channels: Front Left - Front Right + Limits: Capture 0 - 63 + Front Left: Capture 63 [100%] [30.00dB] [on] + Front Right: Capture 63 [100%] [30.00dB] [on] + + + + + 2.user@kazuar-endpoint:~$ amixer sget Master + Simple mixer control 'Master',0 + Capabilities: pvolume pvolume-joined pswitch pswitch-joined + Playback channels: Mono + Limits: Playback 0 - 87 + Mono: Playback 87 [100%] [0.00dB] [on] + + + + + + 3.user@kazuar-endpoint:~$ amixer sget Speaker + Simple mixer control 'Speaker',0 + Capabilities: pvolume pswitch + Playback channels: Front Left - Front Right + Limits: Playback 0 - 87 + Mono: + Front Left: Playback 87 [100%] [0.00dB] [on] + Front Right: Playback 87 [100%] [0.00dB] [on] + + + + + 4.user@kazuar-endpoint:~$ amixer sget Headphone + Simple mixer control 'Headphone',0 + Capabilities: pvolume pswitch + Playback channels: Front Left - Front Right + Limits: Playback 0 - 87 + Mono: + Front Left: Playback 0 [0%] [-65.25dB] [off] + Front Right: Playback 0 [0%] [-65.25dB] [off] + Parameters: data: (string) text data to parse raw: (boolean) unprocessed output if True quiet: (boolean) suppress warning messages if True + Returns: List of Dictionaries. Raw or processed structured data. push test @@ -121,64 +177,6 @@ def parse( return mapping -""" -Input Explained/Rules/Pseudo Algorithm: -1. There will always be the first line which tells the user about the control name. -2. There will always be the Capabilities which include many of capabilities - It will be listed and separated by `" "`. -3. After that we'll need to distinct between the Channel - Could be many of channels - It will be listed and separated - by `" "`. - 3a. Capture channels - List of channels - 3b. Playback channels - List of channels -4. Limits - We'll always have the minimum limit and the maximum limit. - -Input Example: - -1.user@kazuar-endpoint:~$ amixer sget Capture -Simple mixer control 'Capture',0 - Capabilities: cvolume cswitch - Capture channels: Front Left - Front Right - Limits: Capture 0 - 63 - Front Left: Capture 63 [100%] [30.00dB] [on] - Front Right: Capture 63 [100%] [30.00dB] [on] - - - - -2.user@kazuar-endpoint:~$ amixer sget Master -Simple mixer control 'Master',0 - Capabilities: pvolume pvolume-joined pswitch pswitch-joined - Playback channels: Mono - Limits: Playback 0 - 87 - Mono: Playback 87 [100%] [0.00dB] [on] - - - - - -3.user@kazuar-endpoint:~$ amixer sget Speaker -Simple mixer control 'Speaker',0 - Capabilities: pvolume pswitch - Playback channels: Front Left - Front Right - Limits: Playback 0 - 87 - Mono: - Front Left: Playback 87 [100%] [0.00dB] [on] - Front Right: Playback 87 [100%] [0.00dB] [on] - - - - -4.user@kazuar-endpoint:~$ amixer sget Headphone -Simple mixer control 'Headphone',0 - Capabilities: pvolume pswitch - Playback channels: Front Left - Front Right - Limits: Playback 0 - 87 - Mono: - Front Left: Playback 0 [0%] [-65.25dB] [off] - Front Right: Playback 0 [0%] [-65.25dB] [off] - - - -""" if __name__ == '__main__': data_sget_master = """Simple mixer control 'Master',0 From d0b8d05cfedafd1b00fa2acd2b6f2ae2c8416631 Mon Sep 17 00:00:00 2001 From: Eden Refael Date: Thu, 28 Nov 2024 01:20:40 +0200 Subject: [PATCH 10/30] created raw implementation, but it's raw either or either. --- jc/parsers/amixer.py | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/jc/parsers/amixer.py b/jc/parsers/amixer.py index e06fd206..ae152df4 100644 --- a/jc/parsers/amixer.py +++ b/jc/parsers/amixer.py @@ -16,31 +16,17 @@ class info(): __version__ = info.version -def _process(proc_data: List[Dict]) -> List[Dict]: +def _process(proc_data: dict) -> dict: """ - Final processing to conform to the schema. - Parameters: proc_data: (List of Dictionaries) raw structured data to process Returns: - List of Dictionaries. Structured data to conform to the schema: + dictionary of amixer sget """ - # int_list = {'expires'} - # - # # in BSD style, change name to null if it is a question mark - # for entry in proc_data: - # if 'name' in entry and entry['name'] == '?': - # entry['name'] = None - # - # for key in entry: - # if key in int_list: - # entry[key] = jc.utils.convert_to_int(entry[key]) - # - # return proc_data - pass + return proc_data def parse( @@ -174,8 +160,7 @@ def parse( "dB": db_value, "status": status } - - return mapping + return _process(mapping) if raw else mapping if __name__ == '__main__': From 2c9d6dc24eea452b2d27bf8d8dcf9200e8c3fb1d Mon Sep 17 00:00:00 2001 From: Eden Refael Date: Thu, 28 Nov 2024 11:21:33 +0200 Subject: [PATCH 11/30] orginized the content inside the amixer parser --- jc/parsers/amixer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jc/parsers/amixer.py b/jc/parsers/amixer.py index ae152df4..56390d00 100644 --- a/jc/parsers/amixer.py +++ b/jc/parsers/amixer.py @@ -203,4 +203,4 @@ def parse( 'capture': output_data_sget_capture} for key, val in di.items(): print(f"[info] for key: {key}") - print(f"[info] the output is: {val}") \ No newline at end of file + print(f"[info] the output is: {val}") From 9a4c0559f07c40ce919285ec1a39e74bd3489744 Mon Sep 17 00:00:00 2001 From: Eden Refael Date: Thu, 28 Nov 2024 20:05:32 +0200 Subject: [PATCH 12/30] removed endpoint name --- jc/parsers/amixer.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/jc/parsers/amixer.py b/jc/parsers/amixer.py index 56390d00..8c345f12 100644 --- a/jc/parsers/amixer.py +++ b/jc/parsers/amixer.py @@ -49,7 +49,7 @@ def parse( Input Example: - 1.user@kazuar-endpoint:~$ amixer sget Capture + 1."":~$ amixer sget Capture Simple mixer control 'Capture',0 Capabilities: cvolume cswitch Capture channels: Front Left - Front Right @@ -60,7 +60,7 @@ def parse( - 2.user@kazuar-endpoint:~$ amixer sget Master + 2."":~$ amixer sget Master Simple mixer control 'Master',0 Capabilities: pvolume pvolume-joined pswitch pswitch-joined Playback channels: Mono @@ -71,7 +71,7 @@ def parse( - 3.user@kazuar-endpoint:~$ amixer sget Speaker + 3."":~$ amixer sget Speaker Simple mixer control 'Speaker',0 Capabilities: pvolume pswitch Playback channels: Front Left - Front Right @@ -83,7 +83,7 @@ def parse( - 4.user@kazuar-endpoint:~$ amixer sget Headphone + 4."":~$ amixer sget Headphone Simple mixer control 'Headphone',0 Capabilities: pvolume pswitch Playback channels: Front Left - Front Right From b6032863aca8e2a4eff75f8799e6dc0578028eec Mon Sep 17 00:00:00 2001 From: Eden Refael Date: Thu, 28 Nov 2024 20:13:06 +0200 Subject: [PATCH 13/30] added amixer to the jc parser in lib --- jc/lib.py | 1 + 1 file changed, 1 insertion(+) diff --git a/jc/lib.py b/jc/lib.py index 0b4341ba..f1e92cc6 100644 --- a/jc/lib.py +++ b/jc/lib.py @@ -21,6 +21,7 @@ 'arp', 'asciitable', 'asciitable-m', + 'amixer', 'blkid', 'bluetoothctl', 'cbt', From f26d28638af3eddd164c1df4008ce2bd9e5e66bd Mon Sep 17 00:00:00 2001 From: Eden Refael Date: Thu, 28 Nov 2024 20:21:37 +0200 Subject: [PATCH 14/30] more explanations --- jc/parsers/amixer.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/jc/parsers/amixer.py b/jc/parsers/amixer.py index 8c345f12..aa97c045 100644 --- a/jc/parsers/amixer.py +++ b/jc/parsers/amixer.py @@ -109,10 +109,9 @@ def parse( # check if string jc.utils.input_type_check(data) - # split the output lines \r\n or \n - # raw_output = [] - # lines: list = list(filter(None, data.splitlines())) # lines=cleandata + # starts the parsing from here mapping = {} + # split lines and than work on each line lines = data.splitlines() first_line = lines[0].strip() From 85c9c8e777ed0015532431f8e01ee67e360ccd46 Mon Sep 17 00:00:00 2001 From: Eden Refael Date: Thu, 28 Nov 2024 23:22:32 +0200 Subject: [PATCH 15/30] added tests for the amixer sget --- test_file.py | 12 ++++++ .../ubuntu-22.04/amixer-control-capture.json | 1 + .../ubuntu-22.04/amixer-control-capture.out | 6 +++ .../amixer-control-headphone.json | 1 + .../ubuntu-22.04/amixer-control-headphone.out | 7 ++++ .../ubuntu-22.04/amixer-control-master.json | 1 + .../ubuntu-22.04/amixer-control-master.out | 5 +++ .../ubuntu-22.04/amixer-control-speakers.json | 1 + .../ubuntu-22.04/amixer-control-speakers.out | 7 ++++ tests/test_amixer.py | 37 +++++++++++++++++++ 10 files changed, 78 insertions(+) create mode 100644 test_file.py create mode 100644 tests/fixtures/ubuntu-22.04/amixer-control-capture.json create mode 100644 tests/fixtures/ubuntu-22.04/amixer-control-capture.out create mode 100644 tests/fixtures/ubuntu-22.04/amixer-control-headphone.json create mode 100644 tests/fixtures/ubuntu-22.04/amixer-control-headphone.out create mode 100644 tests/fixtures/ubuntu-22.04/amixer-control-master.json create mode 100644 tests/fixtures/ubuntu-22.04/amixer-control-master.out create mode 100644 tests/fixtures/ubuntu-22.04/amixer-control-speakers.json create mode 100644 tests/fixtures/ubuntu-22.04/amixer-control-speakers.out create mode 100644 tests/test_amixer.py diff --git a/test_file.py b/test_file.py new file mode 100644 index 00000000..8cf25450 --- /dev/null +++ b/test_file.py @@ -0,0 +1,12 @@ +import jc + +data_sget_headphones = """Simple mixer control 'Headphone',0 + Capabilities: pvolume pswitch + Playback channels: Front Left - Front Right + Limits: Playback 0 - 87 + Mono: + Front Left: Playback 0 [0%] [-65.25dB] [off] + Front Right: Playback 0 [0%] [-65.25dB] [off]""" + +output_data_sget_headphones = jc.parse("amixer", data_sget_headphones) +print(output_data_sget_headphones) diff --git a/tests/fixtures/ubuntu-22.04/amixer-control-capture.json b/tests/fixtures/ubuntu-22.04/amixer-control-capture.json new file mode 100644 index 00000000..86e7bead --- /dev/null +++ b/tests/fixtures/ubuntu-22.04/amixer-control-capture.json @@ -0,0 +1 @@ +{"control_name": "Capture", "capabilities": ["cvolume", "cswitch"], "limits": {"playback_min": "0", "playback_max": "63"}, "front_left": {"playback_value": "63", "percentage": "100%", "dB": "30.00dB", "status": "on"}, "front_right": {"playback_value": "63", "percentage": "100%", "dB": "30.00dB", "status": "on"}} \ No newline at end of file diff --git a/tests/fixtures/ubuntu-22.04/amixer-control-capture.out b/tests/fixtures/ubuntu-22.04/amixer-control-capture.out new file mode 100644 index 00000000..906458e5 --- /dev/null +++ b/tests/fixtures/ubuntu-22.04/amixer-control-capture.out @@ -0,0 +1,6 @@ +Simple mixer control 'Capture',0 + Capabilities: cvolume cswitch + Capture channels: Front Left - Front Right + Limits: Capture 0 - 63 + Front Left: Capture 63 [100%] [30.00dB] [on] + Front Right: Capture 63 [100%] [30.00dB] [on] diff --git a/tests/fixtures/ubuntu-22.04/amixer-control-headphone.json b/tests/fixtures/ubuntu-22.04/amixer-control-headphone.json new file mode 100644 index 00000000..046fa919 --- /dev/null +++ b/tests/fixtures/ubuntu-22.04/amixer-control-headphone.json @@ -0,0 +1 @@ +{"control_name": "Headphone", "capabilities": ["pvolume", "pswitch"], "playback_channels": ["Front Left", "Front Right"], "limits": {"playback_min": "0", "playback_max": "87"}, "front_left": {"playback_value": "0", "percentage": "0%", "dB": "-65.25dB", "status": "off"}, "front_right": {"playback_value": "0", "percentage": "0%", "dB": "-65.25dB", "status": "off"}} \ No newline at end of file diff --git a/tests/fixtures/ubuntu-22.04/amixer-control-headphone.out b/tests/fixtures/ubuntu-22.04/amixer-control-headphone.out new file mode 100644 index 00000000..b83219e0 --- /dev/null +++ b/tests/fixtures/ubuntu-22.04/amixer-control-headphone.out @@ -0,0 +1,7 @@ +Simple mixer control 'Headphone',0 + Capabilities: pvolume pswitch + Playback channels: Front Left - Front Right + Limits: Playback 0 - 87 + Mono: + Front Left: Playback 0 [0%] [-65.25dB] [off] + Front Right: Playback 0 [0%] [-65.25dB] [off] \ No newline at end of file diff --git a/tests/fixtures/ubuntu-22.04/amixer-control-master.json b/tests/fixtures/ubuntu-22.04/amixer-control-master.json new file mode 100644 index 00000000..9fc00835 --- /dev/null +++ b/tests/fixtures/ubuntu-22.04/amixer-control-master.json @@ -0,0 +1 @@ +{"control_name": "Master", "capabilities": ["pvolume", "pvolume-joined", "pswitch", "pswitch-joined"], "playback_channels": ["Mono"], "limits": {"playback_min": "0", "playback_max": "87"}, "mono": {"playback_value": "87", "percentage": "100%", "dB": "0.00dB", "status": "on"}} \ No newline at end of file diff --git a/tests/fixtures/ubuntu-22.04/amixer-control-master.out b/tests/fixtures/ubuntu-22.04/amixer-control-master.out new file mode 100644 index 00000000..e3ff2463 --- /dev/null +++ b/tests/fixtures/ubuntu-22.04/amixer-control-master.out @@ -0,0 +1,5 @@ +Simple mixer control 'Master',0 + Capabilities: pvolume pvolume-joined pswitch pswitch-joined + Playback channels: Mono + Limits: Playback 0 - 87 + Mono: Playback 87 [100%] [0.00dB] [on] \ No newline at end of file diff --git a/tests/fixtures/ubuntu-22.04/amixer-control-speakers.json b/tests/fixtures/ubuntu-22.04/amixer-control-speakers.json new file mode 100644 index 00000000..80364036 --- /dev/null +++ b/tests/fixtures/ubuntu-22.04/amixer-control-speakers.json @@ -0,0 +1 @@ +{"control_name": "Speaker", "capabilities": ["pvolume", "pswitch"], "playback_channels": ["Front Left", "Front Right"], "limits": {"playback_min": "0", "playback_max": "87"}, "front_left": {"playback_value": "87", "percentage": "100%", "dB": "0.00dB", "status": "on"}, "front_right": {"playback_value": "87", "percentage": "100%", "dB": "0.00dB", "status": "on"}} \ No newline at end of file diff --git a/tests/fixtures/ubuntu-22.04/amixer-control-speakers.out b/tests/fixtures/ubuntu-22.04/amixer-control-speakers.out new file mode 100644 index 00000000..29a2bd50 --- /dev/null +++ b/tests/fixtures/ubuntu-22.04/amixer-control-speakers.out @@ -0,0 +1,7 @@ +Simple mixer control 'Speaker',0 + Capabilities: pvolume pswitch + Playback channels: Front Left - Front Right + Limits: Playback 0 - 87 + Mono: + Front Left: Playback 87 [100%] [0.00dB] [on] + Front Right: Playback 87 [100%] [0.00dB] [on] \ No newline at end of file diff --git a/tests/test_amixer.py b/tests/test_amixer.py new file mode 100644 index 00000000..df56d8a3 --- /dev/null +++ b/tests/test_amixer.py @@ -0,0 +1,37 @@ +import unittest +import jc.parsers.amixer +import os +import json + +THIS_DIR = os.path.dirname(os.path.abspath(__file__)) + + +class AmixerTests(unittest.TestCase): + AMIXER_CMD = 'amixer' + UBUNTU_22_04_TEST_FIXTURES_PATH = f'tests/fixtures/ubuntu-22.04/' + AMIXER_CONTROL_PATH = f'{UBUNTU_22_04_TEST_FIXTURES_PATH}amixer-control-' + TEST_FILES_NAME = [ + f"{AMIXER_CONTROL_PATH}capture", + f'{AMIXER_CONTROL_PATH}headphone', + f'{AMIXER_CONTROL_PATH}master', + f'{AMIXER_CONTROL_PATH}speakers', + ] + + def setUp(self): + self.test_files_out = [f'{file}.out' for file in self.TEST_FILES_NAME] + self.test_files_json = [f'{file}.json' for file in self.TEST_FILES_NAME] + + def test_amixer_sget(self): + for file_out, file_json in zip(self.test_files_out, self.test_files_json): + with open(file_out, 'r') as f: + amixer_sget_raw_output: str = f.read() + with open(file_json, 'r') as f: + expected_amixer_sget_json_output: str = f.read() + expected_amixer_sget_json_map: dict = json.loads(expected_amixer_sget_json_output) + amixer_sget_json_map: dict = jc.parse(self.AMIXER_CMD, amixer_sget_raw_output) + self.assertEqual(amixer_sget_json_map, expected_amixer_sget_json_map) + + + +if __name__ == '__main__': + unittest.main() From 3e39cdc9ebc07d3bf4d9ae750f87a94118dac5fe Mon Sep 17 00:00:00 2001 From: Eden Refael Date: Thu, 28 Nov 2024 23:29:10 +0200 Subject: [PATCH 16/30] added tests for the amixer sget --- tests/test_amixer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_amixer.py b/tests/test_amixer.py index df56d8a3..bcd4dcd0 100644 --- a/tests/test_amixer.py +++ b/tests/test_amixer.py @@ -8,7 +8,7 @@ class AmixerTests(unittest.TestCase): AMIXER_CMD = 'amixer' - UBUNTU_22_04_TEST_FIXTURES_PATH = f'tests/fixtures/ubuntu-22.04/' + UBUNTU_22_04_TEST_FIXTURES_PATH = f'{THIS_DIR}/fixtures/ubuntu-22.04/' AMIXER_CONTROL_PATH = f'{UBUNTU_22_04_TEST_FIXTURES_PATH}amixer-control-' TEST_FILES_NAME = [ f"{AMIXER_CONTROL_PATH}capture", From 8f20ea4114612802acb23b389445c262d86ba72e Mon Sep 17 00:00:00 2001 From: Eden Refael Date: Fri, 29 Nov 2024 11:08:32 +0200 Subject: [PATCH 17/30] fine versioning fix --- jc/parsers/amixer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jc/parsers/amixer.py b/jc/parsers/amixer.py index aa97c045..00c7bb23 100644 --- a/jc/parsers/amixer.py +++ b/jc/parsers/amixer.py @@ -5,7 +5,7 @@ class info(): """Provides parser metadata (version, author, etc.)""" - version = '1' + version = '1.0' description = '`amixer` command parser' author = 'Eden Refael' author_email = 'edenraf@hotmail.com' From 896660017cb8367a9c687fa69f2350ca3b82b5b5 Mon Sep 17 00:00:00 2001 From: Eden Refael Date: Fri, 29 Nov 2024 11:43:46 +0200 Subject: [PATCH 18/30] created docstring+another explanations seperated. --- jc/parsers/amixer.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/jc/parsers/amixer.py b/jc/parsers/amixer.py index 00c7bb23..06519cc0 100644 --- a/jc/parsers/amixer.py +++ b/jc/parsers/amixer.py @@ -38,6 +38,17 @@ def parse( Main text parsing function, The amixer is alsa mixer tool and output, Will work with Linux OS only. + Parameters: + data: (string) text data to parse + raw: (boolean) unprocessed output if True + quiet: (boolean) suppress warning messages if True + + + Returns: + List of Dictionaries. Raw or processed structured data. + push test + """ + """ The Algorithm for parsing the `amixer sget` command, Input Explained/Rules/Pseudo Algorithm: 1. There will always be the first line which tells the user about the control name. 2. There will always be the Capabilities which include many of capabilities - It will be listed and separated by `" "`. @@ -91,17 +102,6 @@ def parse( Mono: Front Left: Playback 0 [0%] [-65.25dB] [off] Front Right: Playback 0 [0%] [-65.25dB] [off] - - - Parameters: - data: (string) text data to parse - raw: (boolean) unprocessed output if True - quiet: (boolean) suppress warning messages if True - - - Returns: - List of Dictionaries. Raw or processed structured data. - push test """ # checks os compatibility and print a stderr massage if not compatible. quiet True could remove this check. jc.utils.compatibility(__name__, info.compatible, quiet) From 1876e2dfd8a7f9a1d116e8d0dc898897358ae393 Mon Sep 17 00:00:00 2001 From: Eden Refael Date: Fri, 29 Nov 2024 11:57:57 +0200 Subject: [PATCH 19/30] created the amixer parser docu --- jc/parsers/amixer.py | 56 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/jc/parsers/amixer.py b/jc/parsers/amixer.py index 06519cc0..3df23781 100644 --- a/jc/parsers/amixer.py +++ b/jc/parsers/amixer.py @@ -1,3 +1,59 @@ +r"""jc - JSON Convert `amixer sget` command output parser +Usage (cli): + $ amixer sget | jc --amixer + $ amixer sget Master | jc --amixer + $ amixer sget Capture | jc --amixer + $ amixer sget Speakers | jc --amixer +Usage (module): + import jc + result = jc.parse('amixer', ) +Schema: +{ + "control_name": string, + "capabilities": [ + string + ], + "playback_channels": [ + string + ], + "limits": { + "playback_min": string, + "playback_max": string + }, + "mono": { + "playback_value": string, + "percentage": string, + "dB": string, + "status": string + } +} + +Examples: +$ amixer sget Master | jc --amixer -p +{ + "control_name": "Master", + "capabilities": [ + "pvolume", + "pvolume-joined", + "pswitch", + "pswitch-joined" + ], + "playback_channels": [ + "Mono" + ], + "limits": { + "playback_min": "0", + "playback_max": "87" + }, + "mono": { + "playback_value": "87", + "percentage": "100%", + "dB": "0.00dB", + "status": "on" + } +} + +""" from typing import List, Dict import jc.utils From 58990f3a39ee9218b40c03f0aa6a354051250f95 Mon Sep 17 00:00:00 2001 From: Eden Refael Date: Tue, 3 Dec 2024 22:23:30 +0200 Subject: [PATCH 20/30] added the amixer in alphabet order to the json convert lib --- jc/lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jc/lib.py b/jc/lib.py index af0556e2..ff0b8807 100644 --- a/jc/lib.py +++ b/jc/lib.py @@ -16,12 +16,12 @@ 'acpi', 'airport', 'airport-s', + 'amixer', 'apt-cache-show', 'apt-get-sqq', 'arp', 'asciitable', 'asciitable-m', - 'amixer', 'blkid', 'bluetoothctl', 'cbt', From 241d1a1c630bd21ef7bd443d55a3eb149d77c943 Mon Sep 17 00:00:00 2001 From: Eden Refael Date: Tue, 3 Dec 2024 22:28:08 +0200 Subject: [PATCH 21/30] Fix PEP 8: E302 violation as part of boy scout principle --- jc/lib.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/jc/lib.py b/jc/lib.py index ff0b8807..86102285 100644 --- a/jc/lib.py +++ b/jc/lib.py @@ -239,14 +239,17 @@ 'zpool-status' ] + def _cliname_to_modname(parser_cli_name: str) -> str: """Return real module name (dashes converted to underscores)""" return parser_cli_name.replace('--', '').replace('-', '_') + def _modname_to_cliname(parser_mod_name: str) -> str: """Return module's cli name (underscores converted to dashes)""" return parser_mod_name.replace('_', '-') + def _is_valid_parser_plugin(name: str, local_parsers_dir: str) -> bool: if re.match(r'\w+\.py$', name) and os.path.isfile(os.path.join(local_parsers_dir, name)): try: @@ -283,11 +286,13 @@ def _is_valid_parser_plugin(name: str, local_parsers_dir: str) -> bool: except Exception: pass + def _parser_argument(parser_mod_name: str) -> str: """Return short name of the parser with dashes and with -- prefix""" parser = _modname_to_cliname(parser_mod_name) return f'--{parser}' + def get_parser(parser_mod_name: Union[str, ModuleType]) -> ModuleType: """ Return the parser module object and check that the module is a valid @@ -327,6 +332,7 @@ def get_parser(parser_mod_name: Union[str, ModuleType]) -> ModuleType: return jc_parser + def _get_parser(parser_mod_name: str) -> ModuleType: """Return the parser module object""" # ensure parser_mod_name is a true module name and not a cli name @@ -344,6 +350,7 @@ def _get_parser(parser_mod_name: str) -> ModuleType: return mod + def _parser_is_slurpable(parser: ModuleType) -> bool: """ Returns True if this parser can use the `--slurp` command option, else False @@ -356,6 +363,7 @@ def _parser_is_slurpable(parser: ModuleType) -> bool: return False + def _parser_is_streaming(parser: ModuleType) -> bool: """ Returns True if this is a streaming parser, else False @@ -367,6 +375,7 @@ def _parser_is_streaming(parser: ModuleType) -> bool: return False + def _parser_is_hidden(parser: ModuleType) -> bool: """ Returns True if this is a hidden parser, else False @@ -378,6 +387,7 @@ def _parser_is_hidden(parser: ModuleType) -> bool: return False + def _parser_is_deprecated(parser: ModuleType) -> bool: """ Returns True if this is a deprecated parser, else False @@ -389,6 +399,7 @@ def _parser_is_deprecated(parser: ModuleType) -> bool: return False + def parse( parser_mod_name: Union[str, ModuleType], data: Union[str, bytes, Iterable[str]], @@ -498,6 +509,7 @@ def parse( return jc_parser.parse(data, quiet=quiet, raw=raw, **kwargs) + def parser_mod_list( show_hidden: bool = False, show_deprecated: bool = False @@ -517,6 +529,7 @@ def parser_mod_list( return plist + def plugin_parser_mod_list( show_hidden: bool = False, show_deprecated: bool = False @@ -539,6 +552,7 @@ def plugin_parser_mod_list( return plist + def standard_parser_mod_list( show_hidden: bool = False, show_deprecated: bool = False @@ -564,6 +578,7 @@ def standard_parser_mod_list( return plist + def streaming_parser_mod_list( show_hidden: bool = False, show_deprecated: bool = False @@ -588,6 +603,7 @@ def streaming_parser_mod_list( return plist + def slurpable_parser_mod_list( show_hidden: bool = False, show_deprecated: bool = False @@ -612,6 +628,7 @@ def slurpable_parser_mod_list( return plist + def parser_info( parser_mod_name: Union[str, ModuleType], documentation: bool = False @@ -652,6 +669,7 @@ def parser_info( return info_dict + def all_parser_info( documentation: bool = False, show_hidden: bool = False, @@ -686,6 +704,7 @@ def all_parser_info( return p_info_list + def get_help(parser_mod_name: Union[str, ModuleType]) -> None: """ Show help screen for the selected parser. From 99a37577343d5c5b6b2d607c072c0527dc416794 Mon Sep 17 00:00:00 2001 From: Eden Refael Date: Wed, 4 Dec 2024 10:18:30 +0200 Subject: [PATCH 22/30] deleted not necessary file --- test_file.py | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 test_file.py diff --git a/test_file.py b/test_file.py deleted file mode 100644 index 8cf25450..00000000 --- a/test_file.py +++ /dev/null @@ -1,12 +0,0 @@ -import jc - -data_sget_headphones = """Simple mixer control 'Headphone',0 - Capabilities: pvolume pswitch - Playback channels: Front Left - Front Right - Limits: Playback 0 - 87 - Mono: - Front Left: Playback 0 [0%] [-65.25dB] [off] - Front Right: Playback 0 [0%] [-65.25dB] [off]""" - -output_data_sget_headphones = jc.parse("amixer", data_sget_headphones) -print(output_data_sget_headphones) From a2ea7fcf34f76124dbf99f6cfd78e60bae7f1cf9 Mon Sep 17 00:00:00 2001 From: Eden Refael Date: Wed, 4 Dec 2024 10:23:51 +0200 Subject: [PATCH 23/30] fixed the spaces between sections in the amixer description --- jc/parsers/amixer.py | 86 +++++++++++++++++++++++--------------------- 1 file changed, 46 insertions(+), 40 deletions(-) diff --git a/jc/parsers/amixer.py b/jc/parsers/amixer.py index 3df23781..08c689d9 100644 --- a/jc/parsers/amixer.py +++ b/jc/parsers/amixer.py @@ -1,57 +1,63 @@ r"""jc - JSON Convert `amixer sget` command output parser + Usage (cli): + $ amixer sget | jc --amixer $ amixer sget Master | jc --amixer $ amixer sget Capture | jc --amixer $ amixer sget Speakers | jc --amixer + Usage (module): + import jc result = jc.parse('amixer', ) Schema: -{ - "control_name": string, - "capabilities": [ - string - ], - "playback_channels": [ - string - ], - "limits": { - "playback_min": string, - "playback_max": string - }, - "mono": { - "playback_value": string, - "percentage": string, - "dB": string, - "status": string + + { + "control_name": string, + "capabilities": [ + string + ], + "playback_channels": [ + string + ], + "limits": { + "playback_min": string, + "playback_max": string + }, + "mono": { + "playback_value": string, + "percentage": string, + "dB": string, + "status": string + } } -} Examples: -$ amixer sget Master | jc --amixer -p -{ - "control_name": "Master", - "capabilities": [ - "pvolume", - "pvolume-joined", - "pswitch", - "pswitch-joined" - ], - "playback_channels": [ - "Mono" - ], - "limits": { - "playback_min": "0", - "playback_max": "87" - }, - "mono": { - "playback_value": "87", - "percentage": "100%", - "dB": "0.00dB", - "status": "on" + + $ amixer sget Master | jc --amixer -p + { + "control_name": "Master", + "capabilities": [ + "pvolume", + "pvolume-joined", + "pswitch", + "pswitch-joined" + ], + "playback_channels": [ + "Mono" + ], + "limits": { + "playback_min": "0", + "playback_max": "87" + }, + "mono": { + "playback_value": "87", + "percentage": "100%", + "dB": "0.00dB", + "status": "on" + } } -} """ from typing import List, Dict From d14b27357bb4f6d9c2f53223c04d4c7200a7a09c Mon Sep 17 00:00:00 2001 From: Eden Refael Date: Wed, 4 Dec 2024 23:45:43 +0200 Subject: [PATCH 24/30] resolved commits such as amixer module docstring and preperations for parser for raw=False. --- jc/parsers/amixer.py | 65 ++++++++++---------------------------------- 1 file changed, 15 insertions(+), 50 deletions(-) diff --git a/jc/parsers/amixer.py b/jc/parsers/amixer.py index 08c689d9..ea79e8a9 100644 --- a/jc/parsers/amixer.py +++ b/jc/parsers/amixer.py @@ -11,6 +11,7 @@ import jc result = jc.parse('amixer', ) + Schema: { @@ -22,20 +23,23 @@ string ], "limits": { - "playback_min": string, - "playback_max": string + "playback_min": integer, + "playback_max": integer }, "mono": { - "playback_value": string, - "percentage": string, - "dB": string, - "status": string + "playback_value": integer, + "percentage": integer, + "dB": float, + "status": boolean } } Examples: $ amixer sget Master | jc --amixer -p + + + $ amixer sget Master | jc --amixer -p -r { "control_name": "Master", "capabilities": [ @@ -59,6 +63,7 @@ } } + """ from typing import List, Dict @@ -78,6 +83,7 @@ class info(): __version__ = info.version + def _process(proc_data: dict) -> dict: """ Parameters: @@ -88,6 +94,7 @@ def _process(proc_data: dict) -> dict: dictionary of amixer sget """ + return proc_data @@ -221,47 +228,5 @@ def parse( "dB": db_value, "status": status } - return _process(mapping) if raw else mapping - - -if __name__ == '__main__': - data_sget_master = """Simple mixer control 'Master',0 - Capabilities: pvolume pvolume-joined pswitch pswitch-joined - Playback channels: Mono - Limits: Playback 0 - 87 - Mono: Playback 87 [100%] [0.00dB] [on]""" - - data_sget_capture = """Simple mixer control 'Capture',0 - Capabilities: cvolume cswitch - Capture channels: Front Left - Front Right - Limits: Capture 0 - 63 - Front Left: Capture 63 [100%] [30.00dB] [on] - Front Right: Capture 63 [100%] [30.00dB] [on]""" - - - data_sget_speakers = """Simple mixer control 'Speaker',0 - Capabilities: pvolume pswitch - Playback channels: Front Left - Front Right - Limits: Playback 0 - 87 - Mono: - Front Left: Playback 87 [100%] [0.00dB] [on] - Front Right: Playback 87 [100%] [0.00dB] [on]""" - - data_sget_headphones = """Simple mixer control 'Headphone',0 - Capabilities: pvolume pswitch - Playback channels: Front Left - Front Right - Limits: Playback 0 - 87 - Mono: - Front Left: Playback 0 [0%] [-65.25dB] [off] - Front Right: Playback 0 [0%] [-65.25dB] [off]""" - output_data_sget_master = parse(data=data_sget_master) - output_data_sget_speakers = parse(data=data_sget_speakers) - output_data_sget_headphones = parse(data=data_sget_headphones) - output_data_sget_capture = parse(data=data_sget_capture) - di = {'master': output_data_sget_master, - 'speakers': output_data_sget_speakers, - 'headphones': output_data_sget_headphones, - 'capture': output_data_sget_capture} - for key, val in di.items(): - print(f"[info] for key: {key}") - print(f"[info] the output is: {val}") + + return mapping if raw else _process(mapping) From 6741f19bb5aa09af6a96304f75f0a2739dfd2d13 Mon Sep 17 00:00:00 2001 From: Eden Refael Date: Wed, 4 Dec 2024 23:51:26 +0200 Subject: [PATCH 25/30] Revert "Fix PEP 8: E302 violation as part of boy scout principle" This reverts commit 241d1a1c630bd21ef7bd443d55a3eb149d77c943. --- jc/lib.py | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/jc/lib.py b/jc/lib.py index 86102285..ff0b8807 100644 --- a/jc/lib.py +++ b/jc/lib.py @@ -239,17 +239,14 @@ 'zpool-status' ] - def _cliname_to_modname(parser_cli_name: str) -> str: """Return real module name (dashes converted to underscores)""" return parser_cli_name.replace('--', '').replace('-', '_') - def _modname_to_cliname(parser_mod_name: str) -> str: """Return module's cli name (underscores converted to dashes)""" return parser_mod_name.replace('_', '-') - def _is_valid_parser_plugin(name: str, local_parsers_dir: str) -> bool: if re.match(r'\w+\.py$', name) and os.path.isfile(os.path.join(local_parsers_dir, name)): try: @@ -286,13 +283,11 @@ def _is_valid_parser_plugin(name: str, local_parsers_dir: str) -> bool: except Exception: pass - def _parser_argument(parser_mod_name: str) -> str: """Return short name of the parser with dashes and with -- prefix""" parser = _modname_to_cliname(parser_mod_name) return f'--{parser}' - def get_parser(parser_mod_name: Union[str, ModuleType]) -> ModuleType: """ Return the parser module object and check that the module is a valid @@ -332,7 +327,6 @@ def get_parser(parser_mod_name: Union[str, ModuleType]) -> ModuleType: return jc_parser - def _get_parser(parser_mod_name: str) -> ModuleType: """Return the parser module object""" # ensure parser_mod_name is a true module name and not a cli name @@ -350,7 +344,6 @@ def _get_parser(parser_mod_name: str) -> ModuleType: return mod - def _parser_is_slurpable(parser: ModuleType) -> bool: """ Returns True if this parser can use the `--slurp` command option, else False @@ -363,7 +356,6 @@ def _parser_is_slurpable(parser: ModuleType) -> bool: return False - def _parser_is_streaming(parser: ModuleType) -> bool: """ Returns True if this is a streaming parser, else False @@ -375,7 +367,6 @@ def _parser_is_streaming(parser: ModuleType) -> bool: return False - def _parser_is_hidden(parser: ModuleType) -> bool: """ Returns True if this is a hidden parser, else False @@ -387,7 +378,6 @@ def _parser_is_hidden(parser: ModuleType) -> bool: return False - def _parser_is_deprecated(parser: ModuleType) -> bool: """ Returns True if this is a deprecated parser, else False @@ -399,7 +389,6 @@ def _parser_is_deprecated(parser: ModuleType) -> bool: return False - def parse( parser_mod_name: Union[str, ModuleType], data: Union[str, bytes, Iterable[str]], @@ -509,7 +498,6 @@ def parse( return jc_parser.parse(data, quiet=quiet, raw=raw, **kwargs) - def parser_mod_list( show_hidden: bool = False, show_deprecated: bool = False @@ -529,7 +517,6 @@ def parser_mod_list( return plist - def plugin_parser_mod_list( show_hidden: bool = False, show_deprecated: bool = False @@ -552,7 +539,6 @@ def plugin_parser_mod_list( return plist - def standard_parser_mod_list( show_hidden: bool = False, show_deprecated: bool = False @@ -578,7 +564,6 @@ def standard_parser_mod_list( return plist - def streaming_parser_mod_list( show_hidden: bool = False, show_deprecated: bool = False @@ -603,7 +588,6 @@ def streaming_parser_mod_list( return plist - def slurpable_parser_mod_list( show_hidden: bool = False, show_deprecated: bool = False @@ -628,7 +612,6 @@ def slurpable_parser_mod_list( return plist - def parser_info( parser_mod_name: Union[str, ModuleType], documentation: bool = False @@ -669,7 +652,6 @@ def parser_info( return info_dict - def all_parser_info( documentation: bool = False, show_hidden: bool = False, @@ -704,7 +686,6 @@ def all_parser_info( return p_info_list - def get_help(parser_mod_name: Union[str, ModuleType]) -> None: """ Show help screen for the selected parser. From b45b1d6b0d84edd37dd5820bc3cbcc1d74ac1d74 Mon Sep 17 00:00:00 2001 From: Eden Refael Date: Thu, 5 Dec 2024 01:04:11 +0200 Subject: [PATCH 26/30] created the dedicated _process for raw=False --- jc/parsers/amixer.py | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/jc/parsers/amixer.py b/jc/parsers/amixer.py index ea79e8a9..d2e48a96 100644 --- a/jc/parsers/amixer.py +++ b/jc/parsers/amixer.py @@ -86,16 +86,38 @@ class info(): def _process(proc_data: dict) -> dict: """ - Parameters: + Processes raw structured data to match the schema requirements. - proc_data: (List of Dictionaries) raw structured data to process + Parameters: + proc_data: (dict) raw structured data from the parser Returns: - - dictionary of amixer sget + (dict) processed structured data adhering to the schema """ + # Initialize the processed dictionary + processed = { + "control_name": proc_data.get("control_name", ""), + "capabilities": proc_data.get("capabilities", []), + "playback_channels": proc_data.get("playback_channels", []), + "limits": { + "playback_min": int(proc_data.get("limits", {}).get("playback_min", 0)), + "playback_max": int(proc_data.get("limits", {}).get("playback_max", 0)), + }, + } + + # Process Mono or channel-specific data + channels = ["mono", "front_left", "front_right"] + for channel in channels: + if channel in proc_data: + channel_data = proc_data[channel] + processed[channel] = { + "playback_value": int(channel_data.get("playback_value", 0)), + "percentage": int(channel_data.get("percentage", "0%").strip("%")), + "dB": float(channel_data.get("dB", "0.0dB").strip("dB")), + "status": channel_data.get("status", "off") == "on", + } - return proc_data + return processed def parse( From ce338f62e52433ee4ab59fcc1a570ba7e1b68a39 Mon Sep 17 00:00:00 2001 From: Eden Refael Date: Thu, 5 Dec 2024 01:04:40 +0200 Subject: [PATCH 27/30] created the dedicated _process for raw=False --- jc/parsers/amixer.py | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/jc/parsers/amixer.py b/jc/parsers/amixer.py index d2e48a96..007930b6 100644 --- a/jc/parsers/amixer.py +++ b/jc/parsers/amixer.py @@ -37,7 +37,30 @@ Examples: $ amixer sget Master | jc --amixer -p - + { + "control_name": "Capture", + "capabilities": [ + "cvolume", + "cswitch" + ], + "playback_channels": [], + "limits": { + "playback_min": 0, + "playback_max": 63 + }, + "front_left": { + "playback_value": 63, + "percentage": 100, + "dB": 30.0, + "status": true + }, + "front_right": { + "playback_value": 63, + "percentage": 100, + "dB": 30.0, + "status": true + } + } $ amixer sget Master | jc --amixer -p -r { From 4b098bc6f0f6d48e4db096e5220b09fbdacc0a4e Mon Sep 17 00:00:00 2001 From: Eden Refael Date: Thu, 5 Dec 2024 01:05:28 +0200 Subject: [PATCH 28/30] added tests for the _process raw=False. --- .../amixer-control-capture-processed.json | 1 + .../amixer-control-headphone-processed.json | 1 + .../amixer-control-master-processed.json | 1 + .../amixer-control-speakers-processed.json | 1 + tests/test_amixer.py | 15 ++++++++++++--- 5 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 tests/fixtures/ubuntu-22.04/amixer-control-capture-processed.json create mode 100644 tests/fixtures/ubuntu-22.04/amixer-control-headphone-processed.json create mode 100644 tests/fixtures/ubuntu-22.04/amixer-control-master-processed.json create mode 100644 tests/fixtures/ubuntu-22.04/amixer-control-speakers-processed.json diff --git a/tests/fixtures/ubuntu-22.04/amixer-control-capture-processed.json b/tests/fixtures/ubuntu-22.04/amixer-control-capture-processed.json new file mode 100644 index 00000000..d9af26db --- /dev/null +++ b/tests/fixtures/ubuntu-22.04/amixer-control-capture-processed.json @@ -0,0 +1 @@ +{"control_name":"Capture","capabilities":["cvolume","cswitch"],"playback_channels":[],"limits":{"playback_min":0,"playback_max":63},"front_left":{"playback_value":63,"percentage":100,"dB":30.0,"status":true},"front_right":{"playback_value":63,"percentage":100,"dB":30.0,"status":true}} \ No newline at end of file diff --git a/tests/fixtures/ubuntu-22.04/amixer-control-headphone-processed.json b/tests/fixtures/ubuntu-22.04/amixer-control-headphone-processed.json new file mode 100644 index 00000000..56749279 --- /dev/null +++ b/tests/fixtures/ubuntu-22.04/amixer-control-headphone-processed.json @@ -0,0 +1 @@ +{"control_name":"Headphone","capabilities":["pvolume","pswitch"],"playback_channels":["Front Left","Front Right"],"limits":{"playback_min":0,"playback_max":87},"front_left":{"playback_value":0,"percentage":0,"dB":-65.25,"status":false},"front_right":{"playback_value":0,"percentage":0,"dB":-65.25,"status":false}} \ No newline at end of file diff --git a/tests/fixtures/ubuntu-22.04/amixer-control-master-processed.json b/tests/fixtures/ubuntu-22.04/amixer-control-master-processed.json new file mode 100644 index 00000000..c749ba54 --- /dev/null +++ b/tests/fixtures/ubuntu-22.04/amixer-control-master-processed.json @@ -0,0 +1 @@ +{"control_name":"Master","capabilities":["pvolume","pvolume-joined","pswitch","pswitch-joined"],"playback_channels":["Mono"],"limits":{"playback_min":0,"playback_max":87},"mono":{"playback_value":87,"percentage":100,"dB":0.0,"status":true}} \ No newline at end of file diff --git a/tests/fixtures/ubuntu-22.04/amixer-control-speakers-processed.json b/tests/fixtures/ubuntu-22.04/amixer-control-speakers-processed.json new file mode 100644 index 00000000..39fb00e1 --- /dev/null +++ b/tests/fixtures/ubuntu-22.04/amixer-control-speakers-processed.json @@ -0,0 +1 @@ +{"control_name":"Speaker","capabilities":["pvolume","pswitch"],"playback_channels":["Front Left","Front Right"],"limits":{"playback_min":0,"playback_max":87},"front_left":{"playback_value":87,"percentage":100,"dB":0.0,"status":true},"front_right":{"playback_value":87,"percentage":100,"dB":0.0,"status":true}} \ No newline at end of file diff --git a/tests/test_amixer.py b/tests/test_amixer.py index bcd4dcd0..0bca609f 100644 --- a/tests/test_amixer.py +++ b/tests/test_amixer.py @@ -20,17 +20,26 @@ class AmixerTests(unittest.TestCase): def setUp(self): self.test_files_out = [f'{file}.out' for file in self.TEST_FILES_NAME] self.test_files_json = [f'{file}.json' for file in self.TEST_FILES_NAME] + self.test_files_processed_json = [f'{file}-processed.json' for file in self.TEST_FILES_NAME] def test_amixer_sget(self): - for file_out, file_json in zip(self.test_files_out, self.test_files_json): + for file_out, file_json, file_processed_json in zip(self.test_files_out, self.test_files_json, + self.test_files_processed_json): with open(file_out, 'r') as f: amixer_sget_raw_output: str = f.read() with open(file_json, 'r') as f: expected_amixer_sget_json_output: str = f.read() expected_amixer_sget_json_map: dict = json.loads(expected_amixer_sget_json_output) - amixer_sget_json_map: dict = jc.parse(self.AMIXER_CMD, amixer_sget_raw_output) - self.assertEqual(amixer_sget_json_map, expected_amixer_sget_json_map) + with open(file_processed_json, 'r') as f: + expected_amixer_sget_processed_json_output: str = f.read() + expected_amixer_sget_processed_json_map: dict = json.loads(expected_amixer_sget_processed_json_output) + # Tests for raw=True + amixer_sget_json_map: dict = jc.parse(self.AMIXER_CMD, amixer_sget_raw_output, raw=True) + self.assertEqual(amixer_sget_json_map, expected_amixer_sget_json_map) + # Tests for raw=False process + amixer_sget_json_processed_map: dict = jc.parse(self.AMIXER_CMD, amixer_sget_raw_output, raw=False) + self.assertEqual(amixer_sget_json_processed_map, expected_amixer_sget_processed_json_map) if __name__ == '__main__': From f0c710c14d5dd4d2c17b421fb9d238cf53fa779f Mon Sep 17 00:00:00 2001 From: Eden Refael Date: Wed, 18 Dec 2024 10:49:14 +0200 Subject: [PATCH 29/30] changed keys to be lowercase snake-case - Change 'dB' to 'db' --- jc/parsers/amixer.py | 4 ++-- .../ubuntu-22.04/amixer-control-capture-processed.json | 2 +- tests/fixtures/ubuntu-22.04/amixer-control-capture.json | 2 +- .../ubuntu-22.04/amixer-control-headphone-processed.json | 2 +- tests/fixtures/ubuntu-22.04/amixer-control-headphone.json | 2 +- .../ubuntu-22.04/amixer-control-master-processed.json | 2 +- tests/fixtures/ubuntu-22.04/amixer-control-master.json | 2 +- .../ubuntu-22.04/amixer-control-speakers-processed.json | 2 +- tests/fixtures/ubuntu-22.04/amixer-control-speakers.json | 2 +- tests/test_amixer.py | 6 ++++-- 10 files changed, 14 insertions(+), 12 deletions(-) diff --git a/jc/parsers/amixer.py b/jc/parsers/amixer.py index 007930b6..27a1a39d 100644 --- a/jc/parsers/amixer.py +++ b/jc/parsers/amixer.py @@ -136,7 +136,7 @@ def _process(proc_data: dict) -> dict: processed[channel] = { "playback_value": int(channel_data.get("playback_value", 0)), "percentage": int(channel_data.get("percentage", "0%").strip("%")), - "dB": float(channel_data.get("dB", "0.0dB").strip("dB")), + "db": float(channel_data.get("db", "0.0dB").strip("db")), "status": channel_data.get("status", "off") == "on", } @@ -270,7 +270,7 @@ def parse( mapping[channel_name] = { "playback_value": playback_value, "percentage": percentage, - "dB": db_value, + "db": db_value.lower(), "status": status } diff --git a/tests/fixtures/ubuntu-22.04/amixer-control-capture-processed.json b/tests/fixtures/ubuntu-22.04/amixer-control-capture-processed.json index d9af26db..a920155e 100644 --- a/tests/fixtures/ubuntu-22.04/amixer-control-capture-processed.json +++ b/tests/fixtures/ubuntu-22.04/amixer-control-capture-processed.json @@ -1 +1 @@ -{"control_name":"Capture","capabilities":["cvolume","cswitch"],"playback_channels":[],"limits":{"playback_min":0,"playback_max":63},"front_left":{"playback_value":63,"percentage":100,"dB":30.0,"status":true},"front_right":{"playback_value":63,"percentage":100,"dB":30.0,"status":true}} \ No newline at end of file +{"control_name":"Capture","capabilities":["cvolume","cswitch"],"playback_channels":[],"limits":{"playback_min":0,"playback_max":63},"front_left":{"playback_value":63,"percentage":100,"db":30.0,"status":true},"front_right":{"playback_value":63,"percentage":100,"db":30.0,"status":true}} \ No newline at end of file diff --git a/tests/fixtures/ubuntu-22.04/amixer-control-capture.json b/tests/fixtures/ubuntu-22.04/amixer-control-capture.json index 86e7bead..db020ce4 100644 --- a/tests/fixtures/ubuntu-22.04/amixer-control-capture.json +++ b/tests/fixtures/ubuntu-22.04/amixer-control-capture.json @@ -1 +1 @@ -{"control_name": "Capture", "capabilities": ["cvolume", "cswitch"], "limits": {"playback_min": "0", "playback_max": "63"}, "front_left": {"playback_value": "63", "percentage": "100%", "dB": "30.00dB", "status": "on"}, "front_right": {"playback_value": "63", "percentage": "100%", "dB": "30.00dB", "status": "on"}} \ No newline at end of file +{"control_name": "Capture", "capabilities": ["cvolume", "cswitch"], "limits": {"playback_min": "0", "playback_max": "63"}, "front_left": {"playback_value": "63", "percentage": "100%", "db": "30.00db", "status": "on"}, "front_right": {"playback_value": "63", "percentage": "100%", "db": "30.00db", "status": "on"}} \ No newline at end of file diff --git a/tests/fixtures/ubuntu-22.04/amixer-control-headphone-processed.json b/tests/fixtures/ubuntu-22.04/amixer-control-headphone-processed.json index 56749279..9156c7de 100644 --- a/tests/fixtures/ubuntu-22.04/amixer-control-headphone-processed.json +++ b/tests/fixtures/ubuntu-22.04/amixer-control-headphone-processed.json @@ -1 +1 @@ -{"control_name":"Headphone","capabilities":["pvolume","pswitch"],"playback_channels":["Front Left","Front Right"],"limits":{"playback_min":0,"playback_max":87},"front_left":{"playback_value":0,"percentage":0,"dB":-65.25,"status":false},"front_right":{"playback_value":0,"percentage":0,"dB":-65.25,"status":false}} \ No newline at end of file +{"control_name":"Headphone","capabilities":["pvolume","pswitch"],"playback_channels":["Front Left","Front Right"],"limits":{"playback_min":0,"playback_max":87},"front_left":{"playback_value":0,"percentage":0,"db":-65.25,"status":false},"front_right":{"playback_value":0,"percentage":0,"db":-65.25,"status":false}} \ No newline at end of file diff --git a/tests/fixtures/ubuntu-22.04/amixer-control-headphone.json b/tests/fixtures/ubuntu-22.04/amixer-control-headphone.json index 046fa919..ad0bbb7e 100644 --- a/tests/fixtures/ubuntu-22.04/amixer-control-headphone.json +++ b/tests/fixtures/ubuntu-22.04/amixer-control-headphone.json @@ -1 +1 @@ -{"control_name": "Headphone", "capabilities": ["pvolume", "pswitch"], "playback_channels": ["Front Left", "Front Right"], "limits": {"playback_min": "0", "playback_max": "87"}, "front_left": {"playback_value": "0", "percentage": "0%", "dB": "-65.25dB", "status": "off"}, "front_right": {"playback_value": "0", "percentage": "0%", "dB": "-65.25dB", "status": "off"}} \ No newline at end of file +{"control_name": "Headphone", "capabilities": ["pvolume", "pswitch"], "playback_channels": ["Front Left", "Front Right"], "limits": {"playback_min": "0", "playback_max": "87"}, "front_left": {"playback_value": "0", "percentage": "0%", "db": "-65.25db", "status": "off"}, "front_right": {"playback_value": "0", "percentage": "0%", "db": "-65.25db", "status": "off"}} \ No newline at end of file diff --git a/tests/fixtures/ubuntu-22.04/amixer-control-master-processed.json b/tests/fixtures/ubuntu-22.04/amixer-control-master-processed.json index c749ba54..687f96c8 100644 --- a/tests/fixtures/ubuntu-22.04/amixer-control-master-processed.json +++ b/tests/fixtures/ubuntu-22.04/amixer-control-master-processed.json @@ -1 +1 @@ -{"control_name":"Master","capabilities":["pvolume","pvolume-joined","pswitch","pswitch-joined"],"playback_channels":["Mono"],"limits":{"playback_min":0,"playback_max":87},"mono":{"playback_value":87,"percentage":100,"dB":0.0,"status":true}} \ No newline at end of file +{"control_name":"Master","capabilities":["pvolume","pvolume-joined","pswitch","pswitch-joined"],"playback_channels":["Mono"],"limits":{"playback_min":0,"playback_max":87},"mono":{"playback_value":87,"percentage":100,"db":0.0,"status":true}} \ No newline at end of file diff --git a/tests/fixtures/ubuntu-22.04/amixer-control-master.json b/tests/fixtures/ubuntu-22.04/amixer-control-master.json index 9fc00835..4485b058 100644 --- a/tests/fixtures/ubuntu-22.04/amixer-control-master.json +++ b/tests/fixtures/ubuntu-22.04/amixer-control-master.json @@ -1 +1 @@ -{"control_name": "Master", "capabilities": ["pvolume", "pvolume-joined", "pswitch", "pswitch-joined"], "playback_channels": ["Mono"], "limits": {"playback_min": "0", "playback_max": "87"}, "mono": {"playback_value": "87", "percentage": "100%", "dB": "0.00dB", "status": "on"}} \ No newline at end of file +{"control_name": "Master", "capabilities": ["pvolume", "pvolume-joined", "pswitch", "pswitch-joined"], "playback_channels": ["Mono"], "limits": {"playback_min": "0", "playback_max": "87"}, "mono": {"playback_value": "87", "percentage": "100%", "db": "0.00db", "status": "on"}} \ No newline at end of file diff --git a/tests/fixtures/ubuntu-22.04/amixer-control-speakers-processed.json b/tests/fixtures/ubuntu-22.04/amixer-control-speakers-processed.json index 39fb00e1..651b1aa9 100644 --- a/tests/fixtures/ubuntu-22.04/amixer-control-speakers-processed.json +++ b/tests/fixtures/ubuntu-22.04/amixer-control-speakers-processed.json @@ -1 +1 @@ -{"control_name":"Speaker","capabilities":["pvolume","pswitch"],"playback_channels":["Front Left","Front Right"],"limits":{"playback_min":0,"playback_max":87},"front_left":{"playback_value":87,"percentage":100,"dB":0.0,"status":true},"front_right":{"playback_value":87,"percentage":100,"dB":0.0,"status":true}} \ No newline at end of file +{"control_name":"Speaker","capabilities":["pvolume","pswitch"],"playback_channels":["Front Left","Front Right"],"limits":{"playback_min":0,"playback_max":87},"front_left":{"playback_value":87,"percentage":100,"db":0.0,"status":true},"front_right":{"playback_value":87,"percentage":100,"db":0.0,"status":true}} \ No newline at end of file diff --git a/tests/fixtures/ubuntu-22.04/amixer-control-speakers.json b/tests/fixtures/ubuntu-22.04/amixer-control-speakers.json index 80364036..35b9b59d 100644 --- a/tests/fixtures/ubuntu-22.04/amixer-control-speakers.json +++ b/tests/fixtures/ubuntu-22.04/amixer-control-speakers.json @@ -1 +1 @@ -{"control_name": "Speaker", "capabilities": ["pvolume", "pswitch"], "playback_channels": ["Front Left", "Front Right"], "limits": {"playback_min": "0", "playback_max": "87"}, "front_left": {"playback_value": "87", "percentage": "100%", "dB": "0.00dB", "status": "on"}, "front_right": {"playback_value": "87", "percentage": "100%", "dB": "0.00dB", "status": "on"}} \ No newline at end of file +{"control_name": "Speaker", "capabilities": ["pvolume", "pswitch"], "playback_channels": ["Front Left", "Front Right"], "limits": {"playback_min": "0", "playback_max": "87"}, "front_left": {"playback_value": "87", "percentage": "100%", "db": "0.00db", "status": "on"}, "front_right": {"playback_value": "87", "percentage": "100%", "db": "0.00db", "status": "on"}} \ No newline at end of file diff --git a/tests/test_amixer.py b/tests/test_amixer.py index 0bca609f..bfa21616 100644 --- a/tests/test_amixer.py +++ b/tests/test_amixer.py @@ -35,10 +35,12 @@ def test_amixer_sget(self): expected_amixer_sget_processed_json_map: dict = json.loads(expected_amixer_sget_processed_json_output) # Tests for raw=True - amixer_sget_json_map: dict = jc.parse(self.AMIXER_CMD, amixer_sget_raw_output, raw=True) + amixer_sget_json_map: dict = jc.parse(self.AMIXER_CMD, amixer_sget_raw_output, raw=True, + quiet=True) self.assertEqual(amixer_sget_json_map, expected_amixer_sget_json_map) # Tests for raw=False process - amixer_sget_json_processed_map: dict = jc.parse(self.AMIXER_CMD, amixer_sget_raw_output, raw=False) + amixer_sget_json_processed_map: dict = jc.parse(self.AMIXER_CMD, amixer_sget_raw_output, raw=False, + quiet=True) self.assertEqual(amixer_sget_json_processed_map, expected_amixer_sget_processed_json_map) From ccb99038b21ba446afd5d29a7550f57e7420d772 Mon Sep 17 00:00:00 2001 From: Eden Refael Date: Wed, 18 Dec 2024 11:01:33 +0200 Subject: [PATCH 30/30] added more dB -> db changes and used int convertor of the jc utils --- jc/parsers/amixer.py | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/jc/parsers/amixer.py b/jc/parsers/amixer.py index 27a1a39d..e65811bb 100644 --- a/jc/parsers/amixer.py +++ b/jc/parsers/amixer.py @@ -29,7 +29,7 @@ "mono": { "playback_value": integer, "percentage": integer, - "dB": float, + "db": float, "status": boolean } } @@ -51,13 +51,13 @@ "front_left": { "playback_value": 63, "percentage": 100, - "dB": 30.0, + "db": 30.0, "status": true }, "front_right": { "playback_value": 63, "percentage": 100, - "dB": 30.0, + "db": 30.0, "status": true } } @@ -81,7 +81,7 @@ "mono": { "playback_value": "87", "percentage": "100%", - "dB": "0.00dB", + "db": "0.00db", "status": "on" } } @@ -91,7 +91,7 @@ from typing import List, Dict import jc.utils - +from jc.utils import convert_to_int class info(): """Provides parser metadata (version, author, etc.)""" @@ -123,8 +123,8 @@ def _process(proc_data: dict) -> dict: "capabilities": proc_data.get("capabilities", []), "playback_channels": proc_data.get("playback_channels", []), "limits": { - "playback_min": int(proc_data.get("limits", {}).get("playback_min", 0)), - "playback_max": int(proc_data.get("limits", {}).get("playback_max", 0)), + "playback_min": convert_to_int(proc_data.get("limits", {}).get("playback_min", 0)), + "playback_max": convert_to_int(proc_data.get("limits", {}).get("playback_max", 0)), }, } @@ -134,9 +134,9 @@ def _process(proc_data: dict) -> dict: if channel in proc_data: channel_data = proc_data[channel] processed[channel] = { - "playback_value": int(channel_data.get("playback_value", 0)), - "percentage": int(channel_data.get("percentage", "0%").strip("%")), - "db": float(channel_data.get("db", "0.0dB").strip("db")), + "playback_value": convert_to_int(channel_data.get("playback_value", 0)), + "percentage": convert_to_int(channel_data.get("percentage", "0%").strip("%")), + "db": float(channel_data.get("db", "0.0db").strip("db")), "status": channel_data.get("status", "off") == "on", } @@ -179,8 +179,8 @@ def parse( Capabilities: cvolume cswitch Capture channels: Front Left - Front Right Limits: Capture 0 - 63 - Front Left: Capture 63 [100%] [30.00dB] [on] - Front Right: Capture 63 [100%] [30.00dB] [on] + Front Left: Capture 63 [100%] [30.00db] [on] + Front Right: Capture 63 [100%] [30.00db] [on] @@ -190,7 +190,7 @@ def parse( Capabilities: pvolume pvolume-joined pswitch pswitch-joined Playback channels: Mono Limits: Playback 0 - 87 - Mono: Playback 87 [100%] [0.00dB] [on] + Mono: Playback 87 [100%] [0.00db] [on] @@ -202,8 +202,8 @@ def parse( Playback channels: Front Left - Front Right Limits: Playback 0 - 87 Mono: - Front Left: Playback 87 [100%] [0.00dB] [on] - Front Right: Playback 87 [100%] [0.00dB] [on] + Front Left: Playback 87 [100%] [0.00db] [on] + Front Right: Playback 87 [100%] [0.00db] [on] @@ -214,8 +214,8 @@ def parse( Playback channels: Front Left - Front Right Limits: Playback 0 - 87 Mono: - Front Left: Playback 0 [0%] [-65.25dB] [off] - Front Right: Playback 0 [0%] [-65.25dB] [off] + Front Left: Playback 0 [0%] [-65.25db] [off] + Front Right: Playback 0 [0%] [-65.25db] [off] """ # checks os compatibility and print a stderr massage if not compatible. quiet True could remove this check. jc.utils.compatibility(__name__, info.compatible, quiet) @@ -257,13 +257,13 @@ def parse( # Identify the channel name and parse its information channel_name = line.split(":")[0].strip().lower().replace(" ", "_") channel_info = line.split(":")[1].strip() - # Example: "Playback 255 [100%] [0.00dB] [on]" + # Example: "Playback 255 [100%] [0.00db] [on]" channel_data = channel_info.split(" ") if channel_data[0] == "": continue playback_value = channel_data[1] percentage = channel_data[2].strip("[]") # Extract percentage e.g., "100%" - db_value = channel_data[3].strip("[]") # Extract dB value e.g., "0.00dB" + db_value = channel_data[3].strip("[]") # Extract db value e.g., "0.00db" status = channel_data[4].strip("[]") # Extract status e.g., "on" or "off" # Store channel mapping in the dictionary