Merge pull request #64 from tcfranks/main

resubmit pr Typting Annotations
This commit is contained in:
Alec Delaney 2023-03-15 12:28:08 -04:00 committed by GitHub
commit 0061f33008
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 124 additions and 47 deletions

View file

@ -31,6 +31,14 @@ from digitalio import Direction
from micropython import const from micropython import const
try:
from typing import Optional, Tuple, Union
from typing_extensions import Literal
from circuitpython_typing import ReadableBuffer
from digitalio import DigitalInOut # pylint: disable=ungrouped-imports
except ImportError:
pass
__version__ = "0.0.0+auto.0" __version__ = "0.0.0+auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_PN532.git" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_PN532.git"
@ -151,7 +159,13 @@ class BusyError(Exception):
class PN532: class PN532:
"""PN532 driver base, must be extended for I2C/SPI/UART interfacing""" """PN532 driver base, must be extended for I2C/SPI/UART interfacing"""
def __init__(self, *, debug=False, irq=None, reset=None): def __init__(
self,
*,
debug: bool = False,
irq: Optional[DigitalInOut] = None,
reset: Optional[DigitalInOut] = None
) -> None:
"""Create an instance of the PN532 class""" """Create an instance of the PN532 class"""
self.low_power = True self.low_power = True
self.debug = debug self.debug = debug
@ -160,26 +174,26 @@ class PN532:
self.reset() self.reset()
_ = self.firmware_version _ = self.firmware_version
def _read_data(self, count): def _read_data(self, count: int) -> Union[bytes, bytearray]:
# Read raw data from device, not including status bytes: # Read raw data from device, not including status bytes:
# Subclasses MUST implement this! # Subclasses MUST implement this!
raise NotImplementedError raise NotImplementedError
def _write_data(self, framebytes): def _write_data(self, framebytes: bytes) -> None:
# Write raw bytestring data to device, not including status bytes: # Write raw bytestring data to device, not including status bytes:
# Subclasses MUST implement this! # Subclasses MUST implement this!
raise NotImplementedError raise NotImplementedError
def _wait_ready(self, timeout): def _wait_ready(self, timeout: float) -> bool:
# Check if busy up to max length of 'timeout' seconds # Check if busy up to max length of 'timeout' seconds
# Subclasses MUST implement this! # Subclasses MUST implement this!
raise NotImplementedError raise NotImplementedError
def _wakeup(self): def _wakeup(self) -> None:
# Send special command to wake up # Send special command to wake up
raise NotImplementedError raise NotImplementedError
def reset(self): def reset(self) -> None:
"""Perform a hardware reset toggle and then wake up the PN532""" """Perform a hardware reset toggle and then wake up the PN532"""
if self._reset_pin: if self._reset_pin:
if self.debug: if self.debug:
@ -191,7 +205,7 @@ class PN532:
time.sleep(0.1) time.sleep(0.1)
self._wakeup() self._wakeup()
def _write_frame(self, data): def _write_frame(self, data: bytearray) -> None:
"""Write a frame to the PN532 with the specified data bytearray.""" """Write a frame to the PN532 with the specified data bytearray."""
assert ( assert (
data is not None and 1 < len(data) < 255 data is not None and 1 < len(data) < 255
@ -221,7 +235,7 @@ class PN532:
print("Write frame: ", [hex(i) for i in frame]) print("Write frame: ", [hex(i) for i in frame])
self._write_data(bytes(frame)) self._write_data(bytes(frame))
def _read_frame(self, length): def _read_frame(self, length: int) -> Union[bytes, bytearray]:
"""Read a response frame from the PN532 of at most length bytes in size. """Read a response frame from the PN532 of at most length bytes in size.
Returns the data inside the frame if found, otherwise raises an exception Returns the data inside the frame if found, otherwise raises an exception
if there is an error parsing the frame. Note that less than length bytes if there is an error parsing the frame. Note that less than length bytes
@ -257,8 +271,12 @@ class PN532:
return response[offset + 2 : offset + 2 + frame_len] return response[offset + 2 : offset + 2 + frame_len]
def call_function( def call_function(
self, command, response_length=0, params=[], timeout=1 self,
): # pylint: disable=dangerous-default-value command: int,
response_length: int = 0,
params: ReadableBuffer = b"",
timeout: float = 1,
) -> Optional[Union[bytes, bytearray]]:
"""Send specified command to the PN532 and expect up to response_length """Send specified command to the PN532 and expect up to response_length
bytes back in a response. Note that less than the expected bytes might bytes back in a response. Note that less than the expected bytes might
be returned! Params can optionally specify an array of bytes to send as be returned! Params can optionally specify an array of bytes to send as
@ -273,11 +291,11 @@ class PN532:
) )
def send_command( def send_command(
self, command, params=[], timeout=1 self, command: int, params: ReadableBuffer = b"", timeout: float = 1
): # pylint: disable=dangerous-default-value ) -> bool:
"""Send specified command to the PN532 and wait for an acknowledgment. """Send specified command to the PN532 and wait for an acknowledgment.
Will wait up to timeout seconds for the acknowlegment and return True. Will wait up to timeout seconds for the acknowledgment and return True.
If no acknowlegment is received, False is returned. If no acknowledgment is received, False is returned.
""" """
if self.low_power: if self.low_power:
self._wakeup() self._wakeup()
@ -300,7 +318,9 @@ class PN532:
raise RuntimeError("Did not receive expected ACK from PN532!") raise RuntimeError("Did not receive expected ACK from PN532!")
return True return True
def process_response(self, command, response_length=0, timeout=1): def process_response(
self, command: int, response_length: int = 0, timeout: float = 1
) -> Optional[Union[bytes, bytearray]]:
"""Process the response from the PN532 and expect up to response_length """Process the response from the PN532 and expect up to response_length
bytes back in a response. Note that less than the expected bytes might bytes back in a response. Note that less than the expected bytes might
be returned! Will wait up to timeout seconds for a response and return be returned! Will wait up to timeout seconds for a response and return
@ -317,7 +337,7 @@ class PN532:
# Return response data. # Return response data.
return response[2:] return response[2:]
def power_down(self): def power_down(self) -> bool:
"""Put the PN532 into a low power state. If the reset pin is connected a """Put the PN532 into a low power state. If the reset pin is connected a
hard power down is performed, if not, a soft power down is performed hard power down is performed, if not, a soft power down is performed
instead. Returns True if the PN532 was powered down successfully or instead. Returns True if the PN532 was powered down successfully or
@ -333,7 +353,7 @@ class PN532:
return self.low_power return self.low_power
@property @property
def firmware_version(self): def firmware_version(self) -> Tuple[int, int, int, int]:
"""Call PN532 GetFirmwareVersion function and return a tuple with the IC, """Call PN532 GetFirmwareVersion function and return a tuple with the IC,
Ver, Rev, and Support values. Ver, Rev, and Support values.
""" """
@ -342,7 +362,7 @@ class PN532:
raise RuntimeError("Failed to detect the PN532") raise RuntimeError("Failed to detect the PN532")
return tuple(response) return tuple(response)
def SAM_configuration(self): # pylint: disable=invalid-name def SAM_configuration(self) -> None: # pylint: disable=invalid-name
"""Configure the PN532 to read MiFare cards.""" """Configure the PN532 to read MiFare cards."""
# Send SAM configuration command with configuration for: # Send SAM configuration command with configuration for:
# - 0x01, normal mode # - 0x01, normal mode
@ -352,7 +372,9 @@ class PN532:
# check the command was executed as expected. # check the command was executed as expected.
self.call_function(_COMMAND_SAMCONFIGURATION, params=[0x01, 0x14, 0x01]) self.call_function(_COMMAND_SAMCONFIGURATION, params=[0x01, 0x14, 0x01])
def read_passive_target(self, card_baud=_MIFARE_ISO14443A, timeout=1): def read_passive_target(
self, card_baud: int = _MIFARE_ISO14443A, timeout: float = 1
) -> Optional[bytearray]:
"""Wait for a MiFare card to be available and return its UID when found. """Wait for a MiFare card to be available and return its UID when found.
Will wait up to timeout seconds and return None if no card is found, Will wait up to timeout seconds and return None if no card is found,
otherwise a bytearray with the UID of the found card is returned. otherwise a bytearray with the UID of the found card is returned.
@ -364,9 +386,11 @@ class PN532:
return None return None
return self.get_passive_target(timeout=timeout) return self.get_passive_target(timeout=timeout)
def listen_for_passive_target(self, card_baud=_MIFARE_ISO14443A, timeout=1): def listen_for_passive_target(
self, card_baud: int = _MIFARE_ISO14443A, timeout: float = 1
) -> bool:
"""Send command to PN532 to begin listening for a Mifare card. This """Send command to PN532 to begin listening for a Mifare card. This
returns True if the command was received succesfully. Note, this does returns True if the command was received successfully. Note, this does
not also return the UID of a card! `get_passive_target` must be called not also return the UID of a card! `get_passive_target` must be called
to read the UID when a card is found. If just looking to see if a card to read the UID when a card is found. If just looking to see if a card
is currently present use `read_passive_target` instead. is currently present use `read_passive_target` instead.
@ -380,7 +404,9 @@ class PN532:
return False # _COMMAND_INLISTPASSIVETARGET failed return False # _COMMAND_INLISTPASSIVETARGET failed
return response return response
def get_passive_target(self, timeout=1): def get_passive_target(
self, timeout: float = 1
) -> Optional[Union[bytes, bytearray]]:
"""Will wait up to timeout seconds and return None if no card is found, """Will wait up to timeout seconds and return None if no card is found,
otherwise a bytearray with the UID of the found card is returned. otherwise a bytearray with the UID of the found card is returned.
`listen_for_passive_target` must have been called first in order to put `listen_for_passive_target` must have been called first in order to put
@ -404,9 +430,13 @@ class PN532:
# Return UID of card. # Return UID of card.
return response[6 : 6 + response[5]] return response[6 : 6 + response[5]]
def mifare_classic_authenticate_block( def mifare_classic_authenticate_block( # pylint: disable=invalid-name
self, uid, block_number, key_number, key self,
): # pylint: disable=invalid-name uid: ReadableBuffer,
block_number: int,
key_number: Literal[0x60, 0x61],
key: ReadableBuffer,
) -> bool:
"""Authenticate specified block number for a MiFare classic card. Uid """Authenticate specified block number for a MiFare classic card. Uid
should be a byte array with the UID of the card, block number should be should be a byte array with the UID of the card, block number should be
the block to authenticate, key number should be the key type (like the block to authenticate, key number should be the key type (like
@ -429,7 +459,9 @@ class PN532:
) )
return response[0] == 0x00 return response[0] == 0x00
def mifare_classic_read_block(self, block_number): def mifare_classic_read_block(
self, block_number: int
) -> Optional[Union[bytes, bytearray]]:
"""Read a block of data from the card. Block number should be the block """Read a block of data from the card. Block number should be the block
to read. If the block is successfully read a bytearray of length 16 with to read. If the block is successfully read a bytearray of length 16 with
data starting at the specified block will be returned. If the block is data starting at the specified block will be returned. If the block is
@ -447,7 +479,9 @@ class PN532:
# Return first 4 bytes since 16 bytes are always returned. # Return first 4 bytes since 16 bytes are always returned.
return response[1:] return response[1:]
def mifare_classic_write_block(self, block_number, data): def mifare_classic_write_block(
self, block_number: int, data: ReadableBuffer
) -> bool:
"""Write a block of data to the card. Block number should be the block """Write a block of data to the card. Block number should be the block
to write and data should be a byte array of length 16 with the data to to write and data should be a byte array of length 16 with the data to
write. If the data is successfully written then True is returned, write. If the data is successfully written then True is returned,
@ -468,7 +502,7 @@ class PN532:
) )
return response[0] == 0x0 return response[0] == 0x0
def ntag2xx_write_block(self, block_number, data): def ntag2xx_write_block(self, block_number: int, data: ReadableBuffer) -> bool:
"""Write a block of data to the card. Block number should be the block """Write a block of data to the card. Block number should be the block
to write and data should be a byte array of length 4 with the data to to write and data should be a byte array of length 4 with the data to
write. If the data is successfully written then True is returned, write. If the data is successfully written then True is returned,
@ -487,7 +521,9 @@ class PN532:
) )
return response[0] == 0x00 return response[0] == 0x00
def ntag2xx_read_block(self, block_number): def ntag2xx_read_block(
self, block_number: int
) -> Optional[Union[bytes, bytearray]]:
"""Read a block of data from the card. Block number should be the block """Read a block of data from the card. Block number should be the block
to read. If the block is successfully read the first 4 bytes (after the to read. If the block is successfully read the first 4 bytes (after the
leading 0x00 byte) will be returned. leading 0x00 byte) will be returned.

