diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0bc5b768..8a18a717 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,22 +2,22 @@ name: CI Pipeline on: push: - branches: 'master' + branches: ['main', 'explorergt92-1'] pull_request: branches: '*' jobs: build: - runs-on: ubuntu-18.04 + runs-on: ubuntu-24.04 strategy: max-parallel: 4 fail-fast: false matrix: - python-version: ['3.8', '3.9', '3.10'] + python-version: ['3.11', '3.12', '3.13'] steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install dependencies diff --git a/.gitignore b/.gitignore index b85b092d..4b0c633a 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,9 @@ docs/_build/ # PyBuilder target/ + +.venv +.env +.idea +.idea.md +nextgen_obd \ No newline at end of file diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 9c6dea9a..7d2c35ed 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -1,9 +1,9 @@ version: 2 build: - os: ubuntu-22.04 + os: ubuntu-24.04 tools: - python: "3.11" + python: "3.12" python: install: diff --git a/README.md b/README.md index 319594e7..fdbae52a 100644 --- a/README.md +++ b/README.md @@ -82,5 +82,6 @@ This library is forked from: - - +- Enjoy and drive safe! diff --git a/docs/Async Connections.md b/docs/Async Connections.md index d655faff..3f092ae2 100644 --- a/docs/Async Connections.md +++ b/docs/Async Connections.md @@ -72,7 +72,7 @@ Stops the update loop. ### paused() -A helper function for use in a Context Manager (a `with` statement) to temporarily stop the update loop. This makes it easy to protect your `watch()` and `unwatch()` calls. If the update loop was running at the time of being paused, it will be restarted upon exitting the context block. For instance: +A helper function for use in a Context Manager (a `with` statement) to temporarily stop the update loop. This makes it easy to protect your `watch()` and `unwatch()` calls. If the update loop was running at the time of being paused, it will be restarted upon exiting the context block. For instance: ```python with connection.paused() as was_running: diff --git a/docs/Command Lookup.md b/docs/Command Lookup.md index f87ea39a..8fd93005 100644 --- a/docs/Command Lookup.md +++ b/docs/Command Lookup.md @@ -22,7 +22,7 @@ The `commands` table also has a few helper methods for determining if a particul ### has_command(command) -Checks the internal command tables for the existance of the given `OBDCommand` object. Commands are compared by mode and PID value. +Checks the internal command tables for the existence of the given `OBDCommand` object. Commands are compared by mode and PID value. ```python import obd diff --git a/docs/Command Tables.md b/docs/Command Tables.md index aebaf85d..5a1bb560 100644 --- a/docs/Command Tables.md +++ b/docs/Command Tables.md @@ -83,7 +83,7 @@ | 43 | ABSOLUTE_LOAD | Absolute load value | Unit.percent | | 44 | COMMANDED_EQUIV_RATIO | Commanded equivalence ratio | Unit.ratio | | 45 | RELATIVE_THROTTLE_POS | Relative throttle position | Unit.percent | -| 46 | AMBIANT_AIR_TEMP | Ambient air temperature | Unit.celsius | +| 46 | AMBIENT_AIR_TEMP | Ambient air temperature | Unit.celsius | | 47 | THROTTLE_POS_B | Absolute throttle position B | Unit.percent | | 48 | THROTTLE_POS_C | Absolute throttle position C | Unit.percent | | 49 | ACCELERATOR_POS_D | Accelerator pedal position D | Unit.percent | diff --git a/docs/Connections.md b/docs/Connections.md index 1df861fd..2bebb19b 100644 --- a/docs/Connections.md +++ b/docs/Connections.md @@ -38,9 +38,9 @@ Disabling fast mode will guarantee that python-OBD outputs the unaltered command `timeout`: Specifies the connection timeout in seconds. -`check_voltage`: Optional argument that is `True` by default and when set to `False` disables the detection of the car supply voltage on OBDII port (which should be about 12V). This control assumes that, if the voltage is lower than 6V, the OBDII port is disconnected from the car. If the option is enabled, it adds the `OBDStatus.OBD_CONNECTED` status, which is set when enough voltage is returned (socket connected to the car) but the ignition is off (no communication with the vehicle). Setting the option to `False` should be needed when the adapter does not support the voltage pin or more generally when the hardware provides unreliable results, or if the pin reads the switched ignition voltage rather than the battery positive (this depends on the car). +`check_voltage`: Optional argument that is `True` by default and when set to `False` disables the detection of the car supply voltage on OBD-II port (which should be about 12V). This control assumes that, if the voltage is lower than 6V, the OBD-II port is disconnected from the car. If the option is enabled, it adds the `OBDStatus.OBD_CONNECTED` status, which is set when enough voltage is returned (socket connected to the car) but the ignition is off (no communication with the vehicle). Setting the option to `False` should be needed when the adapter does not support the voltage pin or more generally when the hardware provides unreliable results, or if the pin reads the switched ignition voltage rather than the battery positive (this depends on the car). -`start_low_power`: Optional argument that defaults to `False`. If set to `True` the initial connection will take longer (roughly 1 more second) but will support waking the ELM327 from low power mode before starting the connection. It does this by sending a space to the chip to trigger a charecter being received on the RS232 input line. This is sent before the baud rate is setup, to ensure the device is awake to detect the baud rate. +`start_low_power`: Optional argument that defaults to `False`. If set to `True` the initial connection will take longer (roughly 1 more second) but will support waking the ELM327 from low power mode before starting the connection. It does this by sending a space to the chip to trigger a character being received on the RS232 input line. This is sent before the baud rate is setup, to ensure the device is awake to detect the baud rate.
@@ -58,6 +58,36 @@ connection = obd.OBD() r = connection.query(obd.commands.RPM) # returns the response from the car ``` +
+ +--- + +### query_multi(*commands, force=False) + +Similar to the standard `query()` function, but allows up to 6 `OBDCommands` to be sent simultaneously. Returns a tuple of `OBDResponse`s in the same order as the commands were specified. + +*For non-blocking querying, see [Async Querying](Async Connections.md)* + +```python +responses = connection.query_multi(obd.commands.RPM, obd.commands.SPEED) + +# OR (using python tuple unpacking) + +rpm, speed = connection.query_multi(obd.commands.RPM, obd.commands.SPEED) + +# OR (specifying a list as varargs) + +commands = [obd.commands.RPM, obd.commands.SPEED] +responses = connection.query_multi(*commands) +``` + +*NOTE: this function only performs faster over CAN protocols. Using this function over non-CAN protocols will simply iteratively call `query()`, and is equivalent to writing:* + +```python +commands = [obd.commands.RPM, obd.commands.SPEED] +responses = tuple(connection.query(cmd) for cmd in commands) +``` +
--- @@ -88,7 +118,7 @@ The status is set by `OBD()` or `Async()` methods and remains unmodified during `ELM_CONNECTED` and `OBD_CONNECTED` are mostly for diagnosing errors. When a proper connection is established with the vehicle, you will never encounter these values. -The ELM327 controller allows OBD Commands and AT Commands. In general, OBD Commands (which interact with the car) can be succesfully performed when the ignition is on, while AT Commands (which generally interact with the ELM327 controller) are always accepted. As the connection phase (for both `OBD` and `Async` objects) also performs OBD protocol commands (after the initial set of AT Commands) and returns the “Car Connected” status (“CAR_CONNECTED”) if the overall connection phase is successful, this status means that the serial communication is valid, that the ELM327 adapter is appropriately responding, that the OBDII socket is connected to the car and also that the ignition is on. “OBD Connected” status (“OBD_CONNECTED”) is returned when the OBDII socket is connected and the ignition is off, while the "ELM Connected" status (“ELM_CONNECTED”) means that the ELM327 processor is reached but the OBDII socket is not connected to the car. “OBD Connected” is controlled by the `check_voltage` option that by default is set to `True` and gets the ignition status when the socket is connected. If the OBDII socket does not support the unswitched battery positive supply, or the OBDII adapter cannot detect it, then the `check_voltage` option should be set to `False`; in such case, the "ELM Connected" status is returned when the socket is not connected or when the ignition is off, with no differentiation. +The ELM327 controller allows OBD Commands and AT Commands. In general, OBD Commands (which interact with the car) can be successfully performed when the ignition is on, while AT Commands (which generally interact with the ELM327 controller) are always accepted. As the connection phase (for both `OBD` and `Async` objects) also performs OBD protocol commands (after the initial set of AT Commands) and returns the “Car Connected” status (“CAR_CONNECTED”) if the overall connection phase is successful, this status means that the serial communication is valid, that the ELM327 adapter is appropriately responding, that the OBD-II socket is connected to the car and also that the ignition is on. “OBD Connected” status (“OBD_CONNECTED”) is returned when the OBD-II socket is connected and the ignition is off, while the "ELM Connected" status (“ELM_CONNECTED”) means that the ELM327 processor is reached but the OBD-II socket is not connected to the car. “OBD Connected” is controlled by the `check_voltage` option that by default is set to `True` and gets the ignition status when the socket is connected. If the OBD-II socket does not support the un-switched battery positive supply, or the OBD-II adapter cannot detect it, then the `check_voltage` option should be set to `False`; in such case, the "ELM Connected" status is returned when the socket is not connected or when the ignition is off, with no differentiation. --- diff --git a/docs/Custom Commands.md b/docs/Custom Commands.md index e726030d..4d39dc19 100644 --- a/docs/Custom Commands.md +++ b/docs/Custom Commands.md @@ -10,7 +10,7 @@ If the command you need is not in python-OBDs tables, you can create a new `OBDC | bytes | int | Number of bytes expected in response (zero means unknown) | | decoder | callable | Function used for decoding messages from the OBD adapter | | ecu (optional) | ECU | ID of the ECU this command should listen to (`ECU.ALL` by default) | -| fast (optional) | bool | Allows python-OBD to alter this command for efficieny (`False` by default) | +| fast (optional) | bool | Allows python-OBD to alter this command for efficiency (`False` by default) | | header (optional) | string | If set, use a custom header instead of the default one (7E0) | @@ -25,7 +25,7 @@ def rpm(messages): """ decoder for RPM messages """ d = messages[0].data # only operate on a single message d = d[2:] # chop off mode and PID bytes - v = bytes_to_int(d) / 4.0 # helper function for converting byte arrays to ints + v = bytes_to_int(d) / 4.0 # helper function for converting byte arrays to integer return v * Unit.RPM # construct a Pint Quantity c = OBDCommand("RPM", \ # name @@ -68,7 +68,7 @@ def (): return ``` -The return value of your decoder will be loaded into the `OBDResponse.value` field. Decoders are given a list of `Message` objects as an argument. If your decoder is called, this list is garaunteed to have at least one message object. Each `Message` object has a `data` property, which holds a parsed bytearray, and is also garauteed to have the number of bytes specified by the command. This bytearray includes any mode and PID bytes in the vehicle's response. +The return value of your decoder will be loaded into the `OBDResponse.value` field. Decoders are given a list of `Message` objects as an argument. If your decoder is called, this list is guaranteed to have at least one message object. Each `Message` object has a `data` property, which holds a parsed bytearray, and is also guaranteed to have the number of bytes specified by the command. This bytearray includes any mode and PID bytes in the vehicle's response. *NOTE: If you are transitioning from an older version of Python-OBD (where decoders were given raw hex strings as arguments), you can use the `Message.hex()` function as a patch.* @@ -96,7 +96,7 @@ The `ecu` argument is a constant used to filter incoming messages. Some commands ## OBDCommand.fast -The optional `fast` argument tells python-OBD whether it is safe to append a `"01"` to the end of the command. This will instruct the adapter to return the first response it recieves, rather than waiting for more (and eventually reaching a timeout). This can speed up requests significantly, and is enabled for most of python-OBDs internal commands. However, for unusual commands, it is safest to leave this disabled. +The optional `fast` argument tells python-OBD whether it is safe to append a `"01"` to the end of the command. This will instruct the adapter to return the first response it receives, rather than waiting for more (and eventually reaching a timeout). This can speed up requests significantly, and is enabled for most of python-OBDs internal commands. However, for unusual commands, it is safest to leave this disabled. --- diff --git a/docs/Examples.md b/docs/Examples.md new file mode 100644 index 00000000..0f0a122d --- /dev/null +++ b/docs/Examples.md @@ -0,0 +1,94 @@ + +# RPM Logger + +```python + +import obd + + +def main(): + + # connect to the car + connection = obd.OBD() + + # handles connection errors + if not connection.is_connected(): + print("Failed to connect") + return + + # open the output file + with open("rpm.txt", "w") as f: + + # loop indefinitely + while True: + + # read the car's RPM + r = connection.query(obd.commands.RPM) + + if not r.is_null(): + # write CSV "time, RPM" to the log file + f.write("%d, %d" % (r.time, r.value)) + + +if __name__ == "__main__": + main() + +``` + +
+ +# Async RPM Logger + +```python + +import obd +import time + + +log_file = None + +# callback fired on every new RPM response +def on_RPM(r): + if not r.is_null(): + # write CSV "time, RPM" to the log file + log_file.write("%d, %d" % (r.time, r.value)) + + +def main(): + global log_file + + # connect to the car + connection = obd.Async() + + # handles connection errors + if not connection.is_connected(): + print("Failed to connect") + return + + # listen to the car's RPM, and subscribe the callback + connection.watch(obd.commands.RPM, callback=on_RPM) + + # open the output file + with open("rpm.txt", "w") as f: + + log_file = f + + # begin data collection + connection.start() + + # record for 10 seconds + time.sleep(10) + + # stop recording + connection.stop() + + + +if __name__ == "__main__": + main() + +``` + +--- + +
\ No newline at end of file diff --git a/docs/Troubleshooting.md b/docs/Troubleshooting.md index 7889e251..0ebe2cb2 100644 --- a/docs/Troubleshooting.md +++ b/docs/Troubleshooting.md @@ -60,12 +60,12 @@ Here are some common logs from python-OBD, and their meanings: [obd] wait: 1 seconds [obd] __read() found nothing [obd] __read() found nothing -[obd] __read() never recieved prompt character +[obd] __read() never received prompt character [obd] read: '' [obd] write: 'ATE0\r\n' [obd] __read() found nothing [obd] __read() found nothing -[obd] __read() never recieved prompt character +[obd] __read() never received prompt character [obd] read: '' [obd] Connection Error: [obd] ATE0 did not return 'OK' diff --git a/mkdocs.yml b/mkdocs.yml index ad9086bb..350bc1cf 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,6 +1,6 @@ site_name: python-OBD -repo_url: https://github.com/brendan-w/python-OBD -repo_name: GitHub +repo_url: https://github.com/Explorergt92/python-OBD +repo_name: GitHub Explorergt92/python-OBD extra_javascript: - assets/extra.js nav: @@ -12,6 +12,7 @@ nav: - 'Async Connections': 'Async Connections.md' - 'Custom Commands': 'Custom Commands.md' - 'Debug': 'Debug.md' +- 'Examples': 'Examples.md' - 'Troubleshooting': 'Troubleshooting.md' theme: readthedocs diff --git a/obd/OBDResponse.py b/obd/OBDResponse.py index e72c7e4c..b2eed13b 100644 --- a/obd/OBDResponse.py +++ b/obd/OBDResponse.py @@ -32,18 +32,12 @@ import logging -import sys import time from .codes import * logger = logging.getLogger(__name__) -if sys.version[0] < '3': - string_types = (str, unicode) -else: - string_types = (str,) - class OBDResponse: """ Standard response object for any OBDCommand """ @@ -140,7 +134,7 @@ def __len__(self): def __getitem__(self, key): if isinstance(key, int): return self._tests.get(key, MonitorTest()) - elif isinstance(key, string_types): + elif isinstance(key, str): return self.__dict__.get(key, MonitorTest()) else: logger.warning("Monitor test results can only be retrieved by TID value or property name") diff --git a/obd/commands.py b/obd/commands.py index 9162f1c8..75f0ec74 100644 --- a/obd/commands.py +++ b/obd/commands.py @@ -124,7 +124,7 @@ OBDCommand("ABSOLUTE_LOAD" , "Absolute load value" , b"0143", 4, absolute_load, ECU.ENGINE, True), OBDCommand("COMMANDED_EQUIV_RATIO" , "Commanded equivalence ratio" , b"0144", 4, uas(0x1E), ECU.ENGINE, True), OBDCommand("RELATIVE_THROTTLE_POS" , "Relative throttle position" , b"0145", 3, percent, ECU.ENGINE, True), - OBDCommand("AMBIANT_AIR_TEMP" , "Ambient air temperature" , b"0146", 3, temp, ECU.ENGINE, True), + OBDCommand("AMBIENT_AIR_TEMP" , "Ambient air temperature" , b"0146", 3, temp, ECU.ENGINE, True), OBDCommand("THROTTLE_POS_B" , "Absolute throttle position B" , b"0147", 3, percent, ECU.ENGINE, True), OBDCommand("THROTTLE_POS_C" , "Absolute throttle position C" , b"0148", 3, percent, ECU.ENGINE, True), OBDCommand("ACCELERATOR_POS_D" , "Accelerator pedal position D" , b"0149", 3, percent, ECU.ENGINE, True), @@ -152,7 +152,7 @@ OBDCommand("EMISSION_REQ" , "Designed emission requirements" , b"015F", 3, drop, ECU.ENGINE, True), ] -# mode 2 is the same as mode 1, but returns values from when the DTC occured +# mode 2 is the same as mode 1, but returns values from when the DTC occurred __mode2__ = [] for c in __mode1__: c = c.clone() @@ -389,16 +389,16 @@ def pid_getters(self): return getters def has_command(self, c): - """ checks for existance of a command by OBDCommand object """ + """ checks for existence of a command by OBDCommand object """ return c in self.__dict__.values() def has_name(self, name): - """ checks for existance of a command by name """ + """ checks for existence of a command by name """ # isupper() rejects all the normal properties return name.isupper() and name in self.__dict__ def has_pid(self, mode, pid): - """ checks for existance of a command by int mode and int pid """ + """ checks for existence of a command by int mode and int pid """ if (mode < 0) or (pid < 0): return False if mode >= len(self.modes): diff --git a/obd/decoders.py b/obd/decoders.py index fffca50a..c4806164 100644 --- a/obd/decoders.py +++ b/obd/decoders.py @@ -290,7 +290,7 @@ def status(messages): # ┌MIL ||||||┌Misfire supported # | ||||||| # 10000011 00000111 11111111 00000000 - # [# DTC] X [supprt] [~ready] + # [# DTC] X [support] [~ready] output = Status() output.MIL = bits[0] diff --git a/obd/elm327.py b/obd/elm327.py index b543effc..211ed2d8 100644 --- a/obd/elm327.py +++ b/obd/elm327.py @@ -107,7 +107,7 @@ class ELM327: def __init__(self, portname, baudrate, protocol, timeout, check_voltage=True, start_low_power=False): - """Initializes port by resetting device and gettings supported PIDs. """ + """Initializes port by resetting device and getting supported PIDs. """ logger.info("Initializing ELM327: PORT=%s BAUD=%s PROTOCOL=%s" % ( @@ -167,13 +167,13 @@ def __init__(self, portname, baudrate, protocol, timeout, self.__error("ATH1 did not return 'OK', or echoing is still ON") return - # ------------------------ ATL0 (linefeeds OFF) ----------------------- + # ------------------------ ATL0 (line feeds OFF) ----------------------- r = self.__send(b"ATL0") if not self.__isok(r): self.__error("ATL0 did not return 'OK'") return - # by now, we've successfuly communicated with the ELM, but not the car + # by now, we've successfully communicated with the ELM, but not the car self.__status = OBDStatus.ELM_CONNECTED # -------------------------- AT RV (read volt) ------------------------ @@ -189,7 +189,7 @@ def __init__(self, portname, baudrate, protocol, timeout, except ValueError as e: self.__error("Incorrect response from 'AT RV'") return - # by now, we've successfuly connected to the OBD socket + # by now, we've successfully connected to the OBD socket self.__status = OBDStatus.OBD_CONNECTED # try to communicate with the car, and load the correct protocol parser @@ -302,23 +302,16 @@ def auto_baudrate(self): Returns boolean for success. """ - # before we change the timout, save the "normal" value + # before we change the timeout, save the "normal" value timeout = self.__port.timeout self.__port.timeout = self.timeout # we're only talking with the ELM, so things should go quickly for baud in self._TRY_BAUDS: self.__port.baudrate = baud - self.__port.flushInput() - self.__port.flushOutput() + self.__port.reset_input_buffer() # dump everything in the input buffer + self.__port.reset_output_buffer() # dump everything in the output buffer - # Send a nonsense command to get a prompt back from the scanner - # (an empty command runs the risk of repeating a dangerous command) - # The first character might get eaten if the interface was busy, - # so write a second one (again so that the lone CR doesn't repeat - # the previous command) - - # All commands should be terminated with carriage return according - # to ELM327 and STN11XX specifications + # All commands should be terminated with carriage return according to ELM327 and STN11XX specifications self.__port.write(b"\x7F\x7F\r") self.__port.flush() response = self.__port.read(1024) @@ -503,7 +496,7 @@ def __write(self, cmd): cmd += b"\r" # terminate with carriage return in accordance with ELM327 and STN11XX specifications logger.debug("write: " + repr(cmd)) try: - self.__port.flushInput() # dump everything in the input buffer + self.__port.reset_input_buffer() # dump everything in the input buffer self.__port.write(cmd) # turn the string into bytes and write self.__port.flush() # wait for the output buffer to finish transmitting except Exception: diff --git a/obd/protocols/README.md b/obd/protocols/README.md index 693cf141..689795ee 100644 --- a/obd/protocols/README.md +++ b/obd/protocols/README.md @@ -31,13 +31,13 @@ All protocol objects must implement the following: #### parse_frame(self, frame) -Recieves a single `Frame` object with `Frame.raw` preloaded with the raw line recieved from the car (in string form). This function is responsible for parsing `Frame.raw` into a bytearray, and filling the remaining fields in the `Frame` object. If the frame is invalid, or the parse fails, this function should return `False`, and the frame will be dropped. +Receives a single `Frame` object with `Frame.raw` preloaded with the raw line received from the car (in string form). This function is responsible for parsing `Frame.raw` into a bytearray, and filling the remaining fields in the `Frame` object. If the frame is invalid, or the parse fails, this function should return `False`, and the frame will be dropped. ---------------------------------------- #### parse_message(self, message) -Recieves a single `Message` object with `Message.frames` preloaded with a list of `Frame` objects. This function is responsible for assembling the frames into the `Message.data` field in the `Message` object. This is where multi-line responses are assembled. If the message is found to be invalid, this function should return `False`, and the entire message will be dropped. +Receives a single `Message` object with `Message.frames` preloaded with a list of `Frame` objects. This function is responsible for assembling the frames into the `Message.data` field in the `Message` object. This is where multi-line responses are assembled. If the message is found to be invalid, this function should return `False`, and the entire message will be dropped. ---------------------------------------- diff --git a/obd/protocols/protocol.py b/obd/protocols/protocol.py index b07d2f7c..555ce678 100644 --- a/obd/protocols/protocol.py +++ b/obd/protocols/protocol.py @@ -297,7 +297,7 @@ def parse_frame(self, frame): """ override in subclass for each protocol - Function recieves a Frame object preloaded + Function receives a Frame object preloaded with the raw string line from the car. Function should return a boolean. If fatal errors were @@ -309,7 +309,7 @@ def parse_message(self, message): """ override in subclass for each protocol - Function recieves a Message object + Function receives a Message object preloaded with a list of Frame objects. Function should return a boolean. If fatal errors were diff --git a/obd/protocols/protocol_can.py b/obd/protocols/protocol_can.py index ee2c3528..fd30673e 100644 --- a/obd/protocols/protocol_can.py +++ b/obd/protocols/protocol_can.py @@ -168,7 +168,7 @@ def parse_message(self, message): frame = frames[0] if frame.type != self.FRAME_TYPE_SF: - logger.debug("Recieved lone frame not marked as single frame") + logger.debug("Received lone frame not marked as single frame") return False # extract data, ignore PCI byte and anything after the marked length @@ -193,7 +193,7 @@ def parse_message(self, message): # check that we captured only one first-frame if len(ff) > 1: - logger.debug("Recieved multiple frames marked FF") + logger.debug("Received multiple frames marked FF") return False elif len(ff) == 0: logger.debug("Never received frame marked FF") @@ -224,7 +224,7 @@ def parse_message(self, message): # check contiguity, and that we aren't missing any frames indices = [f.seq_index for f in cf] if not contiguous(indices, 1, len(cf)): - logger.debug("Recieved multiline response with missing frames") + logger.debug("Received multiline response with missing frames") return False # first frame: diff --git a/obd/protocols/protocol_legacy.py b/obd/protocols/protocol_legacy.py index ca393769..f8d67aa0 100644 --- a/obd/protocols/protocol_legacy.py +++ b/obd/protocols/protocol_legacy.py @@ -89,7 +89,7 @@ def parse_message(self, message): # test that all frames are responses to the same Mode (SID) if len(frames) > 1: if not all([mode == f.data[0] for f in frames[1:]]): - logger.debug("Recieved frames from multiple commands") + logger.debug("Received frames from multiple commands") return False # legacy protocols have different re-assembly @@ -148,7 +148,7 @@ def parse_message(self, message): # check contiguity indices = [f.data[2] for f in frames] if not contiguous(indices, 1, len(frames)): - logger.debug("Recieved multiline response with missing frames") + logger.debug("Received multiline response with missing frames") return False # now that they're in order, accumulate the data from each frame diff --git a/obd/utils.py b/obd/utils.py index 221d3e9c..fb436605 100644 --- a/obd/utils.py +++ b/obd/utils.py @@ -131,7 +131,7 @@ def isHex(_hex): def contiguous(l, start, end): - """ checks that a list of integers are consequtive """ + """ checks that a list of integers are consecutive """ if not l: return False if l[0] != start: @@ -139,7 +139,7 @@ def contiguous(l, start, end): if l[-1] != end: return False - # for consequtiveness, look at the integers in pairs + # Check consecutive integers by comparing each adjacent pair. pairs = zip(l, l[1:]) if not all([p[0] + 1 == p[1] for p in pairs]): return False @@ -183,7 +183,7 @@ def scan_serial(): ] possible_ports += [port for port in glob.glob('/dev/tty.*') if port not in exclude] - # possible_ports += glob.glob('/dev/pts/[0-9]*') # for obdsim + # possible_ports += glob.glob('/dev/pts/[0-9]*') # for OBD Sim for port in possible_ports: if try_port(port): diff --git a/pyproject.toml b/pyproject.toml index f91d6e3f..01b86aec 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,16 +6,16 @@ authors = [ { name="Alistair Francis", email="alistair@alistair23.me" }, { name="Paul Bartek" }, { name="Peter Harris" }, + { name="Jesse Decker" }, + { name="John Scott", email="john.s@eloq-algos.com" }, ] description = "Serial module for handling live sensor data from a vehicle's OBD-II port" readme = "README.md" -requires-python = ">=3.9" +requires-python = ">=3.11" classifiers = [ "Operating System :: POSIX :: Linux", "Topic :: System :: Monitoring", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", @@ -23,7 +23,7 @@ classifiers = [ "Topic :: System :: Logging", "Intended Audience :: Developers", ] -keywords = ["obd", "obdii", "obd-ii", "obd2", "car", "serial", "vehicle", "diagnostic"] +keywords = ["obd", "obdii", "obd-ii", "obd2", "car", "serial", "vehicle", "diagnostic", "elm327", "elm-327", "automotive", "monitoring"] dependencies = [ "pyserial==3.*", "pint==0.24.*", @@ -32,5 +32,5 @@ license = "GPL-2.0-only" license-files = ["LICENSE"] [project.urls] -Homepage = "https://github.com/brendan-w/python-OBD" -Issues = "https://github.com/brendan-w/python-OBD/issues" +Homepage = "https://github.com/Explorergt92/python-OBD" +Issues = "https://github.com/Explorergt92/python-OBD/issues" diff --git a/tests/README.md b/tests/README.md index 5f9c4573..6ede9c73 100644 --- a/tests/README.md +++ b/tests/README.md @@ -31,4 +31,4 @@ This directory also contains a set of end-to-end tests that require [obdsim](htt py.test --port=/dev/pts/ ``` -For more information on pytest with virtualenvs, [read more here](https://pytest.org/dev/goodpractises.html) \ No newline at end of file +For more information on pytest with virtualenv, [read more here](https://pytest.org/dev/goodpractises.html) \ No newline at end of file diff --git a/tox.ini b/tox.ini index 6fe55f43..a66315a8 100644 --- a/tox.ini +++ b/tox.ini @@ -1,12 +1,10 @@ [tox] envlist = - py{39,310,311,312,313}, + py{311,312,313}, coverage [gh-actions] python = - 3.9: py39 - 3.10: py310 3.11: py311 3.12: py312 3.13: py313 @@ -16,9 +14,9 @@ usedevelop = true setenv = COVERAGE_FILE={toxinidir}/.coverage_{envname} deps = - pdbpp==0.10.3 - pytest==7.2.1 - pytest-cov==4.0.0 + pdbpp==0.11.6 + pytest==8.3.5 + pytest-cov==6.1.1 commands = pytest --cov-report= --cov=obd {posargs} @@ -40,6 +38,7 @@ max-line-length = 120 omit = .tox/* env/* + .env [coverage:paths] source =