From e88f9ff784abdebf0d88e0c02c12d332613e9506 Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Thu, 11 May 2023 14:52:42 -0700 Subject: [PATCH 1/2] Added improved Pi Revision Code detection --- adafruit_platformdetect/board.py | 12 +- adafruit_platformdetect/constants/boards.py | 27 ++ adafruit_platformdetect/revcodes.py | 264 ++++++++++++++++++++ bin/rev_code_tester.py | 57 +++++ bin/rpi_info.py | 54 ++++ 5 files changed, 411 insertions(+), 3 deletions(-) create mode 100644 adafruit_platformdetect/revcodes.py create mode 100644 bin/rev_code_tester.py create mode 100644 bin/rpi_info.py diff --git a/adafruit_platformdetect/board.py b/adafruit_platformdetect/board.py index 7fbd8222..77b27379 100644 --- a/adafruit_platformdetect/board.py +++ b/adafruit_platformdetect/board.py @@ -201,10 +201,16 @@ def _pi_id(self) -> Optional[str]: # Check for Pi boards: pi_rev_code = self._pi_rev_code() if pi_rev_code: - for model, codes in boards._PI_REV_CODES.items(): - if pi_rev_code in codes: - return model + from adafruit_platformdetect.revcodes import PiDecoder + try: + decoder = PiDecoder(pi_rev_code) + model = boards._PI_MODELS[decoder.type_raw] + if isinstance(model, dict): + model = model[decoder.revision] + return model + except ValueError: + pass # We may be on a non-Raspbian OS, so try to lazily determine # the version based on `get_device_model` else: diff --git a/adafruit_platformdetect/constants/boards.py b/adafruit_platformdetect/constants/boards.py index 0176ede4..058e11a2 100644 --- a/adafruit_platformdetect/constants/boards.py +++ b/adafruit_platformdetect/constants/boards.py @@ -123,6 +123,7 @@ RASPBERRY_PI_AVNET_IIOT_GW = "RASPBERY_PI_AVNET_IIOT_GW" RASPBERRY_PI_400 = "RASPBERRY_PI_400" RASPBERRY_PI_CM4 = "RASPBERRY_PI_CM4" +RASPBERRY_PI_CM4S = "RASPBERRY_PI_CM4S" ODROID_C1 = "ODROID_C1" ODROID_C1_PLUS = "ODROID_C1_PLUS" @@ -341,6 +342,7 @@ RASPBERRY_PI_CM3, RASPBERRY_PI_CM3_PLUS, RASPBERRY_PI_CM4, + RASPBERRY_PI_CM4S, ) _ODROID_40_PIN_IDS = ( @@ -564,6 +566,31 @@ RASPBERRY_PI_ZERO_2_W: ("902120", "2902120"), } +_PI_MODELS = { + 0x00: RASPBERRY_PI_A, + 0x01: { + 1.0: RASPBERRY_PI_B_REV1, + 2.0: RASPBERRY_PI_B_REV2, + }, + 0x02: RASPBERRY_PI_A_PLUS, + 0x03: RASPBERRY_PI_B_PLUS, + 0x04: RASPBERRY_PI_2B, + 0x06: RASPBERRY_PI_CM1, + 0x08: RASPBERRY_PI_3B, + 0x09: RASPBERRY_PI_ZERO, + 0x0A: RASPBERRY_PI_CM3, + 0x0B: RASPBERRY_PI_AVNET_IIOT_GW, + 0x0C: RASPBERRY_PI_ZERO_W, + 0x0D: RASPBERRY_PI_3B_PLUS, + 0x0E: RASPBERRY_PI_3A_PLUS, + 0x10: RASPBERRY_PI_CM3_PLUS, + 0x11: RASPBERRY_PI_4B, + 0x12: RASPBERRY_PI_ZERO_2_W, + 0x13: RASPBERRY_PI_400, + 0x14: RASPBERRY_PI_CM4, + 0x15: RASPBERRY_PI_CM4S, +} + # Onion omega boards _ONION_OMEGA_BOARD_IDS = (ONION_OMEGA, ONION_OMEGA2) diff --git a/adafruit_platformdetect/revcodes.py b/adafruit_platformdetect/revcodes.py new file mode 100644 index 00000000..b461c68c --- /dev/null +++ b/adafruit_platformdetect/revcodes.py @@ -0,0 +1,264 @@ +# SPDX-FileCopyrightText: 2023 Melissa LeBlanc-Williams for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +# Class to help with Raspberry Pi Rev Codes +# Written by Melissa LeBlanc-Williams for Adafruit Industries +# +# Data values from https://github.com/raspberrypi/documentation/blob/develop/ +# documentation/asciidoc/computers/raspberry-pi/revision-codes.adoc#new-style-revision-codes + +NEW_OVERVOLTAGE = ( + "Overvoltage allowed", + "Overvoltage disallowed", +) + +NEW_OTP_PROGRAM = ( + "OTP programming is allowed", + "OTP programming is disallowed", +) + +NEW_OTP_READ = ( + "OTP reading is allowed", + "OTP reading is disallowed", +) + +NEW_WARRANTY_BIT = ( + "Warranty is intact", + "Warranty has been voided by overclocking", +) + +NEW_REV_STYLE = ( + "Old-style revision", + "New-style revision", +) + +NEW_MEMORY_SIZE = ( + "256MB", + "512MB", + "1GB", + "2GB", + "4GB", + "8GB", +) + +NEW_MANUFACTURER = ( + "Sony UK", + "Egoman", + "Embest", + "Sony Japan", + "Embest", + "Stadium", +) + +NEW_PROCESSOR = ( + "BCM2835", + "BCM2836", + "BCM2837", + "BCM2711", +) + +PI_TYPE = { + 0x00: "A", + 0x01: "B", + 0x02: "A+", + 0x03: "B+", + 0x04: "2B", + 0x05: "Alpha (early prototype)", + 0x06: "CM1", + 0x08: "3B", + 0x09: "Zero", + 0x0A: "CM3", + 0x0B: "Custom", + 0x0C: "Zero W", + 0x0D: "3B+", + 0x0E: "3A+", + 0x0F: "Internal use only", + 0x10: "CM3+", + 0x11: "4B", + 0x12: "Zero 2 W", + 0x13: "400", + 0x14: "CM4", + 0x15: "CM4S", +} + +OLD_MANUFACTURER = ( + "Sony UK", + "Egoman", + "Embest", + "Qisda", +) + +OLD_MEMORY_SIZE = ("256MB", "512MB", "256MB/512MB") + +NEW_REV_STRUCTURE = { + "overvoltage": (31, 1, NEW_OVERVOLTAGE), + "otp_program": (30, 1, NEW_OTP_PROGRAM), + "otp_read": (29, 1, NEW_OTP_READ), + "warranty": (25, 1, NEW_WARRANTY_BIT), + "rev_style": (23, 1, NEW_REV_STYLE), + "memory_size": (20, 3, NEW_MEMORY_SIZE), + "manufacturer": (16, 4, NEW_MANUFACTURER), + "processor": (12, 4, NEW_PROCESSOR), + "type": (4, 8, PI_TYPE), + "revision": (0, 4, int), +} + +OLD_REV_STRUCTURE = { + "type": (0, PI_TYPE), + "revision": (1, float), + "memory_size": (2, OLD_MEMORY_SIZE), + "manufacturer": (3, OLD_MANUFACTURER), +} + +OLD_REV_EXTRA_PROPS = { + "warranty": (24, 1, NEW_WARRANTY_BIT), +} + +OLD_REV_LUT = { + 0x02: (1, 1.0, 0, 1), + 0x03: (1, 1.0, 0, 1), + 0x04: (1, 2.0, 0, 0), + 0x05: (1, 2.0, 0, 3), + 0x06: (1, 2.0, 0, 1), + 0x07: (0, 2.0, 0, 1), + 0x08: (0, 2.0, 0, 0), + 0x09: (0, 2.0, 0, 3), + 0x0D: (1, 2.0, 1, 1), + 0x0E: (1, 2.0, 1, 0), + 0x0F: (1, 2.0, 1, 1), + 0x10: (3, 1.2, 1, 0), + 0x11: (6, 1.0, 1, 0), + 0x12: (2, 1.1, 0, 0), + 0x13: (3, 1.2, 1, 2), + 0x14: (6, 1.0, 1, 2), + 0x15: (2, 1.1, 2, 2), +} + +class PiDecoder: + def __init__(self, rev_code): + try: + self.rev_code = int(rev_code, 16) & 0xFFFFFFFF + except ValueError: + print("Invalid revision code. It should be a hexadecimal value.") + + def is_valid_code(self): + # This is a check intended to quickly check the validity of a code + if self.is_new_format(): + for format in NEW_REV_STRUCTURE.values(): + lower_bit, bit_size, values = format + prop_value = (self.rev_code >> lower_bit) & ((1 << bit_size) - 1) + if not self._valid_value(prop_value, values): + return False + else: + if (self.rev_code & 0xFFFF) not in OLD_REV_LUT.keys(): + return False + for format in OLD_REV_STRUCTURE.values(): + index, values = format + format = OLD_REV_LUT[self.rev_code & 0xFFFF] + if index >= len(format): + return False + if not self._valid_value(format[index], values): + return False + return True + + def _get_rev_prop_value(self, name, structure=NEW_REV_STRUCTURE, raw=False): + if name not in structure.keys(): + raise ValueError(f"Unknown property {name}") + lower_bit, bit_size, values = structure[name] + prop_value = self._get_bits_value(lower_bit, bit_size) + if not self._valid_value(prop_value, values): + raise ValueError(f"Invalid value {prop_value} for property {name}") + if raw: + return prop_value + return self._format_value(prop_value, values) + + def _get_bits_value(self, lower_bit, bit_size): + return (self.rev_code >> lower_bit) & ((1 << bit_size) - 1) + + def _get_old_rev_prop_value(self, name, raw=False): + if name not in OLD_REV_STRUCTURE.keys(): + raise ValueError(f"Unknown property {name}") + index, values = OLD_REV_STRUCTURE[name] + data = OLD_REV_LUT[self.rev_code & 0xFFFF] + if index >= len(data): + raise IndexError(f"Index {index} out of range for property {name}") + if not self._valid_value(data[index], values): + raise ValueError(f"Invalid value {data[index]} for property {name}") + if raw: + return data[index] + return self._format_value(data[index], values) + + def _format_value(self, value, valid_values): + if valid_values is float or valid_values is int: + return valid_values(value) + return valid_values[value] + + def _valid_value(self, value, valid_values): + if valid_values is float or valid_values is int: + return isinstance(value, valid_values) + if isinstance(valid_values, (tuple, list)) and 0 <= value < len(valid_values): + return True + if isinstance(valid_values, dict) and value in valid_values.keys(): + return True + return False + + def _get_property(self, name, raw=False): + if name not in NEW_REV_STRUCTURE: + raise ValueError(f"Unknown property {name}") + if self.is_new_format(): + return self._get_rev_prop_value(name, raw=raw) + elif name in OLD_REV_EXTRA_PROPS: + return self._get_rev_prop_value( + name, structure=OLD_REV_EXTRA_PROPS, raw=raw + ) + else: + return self._get_old_rev_prop_value(name, raw=raw) + + def is_new_format(self): + return self._get_rev_prop_value("rev_style", raw=True) == 1 + + @property + def overvoltage(self): + return self._get_property("overvoltage") + + @property + def warranty_bit(self): + return self._get_property("warranty") + + @property + def otp_program(self): + return self._get_property("otp_program") + + @property + def otp_read(self): + return self._get_property("otp_read") + + @property + def rev_style(self): + # Force new style for Rev Style + return self._get_rev_prop_value("rev_style") + + @property + def memory_size(self): + return self._get_property("memory_size") + + @property + def manufacturer(self): + return self._get_property("manufacturer") + + @property + def processor(self): + return self._get_property("processor") + + @property + def type(self): + return self._get_property("type") + + @property + def type_raw(self): + return self._get_property("type", raw = True) + + @property + def revision(self): + return self._get_property("revision") diff --git a/bin/rev_code_tester.py b/bin/rev_code_tester.py new file mode 100644 index 00000000..498fadda --- /dev/null +++ b/bin/rev_code_tester.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 + +# SPDX-FileCopyrightText: 2023 Melissa LeBlanc-Williams for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +# This tests that all existing rev codes in the boards constant file +# match what the decoder finds. + +import adafruit_platformdetect +import adafruit_platformdetect.constants.boards as ap_board +from adafruit_platformdetect.revcodes import PiDecoder + +detector = adafruit_platformdetect.Detector() + +def print_property(label, value): + print(f"{label}: {value}") + +def print_info(pi_decoder): + if pi_decoder.is_new_format(): + print_property("Overvoltage", pi_decoder.overvoltage) + print_property("OTP Program", pi_decoder.otp_program) + print_property("OTP Read", pi_decoder.otp_read) + print_property("Warranty bit", pi_decoder.warranty_bit) + print_property("New flag", pi_decoder.rev_style) + print_property("Memory size", pi_decoder.memory_size) + print_property("Manufacturer", pi_decoder.manufacturer) + print_property("Processor", pi_decoder.processor) + print_property("Type", pi_decoder.type) + print_property("Revision", pi_decoder.revision) + else: + print_property("Warranty bit", pi_decoder.warranty_bit) + print_property("Model", pi_decoder.type) + print_property("Revision", pi_decoder.revision) + print_property("RAM", pi_decoder.memory_size) + print_property("Manufacturer", pi_decoder.manufacturer) + +# Iterate through the _PI_REV_CODES dictionary to find the model +# Run the code through the decoder to check that: +# - It is a valid code +# - It matches the model +for model, codes in ap_board._PI_REV_CODES.items(): + for pi_rev_code in codes: + try: + decoder = PiDecoder(pi_rev_code) + except ValueError as e: + print("Invalid revision code. It should be a hexadecimal value.") + decoded_model = ap_board._PI_MODELS[decoder.type_raw] + if isinstance(decoded_model, dict): + decoded_model = decoded_model[decoder.revision] + if decoded_model == model: + print(f"Decoded model matches expected model: {model}") + else: + print(f"Decoded model does not match expected model: {model}") + print(f"Decoded model: {decoded_model}") + print(f"Expected model: {model}") + print_info(decoder) diff --git a/bin/rpi_info.py b/bin/rpi_info.py new file mode 100644 index 00000000..2bacad1c --- /dev/null +++ b/bin/rpi_info.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 + +# SPDX-FileCopyrightText: 2023 Melissa LeBlanc-Williams for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +# Interactive mode will prompt for the revision code +# Otherwise it will be detected automatically + +import sys +import adafruit_platformdetect +from adafruit_platformdetect.revcodes import PiDecoder + +pi_rev_code = None + +detector = adafruit_platformdetect.Detector() +pi_rev_code = detector.board._pi_rev_code() + +if pi_rev_code is None: + print("Raspberry Pi not detected. Using interactive mode") + pi_rev_code = input("Enter a Raspberry Pi revision code (e.g. d03114 or 000f): ") + +try: + decoder = PiDecoder(pi_rev_code) +except ValueError as e: + print("Invalid revision code. It should be a hexadecimal value.") + sys.exit(1) + +# if not decoder.is_valid_code(): +# print("Code is invalid. This rev code includes at least one value that is outside of the expected range.") +# sys.exit(1) + + +def print_property(label, value): + print(f"{label}: {value}") + + +if decoder.is_new_format(): + print_property("Overvoltage", decoder.overvoltage) + print_property("OTP Program", decoder.otp_program) + print_property("OTP Read", decoder.otp_read) + print_property("Warranty bit", decoder.warranty_bit) + print_property("New flag", decoder.rev_style) + print_property("Memory size", decoder.memory_size) + print_property("Manufacturer", decoder.manufacturer) + print_property("Processor", decoder.processor) + print_property("Type", decoder.type) + print_property("Revision", decoder.revision) +else: + print_property("Warranty bit", decoder.warranty_bit) + print_property("Model", decoder.type) + print_property("Revision", decoder.revision) + print_property("RAM", decoder.memory_size) + print_property("Manufacturer", decoder.manufacturer) From 7a1ff1b2324fd6197116e778f062825db49f29ca Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Thu, 11 May 2023 15:16:17 -0700 Subject: [PATCH 2/2] Update pre-commit and re-lint --- adafruit_platformdetect/revcodes.py | 72 +++++++++++++++++++++-------- bin/rev_code_tester.py | 30 ++++++++++-- bin/rpi_info.py | 31 ++++++++++--- 3 files changed, 103 insertions(+), 30 deletions(-) diff --git a/adafruit_platformdetect/revcodes.py b/adafruit_platformdetect/revcodes.py index b461c68c..d8db8b6d 100644 --- a/adafruit_platformdetect/revcodes.py +++ b/adafruit_platformdetect/revcodes.py @@ -2,11 +2,25 @@ # # SPDX-License-Identifier: MIT -# Class to help with Raspberry Pi Rev Codes -# Written by Melissa LeBlanc-Williams for Adafruit Industries -# -# Data values from https://github.com/raspberrypi/documentation/blob/develop/ -# documentation/asciidoc/computers/raspberry-pi/revision-codes.adoc#new-style-revision-codes +""" +`adafruit_platformdetect.revcodes` +================================================================================ + +Class to help with Raspberry Pi Rev Codes + +* Author(s): Melissa LeBlanc-Williams + +Implementation Notes +-------------------- + +**Software and Dependencies:** + +* Linux and Python 3.7 or Higher + +Data values from https://github.com/raspberrypi/documentation/blob/develop/ +documentation/asciidoc/computers/raspberry-pi/revision-codes.adoc#new-style-revision-codes + +""" NEW_OVERVOLTAGE = ( "Overvoltage allowed", @@ -135,7 +149,10 @@ 0x15: (2, 1.1, 2, 2), } + class PiDecoder: + """Raspberry Pi Revision Code Decoder""" + def __init__(self, rev_code): try: self.rev_code = int(rev_code, 16) & 0xFFFFFFFF @@ -143,26 +160,28 @@ def __init__(self, rev_code): print("Invalid revision code. It should be a hexadecimal value.") def is_valid_code(self): - # This is a check intended to quickly check the validity of a code + """Quickly check the validity of a code""" if self.is_new_format(): - for format in NEW_REV_STRUCTURE.values(): - lower_bit, bit_size, values = format + for code_format in NEW_REV_STRUCTURE.values(): + lower_bit, bit_size, values = code_format prop_value = (self.rev_code >> lower_bit) & ((1 << bit_size) - 1) if not self._valid_value(prop_value, values): return False else: if (self.rev_code & 0xFFFF) not in OLD_REV_LUT.keys(): return False - for format in OLD_REV_STRUCTURE.values(): - index, values = format - format = OLD_REV_LUT[self.rev_code & 0xFFFF] - if index >= len(format): + for code_format in OLD_REV_STRUCTURE.values(): + index, values = code_format + code_format = OLD_REV_LUT[self.rev_code & 0xFFFF] + if index >= len(code_format): return False - if not self._valid_value(format[index], values): + if not self._valid_value(code_format[index], values): return False return True - def _get_rev_prop_value(self, name, structure=NEW_REV_STRUCTURE, raw=False): + def _get_rev_prop_value(self, name, structure=None, raw=False): + if structure is None: + structure = NEW_REV_STRUCTURE if name not in structure.keys(): raise ValueError(f"Unknown property {name}") lower_bit, bit_size, values = structure[name] @@ -189,12 +208,14 @@ def _get_old_rev_prop_value(self, name, raw=False): return data[index] return self._format_value(data[index], values) - def _format_value(self, value, valid_values): + @staticmethod + def _format_value(value, valid_values): if valid_values is float or valid_values is int: return valid_values(value) return valid_values[value] - def _valid_value(self, value, valid_values): + @staticmethod + def _valid_value(value, valid_values): if valid_values is float or valid_values is int: return isinstance(value, valid_values) if isinstance(valid_values, (tuple, list)) and 0 <= value < len(valid_values): @@ -208,57 +229,68 @@ def _get_property(self, name, raw=False): raise ValueError(f"Unknown property {name}") if self.is_new_format(): return self._get_rev_prop_value(name, raw=raw) - elif name in OLD_REV_EXTRA_PROPS: + if name in OLD_REV_EXTRA_PROPS: return self._get_rev_prop_value( name, structure=OLD_REV_EXTRA_PROPS, raw=raw ) - else: - return self._get_old_rev_prop_value(name, raw=raw) + return self._get_old_rev_prop_value(name, raw=raw) def is_new_format(self): + """Check if the code is in the new format""" return self._get_rev_prop_value("rev_style", raw=True) == 1 @property def overvoltage(self): + """Overvoltage allowed/disallowed""" return self._get_property("overvoltage") @property def warranty_bit(self): + """Warranty bit""" return self._get_property("warranty") @property def otp_program(self): + """OTP programming allowed/disallowed""" return self._get_property("otp_program") @property def otp_read(self): + """OTP reading allowed/disallowed""" return self._get_property("otp_read") @property def rev_style(self): + """Revision Code style""" # Force new style for Rev Style return self._get_rev_prop_value("rev_style") @property def memory_size(self): + """Memory size""" return self._get_property("memory_size") @property def manufacturer(self): + """Manufacturer""" return self._get_property("manufacturer") @property def processor(self): + """Processor""" return self._get_property("processor") @property def type(self): + """Specific Model""" return self._get_property("type") @property def type_raw(self): - return self._get_property("type", raw = True) + """Raw Value of Specific Model""" + return self._get_property("type", raw=True) @property def revision(self): + """Revision Number""" return self._get_property("revision") diff --git a/bin/rev_code_tester.py b/bin/rev_code_tester.py index 498fadda..1e5e5dd1 100644 --- a/bin/rev_code_tester.py +++ b/bin/rev_code_tester.py @@ -4,8 +4,23 @@ # # SPDX-License-Identifier: MIT -# This tests that all existing rev codes in the boards constant file -# match what the decoder finds. +""" +`bin.rev_code_tester` +================================================================================ + +Tests that all existing rev codes in the boards constant file + match what the decoder finds. + +* Author(s): Melissa LeBlanc-Williams + +Implementation Notes +-------------------- + +**Software and Dependencies:** + +* Linux and Python 3.7 or Higher + +""" import adafruit_platformdetect import adafruit_platformdetect.constants.boards as ap_board @@ -13,10 +28,14 @@ detector = adafruit_platformdetect.Detector() + def print_property(label, value): + "Format and print a property" print(f"{label}: {value}") + def print_info(pi_decoder): + "Print the info for the board" if pi_decoder.is_new_format(): print_property("Overvoltage", pi_decoder.overvoltage) print_property("OTP Program", pi_decoder.otp_program) @@ -35,17 +54,20 @@ def print_info(pi_decoder): print_property("RAM", pi_decoder.memory_size) print_property("Manufacturer", pi_decoder.manufacturer) + # Iterate through the _PI_REV_CODES dictionary to find the model # Run the code through the decoder to check that: # - It is a valid code # - It matches the model -for model, codes in ap_board._PI_REV_CODES.items(): +for model, codes in ap_board._PI_REV_CODES.items(): # pylint: disable=protected-access for pi_rev_code in codes: try: decoder = PiDecoder(pi_rev_code) except ValueError as e: print("Invalid revision code. It should be a hexadecimal value.") - decoded_model = ap_board._PI_MODELS[decoder.type_raw] + decoded_model = ap_board._PI_MODELS[ # pylint: disable=protected-access + decoder.type_raw + ] if isinstance(decoded_model, dict): decoded_model = decoded_model[decoder.revision] if decoded_model == model: diff --git a/bin/rpi_info.py b/bin/rpi_info.py index 2bacad1c..5b018813 100644 --- a/bin/rpi_info.py +++ b/bin/rpi_info.py @@ -4,8 +4,23 @@ # # SPDX-License-Identifier: MIT -# Interactive mode will prompt for the revision code -# Otherwise it will be detected automatically +""" +`bin.rpi_info` +================================================================================ + +Interactive mode will prompt for the revision code +Otherwise it will be detected automatically + +* Author(s): Melissa LeBlanc-Williams + +Implementation Notes +-------------------- + +**Software and Dependencies:** + +* Linux and Python 3.7 or Higher + +""" import sys import adafruit_platformdetect @@ -14,7 +29,7 @@ pi_rev_code = None detector = adafruit_platformdetect.Detector() -pi_rev_code = detector.board._pi_rev_code() +pi_rev_code = detector.board._pi_rev_code() # pylint: disable=protected-access if pi_rev_code is None: print("Raspberry Pi not detected. Using interactive mode") @@ -26,12 +41,16 @@ print("Invalid revision code. It should be a hexadecimal value.") sys.exit(1) -# if not decoder.is_valid_code(): -# print("Code is invalid. This rev code includes at least one value that is outside of the expected range.") -# sys.exit(1) +if not decoder.is_valid_code(): + print( + "Code is invalid. This rev code includes at least one " + "value that is outside of the expected range." + ) + sys.exit(1) def print_property(label, value): + "Format and print a property" print(f"{label}: {value}")