View file

@ -23,6 +23,13 @@ from digitalio import Direction
from micropython import const from micropython import const
from adafruit_pn532.adafruit_pn532 import PN532, BusyError from adafruit_pn532.adafruit_pn532 import PN532, BusyError
try:
from typing import Optional
from digitalio import DigitalInOut # pylint: disable=ungrouped-imports
from busio import I2C
except ImportError:
pass
_I2C_ADDRESS = const(0x24) _I2C_ADDRESS = const(0x24)
@ -30,8 +37,15 @@ class PN532_I2C(PN532):
"""Driver for the PN532 connected over I2C.""" """Driver for the PN532 connected over I2C."""
def __init__( def __init__(
self, i2c, address=_I2C_ADDRESS, *, irq=None, reset=None, req=None, debug=False self,
): i2c: I2C,
address: int = _I2C_ADDRESS,
*,
irq: Optional[DigitalInOut] = None,
reset: Optional[DigitalInOut] = None,
req: Optional[DigitalInOut] = None,
debug: bool = False
) -> None:
"""Create an instance of the PN532 class using I2C. Note that PN532 """Create an instance of the PN532 class using I2C. Note that PN532
uses clock stretching. Optional IRQ pin (not used), uses clock stretching. Optional IRQ pin (not used),
resetp pin and debugging output. resetp pin and debugging output.
@ -41,7 +55,7 @@ class PN532_I2C(PN532):
self._i2c = i2c_device.I2CDevice(i2c, address) self._i2c = i2c_device.I2CDevice(i2c, address)
super().__init__(debug=debug, irq=irq, reset=reset) super().__init__(debug=debug, irq=irq, reset=reset)
def _wakeup(self): def _wakeup(self) -> None:
"""Send any special commands/data to wake up PN532""" """Send any special commands/data to wake up PN532"""
if self._reset_pin: if self._reset_pin:
self._reset_pin.value = True self._reset_pin.value = True
@ -55,7 +69,7 @@ class PN532_I2C(PN532):
self.low_power = False self.low_power = False
self.SAM_configuration() # Put the PN532 back in normal mode self.SAM_configuration() # Put the PN532 back in normal mode
def _wait_ready(self, timeout=1): def _wait_ready(self, timeout: float = 1) -> bool:
"""Poll PN532 if status byte is ready, up to `timeout` seconds""" """Poll PN532 if status byte is ready, up to `timeout` seconds"""
status = bytearray(1) status = bytearray(1)
timestamp = time.monotonic() timestamp = time.monotonic()
@ -67,11 +81,11 @@ class PN532_I2C(PN532):
continue continue
if status == b"\x01": if status == b"\x01":
return True # No longer busy return True # No longer busy
time.sleep(0.01) # lets ask again soon! time.sleep(0.01) # let's ask again soon!
# Timed out! # Timed out!
return False return False
def _read_data(self, count): def _read_data(self, count: int) -> bytearray:
"""Read a specified count of bytes from the PN532.""" """Read a specified count of bytes from the PN532."""
# Build a read request frame. # Build a read request frame.
frame = bytearray(count + 1) frame = bytearray(count + 1)
@ -84,7 +98,7 @@ class PN532_I2C(PN532):
print("Reading: ", [hex(i) for i in frame[1:]]) print("Reading: ", [hex(i) for i in frame[1:]])
return frame[1:] # don't return the status byte return frame[1:] # don't return the status byte
def _write_data(self, framebytes): def _write_data(self, framebytes: bytes) -> None:
"""Write a specified count of bytes to the PN532""" """Write a specified count of bytes to the PN532"""
with self._i2c as i2c: with self._i2c as i2c:
i2c.write(framebytes) i2c.write(framebytes)

