Skip to content

Instantly share code, notes, and snippets.

@disjustin
Created January 6, 2026 23:20
Show Gist options
  • Select an option

  • Save disjustin/9d15d03449df5055aaa2f2b692ef4ac9 to your computer and use it in GitHub Desktop.

Select an option

Save disjustin/9d15d03449df5055aaa2f2b692ef4ac9 to your computer and use it in GitHub Desktop.
Python script to convert dmidecode output to json
"""
This Python script defines a tool to extract and parse specific hardware information from a system's DMI (Desktop Management Interface) / SMBIOS data using the dmidecode command-line utility (commonly available on Linux systems).
Tested on Rocky Linux 9.5 using dmidecode version 3.6 (dmidecode-3.6-1.el9.x86_64)
Original version 1/6/2025 Justin Wong
"""
import subprocess
import json
from datetime import datetime
import re
class DmidecodeParser:
def __init__(self, types=None):
"""
Initialize the DmidecodeParser.
:param types: Optional list of DMI types to parse. If None, all types (0-255) are included.
"""
if types is None:
self.types = list(range(0, 256))
else:
self.types = types
def parse(self, dmidecode_output):
"""
Parse the output from dmidecode into a structured dictionary.
:param dmidecode_output: The raw string output from the dmidecode command.
:return: A dictionary organized by DMI type, handle, with details including handle, type, size, title, and data.
"""
result = {}
lines = dmidecode_output.splitlines()
i = 0
while i < len(lines):
line = lines[i].strip()
if line.startswith("Handle"):
parts = re.split(r',\s*', line)
handle = parts[0].split()[1]
type_str = parts[1].split()[2]
type_num = int(type_str)
size_str = parts[2].split()[0]
dmi_size = int(size_str)
if type_num not in self.types:
# Skip to next handle
i += 1
while i < len(lines) and not lines[i].strip().startswith("Handle"):
i += 1
continue
if type_num not in result:
result[type_num] = {}
current = {
'dmi_handle': handle,
'dmi_type': type_num,
'dmi_size': dmi_size,
'dmi_title' : '',
'data': {}
}
result[type_num][handle] = current
i += 1
# Title
if i < len(lines) and not lines[i].strip().startswith("Handle"):
current['dmi_title'] = lines[i].strip()
i += 1
current_key = None
sub_key = None
in_hex = False
in_strings = False
while i < len(lines) and not lines[i].strip().startswith("Handle"):
# remove trailing whitespace
line = lines[i].rstrip()
# skip empty lines
if not line.strip():
i += 1
continue
indent = len(line) - len(line.lstrip('\t'))
content = line.strip()
# print(f"{indent} - {content}")
if in_hex:
if indent == 1:
current['data']['Header and Data'].append(content)
else:
in_hex = False
if in_strings:
if indent == 1 and content:
current['data']['Strings'].append(content)
else:
in_strings = False
if in_hex or in_strings:
i += 1
continue
# line handler based on indentation level
if indent == 1:
if ':' in content:
key, value = [x.strip() for x in content.split(':', 1)]
if value:
current['data'][key] = value
else:
current['data'][key] = []
current_key = key
sub_key = None
elif content == 'Header and Data:':
current['data']['Header and Data'] = []
in_hex = True
current_key = 'Header and Data'
elif content == 'Strings:':
current['data']['Strings'] = []
in_strings = True
current_key = 'Strings'
else:
# Treat as key without value, start list
current['data'][content] = []
current_key = content
elif indent == 2:
if current_key and isinstance(current['data'].get(current_key), list):
current['data'][current_key].append(content)
elif current_key:
if ':' in content:
sub_key, sub_value = [x.strip() for x in content.split(':', 1)]
if 'subfields' not in current['data'][current_key]:
current['data'][current_key] = {'subfields': {}}
current['data'][current_key]['subfields'][sub_key] = sub_value if sub_value else []
sub_key = sub_key
else:
current['data'][current_key] = content
else:
pass
elif indent == 3:
if current_key and isinstance(current['data'].get(current_key), list):
current['data'][current_key].append(content)
i += 1
else:
i += 1
return result
def to_json(self, data):
"""
Convert the parsed data to a JSON string, sorted by type number.
:param data: The parsed data dictionary from the parse method.
:return: A JSON string representation of the data.
"""
sorted_data = {k: data[k] for k in sorted(data.keys())}
return json.dumps(sorted_data, indent=4)
def main():
try:
output = subprocess.check_output(["dmidecode"]).decode("utf-8")
except subprocess.CalledProcessError:
print("Error running dmidecode. Ensure it's installed and run with sufficient privileges.")
return
# types_list = [14, 39, 40, 42, 43, 44, 45] + list(range(128, 256))
types_list = list(range(0, 256))
parser = DmidecodeParser(types=types_list)
data = parser.parse(output)
json_data = parser.to_json(data)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
try:
# if type 1 parsed, use system serial number
systemsn = data[1]["0x0001"]["data"]["Serial Number"]
filename = f"dmidecode_{systemsn}_{timestamp}.json"
except:
# if type 1 not parsed, use timestamp
filename = f"dmidecode_{timestamp}.json"
with open(filename, "w") as f:
f.write(json_data)
print(f"Exported to {filename}")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment