diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 00000000..e926f443 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1 @@ +recursive-include dali py.typed diff --git a/dali/address.py b/dali/address.py index c064b161..9fec190e 100644 --- a/dali/address.py +++ b/dali/address.py @@ -20,6 +20,8 @@ 7.2.2. Decoding of event messages is currently not implemented. """ +from __future__ import annotations + from dali.exceptions import IncompatibleFrame @@ -261,7 +263,7 @@ class GearShort(GearAddress): address. """ - def __init__(self, address): + def __init__(self, address: int) -> None: if not isinstance(address, int): raise ValueError("address must be an integer") if address < 0 or address > 63: @@ -305,7 +307,7 @@ class DeviceShort(DeviceAddress): address. """ - def __init__(self, address: int): + def __init__(self, address: int) -> None: if not isinstance(address, int): raise ValueError("address must be an integer") if address < 0 or address > 63: @@ -379,7 +381,7 @@ def __str__(self): class _AddressedInstance(Instance): _flags = None - def __init__(self, value): + def __init__(self, value: int) -> None: if not isinstance(value, int): raise ValueError("value must be an integer") if value < 0 or value > 31: diff --git a/dali/command.py b/dali/command.py index e4a49467..0d18b654 100644 --- a/dali/command.py +++ b/dali/command.py @@ -1,6 +1,9 @@ """Declaration of base types for dali commands and their responses.""" +from __future__ import annotations + from enum import IntEnum +from typing import Any from dali import address from dali import frame from dali.exceptions import MissingResponse @@ -46,17 +49,17 @@ class Response: _expected = False _error_acceptable = False - def __init__(self, val): + def __init__(self, val: frame.BackwardFrame | None) -> None: if val is not None and not isinstance(val, frame.BackwardFrame): raise TypeError("Response must be passed None or a BackwardFrame") self._value = val @property - def raw_value(self): + def raw_value(self) -> frame.BackwardFrame | None: return self._value @property - def value(self): + def value(self) -> Any: if self._value is None and self._expected: raise MissingResponse() if self._value and self._value.error and not self._error_acceptable: diff --git a/dali/device/general.py b/dali/device/general.py index 24ac2f54..d11f3bb7 100644 --- a/dali/device/general.py +++ b/dali/device/general.py @@ -128,7 +128,7 @@ class _StandardDeviceCommand(_DeviceCommand): """ _opcode = None - def __init__(self, device): + def __init__(self, device: address.Address | int) -> None: if self._opcode is None: raise NotImplementedError @@ -514,7 +514,9 @@ class _StandardInstanceCommand(_DeviceCommand): _opcode = None - def __init__(self, device, instance): + def __init__( + self, device: address.Address | int, instance: address.Instance + ) -> None: if self._opcode is None: raise NotImplementedError @@ -784,7 +786,7 @@ def __str__(self): class _SpecialDeviceCommandOneParam(_SpecialDeviceCommand): - def __init__(self, param): + def __init__(self, param: int) -> None: if not isinstance(param, int): raise ValueError("parameter must be an integer") if param < 0 or param > 255: diff --git a/dali/device/helpers.py b/dali/device/helpers.py index 5f736448..cdc3a8e8 100644 --- a/dali/device/helpers.py +++ b/dali/device/helpers.py @@ -69,19 +69,21 @@ class DeviceInstanceTypeMapper: through the `from_frame()` method. """ - def __init__(self, initial=None): + def __init__( + self, initial: Optional[dict[tuple[int, int], int]] = None + ) -> None: """ Creates a new DeviceInstanceTypeMapper object, optionally with preloaded mappings as defined by `initial`. :param initial: A dict of data to preload into the mapping """ - self._mapping: dict[(int, int), int] = {} + self._mapping: dict[tuple[int, int], int] = {} if initial: self._mapping = initial @property - def mapping(self) -> dict[(int, int), int]: + def mapping(self) -> dict[tuple[int, int], int]: return self._mapping def autodiscover( @@ -89,7 +91,7 @@ def autodiscover( addresses: int | tuple[int, int] | Iterable[int] = (0, 63), quiescent_settle: float = 0.2, status_attempts: int = 3, - ) -> Generator[Command, Response, None]: + ) -> Generator[Command, Optional[Response], None]: """ A generator sequence to scan a DALI bus for control device instances, and query their types. This information is stored within this diff --git a/dali/device/occupancy.py b/dali/device/occupancy.py index 8c70b809..45057f35 100644 --- a/dali/device/occupancy.py +++ b/dali/device/occupancy.py @@ -80,7 +80,7 @@ def from_event_data(cls, event_data: int): return OccupancyEvent @property - def event_data(self): + def event_data(self) -> EventData: return self._extra_data def _set_event_data( diff --git a/dali/device/py.typed b/dali/device/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/dali/driver/py.typed b/dali/driver/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/dali/exceptions.py b/dali/exceptions.py index fd06e535..86ca0670 100644 --- a/dali/exceptions.py +++ b/dali/exceptions.py @@ -1,3 +1,5 @@ +from __future__ import annotations + ############################################################################### # general ############################################################################### @@ -50,7 +52,7 @@ class DALISequenceError(DALIError): class ProgramShortAddressFailure(DALIError): """A device did not accept programming of its short address.""" - def __init__(self, address): + def __init__(self, address: int) -> None: self.address = address @@ -125,7 +127,7 @@ class TransmissionError(DriverError): ``error_code`` is the interface-specific reason for the rejection. """ - def __init__(self, error_code): + def __init__(self, error_code: int) -> None: self.error_code = error_code super().__init__( f"Frame transmission rejected, error code {error_code}" diff --git a/dali/gear/general.py b/dali/gear/general.py index 99a728c3..d5467b93 100644 --- a/dali/gear/general.py +++ b/dali/gear/general.py @@ -7,6 +7,8 @@ or DTR0, the abbreviation has been kept in capitals. """ +from __future__ import annotations + from dali import command from dali import address from dali import frame @@ -61,7 +63,7 @@ class _StandardCommand(_GearCommand): _cmdval = None _hasparam = False - def __init__(self, destination, *args): + def __init__(self, destination: address.Address | int, *args: int) -> None: if self._cmdval is None: raise NotImplementedError @@ -155,7 +157,7 @@ class DAPC(_GearCommand): be respected. """ - def __init__(self, destination, power): + def __init__(self, destination: address.Address | int, power: int | str) -> None: if power == "OFF": power = 0 @@ -1003,7 +1005,7 @@ class _SpecialCommand(_GearCommand): _hasparam = False _cmdval = None - def __init__(self, *args): + def __init__(self, *args: int) -> None: if self._hasparam: if len(args) != 1: raise TypeError( diff --git a/dali/gear/py.typed b/dali/gear/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/dali/gear/sequences.py b/dali/gear/sequences.py index 4baf8a6a..5f003d6b 100644 --- a/dali/gear/sequences.py +++ b/dali/gear/sequences.py @@ -36,7 +36,7 @@ def SetDT8ColourValueTc( - address: GearAddress, + address: GearAddress | int, tc_mired: int, ) -> Generator[command.Command, Optional[command.Response], None]: """ diff --git a/dali/memory/location.py b/dali/memory/location.py index d990009e..41fe98a6 100644 --- a/dali/memory/location.py +++ b/dali/memory/location.py @@ -1,9 +1,11 @@ from __future__ import annotations from collections import namedtuple +from collections.abc import Generator from enum import Enum, auto +from typing import Any, Optional -from dali import device, gear +from dali import command, device, gear from dali.address import Address, DeviceAddress, DeviceShort, GearAddress, GearShort from dali.exceptions import ( LatchingNotSupported, @@ -165,7 +167,11 @@ def last_address(self, addr): la = yield from self.LastAddress.read(addr) return la - def read_all(self, addr: Address, use_latch: bool = True): + def read_all( + self, addr: Address, use_latch: bool = True + ) -> Generator[ + command.Command, Optional[command.Response], dict[type[MemoryValue], Any] + ]: """Read all available memory values from this memory bank. If the memory bank has a latch, the latch is set during the diff --git a/dali/memory/py.typed b/dali/memory/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/dali/py.typed b/dali/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/dali/sequences.py b/dali/sequences.py index ff444bb0..5aa3080c 100644 --- a/dali/sequences.py +++ b/dali/sequences.py @@ -18,10 +18,16 @@ # Sequences may raise exceptions, which the driver should pass to the # caller. +from __future__ import annotations + +from collections.abc import Iterable +from typing import Generator, Optional + +from dali import command from dali.exceptions import DALISequenceError, ProgramShortAddressFailure from dali.gear.general import * -from dali.address import Broadcast, Short +from dali.address import Broadcast, GearAddress, Short class sleep: @@ -30,7 +36,7 @@ class sleep: Yielded during a sequence to request that the caller wait for at least the specified length of time in seconds """ - def __init__(self, delay): + def __init__(self, delay: float) -> None: self.delay = delay @@ -42,7 +48,12 @@ class progress: both. The amount of progress is just an indication and there is no guarantee that it will not decrease as well as increase. """ - def __init__(self, message=None, completed=None, size=None): + def __init__( + self, + message: str | None = None, + completed: int | None = None, + size: int | None = None, + ) -> None: self.message = message self.completed = completed self.size = size @@ -54,7 +65,9 @@ def __str__(self): return f"Progress: {self.completed}/{self.size}" -def QueryDeviceTypes(addr): +def QueryDeviceTypes( + addr: GearAddress, +) -> Generator[command.Command, Optional[command.Response], list[int]]: """Obtain a list of part 2xx device types supported by control gear """ r = yield QueryDeviceType(addr) @@ -147,8 +160,11 @@ def _find_next(low, high): return (yield from _find_next(midpoint + 1, high)) -def Commissioning(available_addresses=None, readdress=False, - dry_run=False): +def Commissioning( + available_addresses: Iterable[int] | None = None, + readdress: bool = False, + dry_run: bool = False, +) -> Generator[command.Command, Optional[command.Response], None]: """Assign short addresses to control gear If available_addresses is passed, only the specified addresses diff --git a/setup.cfg b/setup.cfg index 33778122..08368d18 100644 --- a/setup.cfg +++ b/setup.cfg @@ -19,6 +19,7 @@ classifiers = [options] python_requires = >=3.10 +include_package_data = True packages = dali dali.device