View file

@ -14,6 +14,14 @@ using SPI.
""" """
try:
from typing import Optional
from circuitpython_typing import ReadableBuffer
from digitalio import DigitalInOut
from busio import SPI
except ImportError:
pass
__version__ = "0.0.0+auto.0" __version__ = "0.0.0+auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_PN532.git" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_PN532.git"
@ -28,7 +36,7 @@ _SPI_DATAREAD = const(0x03)
_SPI_READY = const(0x01) _SPI_READY = const(0x01)
def reverse_bit(num): def reverse_bit(num: int) -> int:
"""Turn an LSB byte to an MSB byte, and vice versa. Used for SPI as """Turn an LSB byte to an MSB byte, and vice versa. Used for SPI as
it is LSB for the PN532, but 99% of SPI implementations are MSB only!""" it is LSB for the PN532, but 99% of SPI implementations are MSB only!"""
result = 0 result = 0
@ -44,13 +52,21 @@ class PN532_SPI(PN532):
SPI device & chip select digitalInOut pin. Optional IRQ pin (not used), SPI device & chip select digitalInOut pin. Optional IRQ pin (not used),
reset pin and debugging output.""" reset pin and debugging output."""
def __init__(self, spi, cs_pin, *, irq=None, reset=None, debug=False): def __init__(
self,
spi: SPI,
cs_pin: DigitalInOut,
*,
irq: Optional[DigitalInOut] = None,
reset: Optional[DigitalInOut] = None,
debug: bool = False
) -> None:
"""Create an instance of the PN532 class using SPI""" """Create an instance of the PN532 class using SPI"""
self.debug = debug self.debug = debug
self._spi = spi_device.SPIDevice(spi, cs_pin) self._spi = spi_device.SPIDevice(spi, cs_pin)
super().__init__(debug=debug, irq=irq, reset=reset) super().__init__(debug=debug, irq=irq, reset=reset)
def _wakeup(self): def _wakeup(self) -> None:
"""Send any special commands/data to wake up PN532""" """Send any special commands/data to wake up PN532"""
if self._reset_pin: if self._reset_pin:
self._reset_pin.value = True self._reset_pin.value = True
@ -61,7 +77,7 @@ class PN532_SPI(PN532):
self.low_power = False self.low_power = False
self.SAM_configuration() # Put the PN532 back in normal mode self.SAM_configuration() # Put the PN532 back in normal mode
def _wait_ready(self, timeout=1): def _wait_ready(self, timeout: float = 1) -> bool:
"""Poll PN532 if status byte is ready, up to `timeout` seconds""" """Poll PN532 if status byte is ready, up to `timeout` seconds"""
status_cmd = bytearray([reverse_bit(_SPI_STATREAD), 0x00]) status_cmd = bytearray([reverse_bit(_SPI_STATREAD), 0x00])
status_response = bytearray([0x00, 0x00]) status_response = bytearray([0x00, 0x00])
@ -77,7 +93,7 @@ class PN532_SPI(PN532):
# We timed out! # We timed out!
return False return False
def _read_data(self, count): def _read_data(self, count: int) -> bytearray:
"""Read a specified count of bytes from the PN532.""" """Read a specified count of bytes from the PN532."""
# Build a read request frame. # Build a read request frame.
frame = bytearray(count + 1) frame = bytearray(count + 1)
@ -92,7 +108,7 @@ class PN532_SPI(PN532):
print("Reading: ", [hex(i) for i in frame[1:]]) print("Reading: ", [hex(i) for i in frame[1:]])
return frame[1:] return frame[1:]
def _write_data(self, framebytes): def _write_data(self, framebytes: ReadableBuffer) -> None:
"""Write a specified count of bytes to the PN532""" """Write a specified count of bytes to the PN532"""
# start by making a frame with data write in front, # start by making a frame with data write in front,
# then rest of bytes, and LSBify it # then rest of bytes, and LSBify it

View file

@ -17,6 +17,13 @@ using UART.
__version__ = "0.0.0+auto.0" __version__ = "0.0.0+auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_PN532.git" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_PN532.git"
try:
from typing import Optional
from circuitpython_typing import ReadableBuffer
from digitalio import DigitalInOut
from busio import UART
except ImportError:
pass
import time import time
from adafruit_pn532.adafruit_pn532 import PN532, BusyError from adafruit_pn532.adafruit_pn532 import PN532, BusyError
@ -25,7 +32,9 @@ from adafruit_pn532.adafruit_pn532 import PN532, BusyError
class PN532_UART(PN532): class PN532_UART(PN532):
"""Driver for the PN532 connected over Serial UART""" """Driver for the PN532 connected over Serial UART"""
def __init__(self, uart, *, reset=None, debug=False): def __init__(
self, uart: UART, *, reset: Optional[DigitalInOut] = None, debug: bool = False
) -> None:
"""Create an instance of the PN532 class using Serial connection. """Create an instance of the PN532 class using Serial connection.
Optional reset pin and debugging output. Optional reset pin and debugging output.
""" """
@ -33,7 +42,7 @@ class PN532_UART(PN532):
self._uart = uart self._uart = uart
super().__init__(debug=debug, reset=reset) super().__init__(debug=debug, reset=reset)
def _wakeup(self): def _wakeup(self) -> None:
"""Send any special commands/data to wake up PN532""" """Send any special commands/data to wake up PN532"""
if self._reset_pin: if self._reset_pin:
self._reset_pin.value = True self._reset_pin.value = True
@ -44,7 +53,7 @@ class PN532_UART(PN532):
) # wake up! ) # wake up!
self.SAM_configuration() self.SAM_configuration()
def _wait_ready(self, timeout=1): def _wait_ready(self, timeout: float = 1) -> bool:
"""Wait `timeout` seconds""" """Wait `timeout` seconds"""
timestamp = time.monotonic() timestamp = time.monotonic()
while (time.monotonic() - timestamp) < timeout: while (time.monotonic() - timestamp) < timeout:
@ -54,7 +63,7 @@ class PN532_UART(PN532):
# Timed out! # Timed out!
return False return False
def _read_data(self, count): def _read_data(self, count: int) -> bytes:
"""Read a specified count of bytes from the PN532.""" """Read a specified count of bytes from the PN532."""
frame = self._uart.read(count) frame = self._uart.read(count)
if not frame: if not frame:
@ -63,7 +72,7 @@ class PN532_UART(PN532):
print("Reading: ", [hex(i) for i in frame]) print("Reading: ", [hex(i) for i in frame])
return frame return frame
def _write_data(self, framebytes): def _write_data(self, framebytes: ReadableBuffer) -> None:
"""Write a specified count of bytes to the PN532""" """Write a specified count of bytes to the PN532"""
self._uart.reset_input_buffer() self._uart.reset_input_buffer()
self._uart.write(framebytes) self._uart.write(framebytes)

View file

@ -4,4 +4,6 @@
Adafruit-Blinka Adafruit-Blinka
adafruit-circuitpython-busdevice adafruit-circuitpython-busdevice
adafruit-circuitpython-typing
pyserial pyserial
typing-extensions~=4.0